Files
saju-web/lib/daeun-calculator.ts
gahusb e233e18a55 feat: 카카오 앱 키 설정, 절기 기준 계산, 대운 정밀화
카카오 앱 키 설정:
- 환경 변수(.env.local)를 통한 안전한 키 관리
- NEXT_PUBLIC_KAKAO_APP_KEY 환경 변수 사용
- layout.tsx에서 환경 변수 읽어서 Kakao SDK 초기화
- .env.local 템플릿 파일 생성 (키 발급 가이드 포함)

음력 변환 정확도 개선:
- 24절기 계산 라이브러리 구현 (solar-terms.ts)
- 절기 기준 월주 계산으로 정확도 향상
- 입춘, 경칩, 청명 등 12개 월 절기 지원
- getSolarTermMonthBranch() - 절기 기준 월 지지 계산
- getCurrentSolarTerm() - 현재 절기 확인
- 사주 결과 페이지에 절기 정보 표시

대운 시작 나이 정밀 계산:
- 절기 기준 대운수 계산 구현
- 양남음녀(순행), 음남양녀(역행) 정확한 일수 계산
- 3일 = 1세 공식 적용
- calculateDaeunStartAge() 함수로 정밀 계산
- 이전 평균 8세 → 실제 계산값 (1~10세 범위)
- 대운 섹션에 시작 나이 계산 근거 표시

문서화:
- SETUP.md 생성
  - 카카오 앱 키 발급 및 설정 가이드
  - 절기 기준 사주 계산 설명
  - 대운 계산 원리 설명
  - 음력 변환 사용법
  - 기술 스택 및 개발 환경

사주 결과 페이지 개선:
- 절기 정보 표시 (녹색 박스)
- 대운 시작 나이 설명 추가
- 사용자에게 계산 원리 투명하게 공개

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 00:07:59 +09:00

189 lines
5.7 KiB
TypeScript

