feat: 사주 웹사이트 핵심 기능 구현
- 사주팔자 계산 라이브러리 구현 (천간, 지지, 십성, 십이운성) - 사주 결과 페이지 구현 (사주팔자 표 및 해석) - 오늘의 운세 페이지 구현 (일일 운세, 행운의 요소) - 궁합 보기 기능 구현 (두 사람 입력 폼) - 궁합 결과 페이지 구현 (종합 점수, 세부 분석) - 페이지 간 네비게이션 연결 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
100
app/compatibility/page.tsx
Normal file
100
app/compatibility/page.tsx
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import CompatibilityForm from '../components/CompatibilityForm';
|
||||||
|
|
||||||
|
export default function CompatibilityPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-pink-50 via-purple-50 to-indigo-50">
|
||||||
|
{/* Navigation */}
|
||||||
|
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="flex justify-between items-center h-16">
|
||||||
|
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="text-gray-700 hover:text-pink-600 transition font-medium"
|
||||||
|
>
|
||||||
|
처음으로
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Hero Section */}
|
||||||
|
<section className="pt-20 pb-32 px-4">
|
||||||
|
<div className="max-w-4xl mx-auto text-center mb-12">
|
||||||
|
<div className="inline-block mb-6 px-6 py-2 bg-white/50 backdrop-blur-sm rounded-full text-pink-700 font-semibold border border-pink-200">
|
||||||
|
두 사람의 궁합을 확인해보세요
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 className="text-5xl md:text-7xl font-bold text-gray-900 mb-6 leading-tight">
|
||||||
|
💕 <span className="bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent">궁합</span> 보기
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p className="text-xl text-gray-600 mb-12 max-w-2xl mx-auto">
|
||||||
|
사주팔자를 비교하여 두 사람의 궁합을 확인할 수 있습니다.
|
||||||
|
연애, 결혼, 사업 등 다양한 관계의 궁합을 알아보세요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Input Form */}
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
<CompatibilityForm />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 궁합 정보 */}
|
||||||
|
<section className="py-20 px-4 bg-white">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<h2 className="text-4xl font-bold text-gray-900 mb-4">궁합이란?</h2>
|
||||||
|
<p className="text-xl text-gray-600">사주팔자를 통해 알아보는 두 사람의 인연</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-3 gap-8">
|
||||||
|
<div className="text-center p-6 rounded-2xl hover:bg-pink-50 transition">
|
||||||
|
<div className="text-5xl mb-4">💑</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-3">연애 궁합</h3>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
두 사람의 사랑이 얼마나 잘 맞는지 확인하고 서로를 이해하는 방법을 알아보세요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center p-6 rounded-2xl hover:bg-purple-50 transition">
|
||||||
|
<div className="text-5xl mb-4">💍</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-3">결혼 궁합</h3>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
평생을 함께할 배우자와의 궁합을 확인하고 행복한 결혼 생활을 준비하세요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center p-6 rounded-2xl hover:bg-indigo-50 transition">
|
||||||
|
<div className="text-5xl mb-4">🤝</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-3">사업 궁합</h3>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
사업 파트너와의 궁합을 확인하고 성공적인 협력 관계를 만들어보세요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<footer className="bg-gray-900 text-white py-12 px-4">
|
||||||
|
<div className="max-w-7xl mx-auto text-center">
|
||||||
|
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-pink-400 to-purple-400 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 mb-6">
|
||||||
|
쟁승메이드가 제공하는 무료 사주 서비스
|
||||||
|
</p>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-pink-400">쟁승메이드</a></p>
|
||||||
|
<p className="mt-2">© 2025 쟁승메이드. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
395
app/compatibility/result/page.tsx
Normal file
395
app/compatibility/result/page.tsx
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { calculateSaju, FIVE_ELEMENTS } from '@/lib/saju-calculator';
|
||||||
|
|
||||||
|
interface PageProps {
|
||||||
|
searchParams: Promise<{
|
||||||
|
year1: string;
|
||||||
|
month1: string;
|
||||||
|
day1: string;
|
||||||
|
hour1?: string;
|
||||||
|
gender1: 'male' | 'female';
|
||||||
|
year2: string;
|
||||||
|
month2: string;
|
||||||
|
day2: string;
|
||||||
|
hour2?: string;
|
||||||
|
gender2: 'male' | 'female';
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function CompatibilityResultPage({ searchParams }: PageProps) {
|
||||||
|
const params = await searchParams;
|
||||||
|
const { year1, month1, day1, hour1, gender1, year2, month2, day2, hour2, gender2 } = params;
|
||||||
|
|
||||||
|
// Person 1 Saju
|
||||||
|
const saju1 = calculateSaju(
|
||||||
|
parseInt(year1),
|
||||||
|
parseInt(month1),
|
||||||
|
parseInt(day1),
|
||||||
|
hour1 ? parseInt(hour1) : null,
|
||||||
|
gender1
|
||||||
|
);
|
||||||
|
|
||||||
|
// Person 2 Saju
|
||||||
|
const saju2 = calculateSaju(
|
||||||
|
parseInt(year2),
|
||||||
|
parseInt(month2),
|
||||||
|
parseInt(day2),
|
||||||
|
hour2 ? parseInt(hour2) : null,
|
||||||
|
gender2
|
||||||
|
);
|
||||||
|
|
||||||
|
// 궁합 점수 계산
|
||||||
|
const calculateCompatibility = () => {
|
||||||
|
let score = 50; // 기본 점수
|
||||||
|
|
||||||
|
// 오행 상생/상극 관계
|
||||||
|
const element1 = saju1.day.element;
|
||||||
|
const element2 = saju2.day.element;
|
||||||
|
|
||||||
|
const produceMap: { [key: string]: string } = {
|
||||||
|
'木': '火', '火': '土', '土': '金', '金': '水', '水': '木'
|
||||||
|
};
|
||||||
|
const overcomeMap: { [key: string]: string } = {
|
||||||
|
'木': '土', '火': '金', '土': '水', '金': '木', '水': '火'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 같은 오행: 보통
|
||||||
|
if (element1 === element2) {
|
||||||
|
score += 10;
|
||||||
|
}
|
||||||
|
// 상생 관계: 매우 좋음
|
||||||
|
else if (produceMap[element1] === element2 || produceMap[element2] === element1) {
|
||||||
|
score += 25;
|
||||||
|
}
|
||||||
|
// 상극 관계: 주의 필요
|
||||||
|
else if (overcomeMap[element1] === element2 || overcomeMap[element2] === element1) {
|
||||||
|
score -= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 지지 삼합/육합 관계 확인
|
||||||
|
const branch1 = saju1.day.branch;
|
||||||
|
const branch2 = saju2.day.branch;
|
||||||
|
|
||||||
|
// 육합 (六合) - 특별히 좋은 궁합
|
||||||
|
const sixHarmony: { [key: string]: string } = {
|
||||||
|
'子': '丑', '丑': '子',
|
||||||
|
'寅': '亥', '亥': '寅',
|
||||||
|
'卯': '戌', '戌': '卯',
|
||||||
|
'辰': '酉', '酉': '辰',
|
||||||
|
'巳': '申', '申': '巳',
|
||||||
|
'午': '未', '未': '午'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sixHarmony[branch1] === branch2) {
|
||||||
|
score += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 삼합 (三合) - 좋은 궁합
|
||||||
|
const threeHarmony = [
|
||||||
|
['申', '子', '辰'], // 수국
|
||||||
|
['寅', '午', '戌'], // 화국
|
||||||
|
['亥', '卯', '未'], // 목국
|
||||||
|
['巳', '酉', '丑'] // 금국
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const harmony of threeHarmony) {
|
||||||
|
if (harmony.includes(branch1) && harmony.includes(branch2)) {
|
||||||
|
score += 15;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 충 (沖) - 나쁜 궁합
|
||||||
|
const conflict: { [key: string]: string } = {
|
||||||
|
'子': '午', '午': '子',
|
||||||
|
'丑': '未', '未': '丑',
|
||||||
|
'寅': '申', '申': '寅',
|
||||||
|
'卯': '酉', '酉': '卯',
|
||||||
|
'辰': '戌', '戌': '辰',
|
||||||
|
'巳': '亥', '亥': '巳'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (conflict[branch1] === branch2) {
|
||||||
|
score -= 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(100, Math.max(0, score));
|
||||||
|
};
|
||||||
|
|
||||||
|
const compatibilityScore = calculateCompatibility();
|
||||||
|
|
||||||
|
const getScoreGrade = (score: number): string => {
|
||||||
|
if (score >= 90) return 'S';
|
||||||
|
if (score >= 80) return 'A';
|
||||||
|
if (score >= 70) return 'B';
|
||||||
|
if (score >= 60) return 'C';
|
||||||
|
return 'D';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScoreText = (score: number): string => {
|
||||||
|
if (score >= 90) return '천생연분';
|
||||||
|
if (score >= 80) return '매우 좋음';
|
||||||
|
if (score >= 70) return '좋음';
|
||||||
|
if (score >= 60) return '보통';
|
||||||
|
return '주의 필요';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScoreColor = (score: number): string => {
|
||||||
|
if (score >= 90) return 'from-pink-500 to-red-500';
|
||||||
|
if (score >= 80) return 'from-pink-400 to-purple-400';
|
||||||
|
if (score >= 70) return 'from-purple-400 to-indigo-400';
|
||||||
|
if (score >= 60) return 'from-indigo-400 to-blue-400';
|
||||||
|
return 'from-gray-400 to-gray-500';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 세부 궁합 점수
|
||||||
|
const detailedScores = [
|
||||||
|
{ name: '연애운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💑' },
|
||||||
|
{ name: '결혼운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💍' },
|
||||||
|
{ name: '금전운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💰' },
|
||||||
|
{ name: '사업운', score: compatibilityScore + Math.floor(Math.random() * 10 - 5), icon: '💼' },
|
||||||
|
].map(item => ({ ...item, score: Math.min(100, Math.max(0, item.score)) }));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-pink-50 via-purple-50 to-indigo-50">
|
||||||
|
{/* Navigation */}
|
||||||
|
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="flex justify-between items-center h-16">
|
||||||
|
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</Link>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Link
|
||||||
|
href="/compatibility"
|
||||||
|
className="text-gray-700 hover:text-pink-600 transition font-medium"
|
||||||
|
>
|
||||||
|
다시 보기
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="text-gray-700 hover:text-pink-600 transition font-medium"
|
||||||
|
>
|
||||||
|
처음으로
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Result Content */}
|
||||||
|
<div className="max-w-6xl mx-auto px-4 py-12">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
|
||||||
|
💕 궁합 결과
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600">
|
||||||
|
두 사람의 사주팔자를 비교한 결과입니다
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 두 사람 정보 */}
|
||||||
|
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||||
|
{/* Person 1 */}
|
||||||
|
<div className="bg-gradient-to-br from-pink-500 to-pink-600 rounded-2xl shadow-xl p-8 text-white">
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<div className="text-5xl mb-3">👤</div>
|
||||||
|
<h3 className="text-2xl font-bold">첫 번째 사람</h3>
|
||||||
|
<p className="text-pink-100 mt-2">
|
||||||
|
{saju1.birthDate.year}년 {saju1.birthDate.month}월 {saju1.birthDate.day}일
|
||||||
|
</p>
|
||||||
|
<p className="text-pink-100">
|
||||||
|
{gender1 === 'male' ? '남성' : '여성'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/20 backdrop-blur-sm rounded-xl p-4">
|
||||||
|
<p className="text-center mb-2 font-semibold">일간 (日干)</p>
|
||||||
|
<div className="text-center">
|
||||||
|
<span className="text-4xl font-bold">{saju1.day.stem}</span>
|
||||||
|
<span className="text-2xl ml-2">({saju1.day.stemKr})</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-center mt-2 text-pink-100">
|
||||||
|
{saju1.day.element} ({['木', '火', '土', '金', '水'].indexOf(saju1.day.element) >= 0
|
||||||
|
? ['목', '화', '토', '금', '수'][['木', '火', '土', '金', '水'].indexOf(saju1.day.element)]
|
||||||
|
: ''})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Person 2 */}
|
||||||
|
<div className="bg-gradient-to-br from-purple-500 to-purple-600 rounded-2xl shadow-xl p-8 text-white">
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<div className="text-5xl mb-3">👤</div>
|
||||||
|
<h3 className="text-2xl font-bold">두 번째 사람</h3>
|
||||||
|
<p className="text-purple-100 mt-2">
|
||||||
|
{saju2.birthDate.year}년 {saju2.birthDate.month}월 {saju2.birthDate.day}일
|
||||||
|
</p>
|
||||||
|
<p className="text-purple-100">
|
||||||
|
{gender2 === 'male' ? '남성' : '여성'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/20 backdrop-blur-sm rounded-xl p-4">
|
||||||
|
<p className="text-center mb-2 font-semibold">일간 (日干)</p>
|
||||||
|
<div className="text-center">
|
||||||
|
<span className="text-4xl font-bold">{saju2.day.stem}</span>
|
||||||
|
<span className="text-2xl ml-2">({saju2.day.stemKr})</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-center mt-2 text-purple-100">
|
||||||
|
{saju2.day.element} ({['木', '火', '土', '金', '水'].indexOf(saju2.day.element) >= 0
|
||||||
|
? ['목', '화', '토', '금', '수'][['木', '火', '土', '金', '水'].indexOf(saju2.day.element)]
|
||||||
|
: ''})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 총합 궁합 점수 */}
|
||||||
|
<div className={`bg-gradient-to-r ${getScoreColor(compatibilityScore)} rounded-3xl shadow-2xl p-12 mb-12 text-white`}>
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-3xl font-bold mb-6">종합 궁합 점수</h2>
|
||||||
|
<div className="flex items-center justify-center gap-8 mb-6">
|
||||||
|
<div className="text-8xl font-bold">{compatibilityScore}</div>
|
||||||
|
<div className="text-left">
|
||||||
|
<div className="text-6xl font-bold mb-2">{getScoreGrade(compatibilityScore)}</div>
|
||||||
|
<div className="text-2xl">{getScoreText(compatibilityScore)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="max-w-2xl mx-auto">
|
||||||
|
<div className="w-full bg-white/30 rounded-full h-4">
|
||||||
|
<div
|
||||||
|
className="bg-white h-4 rounded-full transition-all duration-1000"
|
||||||
|
style={{ width: `${compatibilityScore}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 세부 궁합 */}
|
||||||
|
<div className="bg-white rounded-3xl shadow-2xl p-8 md:p-12 mb-8">
|
||||||
|
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">세부 궁합</h2>
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{detailedScores.map((item) => (
|
||||||
|
<div key={item.name} className="bg-gray-50 rounded-xl p-6">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-3xl">{item.icon}</span>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900">{item.name}</h3>
|
||||||
|
</div>
|
||||||
|
<span className="text-2xl font-bold text-pink-600">{item.score}점</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-3">
|
||||||
|
<div
|
||||||
|
className="bg-gradient-to-r from-pink-500 to-purple-500 h-3 rounded-full transition-all duration-500"
|
||||||
|
style={{ width: `${item.score}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 궁합 해석 */}
|
||||||
|
<div className="grid md:grid-cols-2 gap-8 mb-8">
|
||||||
|
{/* 장점 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8">
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
|
||||||
|
<span className="text-3xl mr-3">✅</span>
|
||||||
|
두 사람의 장점
|
||||||
|
</h3>
|
||||||
|
<ul className="space-y-3 text-gray-700">
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-pink-600 mr-2">•</span>
|
||||||
|
<span>서로의 부족한 점을 잘 보완해줄 수 있습니다.</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-pink-600 mr-2">•</span>
|
||||||
|
<span>대화와 소통이 원활하게 이루어질 수 있습니다.</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-pink-600 mr-2">•</span>
|
||||||
|
<span>함께 있을 때 편안함을 느낄 수 있습니다.</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 주의점 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8">
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
|
||||||
|
<span className="text-3xl mr-3">⚠️</span>
|
||||||
|
주의할 점
|
||||||
|
</h3>
|
||||||
|
<ul className="space-y-3 text-gray-700">
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-purple-600 mr-2">•</span>
|
||||||
|
<span>서로의 가치관 차이를 이해하려는 노력이 필요합니다.</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-purple-600 mr-2">•</span>
|
||||||
|
<span>감정적인 대화보다는 이성적인 대화를 나누세요.</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-purple-600 mr-2">•</span>
|
||||||
|
<span>작은 문제도 소통으로 해결하려는 자세가 중요합니다.</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 조언 */}
|
||||||
|
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 rounded-2xl shadow-lg p-8 text-white mb-8">
|
||||||
|
<h3 className="text-2xl font-bold mb-4 text-center">💡 조언</h3>
|
||||||
|
<p className="text-lg leading-relaxed text-center">
|
||||||
|
궁합은 참고사항일 뿐입니다. 서로를 이해하고 존중하며 노력한다면
|
||||||
|
어떤 궁합이든 행복한 관계를 만들 수 있습니다.
|
||||||
|
사주는 가능성을 보여줄 뿐, 최종 결정은 두 사람의 마음과 노력에 달려있습니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 다른 메뉴 */}
|
||||||
|
<div className="grid md:grid-cols-3 gap-6">
|
||||||
|
<Link
|
||||||
|
href="/compatibility"
|
||||||
|
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">💕</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-2">다시 보기</h3>
|
||||||
|
<p className="text-gray-600 text-sm">다른 궁합 확인하기</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">📜</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-2">사주 보기</h3>
|
||||||
|
<p className="text-gray-600 text-sm">내 사주 확인하기</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<button className="bg-gradient-to-r from-pink-600 to-purple-600 text-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group">
|
||||||
|
<div className="text-4xl mb-3">📥</div>
|
||||||
|
<h3 className="text-xl font-bold mb-2">PDF 저장</h3>
|
||||||
|
<p className="text-sm opacity-90">궁합 결과 저장하기</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<footer className="bg-gray-900 text-white py-12 px-4 mt-20">
|
||||||
|
<div className="max-w-7xl mx-auto text-center">
|
||||||
|
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-pink-400 to-purple-400 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 mb-6">
|
||||||
|
쟁승메이드가 제공하는 무료 사주 서비스
|
||||||
|
</p>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-pink-400">쟁승메이드</a></p>
|
||||||
|
<p className="mt-2">© 2025 쟁승메이드. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
281
app/components/CompatibilityForm.tsx
Normal file
281
app/components/CompatibilityForm.tsx
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
export default function CompatibilityForm() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// Person 1
|
||||||
|
const [year1, setYear1] = useState('');
|
||||||
|
const [month1, setMonth1] = useState('');
|
||||||
|
const [day1, setDay1] = useState('');
|
||||||
|
const [hour1, setHour1] = useState('');
|
||||||
|
const [gender1, setGender1] = useState<'male' | 'female'>('male');
|
||||||
|
|
||||||
|
// Person 2
|
||||||
|
const [year2, setYear2] = useState('');
|
||||||
|
const [month2, setMonth2] = useState('');
|
||||||
|
const [day2, setDay2] = useState('');
|
||||||
|
const [hour2, setHour2] = useState('');
|
||||||
|
const [gender2, setGender2] = useState<'male' | 'female'>('female');
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!year1 || !month1 || !day1) {
|
||||||
|
alert('첫 번째 사람의 생년월일을 모두 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!year2 || !month2 || !day2) {
|
||||||
|
alert('두 번째 사람의 생년월일을 모두 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL 파라미터로 전달
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
year1,
|
||||||
|
month1,
|
||||||
|
day1,
|
||||||
|
gender1,
|
||||||
|
year2,
|
||||||
|
month2,
|
||||||
|
day2,
|
||||||
|
gender2,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hour1) params.append('hour1', hour1);
|
||||||
|
if (hour2) params.append('hour2', hour2);
|
||||||
|
|
||||||
|
router.push(`/compatibility/result?${params.toString()}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} className="bg-white rounded-3xl shadow-2xl p-8 md:p-12">
|
||||||
|
<div className="grid md:grid-cols-2 gap-12">
|
||||||
|
{/* Person 1 */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<h3 className="text-2xl font-bold text-pink-600 mb-2">👤 첫 번째 사람</h3>
|
||||||
|
<p className="text-sm text-gray-600">본인의 정보를 입력하세요</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 생년월일 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
생년월일
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="년"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
|
||||||
|
min="1900"
|
||||||
|
max="2100"
|
||||||
|
value={year1}
|
||||||
|
onChange={(e) => setYear1(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="월"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="12"
|
||||||
|
value={month1}
|
||||||
|
onChange={(e) => setMonth1(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="일"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="31"
|
||||||
|
value={day1}
|
||||||
|
onChange={(e) => setDay1(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 태어난 시간 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
태어난 시간 (선택)
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-pink-500 focus:outline-none transition"
|
||||||
|
value={hour1}
|
||||||
|
onChange={(e) => setHour1(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="">모름 / 선택 안함</option>
|
||||||
|
<option value="0">자시 (23:00-01:00)</option>
|
||||||
|
<option value="1">축시 (01:00-03:00)</option>
|
||||||
|
<option value="3">인시 (03:00-05:00)</option>
|
||||||
|
<option value="5">묘시 (05:00-07:00)</option>
|
||||||
|
<option value="7">진시 (07:00-09:00)</option>
|
||||||
|
<option value="9">사시 (09:00-11:00)</option>
|
||||||
|
<option value="11">오시 (11:00-13:00)</option>
|
||||||
|
<option value="13">미시 (13:00-15:00)</option>
|
||||||
|
<option value="15">신시 (15:00-17:00)</option>
|
||||||
|
<option value="17">유시 (17:00-19:00)</option>
|
||||||
|
<option value="19">술시 (19:00-21:00)</option>
|
||||||
|
<option value="21">해시 (21:00-23:00)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 성별 선택 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
성별
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setGender1('male')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
gender1 === 'male'
|
||||||
|
? 'bg-pink-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-pink-500 hover:text-pink-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
남성
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setGender1('female')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
gender1 === 'female'
|
||||||
|
? 'bg-pink-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-pink-500 hover:text-pink-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
여성
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Person 2 */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<h3 className="text-2xl font-bold text-purple-600 mb-2">👤 두 번째 사람</h3>
|
||||||
|
<p className="text-sm text-gray-600">상대방의 정보를 입력하세요</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 생년월일 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
생년월일
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="년"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
|
||||||
|
min="1900"
|
||||||
|
max="2100"
|
||||||
|
value={year2}
|
||||||
|
onChange={(e) => setYear2(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="월"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="12"
|
||||||
|
value={month2}
|
||||||
|
onChange={(e) => setMonth2(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="일"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="31"
|
||||||
|
value={day2}
|
||||||
|
onChange={(e) => setDay2(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 태어난 시간 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
태어난 시간 (선택)
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:outline-none transition"
|
||||||
|
value={hour2}
|
||||||
|
onChange={(e) => setHour2(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="">모름 / 선택 안함</option>
|
||||||
|
<option value="0">자시 (23:00-01:00)</option>
|
||||||
|
<option value="1">축시 (01:00-03:00)</option>
|
||||||
|
<option value="3">인시 (03:00-05:00)</option>
|
||||||
|
<option value="5">묘시 (05:00-07:00)</option>
|
||||||
|
<option value="7">진시 (07:00-09:00)</option>
|
||||||
|
<option value="9">사시 (09:00-11:00)</option>
|
||||||
|
<option value="11">오시 (11:00-13:00)</option>
|
||||||
|
<option value="13">미시 (13:00-15:00)</option>
|
||||||
|
<option value="15">신시 (15:00-17:00)</option>
|
||||||
|
<option value="17">유시 (17:00-19:00)</option>
|
||||||
|
<option value="19">술시 (19:00-21:00)</option>
|
||||||
|
<option value="21">해시 (21:00-23:00)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 성별 선택 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
성별
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setGender2('male')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
gender2 === 'male'
|
||||||
|
? 'bg-purple-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-purple-500 hover:text-purple-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
남성
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setGender2('female')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
gender2 === 'female'
|
||||||
|
? 'bg-purple-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-purple-500 hover:text-purple-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
여성
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 제출 버튼 */}
|
||||||
|
<div className="mt-8">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-gradient-to-r from-pink-600 to-purple-600 text-white py-4 rounded-xl text-lg font-bold hover:from-pink-700 hover:to-purple-700 transition shadow-lg hover:shadow-xl"
|
||||||
|
>
|
||||||
|
💕 궁합 확인하기 →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-gray-500 text-center mt-4">
|
||||||
|
* 태어난 시간을 정확히 아시면 더 정확한 궁합을 확인할 수 있습니다.
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
181
app/components/SajuForm.tsx
Normal file
181
app/components/SajuForm.tsx
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
export default function SajuForm() {
|
||||||
|
const router = useRouter();
|
||||||
|
const [year, setYear] = useState('');
|
||||||
|
const [month, setMonth] = useState('');
|
||||||
|
const [day, setDay] = useState('');
|
||||||
|
const [hour, setHour] = useState('');
|
||||||
|
const [calendarType, setCalendarType] = useState<'solar' | 'lunar'>('solar');
|
||||||
|
const [gender, setGender] = useState<'male' | 'female'>('male');
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!year || !month || !day) {
|
||||||
|
alert('생년월일을 모두 입력해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL 파라미터로 전달
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
gender,
|
||||||
|
calendarType
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hour) {
|
||||||
|
params.append('hour', hour);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push(`/result?${params.toString()}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
{/* 생년월일 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
생년월일
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="년 (예: 1990)"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
|
||||||
|
min="1900"
|
||||||
|
max="2100"
|
||||||
|
value={year}
|
||||||
|
onChange={(e) => setYear(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="월 (1-12)"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="12"
|
||||||
|
value={month}
|
||||||
|
onChange={(e) => setMonth(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="일 (1-31)"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="31"
|
||||||
|
value={day}
|
||||||
|
onChange={(e) => setDay(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 태어난 시간 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
태어난 시간 (선택)
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
|
||||||
|
value={hour}
|
||||||
|
onChange={(e) => setHour(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="">모름 / 시간 선택 안함</option>
|
||||||
|
<option value="0">자시 (子時) 23:00 - 01:00</option>
|
||||||
|
<option value="1">축시 (丑時) 01:00 - 03:00</option>
|
||||||
|
<option value="3">인시 (寅時) 03:00 - 05:00</option>
|
||||||
|
<option value="5">묘시 (卯時) 05:00 - 07:00</option>
|
||||||
|
<option value="7">진시 (辰時) 07:00 - 09:00</option>
|
||||||
|
<option value="9">사시 (巳時) 09:00 - 11:00</option>
|
||||||
|
<option value="11">오시 (午時) 11:00 - 13:00</option>
|
||||||
|
<option value="13">미시 (未時) 13:00 - 15:00</option>
|
||||||
|
<option value="15">신시 (申時) 15:00 - 17:00</option>
|
||||||
|
<option value="17">유시 (酉時) 17:00 - 19:00</option>
|
||||||
|
<option value="19">술시 (戌時) 19:00 - 21:00</option>
|
||||||
|
<option value="21">해시 (亥時) 21:00 - 23:00</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 양력/음력 선택 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
생일 구분
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setCalendarType('solar')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
calendarType === 'solar'
|
||||||
|
? 'bg-indigo-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
양력
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setCalendarType('lunar')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
calendarType === 'lunar'
|
||||||
|
? 'bg-indigo-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
음력
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 성별 선택 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
성별
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setGender('male')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
gender === 'male'
|
||||||
|
? 'bg-indigo-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
남성
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setGender('female')}
|
||||||
|
className={`px-6 py-3 rounded-xl font-semibold transition ${
|
||||||
|
gender === 'female'
|
||||||
|
? 'bg-indigo-600 text-white'
|
||||||
|
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-indigo-500 hover:text-indigo-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
여성
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 제출 버튼 */}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-gradient-to-r from-indigo-600 to-purple-600 text-white py-4 rounded-xl text-lg font-bold hover:from-indigo-700 hover:to-purple-700 transition shadow-lg hover:shadow-xl"
|
||||||
|
>
|
||||||
|
내 사주 보기 →
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className="text-sm text-gray-500 text-center">
|
||||||
|
* 태어난 시간을 정확히 아시면 더 정확한 사주를 확인할 수 있습니다.
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
268
app/fortune/page.tsx
Normal file
268
app/fortune/page.tsx
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { calculateSaju } from '@/lib/saju-calculator';
|
||||||
|
|
||||||
|
interface PageProps {
|
||||||
|
searchParams: Promise<{
|
||||||
|
year: string;
|
||||||
|
month: string;
|
||||||
|
day: string;
|
||||||
|
hour?: string;
|
||||||
|
gender: 'male' | 'female';
|
||||||
|
calendarType: 'solar' | 'lunar';
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function FortunePage({ searchParams }: PageProps) {
|
||||||
|
const params = await searchParams;
|
||||||
|
const { year, month, day, hour, gender } = params;
|
||||||
|
|
||||||
|
const yearNum = parseInt(year);
|
||||||
|
const monthNum = parseInt(month);
|
||||||
|
const dayNum = parseInt(day);
|
||||||
|
const hourNum = hour ? parseInt(hour) : null;
|
||||||
|
|
||||||
|
const sajuData = calculateSaju(yearNum, monthNum, dayNum, hourNum, gender);
|
||||||
|
|
||||||
|
// 오늘 날짜
|
||||||
|
const today = new Date();
|
||||||
|
const todayYear = today.getFullYear();
|
||||||
|
const todayMonth = today.getMonth() + 1;
|
||||||
|
const todayDay = today.getDate();
|
||||||
|
|
||||||
|
// 오늘의 간지 계산
|
||||||
|
const todayGanzi = calculateSaju(todayYear, todayMonth, todayDay, null, gender);
|
||||||
|
|
||||||
|
// 운세 점수 계산 (간단한 알고리즘)
|
||||||
|
const calculateFortuneScore = (category: string): number => {
|
||||||
|
const seed = sajuData.day.stem.charCodeAt(0) + todayGanzi.day.stem.charCodeAt(0) + category.charCodeAt(0);
|
||||||
|
return 60 + (seed % 40);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fortuneCategories = [
|
||||||
|
{ name: '종합운', icon: '⭐', score: calculateFortuneScore('종합'), color: 'indigo' },
|
||||||
|
{ name: '금전운', icon: '💰', score: calculateFortuneScore('금전'), color: 'green' },
|
||||||
|
{ name: '애정운', icon: '💕', score: calculateFortuneScore('애정'), color: 'pink' },
|
||||||
|
{ name: '건강운', icon: '🏥', score: calculateFortuneScore('건강'), color: 'red' },
|
||||||
|
{ name: '직장운', icon: '💼', score: calculateFortuneScore('직장'), color: 'blue' },
|
||||||
|
{ name: '학업운', icon: '📚', score: calculateFortuneScore('학업'), color: 'purple' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getScoreText = (score: number): string => {
|
||||||
|
if (score >= 90) return '최고';
|
||||||
|
if (score >= 80) return '좋음';
|
||||||
|
if (score >= 70) return '보통';
|
||||||
|
if (score >= 60) return '주의';
|
||||||
|
return '나쁨';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getScoreColor = (score: number): string => {
|
||||||
|
if (score >= 90) return 'text-green-600';
|
||||||
|
if (score >= 80) return 'text-blue-600';
|
||||||
|
if (score >= 70) return 'text-yellow-600';
|
||||||
|
if (score >= 60) return 'text-orange-600';
|
||||||
|
return 'text-red-600';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 행운의 방향과 색깔 (일간 기준)
|
||||||
|
const getLuckyDirection = (stem: string): string => {
|
||||||
|
const directions: { [key: string]: string } = {
|
||||||
|
'甲': '동쪽', '乙': '동쪽',
|
||||||
|
'丙': '남쪽', '丁': '남쪽',
|
||||||
|
'戊': '중앙', '己': '중앙',
|
||||||
|
'庚': '서쪽', '辛': '서쪽',
|
||||||
|
'壬': '북쪽', '癸': '북쪽',
|
||||||
|
};
|
||||||
|
return directions[stem] || '동쪽';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLuckyColor = (element: string): string => {
|
||||||
|
const colors: { [key: string]: string } = {
|
||||||
|
'木': '녹색',
|
||||||
|
'火': '빨강색',
|
||||||
|
'土': '노란색',
|
||||||
|
'金': '흰색',
|
||||||
|
'水': '검정색',
|
||||||
|
};
|
||||||
|
return colors[element] || '녹색';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLuckyNumber = (stem: string): number => {
|
||||||
|
return (stem.charCodeAt(0) % 9) + 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
|
||||||
|
{/* Navigation */}
|
||||||
|
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="flex justify-between items-center h-16">
|
||||||
|
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</Link>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Link
|
||||||
|
href={`/result?${new URLSearchParams(params as any).toString()}`}
|
||||||
|
className="text-gray-700 hover:text-indigo-600 transition font-medium"
|
||||||
|
>
|
||||||
|
사주팔자
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="text-gray-700 hover:text-indigo-600 transition font-medium"
|
||||||
|
>
|
||||||
|
처음으로
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="max-w-6xl mx-auto px-4 py-12">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
|
||||||
|
오늘의 운세
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600">
|
||||||
|
{todayYear}년 {todayMonth}월 {todayDay}일 ({todayGanzi.day.stem}{todayGanzi.day.branch})
|
||||||
|
</p>
|
||||||
|
<p className="text-lg text-gray-500 mt-2">
|
||||||
|
{sajuData.birthDate.year}년생 {gender === 'male' ? '남성' : '여성'}의 오늘 운세
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 운세 점수 카드들 */}
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
|
||||||
|
{fortuneCategories.map((category) => (
|
||||||
|
<div key={category.name} className="bg-white rounded-2xl shadow-lg p-6 hover:shadow-xl transition">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-4xl">{category.icon}</span>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900">{category.name}</h3>
|
||||||
|
</div>
|
||||||
|
<span className={`text-2xl font-bold ${getScoreColor(category.score)}`}>
|
||||||
|
{category.score}점
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-3 mb-2">
|
||||||
|
<div
|
||||||
|
className={`bg-gradient-to-r from-${category.color}-400 to-${category.color}-600 h-3 rounded-full transition-all duration-500`}
|
||||||
|
style={{ width: `${category.score}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-600 text-right">{getScoreText(category.score)}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 오늘의 한마디 */}
|
||||||
|
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 rounded-3xl shadow-2xl p-8 md:p-12 mb-8 text-white">
|
||||||
|
<h2 className="text-3xl font-bold mb-6 text-center">💬 오늘의 한마디</h2>
|
||||||
|
<p className="text-xl leading-relaxed text-center">
|
||||||
|
{sajuData.day.element === '木' && '나무가 자라듯 꾸준히 노력하면 좋은 결과가 있을 것입니다. 새로운 시작에 좋은 날입니다.'}
|
||||||
|
{sajuData.day.element === '火' && '활발한 기운이 가득한 날입니다. 적극적으로 행동하면 좋은 기회를 잡을 수 있습니다.'}
|
||||||
|
{sajuData.day.element === '土' && '안정적인 하루가 될 것입니다. 차근차근 계획을 세우고 실행하면 좋습니다.'}
|
||||||
|
{sajuData.day.element === '金' && '명확한 판단이 필요한 날입니다. 원칙을 지키며 행동하면 좋은 결과가 있을 것입니다.'}
|
||||||
|
{sajuData.day.element === '水' && '유연한 사고가 도움이 되는 날입니다. 주변 사람들과의 소통을 중요하게 여기세요.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 행운의 요소들 */}
|
||||||
|
<div className="grid md:grid-cols-3 gap-8 mb-8">
|
||||||
|
{/* 행운의 방향 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8 text-center">
|
||||||
|
<div className="text-5xl mb-4">🧭</div>
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-3">행운의 방향</h3>
|
||||||
|
<p className="text-3xl font-bold text-indigo-600">{getLuckyDirection(sajuData.day.stem)}</p>
|
||||||
|
<p className="text-sm text-gray-600 mt-2">이 방향으로 외출하면 좋습니다</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 행운의 색깔 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8 text-center">
|
||||||
|
<div className="text-5xl mb-4">🎨</div>
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-3">행운의 색깔</h3>
|
||||||
|
<p className="text-3xl font-bold text-purple-600">{getLuckyColor(sajuData.day.element)}</p>
|
||||||
|
<p className="text-sm text-gray-600 mt-2">이 색깔의 옷을 입으면 좋습니다</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 행운의 숫자 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8 text-center">
|
||||||
|
<div className="text-5xl mb-4">🎲</div>
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-3">행운의 숫자</h3>
|
||||||
|
<p className="text-3xl font-bold text-pink-600">{getLuckyNumber(sajuData.day.stem)}</p>
|
||||||
|
<p className="text-sm text-gray-600 mt-2">오늘의 행운을 가져다 줄 숫자</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 주의사항 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8 mb-8">
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center">
|
||||||
|
<span className="text-3xl mr-3">⚠️</span>
|
||||||
|
오늘 주의할 점
|
||||||
|
</h3>
|
||||||
|
<ul className="space-y-3 text-gray-700">
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-indigo-600 mr-2">•</span>
|
||||||
|
<span>중요한 결정은 오전보다 오후에 하는 것이 좋습니다.</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-indigo-600 mr-2">•</span>
|
||||||
|
<span>감정적인 대화는 피하고 침착하게 대응하세요.</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start">
|
||||||
|
<span className="text-indigo-600 mr-2">•</span>
|
||||||
|
<span>건강 관리에 신경 쓰고 무리하지 마세요.</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 다른 메뉴 */}
|
||||||
|
<div className="grid md:grid-cols-3 gap-6">
|
||||||
|
<Link
|
||||||
|
href={`/result?${new URLSearchParams(params as any).toString()}`}
|
||||||
|
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">📜</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-2">사주팔자</h3>
|
||||||
|
<p className="text-gray-600 text-sm">내 사주 다시 보기</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">💕</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-2">궁합 보기</h3>
|
||||||
|
<p className="text-gray-600 text-sm">두 사람의 궁합 확인</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">🎋</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-2">토정비결</h3>
|
||||||
|
<p className="text-gray-600 text-sm">한 해의 운세 보기</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<footer className="bg-gray-900 text-white py-12 px-4 mt-20">
|
||||||
|
<div className="max-w-7xl mx-auto text-center">
|
||||||
|
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 mb-6">
|
||||||
|
쟁승메이드가 제공하는 무료 사주 서비스
|
||||||
|
</p>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-indigo-400">쟁승메이드</a></p>
|
||||||
|
<p className="mt-2">© 2025 쟁승메이드. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
257
app/page.tsx
257
app/page.tsx
@@ -1,65 +1,214 @@
|
|||||||
import Image from "next/image";
|
import Link from 'next/link';
|
||||||
|
import SajuForm from './components/SajuForm';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
|
||||||
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
{/* Navigation */}
|
||||||
<Image
|
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
|
||||||
className="dark:invert"
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
src="/next.svg"
|
<div className="flex justify-between items-center h-16">
|
||||||
alt="Next.js logo"
|
<div className="text-2xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
|
||||||
width={100}
|
🔮 사주보기
|
||||||
height={20}
|
</div>
|
||||||
priority
|
<div className="hidden md:flex space-x-6">
|
||||||
/>
|
<Link href="#saju" className="text-gray-700 hover:text-indigo-600 transition font-medium">사주팔자</Link>
|
||||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
<Link href="/compatibility" className="text-gray-700 hover:text-indigo-600 transition font-medium">궁합</Link>
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
</div>
|
||||||
To get started, edit the page.tsx file.
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Hero Section */}
|
||||||
|
<section className="pt-20 pb-32 px-4">
|
||||||
|
<div className="max-w-4xl mx-auto text-center">
|
||||||
|
<div className="inline-block mb-6 px-6 py-2 bg-white/50 backdrop-blur-sm rounded-full text-indigo-700 font-semibold border border-indigo-200">
|
||||||
|
무료로 내 사주를 확인해보세요
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 className="text-5xl md:text-7xl font-bold text-gray-900 mb-6 leading-tight">
|
||||||
|
나의 <span className="bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">사주팔자</span>를<br />
|
||||||
|
확인하세요
|
||||||
</h1>
|
</h1>
|
||||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
<p className="text-xl text-gray-600 mb-12 max-w-2xl mx-auto">
|
||||||
<a
|
생년월일시를 입력하면 무료로 사주팔자, 운세, 궁합을 확인할 수 있습니다.
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
쉽고 빠르게 나의 운명을 알아보세요.
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
|
||||||
Templates
|
|
||||||
</a>{" "}
|
|
||||||
or the{" "}
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
|
||||||
Learning
|
|
||||||
</a>{" "}
|
|
||||||
center.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* Main Input Card */}
|
||||||
|
<div className="bg-white rounded-3xl shadow-2xl p-8 md:p-12 max-w-2xl mx-auto">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-8">생년월일시 입력</h2>
|
||||||
|
|
||||||
|
<SajuForm />
|
||||||
|
{/* 생년월일 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
생년월일
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="년 (예: 1990)"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
|
||||||
|
min="1900"
|
||||||
|
max="2100"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="월 (1-12)"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="12"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="일 (1-31)"
|
||||||
|
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition"
|
||||||
|
min="1"
|
||||||
|
max="31"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 태어난 시간 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
태어난 시간 (선택)
|
||||||
|
</label>
|
||||||
|
<select className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-indigo-500 focus:outline-none transition">
|
||||||
|
<option value="">모름 / 시간 선택 안함</option>
|
||||||
|
<option value="23-01">자시 (子時) 23:00 - 01:00</option>
|
||||||
|
<option value="01-03">축시 (丑時) 01:00 - 03:00</option>
|
||||||
|
<option value="03-05">인시 (寅時) 03:00 - 05:00</option>
|
||||||
|
<option value="05-07">묘시 (卯時) 05:00 - 07:00</option>
|
||||||
|
<option value="07-09">진시 (辰時) 07:00 - 09:00</option>
|
||||||
|
<option value="09-11">사시 (巳時) 09:00 - 11:00</option>
|
||||||
|
<option value="11-13">오시 (午時) 11:00 - 13:00</option>
|
||||||
|
<option value="13-15">미시 (未時) 13:00 - 15:00</option>
|
||||||
|
<option value="15-17">신시 (申時) 15:00 - 17:00</option>
|
||||||
|
<option value="17-19">유시 (酉時) 17:00 - 19:00</option>
|
||||||
|
<option value="19-21">술시 (戌時) 19:00 - 21:00</option>
|
||||||
|
<option value="21-23">해시 (亥時) 21:00 - 23:00</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 양력/음력 선택 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
생일 구분
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="px-6 py-3 bg-indigo-600 text-white rounded-xl font-semibold hover:bg-indigo-700 transition"
|
||||||
|
>
|
||||||
|
양력
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="px-6 py-3 bg-white border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-indigo-500 hover:text-indigo-600 transition"
|
||||||
|
>
|
||||||
|
음력
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 성별 선택 */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
성별
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="px-6 py-3 bg-indigo-600 text-white rounded-xl font-semibold hover:bg-indigo-700 transition"
|
||||||
|
>
|
||||||
|
남성
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="px-6 py-3 bg-white border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-indigo-500 hover:text-indigo-600 transition"
|
||||||
|
>
|
||||||
|
여성
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 제출 버튼 */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
</section>
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
{/* Features Section */}
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
<section className="py-20 px-4 bg-white">
|
||||||
target="_blank"
|
<div className="max-w-7xl mx-auto">
|
||||||
rel="noopener noreferrer"
|
<div className="text-center mb-16">
|
||||||
>
|
<h2 className="text-4xl font-bold text-gray-900 mb-4">무엇을 확인할 수 있나요?</h2>
|
||||||
<Image
|
<p className="text-xl text-gray-600">다양한 사주 정보를 한 번에 확인하세요</p>
|
||||||
className="dark:invert"
|
</div>
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
width={16}
|
{/* Feature 1 */}
|
||||||
height={16}
|
<div className="text-center p-6 rounded-2xl hover:bg-indigo-50 transition">
|
||||||
/>
|
<div className="text-5xl mb-4">📜</div>
|
||||||
Deploy Now
|
<h3 className="text-xl font-bold text-gray-900 mb-3">사주팔자</h3>
|
||||||
</a>
|
<p className="text-gray-600">
|
||||||
<a
|
나의 천간, 지지, 십성, 십이운성을 확인하고 운명의 흐름을 파악하세요.
|
||||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
</p>
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
</div>
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
{/* Feature 2 */}
|
||||||
>
|
<div className="text-center p-6 rounded-2xl hover:bg-purple-50 transition">
|
||||||
Documentation
|
<div className="text-5xl mb-4">🌟</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-3">오늘의 운세</h3>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
사주를 확인한 후 오늘 하루의 운세를 확인하고 행운의 방향을 찾아보세요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Feature 3 */}
|
||||||
|
<Link href="/compatibility" className="text-center p-6 rounded-2xl hover:bg-pink-50 transition block">
|
||||||
|
<div className="text-5xl mb-4">💕</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-3">궁합</h3>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
두 사람의 사주를 비교하여 궁합을 확인하세요.
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA Section */}
|
||||||
|
<section className="py-20 px-4 bg-gradient-to-r from-indigo-600 to-purple-600">
|
||||||
|
<div className="max-w-4xl mx-auto text-center text-white">
|
||||||
|
<h2 className="text-4xl md:text-5xl font-bold mb-6">
|
||||||
|
지금 바로 내 사주를 확인해보세요
|
||||||
|
</h2>
|
||||||
|
<p className="text-xl mb-8 opacity-90">
|
||||||
|
무료로 제공되는 정확한 사주 정보로 나의 운명을 알아보세요
|
||||||
|
</p>
|
||||||
|
<a href="#" className="inline-block bg-white text-indigo-600 px-8 py-4 rounded-xl text-lg font-bold hover:bg-gray-100 transition shadow-lg">
|
||||||
|
무료로 시작하기
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</section>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<footer className="bg-gray-900 text-white py-12 px-4">
|
||||||
|
<div className="max-w-7xl mx-auto text-center">
|
||||||
|
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 mb-6">
|
||||||
|
쟁승메이드가 제공하는 무료 사주 서비스
|
||||||
|
</p>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-indigo-400">쟁승메이드</a></p>
|
||||||
|
<p className="mt-2">© 2025 쟁승메이드. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
260
app/result/page.tsx
Normal file
260
app/result/page.tsx
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
import { calculateSaju } from '@/lib/saju-calculator';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
interface PageProps {
|
||||||
|
searchParams: Promise<{
|
||||||
|
year: string;
|
||||||
|
month: string;
|
||||||
|
day: string;
|
||||||
|
hour?: string;
|
||||||
|
gender: 'male' | 'female';
|
||||||
|
calendarType: 'solar' | 'lunar';
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function ResultPage({ searchParams }: PageProps) {
|
||||||
|
const params = await searchParams;
|
||||||
|
const { year, month, day, hour, gender } = params;
|
||||||
|
|
||||||
|
const yearNum = parseInt(year);
|
||||||
|
const monthNum = parseInt(month);
|
||||||
|
const dayNum = parseInt(day);
|
||||||
|
const hourNum = hour ? parseInt(hour) : null;
|
||||||
|
|
||||||
|
const sajuData = calculateSaju(yearNum, monthNum, dayNum, hourNum, gender);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
|
||||||
|
{/* Navigation */}
|
||||||
|
<nav className="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="flex justify-between items-center h-16">
|
||||||
|
<Link href="/" className="text-2xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="text-gray-700 hover:text-indigo-600 transition font-medium"
|
||||||
|
>
|
||||||
|
다시 보기
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Result Content */}
|
||||||
|
<div className="max-w-6xl mx-auto px-4 py-12">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
|
||||||
|
내 사주팔자
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600">
|
||||||
|
{yearNum}년 {monthNum}월 {dayNum}일 {hourNum !== null && `${hourNum}시`}
|
||||||
|
{gender === 'male' ? ' 남성' : ' 여성'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 사주팔자 표 */}
|
||||||
|
<div className="bg-white rounded-3xl shadow-2xl p-8 md:p-12 mb-8">
|
||||||
|
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">사주팔자 (四柱八字)</h2>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-gradient-to-r from-indigo-600 to-purple-600 text-white">
|
||||||
|
<th className="py-4 px-6 text-center font-bold text-lg">구분</th>
|
||||||
|
{sajuData.hour && <th className="py-4 px-6 text-center font-bold text-lg">시주 (時柱)</th>}
|
||||||
|
<th className="py-4 px-6 text-center font-bold text-lg">일주 (日柱)</th>
|
||||||
|
<th className="py-4 px-6 text-center font-bold text-lg">월주 (月柱)</th>
|
||||||
|
<th className="py-4 px-6 text-center font-bold text-lg">년주 (年柱)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{/* 천간 */}
|
||||||
|
<tr className="border-b border-gray-200 hover:bg-indigo-50 transition">
|
||||||
|
<td className="py-4 px-6 text-center font-semibold text-gray-700">천간 (天干)</td>
|
||||||
|
{sajuData.hour && (
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-3xl font-bold text-indigo-600">{sajuData.hour.stem}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.hour.stemKr}</div>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
<td className="py-4 px-6 text-center bg-blue-50">
|
||||||
|
<div className="text-3xl font-bold text-blue-600">{sajuData.day.stem}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.day.stemKr}</div>
|
||||||
|
<div className="text-xs text-blue-600 font-semibold mt-1">일간 (日干)</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-3xl font-bold text-indigo-600">{sajuData.month.stem}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.month.stemKr}</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-3xl font-bold text-indigo-600">{sajuData.year.stem}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.year.stemKr}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* 지지 */}
|
||||||
|
<tr className="border-b border-gray-200 hover:bg-purple-50 transition">
|
||||||
|
<td className="py-4 px-6 text-center font-semibold text-gray-700">지지 (地支)</td>
|
||||||
|
{sajuData.hour && (
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-3xl font-bold text-purple-600">{sajuData.hour.branch}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.hour.branchKr}</div>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
<td className="py-4 px-6 text-center bg-blue-50">
|
||||||
|
<div className="text-3xl font-bold text-blue-600">{sajuData.day.branch}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.day.branchKr}</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-3xl font-bold text-purple-600">{sajuData.month.branch}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.month.branchKr}</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-3xl font-bold text-purple-600">{sajuData.year.branch}</div>
|
||||||
|
<div className="text-sm text-gray-600 mt-1">{sajuData.year.branchKr}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* 십성 */}
|
||||||
|
<tr className="border-b border-gray-200 hover:bg-emerald-50 transition">
|
||||||
|
<td className="py-4 px-6 text-center font-semibold text-gray-700">십성 (十星)</td>
|
||||||
|
{sajuData.hour && (
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-lg font-semibold text-emerald-600">{sajuData.hour.tenGod}</div>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
<td className="py-4 px-6 text-center bg-blue-50">
|
||||||
|
<div className="text-lg font-semibold text-blue-600">{sajuData.day.tenGod}</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-lg font-semibold text-emerald-600">{sajuData.month.tenGod}</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-lg font-semibold text-emerald-600">{sajuData.year.tenGod}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{/* 십이운성 */}
|
||||||
|
<tr className="hover:bg-pink-50 transition">
|
||||||
|
<td className="py-4 px-6 text-center font-semibold text-gray-700">십이운성</td>
|
||||||
|
{sajuData.hour && (
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-lg font-semibold text-pink-600">{sajuData.hour.fortune}</div>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
<td className="py-4 px-6 text-center bg-blue-50">
|
||||||
|
<div className="text-lg font-semibold text-blue-600">{sajuData.day.fortune}</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-lg font-semibold text-pink-600">{sajuData.month.fortune}</div>
|
||||||
|
</td>
|
||||||
|
<td className="py-4 px-6 text-center">
|
||||||
|
<div className="text-lg font-semibold text-pink-600">{sajuData.year.fortune}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 p-4 bg-blue-50 rounded-xl">
|
||||||
|
<p className="text-sm text-gray-700">
|
||||||
|
<strong className="text-blue-600">일간 (日干):</strong> {sajuData.day.stem}({sajuData.day.stemKr}) - 나 자신을 나타내는 중심 기둥입니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 사주 해석 */}
|
||||||
|
<div className="grid md:grid-cols-2 gap-8 mb-8">
|
||||||
|
{/* 성격 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8">
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
|
||||||
|
<span className="text-3xl mr-3">👤</span>
|
||||||
|
성격 특징
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-3 text-gray-700">
|
||||||
|
<p className="leading-relaxed">
|
||||||
|
일간이 <strong className="text-indigo-600">{sajuData.day.stem}({sajuData.day.stemKr})</strong>인 사람은
|
||||||
|
{sajuData.day.element === '木' && ' 나무처럼 성장하고 발전하려는 의지가 강합니다. 창의적이고 진취적인 성향을 가지고 있습니다.'}
|
||||||
|
{sajuData.day.element === '火' && ' 불처럼 열정적이고 활동적입니다. 리더십이 있고 사교성이 뛰어납니다.'}
|
||||||
|
{sajuData.day.element === '土' && ' 흙처럼 안정적이고 신뢰감 있습니다. 포용력이 있고 책임감이 강합니다.'}
|
||||||
|
{sajuData.day.element === '金' && ' 금속처럼 강인하고 원칙적입니다. 결단력 있고 의리를 중시합니다.'}
|
||||||
|
{sajuData.day.element === '水' && ' 물처럼 유연하고 지혜롭습니다. 적응력이 뛰어나고 사려 깊습니다.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 운세 */}
|
||||||
|
<div className="bg-white rounded-2xl shadow-lg p-8">
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-4 flex items-center">
|
||||||
|
<span className="text-3xl mr-3">🌟</span>
|
||||||
|
운세 흐름
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-3 text-gray-700">
|
||||||
|
<p className="leading-relaxed">
|
||||||
|
현재 십이운성이 <strong className="text-purple-600">{sajuData.day.fortune}</strong>으로,
|
||||||
|
{sajuData.day.fortune === '장생' && ' 새로운 시작과 성장의 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '목욕' && ' 정화와 준비의 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '관대' && ' 사회적으로 인정받는 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '건록' && ' 안정되고 왕성한 활동의 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '제왕' && ' 최고의 전성기를 맞이하는 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '쇠' && ' 조금씩 힘이 약해지는 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '병' && ' 어려움이 있을 수 있는 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '사' && ' 끝과 새 시작을 준비하는 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '묘' && ' 잠시 휴식이 필요한 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '절' && ' 극복과 인내가 필요한 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '태' && ' 새로운 기운이 싹트는 시기입니다.'}
|
||||||
|
{sajuData.day.fortune === '양' && ' 성장을 준비하는 시기입니다.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 추가 기능 버튼 */}
|
||||||
|
<div className="grid md:grid-cols-3 gap-6">
|
||||||
|
<Link
|
||||||
|
href={`/fortune?${new URLSearchParams(params as any).toString()}`}
|
||||||
|
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">🌟</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-2">오늘의 운세</h3>
|
||||||
|
<p className="text-gray-600 text-sm">오늘의 운을 확인하기</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="/compatibility"
|
||||||
|
className="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">💕</div>
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-2">궁합 보기</h3>
|
||||||
|
<p className="text-gray-600 text-sm">두 사람의 궁합 확인</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<button className="bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-xl p-6 shadow-lg hover:shadow-xl transition text-center group">
|
||||||
|
<div className="text-4xl mb-3">📥</div>
|
||||||
|
<h3 className="text-xl font-bold mb-2">PDF 저장</h3>
|
||||||
|
<p className="text-sm opacity-90">내 사주 저장하기</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<footer className="bg-gray-900 text-white py-12 px-4 mt-20">
|
||||||
|
<div className="max-w-7xl mx-auto text-center">
|
||||||
|
<div className="text-2xl font-bold mb-4 bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">
|
||||||
|
🔮 사주보기
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 mb-6">
|
||||||
|
쟁승메이드가 제공하는 무료 사주 서비스
|
||||||
|
</p>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
<p>문의: bgg8988@gmail.com | <a href="https://jaengseung-made.com" target="_blank" rel="noopener noreferrer" className="hover:text-indigo-400">쟁승메이드</a></p>
|
||||||
|
<p className="mt-2">© 2025 쟁승메이드. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
269
lib/saju-calculator.ts
Normal file
269
lib/saju-calculator.ts
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
// 천간 (天干) - 10개
|
||||||
|
export const HEAVENLY_STEMS = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'] as const;
|
||||||
|
export const HEAVENLY_STEMS_KR = ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계'] as const;
|
||||||
|
|
||||||
|
// 지지 (地支) - 12개
|
||||||
|
export const EARTHLY_BRANCHES = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'] as const;
|
||||||
|
export const EARTHLY_BRANCHES_KR = ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해'] as const;
|
||||||
|
|
||||||
|
// 오행 (五行)
|
||||||
|
export const FIVE_ELEMENTS = {
|
||||||
|
'甲': '木', '乙': '木',
|
||||||
|
'丙': '火', '丁': '火',
|
||||||
|
'戊': '土', '己': '土',
|
||||||
|
'庚': '金', '辛': '金',
|
||||||
|
'壬': '水', '癸': '水',
|
||||||
|
'寅': '木', '卯': '木',
|
||||||
|
'巳': '火', '午': '火',
|
||||||
|
'辰': '土', '戌': '土', '丑': '土', '未': '土',
|
||||||
|
'申': '金', '酉': '金',
|
||||||
|
'子': '水', '亥': '水',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const FIVE_ELEMENTS_KR = {
|
||||||
|
'木': '목', '火': '화', '土': '토', '金': '금', '水': '수'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 십성 (十星)
|
||||||
|
export const TEN_GODS = {
|
||||||
|
same: { yang: '비견', yin: '겁재' }, // 같은 오행
|
||||||
|
produce: { yang: '식신', yin: '상관' }, // 내가 생하는 오행
|
||||||
|
overcome: { yang: '편재', yin: '정재' }, // 내가 극하는 오행
|
||||||
|
overcome_me: { yang: '편관', yin: '정관' }, // 나를 극하는 오행
|
||||||
|
produce_me: { yang: '편인', yin: '정인' } // 나를 생하는 오행
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 십이운성 (十二運星)
|
||||||
|
export const TWELVE_FORTUNES = [
|
||||||
|
'장생', '목욕', '관대', '건록', '제왕', '쇠', '병', '사', '묘', '절', '태', '양'
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
// 간지 계산을 위한 기준일 (1900년 1월 1일 = 경자년 정축월 병인일)
|
||||||
|
const BASE_YEAR = 1900;
|
||||||
|
const BASE_YEAR_STEM = 6; // 庚
|
||||||
|
const BASE_YEAR_BRANCH = 0; // 子
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 년도의 간지를 계산
|
||||||
|
*/
|
||||||
|
export function getYearGanzi(year: number): { stem: string; branch: string; stemKr: string; branchKr: string } {
|
||||||
|
const yearDiff = year - BASE_YEAR;
|
||||||
|
const stemIndex = (BASE_YEAR_STEM + yearDiff) % 10;
|
||||||
|
const branchIndex = (BASE_YEAR_BRANCH + yearDiff) % 12;
|
||||||
|
|
||||||
|
return {
|
||||||
|
stem: HEAVENLY_STEMS[stemIndex],
|
||||||
|
branch: EARTHLY_BRANCHES[branchIndex],
|
||||||
|
stemKr: HEAVENLY_STEMS_KR[stemIndex],
|
||||||
|
branchKr: EARTHLY_BRANCHES_KR[branchIndex]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 월의 간지를 계산
|
||||||
|
*/
|
||||||
|
export function getMonthGanzi(year: number, month: number): { stem: string; branch: string; stemKr: string; branchKr: string } {
|
||||||
|
// 월 지지는 고정: 1월=인, 2월=묘, 3월=진, 4월=사, 5월=오, 6월=미...
|
||||||
|
// 단, 절기 기준이지만 간단히 월로 계산
|
||||||
|
const branchIndex = (month + 1) % 12; // 1월=인(2), 2월=묘(3)...
|
||||||
|
|
||||||
|
// 월 천간 계산 (년간에 따라 달라짐)
|
||||||
|
const yearStem = getYearGanzi(year).stem;
|
||||||
|
const yearStemIndex = HEAVENLY_STEMS.indexOf(yearStem as any);
|
||||||
|
|
||||||
|
// 월 천간 공식: (년간 * 2 + 월) % 10
|
||||||
|
const stemIndex = (yearStemIndex * 2 + month) % 10;
|
||||||
|
|
||||||
|
return {
|
||||||
|
stem: HEAVENLY_STEMS[stemIndex],
|
||||||
|
branch: EARTHLY_BRANCHES[branchIndex],
|
||||||
|
stemKr: HEAVENLY_STEMS_KR[stemIndex],
|
||||||
|
branchKr: EARTHLY_BRANCHES_KR[branchIndex]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 일의 간지를 계산 (만세력 기준)
|
||||||
|
*/
|
||||||
|
export function getDayGanzi(year: number, month: number, day: number): { stem: string; branch: string; stemKr: string; branchKr: string } {
|
||||||
|
// 기준일 (1900-01-01) 부터의 일수 계산
|
||||||
|
const baseDate = new Date(1900, 0, 1);
|
||||||
|
const targetDate = new Date(year, month - 1, day);
|
||||||
|
const daysDiff = Math.floor((targetDate.getTime() - baseDate.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
// 1900-01-01 = 丙寅일
|
||||||
|
const baseDayStem = 2; // 丙
|
||||||
|
const baseDayBranch = 2; // 寅
|
||||||
|
|
||||||
|
const stemIndex = (baseDayStem + daysDiff) % 10;
|
||||||
|
const branchIndex = (baseDayBranch + daysDiff) % 12;
|
||||||
|
|
||||||
|
return {
|
||||||
|
stem: HEAVENLY_STEMS[stemIndex < 0 ? stemIndex + 10 : stemIndex],
|
||||||
|
branch: EARTHLY_BRANCHES[branchIndex < 0 ? branchIndex + 12 : branchIndex],
|
||||||
|
stemKr: HEAVENLY_STEMS_KR[stemIndex < 0 ? stemIndex + 10 : stemIndex],
|
||||||
|
branchKr: EARTHLY_BRANCHES_KR[branchIndex < 0 ? branchIndex + 12 : branchIndex]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 시의 간지를 계산
|
||||||
|
*/
|
||||||
|
export function getHourGanzi(dayGanzi: { stem: string }, hour: number): { stem: string; branch: string; stemKr: string; branchKr: string } {
|
||||||
|
// 시 지지: 자시(23-01)=0, 축시(01-03)=1, ...
|
||||||
|
let branchIndex: number;
|
||||||
|
|
||||||
|
if (hour >= 23 || hour < 1) branchIndex = 0; // 子
|
||||||
|
else if (hour >= 1 && hour < 3) branchIndex = 1; // 丑
|
||||||
|
else if (hour >= 3 && hour < 5) branchIndex = 2; // 寅
|
||||||
|
else if (hour >= 5 && hour < 7) branchIndex = 3; // 卯
|
||||||
|
else if (hour >= 7 && hour < 9) branchIndex = 4; // 辰
|
||||||
|
else if (hour >= 9 && hour < 11) branchIndex = 5; // 巳
|
||||||
|
else if (hour >= 11 && hour < 13) branchIndex = 6; // 午
|
||||||
|
else if (hour >= 13 && hour < 15) branchIndex = 7; // 未
|
||||||
|
else if (hour >= 15 && hour < 17) branchIndex = 8; // 申
|
||||||
|
else if (hour >= 17 && hour < 19) branchIndex = 9; // 酉
|
||||||
|
else if (hour >= 19 && hour < 21) branchIndex = 10; // 戌
|
||||||
|
else branchIndex = 11; // 亥
|
||||||
|
|
||||||
|
// 시 천간 계산 (일간에 따라 달라짐)
|
||||||
|
const dayStemIndex = HEAVENLY_STEMS.indexOf(dayGanzi.stem as any);
|
||||||
|
const stemIndex = (dayStemIndex * 2 + branchIndex) % 10;
|
||||||
|
|
||||||
|
return {
|
||||||
|
stem: HEAVENLY_STEMS[stemIndex],
|
||||||
|
branch: EARTHLY_BRANCHES[branchIndex],
|
||||||
|
stemKr: HEAVENLY_STEMS_KR[stemIndex],
|
||||||
|
branchKr: EARTHLY_BRANCHES_KR[branchIndex]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 십성 계산
|
||||||
|
*/
|
||||||
|
export function getTenGod(dayStem: string, targetStem: string, isYang: boolean): string {
|
||||||
|
const dayElement = FIVE_ELEMENTS[dayStem as keyof typeof FIVE_ELEMENTS];
|
||||||
|
const targetElement = FIVE_ELEMENTS[targetStem as keyof typeof FIVE_ELEMENTS];
|
||||||
|
|
||||||
|
// 같은 오행
|
||||||
|
if (dayElement === targetElement) {
|
||||||
|
return isYang ? '비견' : '겁재';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 오행 상생/상극 관계 확인
|
||||||
|
const produceMap: { [key: string]: string } = {
|
||||||
|
'木': '火', '火': '土', '土': '金', '金': '水', '水': '木'
|
||||||
|
};
|
||||||
|
const overcomeMap: { [key: string]: string } = {
|
||||||
|
'木': '土', '火': '金', '土': '水', '金': '木', '水': '火'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 내가 생하는 오행
|
||||||
|
if (produceMap[dayElement] === targetElement) {
|
||||||
|
return isYang ? '식신' : '상관';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 내가 극하는 오행
|
||||||
|
if (overcomeMap[dayElement] === targetElement) {
|
||||||
|
return isYang ? '편재' : '정재';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 나를 극하는 오행
|
||||||
|
if (overcomeMap[targetElement] === dayElement) {
|
||||||
|
return isYang ? '편관' : '정관';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 나를 생하는 오행
|
||||||
|
if (produceMap[targetElement] === dayElement) {
|
||||||
|
return isYang ? '편인' : '정인';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '비견';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 십이운성 계산
|
||||||
|
*/
|
||||||
|
export function getTwelveFortune(dayStem: string, branch: string): string {
|
||||||
|
// 간단한 십이운성 계산 (실제로는 더 복잡함)
|
||||||
|
const fortuneMap: { [key: string]: { [key: string]: number } } = {
|
||||||
|
'甲': { '亥': 11, '子': 0, '丑': 1, '寅': 2, '卯': 3, '辰': 4, '巳': 5, '午': 6, '未': 7, '申': 8, '酉': 9, '戌': 10 },
|
||||||
|
'乙': { '午': 11, '未': 0, '申': 1, '酉': 2, '戌': 3, '亥': 4, '子': 5, '丑': 6, '寅': 7, '卯': 8, '辰': 9, '巳': 10 },
|
||||||
|
'丙': { '寅': 11, '卯': 0, '辰': 1, '巳': 2, '午': 3, '未': 4, '申': 5, '酉': 6, '戌': 7, '亥': 8, '子': 9, '丑': 10 },
|
||||||
|
'丁': { '酉': 11, '戌': 0, '亥': 1, '子': 2, '丑': 3, '寅': 4, '卯': 5, '辰': 6, '巳': 7, '午': 8, '未': 9, '申': 10 },
|
||||||
|
'戊': { '寅': 11, '卯': 0, '辰': 1, '巳': 2, '午': 3, '未': 4, '申': 5, '酉': 6, '戌': 7, '亥': 8, '子': 9, '丑': 10 },
|
||||||
|
'己': { '酉': 11, '戌': 0, '亥': 1, '子': 2, '丑': 3, '寅': 4, '卯': 5, '辰': 6, '巳': 7, '午': 8, '未': 9, '申': 10 },
|
||||||
|
'庚': { '巳': 11, '午': 0, '未': 1, '申': 2, '酉': 3, '戌': 4, '亥': 5, '子': 6, '丑': 7, '寅': 8, '卯': 9, '辰': 10 },
|
||||||
|
'辛': { '子': 11, '丑': 0, '寅': 1, '卯': 2, '辰': 3, '巳': 4, '午': 5, '未': 6, '申': 7, '酉': 8, '戌': 9, '亥': 10 },
|
||||||
|
'壬': { '申': 11, '酉': 0, '戌': 1, '亥': 2, '子': 3, '丑': 4, '寅': 5, '卯': 6, '辰': 7, '巳': 8, '午': 9, '未': 10 },
|
||||||
|
'癸': { '卯': 11, '辰': 0, '巳': 1, '午': 2, '未': 3, '申': 4, '酉': 5, '戌': 6, '亥': 7, '子': 8, '丑': 9, '寅': 10 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const index = fortuneMap[dayStem as keyof typeof fortuneMap]?.[branch as keyof typeof fortuneMap['甲']] ?? 0;
|
||||||
|
return TWELVE_FORTUNES[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 사주팔자 전체 계산
|
||||||
|
*/
|
||||||
|
export interface SajuData {
|
||||||
|
year: { stem: string; branch: string; stemKr: string; branchKr: string; element: string; tenGod: string; fortune: string };
|
||||||
|
month: { stem: string; branch: string; stemKr: string; branchKr: string; element: string; tenGod: string; fortune: string };
|
||||||
|
day: { stem: string; branch: string; stemKr: string; branchKr: string; element: string; tenGod: string; fortune: string };
|
||||||
|
hour?: { stem: string; branch: string; stemKr: string; branchKr: string; element: string; tenGod: string; fortune: string };
|
||||||
|
dayStem: string;
|
||||||
|
birthDate: { year: number; month: number; day: number; hour?: number };
|
||||||
|
gender: 'male' | 'female';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateSaju(
|
||||||
|
year: number,
|
||||||
|
month: number,
|
||||||
|
day: number,
|
||||||
|
hour: number | null,
|
||||||
|
gender: 'male' | 'female'
|
||||||
|
): SajuData {
|
||||||
|
const yearGanzi = getYearGanzi(year);
|
||||||
|
const monthGanzi = getMonthGanzi(year, month);
|
||||||
|
const dayGanzi = getDayGanzi(year, month, day);
|
||||||
|
const hourGanzi = hour !== null ? getHourGanzi(dayGanzi, hour) : null;
|
||||||
|
|
||||||
|
const dayStem = dayGanzi.stem;
|
||||||
|
const dayStemIndex = HEAVENLY_STEMS.indexOf(dayStem as any);
|
||||||
|
const isDayYang = dayStemIndex % 2 === 0;
|
||||||
|
|
||||||
|
const result: SajuData = {
|
||||||
|
year: {
|
||||||
|
...yearGanzi,
|
||||||
|
element: FIVE_ELEMENTS[yearGanzi.stem as keyof typeof FIVE_ELEMENTS],
|
||||||
|
tenGod: getTenGod(dayStem, yearGanzi.stem, HEAVENLY_STEMS.indexOf(yearGanzi.stem as any) % 2 === isDayYang ? true : false),
|
||||||
|
fortune: getTwelveFortune(dayStem, yearGanzi.branch)
|
||||||
|
},
|
||||||
|
month: {
|
||||||
|
...monthGanzi,
|
||||||
|
element: FIVE_ELEMENTS[monthGanzi.stem as keyof typeof FIVE_ELEMENTS],
|
||||||
|
tenGod: getTenGod(dayStem, monthGanzi.stem, HEAVENLY_STEMS.indexOf(monthGanzi.stem as any) % 2 === isDayYang ? true : false),
|
||||||
|
fortune: getTwelveFortune(dayStem, monthGanzi.branch)
|
||||||
|
},
|
||||||
|
day: {
|
||||||
|
...dayGanzi,
|
||||||
|
element: FIVE_ELEMENTS[dayGanzi.stem as keyof typeof FIVE_ELEMENTS],
|
||||||
|
tenGod: '일간',
|
||||||
|
fortune: getTwelveFortune(dayStem, dayGanzi.branch)
|
||||||
|
},
|
||||||
|
dayStem,
|
||||||
|
birthDate: { year, month, day, hour: hour ?? undefined },
|
||||||
|
gender
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hourGanzi) {
|
||||||
|
result.hour = {
|
||||||
|
...hourGanzi,
|
||||||
|
element: FIVE_ELEMENTS[hourGanzi.stem as keyof typeof FIVE_ELEMENTS],
|
||||||
|
tenGod: getTenGod(dayStem, hourGanzi.stem, HEAVENLY_STEMS.indexOf(hourGanzi.stem as any) % 2 === isDayYang ? true : false),
|
||||||
|
fortune: getTwelveFortune(dayStem, hourGanzi.branch)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user