feat: 사주 웹사이트 핵심 기능 구현

- 사주팔자 계산 라이브러리 구현 (천간, 지지, 십성, 십이운성)
- 사주 결과 페이지 구현 (사주팔자 표 및 해석)
- 오늘의 운세 페이지 구현 (일일 운세, 행운의 요소)
- 궁합 보기 기능 구현 (두 사람 입력 폼)
- 궁합 결과 페이지 구현 (종합 점수, 세부 분석)
- 페이지 간 네비게이션 연결

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 23:36:22 +09:00
parent f4dbacccad
commit 08d6f71fd1
8 changed files with 1957 additions and 54 deletions

100
app/compatibility/page.tsx Normal file
View 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">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

View 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">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

View 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
View 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
View 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">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -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">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div> </div>
); );
} }

260
app/result/page.tsx Normal file
View 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">&copy; 2025 . All rights reserved.</p>
</div>
</div>
</footer>
</div>
);
}

269
lib/saju-calculator.ts Normal file
View 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;
}