From 08d6f71fd13e32c99f1cfe60b63625b2279e57ea Mon Sep 17 00:00:00 2001 From: gahusb Date: Wed, 11 Feb 2026 23:36:22 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=82=AC=EC=A3=BC=20=EC=9B=B9=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=ED=95=B5=EC=8B=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사주팔자 계산 라이브러리 구현 (천간, 지지, 십성, 십이운성) - 사주 결과 페이지 구현 (사주팔자 표 및 해석) - 오늘의 운세 페이지 구현 (일일 운세, 행운의 요소) - 궁합 보기 기능 구현 (두 사람 입력 폼) - 궁합 결과 페이지 구현 (종합 점수, 세부 분석) - 페이지 간 네비게이션 연결 Co-Authored-By: Claude Sonnet 4.5 --- app/compatibility/page.tsx | 100 +++++++ app/compatibility/result/page.tsx | 395 +++++++++++++++++++++++++++ app/components/CompatibilityForm.tsx | 281 +++++++++++++++++++ app/components/SajuForm.tsx | 181 ++++++++++++ app/fortune/page.tsx | 268 ++++++++++++++++++ app/page.tsx | 257 +++++++++++++---- app/result/page.tsx | 260 ++++++++++++++++++ lib/saju-calculator.ts | 269 ++++++++++++++++++ 8 files changed, 1957 insertions(+), 54 deletions(-) create mode 100644 app/compatibility/page.tsx create mode 100644 app/compatibility/result/page.tsx create mode 100644 app/components/CompatibilityForm.tsx create mode 100644 app/components/SajuForm.tsx create mode 100644 app/fortune/page.tsx create mode 100644 app/result/page.tsx create mode 100644 lib/saju-calculator.ts 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 */} +
+
+
+ 🔮 사주보기 +
+

+ 쟁승메이드가 제공하는 무료 사주 서비스 +

+
+

문의: bgg8988@gmail.com | 쟁승메이드

+

© 2025 쟁승메이드. All rights reserved.

+
+
+
+
+ ); +} 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 */} +
+
+
+ 🔮 사주보기 +
+

+ 쟁승메이드가 제공하는 무료 사주 서비스 +

+
+

문의: bgg8988@gmail.com | 쟁승메이드

+

© 2025 쟁승메이드. All rights reserved.

+
+
+
+
+ ); +} 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 */} +
+
+
+ 🔮 사주보기 +
+

+ 쟁승메이드가 제공하는 무료 사주 서비스 +

+
+

문의: bgg8988@gmail.com | 쟁승메이드

+

© 2025 쟁승메이드. All rights reserved.

+
+
+
+
+ ); +} 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 */} +
+
+
+ 🔮 사주보기 +
+

+ 쟁승메이드가 제공하는 무료 사주 서비스 +

+
+

문의: bgg8988@gmail.com | 쟁승메이드

+

© 2025 쟁승메이드. All rights reserved.

+
+
+
); } 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; +}