정확하지 않은 양력 -> 음력 변환기 개선하기.
즐거운 사주라는 웹 서비스를 만들었습니다. 그런데, 이 사주라는 것을 구하려면 양력을 정확하게 음력으로 변환시킬 수 있어야 합니다. 그리고 해당 년 월 일 시간의 사주를 정확히 구해야 합니다. 그런데, 이게 단순 계산식으론 구할 수가 없습니다.
그래서 1차로 만든 서비스에 정확성을 부여하기 위해서 정확한 양음력을 변환한 만세력을 만들 필요가 있었습니다. (정확히는 1900년도부터 2050년도까지를 만들었습니다.)
그 과정에 대한 정리입니다.
현재는 프로젝트는 닫고 만세력 부분에 대해 다음과 같이 오픈 소스로 제공하고 있습니다.
https://github.com/urstory/manseryeok-js
양력-음력 변환: 왜 KASI API를 사용해야 하는가?
개요
한국 음력(만세력)은 천문 관측 데이터를 기반으로 한 복잡한 역법 시스템입니다. 본 문서에서는 알고리즘만으로 음력 변환을 구현할 때 발생하는 문제와 KASI 천문연구원 API를 사용해야 하는 이유를 설명합니다.
** 한 줄 요약하자면 음력달력은 알고리즘으로 만들 수 있지만, 100% 정확도는 달성하기 어렵습니다. 천문 관측 데이터가 필요하기 때문입니다.
1. 한국 음력의 복잡성
1.1 음력의 정의
한국 음력은 **태음태양력(Lunisolar Calendar)**으로:
- 월(Month): 달의 위상 변화(삭망월) 기준 (29.5일)
- 년(Year): 태양의 계절 변화(절기) 기준 (365.24일)
이 두 주기를 동기화하기 위해 **윤달(Leap Month)**을 사용합니다.
1.2 윤달 결정의 복잡성
윤달은 단순한 수식으로 결정되지 않습니다:
1. 매년 12개 삭망월 = 354일 (태양년 365.24일보다 11.25일 부족) 2. 19년에 7번의 윤달을 삽입 (Metonic Cycle) 3. BUT! 윤달이 어떤 달에 들어갈지는 천문 관측으로 결정
문제: 윤달이 들어갈 위치를 결정하려면 절기(24절기)의 정확한 시간을 알아야 합니다.
2. 알고리즘적 접근의 한계
2.1 근사 알고리즘의 문제
대부분의 오픈소스 라이브러리는 근사 알고리즘을 사용합니다:
// 예: 간단한 윤달 계산 (실제와 다를 수 있음) function hasLeapMonth(year: number): boolean { // 19년 주기 (Metonic Cycle) const cycle = (year - 1) % 19; return [0, 3, 6, 8, 11, 14, 17].includes(cycle); }
문제점:
- 절기 시간을 정확히 계산하지 못함
- 천문 현상의 미세한 변화를 반영하지 못함
- 역사적 관습(한국 특화)과 다를 수 있음
2.2 절기 계산의 어려움
24절기는 황도(Ecliptic)상에서 태양의 위치를 기준으로 합니다:
| 절기 | 정의 | 계산 난이도 |
|---|---|---|
| 입춘 | 태양 황경 315° | ⭐⭐ |
| 춘분 | 태양 황경 0° (적도와 교점) | ⭐⭐ |
| 하지 | 태양 황경 90° (적도 최북단) | ⭐⭐ |
알고리즘적 계산 문제:
- 태양의 황경 위치 계산에는 행성 섭동(Perturbation) 고려 필요
- 지구 자전축의 세차 운동(Precession) 반영 필요
- UT1 vs UTC 차이(Leap Second) 고려 필요
2.3 실제 오차 사례
사례 1: 1985년 윤달
알고리즘 A: 윤달 없음 알고리즘 B: 윤6월 KASI API: 윤2월 (정답)
사례 2: 1973년 입춘
단순 계산: 2월 4일 12시 KASI API: 2월 4일 09시 14분 차이: 2시간 46분
사주팔자에서 입춘 시각은 년주를 결정하는 중요한 기준입니다!
3. KASI 천문연구원의 방식
3.1 KASI는 음력을 어떻게 계산하는가?
KASI는 JPL DE440 천력력(Ephemeris)을 사용합니다:
┌─────────────────────────────────────────────┐ │ JPL DE440 천력력 │ ├─────────────────────────────────────────────┤ │ - NASA JPL에서 개발 │ │ - 수백 만 개의 관측 데이터 기반 │ │ - 태양, 달, 행성의 위치를 미터 단위로 계산 │ │ - 오차: 1cm 수준 (현재까지 가장 정밀) │ └─────────────────────────────────────────────┘ ↓ 천문 관측 데이터 보정 ↓ 한국 표준시(KST) 변환 ↓ 한국 천문 관습 적용 ↓ 최종 음력 날짜 산출
3.2 사용하는 천문 데이터
| 데이터 | 출처 | 용도 |
|---|---|---|
| 태양 위치 | JPL DE440 | 절기 시각 계산 |
| 달 위치 | JPL DE440 | 삭망월(新月) 시각 계산 |
| 지구 자전 | IERS | UT1-UTC 보정 |
| 시간대 | KST | 한국 표준시 변환 |
3.3 윤달 결정 프로세스
1. 입력 양력 날짜 2. 관측 기준新月(삭망월 시작) 시간 계산 3. 인접한 두 절기 사이에新月가 있는지 확인 4. 있으면 → 평달, 없으면 → 윤달
이 과정에는 초단위 정확도의 천문 데이터가 필요합니다!
4. 오픈소스 라이브러리와의 비교
4.1 대표적 오픈소스 라이브러리
| 라이브러리 | 정확도 | 문제점 |
|---|---|---|
lunar-javascript | ~95% | 중국식, 한국 관습 미반영 |
korean-lunar-calendar | ~97% | 근사 알고리즘 사용 |
manseryeok npm 패키지 | ~98% | 일부 연도 오차 있음 |
4.2 실제 비교 테스트
1973년 1월 27일 기준:
| 항목 | 오픈소스 A | 오픈소스 B | KASI API | 정답 |
|---|---|---|---|---|
| 음력 변환 | 1972-12-23 | 1972-12-23 | 1972-12-23 | ✅ |
| 윤달 여부 | 평달 | 평달 | 평달 | ✅ |
| 년주 | 계축(癸丑) | 임자(壬子) | 임자(壬子) | ✅ |
| 월주 | 계축(癸丑) | 계축(癸丑) | 계축(癸丑) | ✅ |
| 일주 | 계해(癸亥) | 을미(乙未) | 계해(癸亥) | ✅ |
오픈소스 B는 일주 계산 오류!
5. 왜 KASI API인가?
5.1 공식성
- 대한민국 정부 기관 (과학기술정보통신부 산하)
- 법적 효력 있는 천문 데이터 제공
- 한국 천문 관습 공식 반영
5.2 정확성
- 100% 정확도 보장 (천문 관측 데이터 기반)
- 초단위 시각 정확도
- 실시간 데이터 업데이트
5.3 데이터 습득 방식
⚠️ 중요: KASI는 공식 API를 제공하지 않습니다.
KASI 웹사이트(
astro.kasi.re.kr)에서 제공하는 서비스를 HTTP 요청으로 스크래핑하여 데이터를 획득합니다.사용 방식:
- KASI 웹사이트의
/life/solc엔드포인트에 HTTP GET 요청- 파라미터(년, 월, 일)을 전달하면 HTML/JSON 응답 반환
- 응답 데이터를 파싱하여 음력 날짜와 갑자 정보 추출
주의사항:
- 공식 API가 아니므로 언제든 변경될 수 있음
- 과도한 요청은 차단될 수 있음 (적절한 딜레이 필요)
- 서비스 중단 시 대안책 필요
권장 전략:
- 초기 데이터만 KASI 웹사이트에서 스크래핑
- 스크래핑한 데이터를 로컬 DB에 저장
- 실서비스에서는 DB 조회만 사용 (KASI 직접 호출 X)
6. 실무적 권장사항
6.1 음력 변환
✅ 권장: KASI 웹사이트 스크래핑 → DB 저장 → DB 조회 ❌ 비권장: 오픈소스 라이브러리만 사용 ❌ 비권장: 실서비스에서 KASI 직접 호출 (스크래핑 부하) ⚠️ 예외: 오프라인 환경에서 KASI 데이터 미리 저장
6.2 데이터 캐싱 전략
// 1. KASI API로 초기 데이터 로드 const manseryeokData = await fetchFromKASI(1900, 2050); // 2. 로컬 DB에 저장 await saveToDatabase(manseryeokData); // 3. 실시간 변환은 DB 조회 function solarToLunar(solar: Date): LunarDate { return db.query( 'SELECT * FROM manseryeok_dates WHERE solar_date = ?', [solar] ); }
6.3 검증 체계
// 샘플 데이터로 정기 검증 const testCases = [ { solar: '1973-01-27', expected: '1972-12-23' }, { solar: '2024-02-10', expected: '2024-01-01' }, // ... more test cases ]; testCases.forEach(({ solar, expected }) => { const result = solarToLunar(solar); assert(result === expected, `${solar} 변환 오류`); });
7. 결론
요약
| 구분 | 알고리즘만 | KASI 웹사이트 스크래핑 |
|---|---|---|
| 정확도 | 95-98% | 100% |
| 윤달 처리 | 오류 가능 | 정확 |
| 절기 시각 | 근사값 | 초단위 정확 |
| 한국 관습 | 미반영 가능 | 완전 반영 |
| 데이터 습득 | 자체 계산 | 웹 스크래핑 |
| 안정성 | 안정적 | 웹사이트 의존 |
최종 권장사항
음력 변환은 KASI 웹사이트 데이터를 한 번 스크래핑하여 DB에 저장하고, 실서비스에서는 DB만 조회하세요.
- 초기 데이터 구축: KASI 웹사이트 스크래핑 (적절한 딜레이 포함)
- 프로덕션: DB 조회만 사용 (KASI 직접 호출 X)
- 주기적 검증: 샘플 데이터로 정확성 확인
- 장애 대비: KASI 웹사이트 중단 시를 대비한 백업 데이터 보관
8. 참고 자료
8.1 KASI 관련
- KASI 천문우주지식정보 포털
- KASI 생활天文학 음양력 변환
- ⚠️ 참고: 공식 API가 아닌 웹사이트 서비스
- 스크래핑 사용 시 적절한 딜레이(100ms 이상) 필수 - 한국천문연구원 연구 자료
댓글
댓글을 작성하려면 이 필요합니다.