diff --git a/app/compatibility/page.tsx b/app/compatibility/page.tsx new file mode 100644 index 0000000..f8d3299 --- /dev/null +++ b/app/compatibility/page.tsx @@ -0,0 +1,100 @@ +import Link from 'next/link'; +import CompatibilityForm from '../components/CompatibilityForm'; + +export default function CompatibilityPage() { + return ( +
+ {/* Navigation */} + + + {/* Hero Section */} +
+
+
+ ๋‘ ์‚ฌ๋žŒ์˜ ๊ถํ•ฉ์„ ํ™•์ธํ•ด๋ณด์„ธ์š” +
+ +

+ ๐Ÿ’• ๊ถํ•ฉ ๋ณด๊ธฐ +

+ +

+ ์‚ฌ์ฃผํŒ”์ž๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋‘ ์‚ฌ๋žŒ์˜ ๊ถํ•ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + ์—ฐ์• , ๊ฒฐํ˜ผ, ์‚ฌ์—… ๋“ฑ ๋‹ค์–‘ํ•œ ๊ด€๊ณ„์˜ ๊ถํ•ฉ์„ ์•Œ์•„๋ณด์„ธ์š”. +

+
+ + {/* Input Form */} +
+ +
+
+ + {/* ๊ถํ•ฉ ์ •๋ณด */} +
+
+
+

๊ถํ•ฉ์ด๋ž€?

+

์‚ฌ์ฃผํŒ”์ž๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด๋Š” ๋‘ ์‚ฌ๋žŒ์˜ ์ธ์—ฐ

+
+ +
+
+
๐Ÿ’‘
+

์—ฐ์•  ๊ถํ•ฉ

+

+ ๋‘ ์‚ฌ๋žŒ์˜ ์‚ฌ๋ž‘์ด ์–ผ๋งˆ๋‚˜ ์ž˜ ๋งž๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์„œ๋กœ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์„ธ์š”. +

+
+ +
+
๐Ÿ’
+

๊ฒฐํ˜ผ ๊ถํ•ฉ

+

+ ํ‰์ƒ์„ ํ•จ๊ป˜ํ•  ๋ฐฐ์šฐ์ž์™€์˜ ๊ถํ•ฉ์„ ํ™•์ธํ•˜๊ณ  ํ–‰๋ณตํ•œ ๊ฒฐํ˜ผ ์ƒํ™œ์„ ์ค€๋น„ํ•˜์„ธ์š”. +

+
+ +
+
๐Ÿค
+

์‚ฌ์—… ๊ถํ•ฉ

+

+ ์‚ฌ์—… ํŒŒํŠธ๋„ˆ์™€์˜ ๊ถํ•ฉ์„ ํ™•์ธํ•˜๊ณ  ์„ฑ๊ณต์ ์ธ ํ˜‘๋ ฅ ๊ด€๊ณ„๋ฅผ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”. +

+
+
+
+
+ + {/* Footer */} + +
+ ); +} diff --git a/app/compatibility/result/page.tsx b/app/compatibility/result/page.tsx new file mode 100644 index 0000000..2b506fd --- /dev/null +++ b/app/compatibility/result/page.tsx @@ -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 ( +
+ {/* Navigation */} + + + {/* Result Content */} +
+ {/* Header */} +
+

+ ๐Ÿ’• ๊ถํ•ฉ ๊ฒฐ๊ณผ +

+

+ ๋‘ ์‚ฌ๋žŒ์˜ ์‚ฌ์ฃผํŒ”์ž๋ฅผ ๋น„๊ตํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค +

+
+ + {/* ๋‘ ์‚ฌ๋žŒ ์ •๋ณด */} +
+ {/* Person 1 */} +
+
+
๐Ÿ‘ค
+

์ฒซ ๋ฒˆ์งธ ์‚ฌ๋žŒ

+

+ {saju1.birthDate.year}๋…„ {saju1.birthDate.month}์›” {saju1.birthDate.day}์ผ +

+

+ {gender1 === 'male' ? '๋‚จ์„ฑ' : '์—ฌ์„ฑ'} +

+
+
+

์ผ๊ฐ„ (ๆ—ฅๅนฒ)

+
+ {saju1.day.stem} + ({saju1.day.stemKr}) +
+

+ {saju1.day.element} ({['ๆœจ', '็ซ', 'ๅœŸ', '้‡‘', 'ๆฐด'].indexOf(saju1.day.element) >= 0 + ? ['๋ชฉ', 'ํ™”', 'ํ† ', '๊ธˆ', '์ˆ˜'][['ๆœจ', '็ซ', 'ๅœŸ', '้‡‘', 'ๆฐด'].indexOf(saju1.day.element)] + : ''}) +

+
+
+ + {/* Person 2 */} +
+
+
๐Ÿ‘ค
+

๋‘ ๋ฒˆ์งธ ์‚ฌ๋žŒ

+

+ {saju2.birthDate.year}๋…„ {saju2.birthDate.month}์›” {saju2.birthDate.day}์ผ +

+

+ {gender2 === 'male' ? '๋‚จ์„ฑ' : '์—ฌ์„ฑ'} +

+
+
+

์ผ๊ฐ„ (ๆ—ฅๅนฒ)

+
+ {saju2.day.stem} + ({saju2.day.stemKr}) +
+

+ {saju2.day.element} ({['ๆœจ', '็ซ', 'ๅœŸ', '้‡‘', 'ๆฐด'].indexOf(saju2.day.element) >= 0 + ? ['๋ชฉ', 'ํ™”', 'ํ† ', '๊ธˆ', '์ˆ˜'][['ๆœจ', '็ซ', 'ๅœŸ', '้‡‘', 'ๆฐด'].indexOf(saju2.day.element)] + : ''}) +

+
+
+
+ + {/* ์ดํ•ฉ ๊ถํ•ฉ ์ ์ˆ˜ */} +
+
+

์ข…ํ•ฉ ๊ถํ•ฉ ์ ์ˆ˜

+
+
{compatibilityScore}
+
+
{getScoreGrade(compatibilityScore)}
+
{getScoreText(compatibilityScore)}
+
+
+
+
+
+
+
+
+
+ + {/* ์„ธ๋ถ€ ๊ถํ•ฉ */} +
+

์„ธ๋ถ€ ๊ถํ•ฉ

+
+ {detailedScores.map((item) => ( +
+
+
+ {item.icon} +

{item.name}

+
+ {item.score}์  +
+
+
+
+
+ ))} +
+
+ + {/* ๊ถํ•ฉ ํ•ด์„ */} +
+ {/* ์žฅ์  */} +
+

+ โœ… + ๋‘ ์‚ฌ๋žŒ์˜ ์žฅ์  +

+
    +
  • + โ€ข + ์„œ๋กœ์˜ ๋ถ€์กฑํ•œ ์ ์„ ์ž˜ ๋ณด์™„ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +
  • +
  • + โ€ข + ๋Œ€ํ™”์™€ ์†Œํ†ต์ด ์›ํ™œํ•˜๊ฒŒ ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +
  • +
  • + โ€ข + ํ•จ๊ป˜ ์žˆ์„ ๋•Œ ํŽธ์•ˆํ•จ์„ ๋А๋‚„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +
  • +
+
+ + {/* ์ฃผ์˜์  */} +
+

+ โš ๏ธ + ์ฃผ์˜ํ•  ์  +

+
    +
  • + โ€ข + ์„œ๋กœ์˜ ๊ฐ€์น˜๊ด€ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•˜๋ ค๋Š” ๋…ธ๋ ฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. +
  • +
  • + โ€ข + ๊ฐ์ •์ ์ธ ๋Œ€ํ™”๋ณด๋‹ค๋Š” ์ด์„ฑ์ ์ธ ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆ„์„ธ์š”. +
  • +
  • + โ€ข + ์ž‘์€ ๋ฌธ์ œ๋„ ์†Œํ†ต์œผ๋กœ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ์ž์„ธ๊ฐ€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. +
  • +
+
+
+ + {/* ์กฐ์–ธ */} +
+

๐Ÿ’ก ์กฐ์–ธ

+

+ ๊ถํ•ฉ์€ ์ฐธ๊ณ ์‚ฌํ•ญ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ์„œ๋กœ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์กด์ค‘ํ•˜๋ฉฐ ๋…ธ๋ ฅํ•œ๋‹ค๋ฉด + ์–ด๋–ค ๊ถํ•ฉ์ด๋“  ํ–‰๋ณตํ•œ ๊ด€๊ณ„๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + ์‚ฌ์ฃผ๋Š” ๊ฐ€๋Šฅ์„ฑ์„ ๋ณด์—ฌ์ค„ ๋ฟ, ์ตœ์ข… ๊ฒฐ์ •์€ ๋‘ ์‚ฌ๋žŒ์˜ ๋งˆ์Œ๊ณผ ๋…ธ๋ ฅ์— ๋‹ฌ๋ ค์žˆ์Šต๋‹ˆ๋‹ค. +

+
+ + {/* ๋‹ค๋ฅธ ๋ฉ”๋‰ด */} +
+ +
๐Ÿ’•
+

๋‹ค์‹œ ๋ณด๊ธฐ

+

๋‹ค๋ฅธ ๊ถํ•ฉ ํ™•์ธํ•˜๊ธฐ

+ + + +
๐Ÿ“œ
+

์‚ฌ์ฃผ ๋ณด๊ธฐ

+

๋‚ด ์‚ฌ์ฃผ ํ™•์ธํ•˜๊ธฐ

+ + + +
+
+ + {/* Footer */} + +
+ ); +} diff --git a/app/components/CompatibilityForm.tsx b/app/components/CompatibilityForm.tsx new file mode 100644 index 0000000..19a9152 --- /dev/null +++ b/app/components/CompatibilityForm.tsx @@ -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 ( +
+
+ {/* Person 1 */} +
+
+

๐Ÿ‘ค ์ฒซ ๋ฒˆ์งธ ์‚ฌ๋žŒ

+

๋ณธ์ธ์˜ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”

+
+ + {/* ์ƒ๋…„์›”์ผ */} +
+ +
+ setYear1(e.target.value)} + required + /> + setMonth1(e.target.value)} + required + /> + setDay1(e.target.value)} + required + /> +
+
+ + {/* ํƒœ์–ด๋‚œ ์‹œ๊ฐ„ */} +
+ + +
+ + {/* ์„ฑ๋ณ„ ์„ ํƒ */} +
+ +
+ + +
+
+
+ + {/* Person 2 */} +
+
+