import { HEAVENLY_STEMS, EARTHLY_BRANCHES, HEAVENLY_STEMS_KR, EARTHLY_BRANCHES_KR } from './saju-calculator';
/**
* 대운 (大運) 정보
*/
export interface DaeunPillar {
age: number; // 시작 나이
startYear: number; // 시작 년도
endYear: number; // 끝 년도
stem: string; // 천간
branch: string; // 지지
stemKr: string; // 천간 한글
branchKr: string; // 지지 한글
}
/**
* 대운 시작 나이 정밀 계산
* @param birthYear 생년
* @param birthMonth 생월
* @param birthDay 생일
* @param gender 성별
* @param isYangYear 양년 여부
* @returns 대운 시작 나이
*/
function calculateDaeunStartAge(
birthYear: number,
birthMonth: number,
birthDay: number,
gender: 'male' | 'female',
isYangYear: boolean
): number {
const { getDaysToNextSolarTerm, getCurrentSolarTerm, getSolarTermDate } = require('./solar-terms');
// 양남음녀는 순행 (다음 절기까지), 음남양녀는 역행 (이전 절기부터)
let days: number;
if ((gender === 'male' && isYangYear) || (gender === 'female' && !isYangYear)) {
// 순행: 생일부터 다음 절기까지의 일수
days = getDaysToNextSolarTerm(birthYear, birthMonth, birthDay);
} else {
// 역행: 이전 절기부터 생일까지의 일수
const currentTerm = getCurrentSolarTerm(birthYear, birthMonth, birthDay);
const termDate = getSolarTermDate(birthYear, currentTerm);
let termYear = termDate.year;
let termMonth = termDate.month;
// 대한, 소한 처리
if (currentTerm >= 22 && birthMonth >= 2) {
termYear = birthYear;
} else if (currentTerm >= 22) {
termYear = birthYear - 1;
}
const termDateObj = new Date(termYear, termMonth - 1, termDate.day);
const birthDateObj = new Date(birthYear, birthMonth - 1, birthDay);
const diffTime = birthDateObj.getTime() - termDateObj.getTime();
days = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
}
// 3일 = 1세 (대운수)
// 정확히는 3일당 1세이지만, 일수를 3으로 나눈 몫
const startAge = Math.floor(days / 3);
// 최소 1세, 최대 10세로 제한
return Math.max(1, Math.min(10, startAge));
}
/**
* 대운 계산
* @param birthYear 생년
* @param birthMonth 생월
* @param birthDay 생일
* @param gender 성별
* @param monthStem 월주 천간 인덱스
* @param monthBranch 월주 지지 인덱스
* @returns 대운 배열 (10년 단위)
*/
export function calculateDaeun(
birthYear: number,
birthMonth: number,
birthDay: number,
gender: 'male' | 'female',
monthStem: string,
monthBranch: string
): DaeunPillar[] {
const monthStemIndex = HEAVENLY_STEMS.indexOf(monthStem as any);
const monthBranchIndex = EARTHLY_BRANCHES.indexOf(monthBranch as any);
if (monthStemIndex === -1 || monthBranchIndex === -1) {
return [];
}
// 양남음녀(陽男陰女)는 순행, 음남양녀(陰男陽女)는 역행
const yearStemIndex = (birthYear - 1900 + 6) % 10;
const isYangYear = yearStemIndex % 2 === 0; // 양년
let isForward: boolean;
if (gender === 'male') {
isForward = isYangYear; // 양남: 순행, 음남: 역행
} else {
isForward = !isYangYear; // 양녀: 역행, 음녀: 순행
}
// 대운 시작 나이 정밀 계산 (절기 기준)
const startAge = calculateDaeunStartAge(birthYear, birthMonth, birthDay, gender, isYangYear);
const daeunList: DaeunPillar[] = [];
for (let i = 0; i < 8; i++) {
const age = startAge + (i * 10);
const startYear = birthYear + age;
const endYear = startYear + 9;
let stemIndex: number;
let branchIndex: number;
if (isForward) {
// 순행: 월주에서 증가
stemIndex = (monthStemIndex + i + 1) % 10;
branchIndex = (monthBranchIndex + i + 1) % 12;
} else {
// 역행: 월주에서 감소
stemIndex = (monthStemIndex - i - 1 + 100) % 10;
branchIndex = (monthBranchIndex - i - 1 + 120) % 12;
}
daeunList.push({
age,
startYear,
endYear,
stem: HEAVENLY_STEMS[stemIndex],
branch: EARTHLY_BRANCHES[branchIndex],
stemKr: HEAVENLY_STEMS_KR[stemIndex],
branchKr: EARTHLY_BRANCHES_KR[branchIndex]
});
}
return daeunList;
}
/**
* 현재 대운 찾기
* @param daeunList 대운 목록
* @param currentYear 현재 년도
*/
export function getCurrentDaeun(daeunList: DaeunPillar[], currentYear: number): DaeunPillar | null {
for (const daeun of daeunList) {
if (currentYear >= daeun.startYear && currentYear <= daeun.endYear) {
return daeun;
}
}
return null;
}
/**
* 대운 해석
* @param daeun 대운 정보
* @param dayStem 일간
*/
export function getDaeunDescription(daeun: DaeunPillar, dayStem: string): string {
const age = daeun.age;
const ganzi = `${daeun.stem}${daeun.branch}`;
let description = `${age}세부터 ${age + 9}세까지의 10년은 ${daeun.stemKr}${daeun.branchKr}(${ganzi}) 대운입니다. `;
// 대운 천간과 일간의 관계에 따른 기본 해석
const stemIndex = HEAVENLY_STEMS.indexOf(daeun.stem as any);
if (age < 20) {
description += '청소년기로 학업과 기초를 다지는 시기입니다. ';
} else if (age < 40) {
description += '성장과 발전의 시기로 사회활동이 왕성한 때입니다. ';
} else if (age < 60) {
description += '안정과 성숙의 시기로 경험이 쌓이는 때입니다. ';
} else {
description += '원숙한 시기로 인생의 지혜를 나누는 때입니다. ';
}
if (stemIndex % 2 === 0) {
description += '적극적이고 외향적인 활동이 유리합니다.';
} else {
description += '차분하고 내실을 다지는 것이 좋습니다.';
}
return description;
}