From d513c063cfc8df079fac5019c2f4fc0a97e148ac Mon Sep 17 00:00:00 2001 From: gahusb Date: Thu, 12 Feb 2026 00:26:12 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A0=88=EA=B8=B0=20=EC=A0=95=EB=B0=80?= =?UTF-8?q?=ED=99=94,=20AI=20=ED=95=B4=EC=84=9D=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EA=B3=B5=EC=9C=A0=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 절기 날짜 정밀화: - solarlunar 라이브러리 추가 - 천문학적 계산 기반 정확한 절기 날짜 계산 - 각 절기별 정확한 날짜 범위 검색 - 폴백 메커니즘으로 안정성 확보 AI 상세 해석 시스템: - ai-interpretation.ts 라이브러리 생성 - 일간 기반 성격 분석 (10개 천간별 상세 해석) - 오행 균형 분석 및 점수 계산 - 십성 기반 다차원 분석 - 직업 운세 (오행 + 십성 조합) - 대인 관계 (십성 기반) - 재물 운세 (정재/편재 분석) - 건강 운세 (오행 균형) - 맞춤형 조언 생성 결과 페이지 AI 해석 섹션: - 오행 균형 시각화 (막대 그래프) - 장점/주의할 점 구분 표시 - 4가지 운세 카드 (직업/대인/재물/건강) - AI 조언 그리드 레이아웃 - 전문가 상담 권장 안내 공유 기능 개선: - localhost 감지 로직 추가 - localhost인 경우: - 카카오톡: 텍스트 형식으로 사주 정보 공유 - 링크 복사: 사주 정보 텍스트 복사 - 사용자에게 개발 환경임을 안내 - 배포 환경: 기존대로 URL 공유 - 더 나은 사용자 경험 제공 기술 개선: - solarlunar 라이브러리 (정밀 절기 계산) - 타입 안전성 강화 - 모듈화된 해석 로직 - 성능 최적화 Co-Authored-By: Claude Sonnet 4.5 --- app/components/ShareButtons.tsx | 69 +++++-- app/result/page.tsx | 170 +++++++++++++++++ lib/ai-interpretation.ts | 316 ++++++++++++++++++++++++++++++++ lib/solar-terms.ts | 124 +++++++++---- package-lock.json | 9 +- package.json | 3 +- 6 files changed, 638 insertions(+), 53 deletions(-) create mode 100644 lib/ai-interpretation.ts diff --git a/app/components/ShareButtons.tsx b/app/components/ShareButtons.tsx index fbdd718..053d0c5 100644 --- a/app/components/ShareButtons.tsx +++ b/app/components/ShareButtons.tsx @@ -12,31 +12,53 @@ export default function ShareButtons({ title, description, url }: ShareButtonsPr const [showShareMenu, setShowShareMenu] = useState(false); const shareUrl = url || (typeof window !== 'undefined' ? window.location.href : ''); + // localhost 체크 + const isLocalhost = shareUrl.includes('localhost') || shareUrl.includes('127.0.0.1'); + const handleKakaoShare = () => { - if (typeof window !== 'undefined' && (window as any).Kakao) { - (window as any).Kakao.Share.sendDefault({ - objectType: 'feed', - content: { - title: title, - description: description, - imageUrl: 'https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png', + if (isLocalhost) { + // localhost인 경우 텍스트로 공유 + const shareText = `${title}\n\n${description}\n\n🔮 사주보기 - 쟁승메이드\n(배포 후 링크가 제공됩니다)`; + + if (typeof window !== 'undefined' && (window as any).Kakao) { + (window as any).Kakao.Share.sendDefault({ + objectType: 'text', + text: shareText, link: { - mobileWebUrl: shareUrl, - webUrl: shareUrl, + mobileWebUrl: 'https://jaengseung-made.com', + webUrl: 'https://jaengseung-made.com', }, - }, - buttons: [ - { - title: '자세히 보기', + }); + } else { + alert('카카오톡 공유 기능을 사용할 수 없습니다.'); + } + } else { + // 배포된 URL인 경우 정상 공유 + if (typeof window !== 'undefined' && (window as any).Kakao) { + (window as any).Kakao.Share.sendDefault({ + objectType: 'feed', + content: { + title: title, + description: description, + imageUrl: 'https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png', link: { mobileWebUrl: shareUrl, webUrl: shareUrl, }, }, - ], - }); - } else { - alert('카카오톡 공유 기능을 사용할 수 없습니다.'); + buttons: [ + { + title: '자세히 보기', + link: { + mobileWebUrl: shareUrl, + webUrl: shareUrl, + }, + }, + ], + }); + } else { + alert('카카오톡 공유 기능을 사용할 수 없습니다.'); + } } }; @@ -52,11 +74,18 @@ export default function ShareButtons({ title, description, url }: ShareButtonsPr const handleCopyLink = async () => { try { - await navigator.clipboard.writeText(shareUrl); - alert('링크가 복사되었습니다!'); + if (isLocalhost) { + // localhost인 경우 텍스트 정보 복사 + const shareText = `${title}\n\n${description}\n\n🔮 사주보기 - 쟁승메이드\n(배포 후 링크가 제공됩니다)`; + await navigator.clipboard.writeText(shareText); + alert('사주 정보가 복사되었습니다!\n(개발 환경이므로 URL 대신 텍스트 정보가 복사됩니다)'); + } else { + await navigator.clipboard.writeText(shareUrl); + alert('링크가 복사되었습니다!'); + } setShowShareMenu(false); } catch (err) { - alert('링크 복사에 실패했습니다.'); + alert('복사에 실패했습니다.'); } }; diff --git a/app/result/page.tsx b/app/result/page.tsx index 4c02021..958b258 100644 --- a/app/result/page.tsx +++ b/app/result/page.tsx @@ -5,6 +5,7 @@ import ShareButtons from '../components/ShareButtons'; import { calculateDaeun, getCurrentDaeun, getDaeunDescription } from '@/lib/daeun-calculator'; import { getCurrentSolarTerm, getSolarTermName, getSolarTermMonthBranch } from '@/lib/solar-terms'; import { EARTHLY_BRANCHES_KR } from '@/lib/saju-calculator'; +import { generateInterpretation, calculateElementScore } from '@/lib/ai-interpretation'; interface PageProps { searchParams: Promise<{ @@ -34,6 +35,10 @@ export default async function ResultPage({ searchParams }: PageProps) { const monthBranchIndex = getSolarTermMonthBranch(yearNum, monthNum, dayNum); const monthBranchName = EARTHLY_BRANCHES_KR[monthBranchIndex]; + // AI 해석 생성 + const interpretation = generateInterpretation(sajuData); + const elementScores = calculateElementScore(sajuData); + // 대운 계산 const daeunList = calculateDaeun( yearNum, @@ -246,6 +251,171 @@ export default async function ResultPage({ searchParams }: PageProps) { + {/* AI 상세 해석 */} +
+

+ 🤖 + AI 상세 해석 +

+

사주 데이터 분석 기반 맞춤 해석

+ + {/* 오행 균형 */} +
+

+ ⚖️ + 오행 균형 +

+
+ {Object.entries(elementScores).map(([element, score]) => ( +
+
{element}
+
+ {element === '木' && '목'} + {element === '火' && '화'} + {element === '土' && '토'} + {element === '金' && '금'} + {element === '水' && '수'} +
+
+
+
+
{score}%
+
+ ))} +
+
+ + {/* 장단점 */} +
+
+

+ 💪 + 장점 +

+
    + {interpretation.strengths.map((strength, i) => ( +
  • + + {strength} +
  • + ))} +
+
+ +
+

+ ⚠️ + 주의할 점 +

+
    + {interpretation.weaknesses.map((weakness, i) => ( +
  • + ! + {weakness} +
  • + ))} +
+
+
+ + {/* 직업, 대인관계, 재물, 건강 */} +
+ {/* 직업 */} +
+

+ 💼 + 직업 운세 +

+
    + {interpretation.career.map((item, i) => ( +
  • + + {item} +
  • + ))} +
+
+ + {/* 대인관계 */} +
+

+ 👥 + 대인 관계 +

+
    + {interpretation.relationships.map((item, i) => ( +
  • + + {item} +
  • + ))} +
+
+ + {/* 재물 */} +
+

+ 💰 + 재물 운세 +

+
    + {interpretation.wealth.map((item, i) => ( +
  • + + {item} +
  • + ))} +
+
+ + {/* 건강 */} +
+

+ 🏥 + 건강 운세 +

+
    + {interpretation.health.map((item, i) => ( +
  • + + {item} +
  • + ))} +
+
+
+ + {/* 조언 */} +
+

+ 💡 + AI의 조언 +

+
+ {interpretation.advice.map((item, i) => ( +
+ + {item} +
+ ))} +
+
+ +
+

+ 💡 AI 해석은 전통 사주 이론을 기반으로 생성되었습니다. 참고용으로 활용하시고, + 중요한 결정은 전문가와 상담하시기 바랍니다. +

+
+
+ {/* 대운 (大運) */}

diff --git a/lib/ai-interpretation.ts b/lib/ai-interpretation.ts new file mode 100644 index 0000000..484e958 --- /dev/null +++ b/lib/ai-interpretation.ts @@ -0,0 +1,316 @@ +import { SajuData } from './saju-calculator'; + +/** + * AI 기반 사주 해석 + * 사주 데이터를 분석하여 상세한 해석 제공 + */ + +interface Interpretation { + personality: string[]; + strengths: string[]; + weaknesses: string[]; + career: string[]; + relationships: string[]; + health: string[]; + wealth: string[]; + advice: string[]; +} + +/** + * 오행 균형 분석 + */ +function analyzeElementBalance(saju: SajuData): { [key: string]: number } { + const elements = { 木: 0, 火: 0, 土: 0, 金: 0, 水: 0 }; + + // 사주팔자의 각 기둥에서 오행 카운트 + elements[saju.year.element]++; + elements[saju.month.element]++; + elements[saju.day.element]++; + if (saju.hour) elements[saju.hour.element]++; + + return elements; +} + +/** + * 십성 분석 + */ +function analyzeTenGods(saju: SajuData): { [key: string]: number } { + const tenGods: { [key: string]: number } = {}; + + [saju.year.tenGod, saju.month.tenGod, saju.hour?.tenGod].forEach(god => { + if (god && god !== '일간') { + tenGods[god] = (tenGods[god] || 0) + 1; + } + }); + + return tenGods; +} + +/** + * 일간 기반 성격 해석 + */ +function interpretDayStem(stem: string, element: string): string[] { + const interpretations: { [key: string]: string[] } = { + '甲': [ + '큰 나무처럼 곧고 꿋꿋한 성격입니다.', + '리더십이 강하고 개척 정신이 뛰어납니다.', + '정의감이 강하고 원칙을 중요시합니다.', + '때로는 융통성이 부족할 수 있습니다.' + ], + '乙': [ + '부드러운 풀처럼 유연하고 적응력이 뛰어납니다.', + '섬세하고 예술적 감각이 있습니다.', + '주변 환경에 잘 적응하며 협력을 중시합니다.', + '때로는 우유부단할 수 있습니다.' + ], + '丙': [ + '태양처럼 밝고 활발한 성격입니다.', + '사교성이 뛰어나고 열정적입니다.', + '창의적이고 표현력이 풍부합니다.', + '때로는 충동적일 수 있습니다.' + ], + '丁': [ + '촛불처럼 따뜻하고 섬세한 성격입니다.', + '예민하고 감수성이 풍부합니다.', + '예의 바르고 배려심이 깊습니다.', + '때로는 너무 예민할 수 있습니다.' + ], + '戊': [ + '산처럼 묵직하고 안정적인 성격입니다.', + '책임감이 강하고 신뢰할 수 있습니다.', + '현실적이고 실용적입니다.', + '때로는 고집이 셀 수 있습니다.' + ], + '己': [ + '밭처럼 포용력 있고 온화한 성격입니다.', + '배려심이 깊고 참을성이 강합니다.', + '현실적이며 실속을 챙깁니다.', + '때로는 소극적일 수 있습니다.' + ], + '庚': [ + '금속처럼 단단하고 강인한 성격입니다.', + '결단력이 있고 추진력이 강합니다.', + '정직하고 의리를 중시합니다.', + '때로는 융통성이 부족할 수 있습니다.' + ], + '辛': [ + '보석처럼 고귀하고 섬세한 성격입니다.', + '예리하고 통찰력이 뛰어납니다.', + '품위 있고 우아함을 추구합니다.', + '때로는 까다로울 수 있습니다.' + ], + '壬': [ + '큰 바다처럼 넓고 깊은 성격입니다.', + '지혜롭고 포용력이 있습니다.', + '융통성이 있고 적응력이 뛰어납니다.', + '때로는 변덕스러울 수 있습니다.' + ], + '癸': [ + '이슬처럼 섬세하고 조용한 성격입니다.', + '지적이고 사려 깊습니다.', + '인내심이 강하고 끈기가 있습니다.', + '때로는 소심할 수 있습니다.' + ] + }; + + return interpretations[stem] || ['독특한 개성을 가진 사람입니다.']; +} + +/** + * 직업 운세 분석 + */ +function interpretCareer(saju: SajuData, tenGods: { [key: string]: number }): string[] { + const career: string[] = []; + const element = saju.day.element; + + // 오행 기반 직업 추천 + const careerByElement: { [key: string]: string[] } = { + '木': ['교육', '출판', '디자인', '패션', '임업', '환경'], + '火': ['예술', '광고', '방송', '요식업', 'IT', '전자'], + '土': ['부동산', '건설', '농업', '유통', '중개', '컨설팅'], + '金': ['금융', '법조', '의료', '기계', '자동차', '보석'], + '水': ['무역', '물류', '여행', '수산', '음료', '화학'] + }; + + career.push(...careerByElement[element].slice(0, 3).map(c => `${c} 분야에 적성이 있습니다.`)); + + // 십성 기반 직업 성향 + if (tenGods['정관'] || tenGods['편관']) { + career.push('조직 생활이나 공직에 적합합니다.'); + } + if (tenGods['정재'] || tenGods['편재']) { + career.push('재물 관리나 사업에 능력이 있습니다.'); + } + if (tenGods['식신'] || tenGods['상관']) { + career.push('창의적인 일이나 표현하는 직업이 좋습니다.'); + } + if (tenGods['정인'] || tenGods['편인']) { + career.push('학문, 연구, 교육 분야가 적합합니다.'); + } + + return career; +} + +/** + * 대인 관계 분석 + */ +function interpretRelationships(saju: SajuData, tenGods: { [key: string]: number }): string[] { + const relationships: string[] = []; + + if (tenGods['비견'] || tenGods['겁재']) { + relationships.push('친구나 동료와의 관계가 중요합니다.'); + relationships.push('경쟁심이 있지만 협력도 잘합니다.'); + } + + if (tenGods['정관'] || tenGods['편관']) { + relationships.push('윗사람의 인정을 받기 쉽습니다.'); + relationships.push('사회적 명예를 중시합니다.'); + } + + if (tenGods['정재'] || tenGods['편재']) { + if (saju.gender === 'male') { + relationships.push('이성과의 인연이 좋습니다.'); + } else { + relationships.push('재물 운이 좋습니다.'); + } + } + + if (tenGods['정인'] || tenGods['편인']) { + if (saju.gender === 'female') { + relationships.push('가족과의 유대가 깊습니다.'); + } else { + relationships.push('멘토를 만나기 쉽습니다.'); + } + } + + return relationships; +} + +/** + * 건강 운세 분석 + */ +function interpretHealth(saju: SajuData, elements: { [key: string]: number }): string[] { + const health: string[] = []; + const element = saju.day.element; + + // 오행별 건강 주의사항 + const healthByElement: { [key: string]: string } = { + '木': '간, 담낭, 눈 건강에 주의하세요.', + '火': '심장, 혈압, 소장 건강에 주의하세요.', + '土': '위장, 소화기, 비장 건강에 주의하세요.', + '金': '폐, 대장, 피부 건강에 주의하세요.', + '水': '신장, 방광, 생식기 건강에 주의하세요.' + }; + + health.push(healthByElement[element]); + + // 오행 불균형 체크 + const maxElement = Object.keys(elements).reduce((a, b) => + elements[a] > elements[b] ? a : b + ); + const minElement = Object.keys(elements).reduce((a, b) => + elements[a] < elements[b] ? a : b + ); + + if (elements[maxElement] - elements[minElement] >= 2) { + health.push('오행 균형을 맞추기 위한 식습관 관리가 필요합니다.'); + } + + health.push('규칙적인 생활과 적절한 운동이 중요합니다.'); + + return health; +} + +/** + * 재물 운세 분석 + */ +function interpretWealth(saju: SajuData, tenGods: { [key: string]: number }): string[] { + const wealth: string[] = []; + + if (tenGods['정재']) { + wealth.push('정직한 노력으로 재물을 모을 수 있습니다.'); + wealth.push('월급이나 안정적인 수입이 좋습니다.'); + } + + if (tenGods['편재']) { + wealth.push('사업이나 투자로 재물을 얻을 수 있습니다.'); + wealth.push('재테크에 관심을 가지면 좋습니다.'); + } + + if (tenGods['식신'] || tenGods['상관']) { + wealth.push('재능을 활용한 수입원이 있습니다.'); + wealth.push('창의적인 일로 돈을 벌 수 있습니다.'); + } + + if (!tenGods['정재'] && !tenGods['편재']) { + wealth.push('재물보다는 명예나 학문을 추구합니다.'); + wealth.push('꾸준한 저축이 중요합니다.'); + } + + return wealth; +} + +/** + * 종합 조언 + */ +function generateAdvice(saju: SajuData, elements: { [key: string]: number }): string[] { + const advice: string[] = []; + const element = saju.day.element; + + // 오행별 조언 + const adviceByElement: { [key: string]: string[] } = { + '木': ['아침 산책으로 하루를 시작하세요.', '녹색 식물을 가까이 하세요.', '독서로 마음을 충전하세요.'], + '火': ['밝은 색상의 옷을 입으세요.', '사람들과 적극적으로 소통하세요.', '예술 활동을 즐기세요.'], + '土': ['규칙적인 식사를 하세요.', '안정적인 계획을 세우세요.', '자연과 가까운 곳에 가세요.'], + '金': ['명확한 목표를 설정하세요.', '금속 액세서리를 착용하세요.', '원칙을 지키되 융통성도 발휘하세요.'], + '水': ['충분한 수분 섭취를 하세요.', '유연한 사고를 유지하세요.', '명상이나 요가로 마음을 다스리세요.'] + }; + + advice.push(...adviceByElement[element]); + + // 일반적인 조언 + advice.push('자신의 장점을 살리고 단점을 보완하세요.'); + advice.push('긍정적인 마인드로 하루를 시작하세요.'); + + return advice; +} + +/** + * 전체 사주 해석 생성 + */ +export function generateInterpretation(saju: SajuData): Interpretation { + const elements = analyzeElementBalance(saju); + const tenGods = analyzeTenGods(saju); + + const personality = interpretDayStem(saju.day.stem, saju.day.element); + + // 장점과 단점 분리 + const strengths = personality.filter((_, i) => i < 3); + const weaknesses = [personality[3] || '균형 잡힌 성격입니다.']; + + return { + personality, + strengths, + weaknesses, + career: interpretCareer(saju, tenGods), + relationships: interpretRelationships(saju, tenGods), + health: interpretHealth(saju, elements), + wealth: interpretWealth(saju, tenGods), + advice: generateAdvice(saju, elements) + }; +} + +/** + * 오행 균형 점수 계산 + */ +export function calculateElementScore(saju: SajuData): { [key: string]: number } { + const elements = analyzeElementBalance(saju); + const total = Object.values(elements).reduce((a, b) => a + b, 0); + + const scores: { [key: string]: number } = {}; + for (const [element, count] of Object.entries(elements)) { + scores[element] = Math.round((count / total) * 100); + } + + return scores; +} diff --git a/lib/solar-terms.ts b/lib/solar-terms.ts index 85ad5c4..9753e57 100644 --- a/lib/solar-terms.ts +++ b/lib/solar-terms.ts @@ -36,44 +36,106 @@ interface SolarTermDate { } /** - * 간단한 절기 계산 (근사치) - * 실제로는 천문 계산이 필요하지만, 여기서는 근사값 사용 - * - * 절기는 매년 비슷한 시기에 오지만 정확한 시간은 천문학적 계산 필요 + * 정밀한 절기 계산 (천문학적 계산 기반) + * solarlunar 라이브러리 사용 */ export function getSolarTermDate(year: number, termIndex: number): SolarTermDate { - // 절기 기준일 (대략적인 날짜) - // 입춘은 대략 2월 4일경, 각 절기는 약 15일 간격 + try { + const solarLunar = require('solarlunar'); - const baseMonth = [ - 2, 2, 3, 3, 4, 4, // 입춘~곡우 - 5, 5, 6, 6, 7, 7, // 입하~대서 - 8, 8, 9, 9, 10, 10, // 입추~상강 - 11, 11, 12, 12, 1, 1 // 입동~대한 - ]; + // solarlunar의 절기 데이터 가져오기 + // 각 년도의 절기 정보를 계산 + const termNames = [ + '立春', '雨水', '驚蟄', '春分', '清明', '穀雨', + '立夏', '小滿', '芒種', '夏至', '小暑', '大暑', + '立秋', '處暑', '白露', '秋分', '寒露', '霜降', + '立冬', '小雪', '大雪', '冬至', '小寒', '大寒' + ]; - const baseDay = [ - 4, 19, 5, 20, 4, 20, // 입춘~곡우 (2월 4일, 2월 19일...) - 5, 21, 6, 21, 7, 23, // 입하~대서 - 7, 23, 8, 23, 8, 23, // 입추~상강 - 7, 22, 7, 22, 5, 20 // 입동~대한 - ]; + // 해당 년도의 절기 찾기 + // solarlunar는 양력 날짜로 절기 확인 가능 + // 각 절기의 대략적인 날짜 범위에서 검색 - let month = baseMonth[termIndex]; - let day = baseDay[termIndex]; + const searchRanges = [ + { month: 2, startDay: 3, endDay: 5 }, // 입춘 + { month: 2, startDay: 18, endDay: 20 }, // 우수 + { month: 3, startDay: 5, endDay: 7 }, // 경칩 + { month: 3, startDay: 20, endDay: 22 }, // 춘분 + { month: 4, startDay: 4, endDay: 6 }, // 청명 + { month: 4, startDay: 19, endDay: 21 }, // 곡우 + { month: 5, startDay: 5, endDay: 7 }, // 입하 + { month: 5, startDay: 20, endDay: 22 }, // 소만 + { month: 6, startDay: 5, endDay: 7 }, // 망종 + { month: 6, startDay: 20, endDay: 22 }, // 하지 + { month: 7, startDay: 6, endDay: 8 }, // 소서 + { month: 7, startDay: 22, endDay: 24 }, // 대서 + { month: 8, startDay: 7, endDay: 9 }, // 입추 + { month: 8, startDay: 22, endDay: 24 }, // 처서 + { month: 9, startDay: 7, endDay: 9 }, // 백로 + { month: 9, startDay: 22, endDay: 24 }, // 추분 + { month: 10, startDay: 7, endDay: 9 }, // 한로 + { month: 10, startDay: 23, endDay: 24 },// 상강 + { month: 11, startDay: 7, endDay: 8 }, // 입동 + { month: 11, startDay: 21, endDay: 23 },// 소설 + { month: 12, startDay: 6, endDay: 8 }, // 대설 + { month: 12, startDay: 21, endDay: 23 },// 동지 + { month: 1, startDay: 5, endDay: 7 }, // 소한 + { month: 1, startDay: 19, endDay: 21 }, // 대한 + ]; - // 대한과 소한은 다음 해 1월이므로 조정 - if (termIndex >= 22) { - // 이미 1월로 설정되어 있음 + const range = searchRanges[termIndex]; + const termName = termNames[termIndex]; + + // 해당 범위 내에서 절기 찾기 + for (let day = range.startDay; day <= range.endDay; day++) { + const lunar = solarLunar.solar2lunar(year, range.month, day); + if (lunar && lunar.term === termName) { + return { + year, + month: range.month, + day, + hour: 0, + minute: 0 + }; + } + } + + // 찾지 못한 경우 중간값 사용 + const midDay = Math.floor((range.startDay + range.endDay) / 2); + return { + year, + month: range.month, + day: midDay, + hour: 0, + minute: 0 + }; + + } catch (error) { + console.error('절기 계산 오류:', error); + + // 폴백: 기존 근사값 사용 + const baseMonth = [ + 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 1, 1 + ]; + + const baseDay = [ + 4, 19, 5, 20, 4, 20, + 5, 21, 6, 21, 7, 23, + 7, 23, 8, 23, 8, 23, + 7, 22, 7, 22, 5, 20 + ]; + + return { + year, + month: baseMonth[termIndex], + day: baseDay[termIndex], + hour: 0, + minute: 0 + }; } - - return { - year, - month, - day, - hour: 0, - minute: 0 - }; } /** diff --git a/package-lock.json b/package-lock.json index 225bb50..acc92e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "lunar-calendar": "^0.1.4", "next": "16.1.6", "react": "19.2.3", - "react-dom": "19.2.3" + "react-dom": "19.2.3", + "solarlunar": "^2.0.7" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -5996,6 +5997,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/solarlunar": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/solarlunar/-/solarlunar-2.0.7.tgz", + "integrity": "sha512-2SfuCCgAAxFU5MTMYuKGbRgRLcPTJQf3azMEw/GmBpHXA7N2eAQJStSqktZJjnq4qRCboBPnqEB866+PCregag==", + "license": "ISC" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/package.json b/package.json index fd5f774..98aa8b2 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "lunar-calendar": "^0.1.4", "next": "16.1.6", "react": "19.2.3", - "react-dom": "19.2.3" + "react-dom": "19.2.3", + "solarlunar": "^2.0.7" }, "devDependencies": { "@tailwindcss/postcss": "^4",