๐Ÿ‘ค ๋‘ ๋ฒˆ์งธ ์‚ฌ๋žŒ

+

์ƒ๋Œ€๋ฐฉ์˜ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”

+
+ + {/* ์ƒ๋…„์›”์ผ */} +
+ +
+ setYear2(e.target.value)} + required + /> + setMonth2(e.target.value)} + required + /> + setDay2(e.target.value)} + required + /> +
+
+ + {/* ํƒœ์–ด๋‚œ ์‹œ๊ฐ„ */} +
+ + +
+ + {/* ์„ฑ๋ณ„ ์„ ํƒ */} +
+ +
+ + +
+
+
+
+ + {/* ์ œ์ถœ ๋ฒ„ํŠผ */} +
+ +
+ +

+ * ํƒœ์–ด๋‚œ ์‹œ๊ฐ„์„ ์ •ํ™•ํžˆ ์•„์‹œ๋ฉด ๋” ์ •ํ™•ํ•œ ๊ถํ•ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +

+
+ ); +} diff --git a/app/components/SajuForm.tsx b/app/components/SajuForm.tsx new file mode 100644 index 0000000..fa7662c --- /dev/null +++ b/app/components/SajuForm.tsx @@ -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 ( +
+ {/* ์ƒ๋…„์›”์ผ */} +
+ +
+ setYear(e.target.value)} + required + /> + setMonth(e.target.value)} + required + /> + setDay(e.target.value)} + required + /> +
+
+ + {/* ํƒœ์–ด๋‚œ ์‹œ๊ฐ„ */} +
+ + +
+ + {/* ์–‘๋ ฅ/์Œ๋ ฅ ์„ ํƒ */} +
+ +
+ + +
+
+ + {/* ์„ฑ๋ณ„ ์„ ํƒ */} +
+ +
+ + +
+
+ + {/* ์ œ์ถœ ๋ฒ„ํŠผ */} + + +

+ * ํƒœ์–ด๋‚œ ์‹œ๊ฐ„์„ ์ •ํ™•ํžˆ ์•„์‹œ๋ฉด ๋” ์ •ํ™•ํ•œ ์‚ฌ์ฃผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +

+
+ ); +} diff --git a/app/fortune/page.tsx b/app/fortune/page.tsx new file mode 100644 index 0000000..5b7669f --- /dev/null +++ b/app/fortune/page.tsx @@ -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 ( +
+ {/* Navigation */} + + + {/* Content */} +
+ {/* Header */} +
+

+ ์˜ค๋Š˜์˜ ์šด์„ธ +

+

+ {todayYear}๋…„ {todayMonth}์›” {todayDay}์ผ ({todayGanzi.day.stem}{todayGanzi.day.branch}) +

+

+ {sajuData.birthDate.year}๋…„์ƒ {gender === 'male' ? '๋‚จ์„ฑ' : '์—ฌ์„ฑ'}์˜ ์˜ค๋Š˜ ์šด์„ธ +

+
+ + {/* ์šด์„ธ ์ ์ˆ˜ ์นด๋“œ๋“ค */} +
+ {fortuneCategories.map((category) => ( +
+
+
+ {category.icon} +

{category.name}

+
+ + {category.score}์  + +
+
+
+
+

{getScoreText(category.score)}

+
+ ))} +
+ + {/* ์˜ค๋Š˜์˜ ํ•œ๋งˆ๋”” */} +
+

๐Ÿ’ฌ ์˜ค๋Š˜์˜ ํ•œ๋งˆ๋””

+

+ {sajuData.day.element === 'ๆœจ' && '๋‚˜๋ฌด๊ฐ€ ์ž๋ผ๋“ฏ ๊พธ์ค€ํžˆ ๋…ธ๋ ฅํ•˜๋ฉด ์ข‹์€ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์‹œ์ž‘์— ์ข‹์€ ๋‚ ์ž…๋‹ˆ๋‹ค.'} + {sajuData.day.element === '็ซ' && 'ํ™œ๋ฐœํ•œ ๊ธฐ์šด์ด ๊ฐ€๋“ํ•œ ๋‚ ์ž…๋‹ˆ๋‹ค. ์ ๊ทน์ ์œผ๋กœ ํ–‰๋™ํ•˜๋ฉด ์ข‹์€ ๊ธฐํšŒ๋ฅผ ์žก์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.'} + {sajuData.day.element === 'ๅœŸ' && '์•ˆ์ •์ ์ธ ํ•˜๋ฃจ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฐจ๊ทผ์ฐจ๊ทผ ๊ณ„ํš์„ ์„ธ์šฐ๊ณ  ์‹คํ–‰ํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.'} + {sajuData.day.element === '้‡‘' && '๋ช…ํ™•ํ•œ ํŒ๋‹จ์ด ํ•„์š”ํ•œ ๋‚ ์ž…๋‹ˆ๋‹ค. ์›์น™์„ ์ง€ํ‚ค๋ฉฐ ํ–‰๋™ํ•˜๋ฉด ์ข‹์€ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.'} + {sajuData.day.element === 'ๆฐด' && '์œ ์—ฐํ•œ ์‚ฌ๊ณ ๊ฐ€ ๋„์›€์ด ๋˜๋Š” ๋‚ ์ž…๋‹ˆ๋‹ค. ์ฃผ๋ณ€ ์‚ฌ๋žŒ๋“ค๊ณผ์˜ ์†Œํ†ต์„ ์ค‘์š”ํ•˜๊ฒŒ ์—ฌ๊ธฐ์„ธ์š”.'} +

+
+ + {/* ํ–‰์šด์˜ ์š”์†Œ๋“ค */} +
+ {/* ํ–‰์šด์˜ ๋ฐฉํ–ฅ */} +
+
๐Ÿงญ
+

ํ–‰์šด์˜ ๋ฐฉํ–ฅ

+

{getLuckyDirection(sajuData.day.stem)}

+

์ด ๋ฐฉํ–ฅ์œผ๋กœ ์™ธ์ถœํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค

+
+ + {/* ํ–‰์šด์˜ ์ƒ‰๊น” */} +
+
๐ŸŽจ
+

ํ–‰์šด์˜ ์ƒ‰๊น”

+

{getLuckyColor(sajuData.day.element)}

+

์ด ์ƒ‰๊น”์˜ ์˜ท์„ ์ž…์œผ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค

+
+ + {/* ํ–‰์šด์˜ ์ˆซ์ž */} +
+
๐ŸŽฒ
+

ํ–‰์šด์˜ ์ˆซ์ž

+

{getLuckyNumber(sajuData.day.stem)}

+

์˜ค๋Š˜์˜ ํ–‰์šด์„ ๊ฐ€์ ธ๋‹ค ์ค„ ์ˆซ์ž

+
+
+ + {/* ์ฃผ์˜์‚ฌํ•ญ */} +
+

+ โš ๏ธ + ์˜ค๋Š˜ ์ฃผ์˜ํ•  ์  +

+
    +
  • + โ€ข + ์ค‘์š”ํ•œ ๊ฒฐ์ •์€ ์˜ค์ „๋ณด๋‹ค ์˜คํ›„์— ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. +
  • +
  • + โ€ข + ๊ฐ์ •์ ์ธ ๋Œ€ํ™”๋Š” ํ”ผํ•˜๊ณ  ์นจ์ฐฉํ•˜๊ฒŒ ๋Œ€์‘ํ•˜์„ธ์š”. +
  • +
  • + โ€ข + ๊ฑด๊ฐ• ๊ด€๋ฆฌ์— ์‹ ๊ฒฝ ์“ฐ๊ณ  ๋ฌด๋ฆฌํ•˜์ง€ ๋งˆ์„ธ์š”. +
  • +
+
+ + {/* ๋‹ค๋ฅธ ๋ฉ”๋‰ด */} +
+ +
๐Ÿ“œ
+

์‚ฌ์ฃผํŒ”์ž

+

๋‚ด ์‚ฌ์ฃผ ๋‹ค์‹œ ๋ณด๊ธฐ

+ + + +
๐Ÿ’•
+

๊ถํ•ฉ ๋ณด๊ธฐ

+

๋‘ ์‚ฌ๋žŒ์˜ ๊ถํ•ฉ ํ™•์ธ

+ + + +
๐ŸŽ‹
+

ํ† ์ •๋น„๊ฒฐ

+

ํ•œ ํ•ด์˜ ์šด์„ธ ๋ณด๊ธฐ

+ +
+
+ + {/* Footer */} + +
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index 295f8fd..d5a9924 100644 --- a/app/page.tsx +++ b/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() { return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. +
+ {/* Navigation */} + + + {/* Hero Section */} +
+
+
+ ๋ฌด๋ฃŒ๋กœ ๋‚ด ์‚ฌ์ฃผ๋ฅผ ํ™•์ธํ•ด๋ณด์„ธ์š” +
+ +

+ ๋‚˜์˜ ์‚ฌ์ฃผํŒ”์ž๋ฅผ
+ ํ™•์ธํ•˜์„ธ์š”

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. + +

+ ์ƒ๋…„์›”์ผ์‹œ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๋ฌด๋ฃŒ๋กœ ์‚ฌ์ฃผํŒ”์ž, ์šด์„ธ, ๊ถํ•ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ๋‚˜์˜ ์šด๋ช…์„ ์•Œ์•„๋ณด์„ธ์š”.

+ + {/* Main Input Card */} +
+

์ƒ๋…„์›”์ผ์‹œ ์ž…๋ ฅ

+ + + {/* ์ƒ๋…„์›”์ผ */} +
+ +
+ + + +
+
+ + {/* ํƒœ์–ด๋‚œ ์‹œ๊ฐ„ */} +
+ + +
+ + {/* ์–‘๋ ฅ/์Œ๋ ฅ ์„ ํƒ */} +
+ +
+ + +
+
+ + {/* ์„ฑ๋ณ„ ์„ ํƒ */} +
+ +
+ + +
+
+ + {/* ์ œ์ถœ ๋ฒ„ํŠผ */} +
-
+ + {/* Features Section */} +
+
+
+

๋ฌด์—‡์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

+

๋‹ค์–‘ํ•œ ์‚ฌ์ฃผ ์ •๋ณด๋ฅผ ํ•œ ๋ฒˆ์— ํ™•์ธํ•˜์„ธ์š”

+
+ +
+ {/* Feature 1 */} +
+
๐Ÿ“œ
+

์‚ฌ์ฃผํŒ”์ž

+

+ ๋‚˜์˜ ์ฒœ๊ฐ„, ์ง€์ง€, ์‹ญ์„ฑ, ์‹ญ์ด์šด์„ฑ์„ ํ™•์ธํ•˜๊ณ  ์šด๋ช…์˜ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜์„ธ์š”. +

+
+ + {/* Feature 2 */} +
+
๐ŸŒŸ
+

์˜ค๋Š˜์˜ ์šด์„ธ

+

+ ์‚ฌ์ฃผ๋ฅผ ํ™•์ธํ•œ ํ›„ ์˜ค๋Š˜ ํ•˜๋ฃจ์˜ ์šด์„ธ๋ฅผ ํ™•์ธํ•˜๊ณ  ํ–‰์šด์˜ ๋ฐฉํ–ฅ์„ ์ฐพ์•„๋ณด์„ธ์š”. +

+
+ + {/* Feature 3 */} + +
๐Ÿ’•
+

๊ถํ•ฉ

+

+ ๋‘ ์‚ฌ๋žŒ์˜ ์‚ฌ์ฃผ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๊ถํ•ฉ์„ ํ™•์ธํ•˜์„ธ์š”. +

+ +
+
+
+ + {/* CTA Section */} +
+ -

+ + + {/* Footer */} +
); } diff --git a/app/result/page.tsx b/app/result/page.tsx new file mode 100644 index 0000000..84a5421 --- /dev/null +++ b/app/result/page.tsx @@ -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 ( +
+ {/* Navigation */} + + + {/* Result Content */} +
+ {/* Header */} +
+

+ ๋‚ด ์‚ฌ์ฃผํŒ”์ž +

+

+ {yearNum}๋…„ {monthNum}์›” {dayNum}์ผ {hourNum !== null && `${hourNum}์‹œ`} + {gender === 'male' ? ' ๋‚จ์„ฑ' : ' ์—ฌ์„ฑ'} +

+
+ + {/* ์‚ฌ์ฃผํŒ”์ž ํ‘œ */} +
+

์‚ฌ์ฃผํŒ”์ž (ๅ››ๆŸฑๅ…ซๅญ—)

+ +
+ + + + + {sajuData.hour && } + + + + + + + {/* ์ฒœ๊ฐ„ */} + + + {sajuData.hour && ( + + )} + + + + + + {/* ์ง€์ง€ */} + + + {sajuData.hour && ( + + )} + + + + + + {/* ์‹ญ์„ฑ */} + + + {sajuData.hour && ( + + )} + + + + + + {/* ์‹ญ์ด์šด์„ฑ */} + + + {sajuData.hour && ( + + )} + + + + + +
๊ตฌ๋ถ„์‹œ์ฃผ (ๆ™‚ๆŸฑ)์ผ์ฃผ (ๆ—ฅๆŸฑ)์›”์ฃผ (ๆœˆๆŸฑ)๋…„์ฃผ (ๅนดๆŸฑ)
์ฒœ๊ฐ„ (ๅคฉๅนฒ) +
{sajuData.hour.stem}
+
{sajuData.hour.stemKr}
+
+
{sajuData.day.stem}
+
{sajuData.day.stemKr}
+
์ผ๊ฐ„ (ๆ—ฅๅนฒ)
+
+
{sajuData.month.stem}
+
{sajuData.month.stemKr}
+
+
{sajuData.year.stem}
+
{sajuData.year.stemKr}
+
์ง€์ง€ (ๅœฐๆ”ฏ) +
{sajuData.hour.branch}
+
{sajuData.hour.branchKr}
+
+
{sajuData.day.branch}
+
{sajuData.day.branchKr}
+
+
{sajuData.month.branch}
+
{sajuData.month.branchKr}
+
+
{sajuData.year.branch}
+
{sajuData.year.branchKr}
+
์‹ญ์„ฑ (ๅๆ˜Ÿ) +
{sajuData.hour.tenGod}
+
+
{sajuData.day.tenGod}
+
+
{sajuData.month.tenGod}
+
+
{sajuData.year.tenGod}
+
์‹ญ์ด์šด์„ฑ +
{sajuData.hour.fortune}
+
+
{sajuData.day.fortune}
+
+
{sajuData.month.fortune}
+
+
{sajuData.year.fortune}
+
+
+ +
+

+ ์ผ๊ฐ„ (ๆ—ฅๅนฒ): {sajuData.day.stem}({sajuData.day.stemKr}) - ๋‚˜ ์ž์‹ ์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ค‘์‹ฌ ๊ธฐ๋‘ฅ์ž…๋‹ˆ๋‹ค. +

+
+
+ + {/* ์‚ฌ์ฃผ ํ•ด์„ */} +
+ {/* ์„ฑ๊ฒฉ */} +
+

+ ๐Ÿ‘ค + ์„ฑ๊ฒฉ ํŠน์ง• +

+
+

+ ์ผ๊ฐ„์ด {sajuData.day.stem}({sajuData.day.stemKr})์ธ ์‚ฌ๋žŒ์€ + {sajuData.day.element === 'ๆœจ' && ' ๋‚˜๋ฌด์ฒ˜๋Ÿผ ์„ฑ์žฅํ•˜๊ณ  ๋ฐœ์ „ํ•˜๋ ค๋Š” ์˜์ง€๊ฐ€ ๊ฐ•ํ•ฉ๋‹ˆ๋‹ค. ์ฐฝ์˜์ ์ด๊ณ  ์ง„์ทจ์ ์ธ ์„ฑํ–ฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.'} + {sajuData.day.element === '็ซ' && ' ๋ถˆ์ฒ˜๋Ÿผ ์—ด์ •์ ์ด๊ณ  ํ™œ๋™์ ์ž…๋‹ˆ๋‹ค. ๋ฆฌ๋”์‹ญ์ด ์žˆ๊ณ  ์‚ฌ๊ต์„ฑ์ด ๋›ฐ์–ด๋‚ฉ๋‹ˆ๋‹ค.'} + {sajuData.day.element === 'ๅœŸ' && ' ํ™์ฒ˜๋Ÿผ ์•ˆ์ •์ ์ด๊ณ  ์‹ ๋ขฐ๊ฐ ์žˆ์Šต๋‹ˆ๋‹ค. ํฌ์šฉ๋ ฅ์ด ์žˆ๊ณ  ์ฑ…์ž„๊ฐ์ด ๊ฐ•ํ•ฉ๋‹ˆ๋‹ค.'} + {sajuData.day.element === '้‡‘' && ' ๊ธˆ์†์ฒ˜๋Ÿผ ๊ฐ•์ธํ•˜๊ณ  ์›์น™์ ์ž…๋‹ˆ๋‹ค. ๊ฒฐ๋‹จ๋ ฅ ์žˆ๊ณ  ์˜๋ฆฌ๋ฅผ ์ค‘์‹œํ•ฉ๋‹ˆ๋‹ค.'} + {sajuData.day.element === 'ๆฐด' && ' ๋ฌผ์ฒ˜๋Ÿผ ์œ ์—ฐํ•˜๊ณ  ์ง€ํ˜œ๋กญ์Šต๋‹ˆ๋‹ค. ์ ์‘๋ ฅ์ด ๋›ฐ์–ด๋‚˜๊ณ  ์‚ฌ๋ ค ๊นŠ์Šต๋‹ˆ๋‹ค.'} +

+
+
+ + {/* ์šด์„ธ */} +
+

+ ๐ŸŒŸ + ์šด์„ธ ํ๋ฆ„ +

+
+

+ ํ˜„์žฌ ์‹ญ์ด์šด์„ฑ์ด {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 === 'ํƒœ' && ' ์ƒˆ๋กœ์šด ๊ธฐ์šด์ด ์‹นํŠธ๋Š” ์‹œ๊ธฐ์ž…๋‹ˆ๋‹ค.'} + {sajuData.day.fortune === '์–‘' && ' ์„ฑ์žฅ์„ ์ค€๋น„ํ•˜๋Š” ์‹œ๊ธฐ์ž…๋‹ˆ๋‹ค.'} +

+
+
+
+ + {/* ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๋ฒ„ํŠผ */} +
+ +
๐ŸŒŸ
+

์˜ค๋Š˜์˜ ์šด์„ธ

+

์˜ค๋Š˜์˜ ์šด์„ ํ™•์ธํ•˜๊ธฐ

+ + + +
๐Ÿ’•
+

๊ถํ•ฉ ๋ณด๊ธฐ

+

๋‘ ์‚ฌ๋žŒ์˜ ๊ถํ•ฉ ํ™•์ธ

+ + + +
+
+ + {/* Footer */} + +
+ ); +} diff --git a/lib/saju-calculator.ts b/lib/saju-calculator.ts new file mode 100644 index 0000000..322bbe5 --- /dev/null +++ b/lib/saju-calculator.ts @@ -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; +}