"생년월일시를 입력하면, 자미두수 점성술의 명반을 자동으로 그리고 길흉 격국까지 판정해 주는 웹 계산 엔진"입니다. 점술 그 자체보다도, 천문 계산 + 규칙 기반 패턴 인식 + 지식 베이스가 하나로 묶인 도메인 엔진의 구조가 학습 가치의 핵심입니다. 별점 앱이 아니라 "규칙이 복잡한 도메인을 코드로 모델링하는 법"의 교본으로 읽으세요.
자미두수는 겉보기엔 신비롭지만, 실제로는 "입력값(생년월일시) → 규칙표 룩업 → 결과 배치 → 패턴 판정"의 결정론적 계산입니다. 세금 계산 소프트웨어가 소득을 넣으면 세법 조항을 적용해 세액을 뱉어내는 것과 똑같습니다.
이 레포는 그 "세법"에 해당하는 천간·지지 룩업 테이블, 사화(四化) 변환표, 격국 판정 규칙 41종을 전부 TypeScript로 박아 넣었습니다. 점성술을 믿든 안 믿든, "모호한 인간 지식을 어떻게 결정론적 코드로 굳히는가"를 보여주는 살아있는 사례입니다.
ZiweiChart라는 타입으로 정의된 JSON 데이터 구조이며, 12개의 Palace 객체와 각 궁의 Star[] 배열로 표현됩니다.자미두수는 보통 비싼 강의나 폐쇄적인 유료 앱으로만 접할 수 있었습니다. 이 레포는 배치 알고리즘 · 사화 시스템 · 1,100줄짜리 격국 지식 베이스 · 고전 원문 데이터를 전부 코드로 공개했습니다. README는 한발 더 나아가 51만 8,400개의 명반 샘플 데이터셋(5.5GB)까지 GitHub Releases로 풀었습니다 — 소형 모델 파인튜닝·RAG 검색 소스로 쓰라는 의도입니다.
이 프로젝트의 진짜 노림수는 점술 앱이 아니라 LLM에게 먹일 정형 지식입니다. README는 "AI 해석 프롬프트와 백엔드 API는 일부러 뺐으니, 공개된 지식 베이스를 근거로 /api/interpret를 직접 만들어 아무 LLM에나 붙이라"고 안내합니다. 격국 판정(코드) + 고전 원문(데이터) + 51만 샘플(파인튜닝) = 전통 점술을 위한 RAG/파인튜닝 풀스택 재료를 한 레포에 담은 셈입니다.
자미두수에는 여러 유파가 있는데, 이 레포는 倪海厦(니하이샤)의 정통 노선을 따르며 "비성파(飛星派)"를 명시적으로 거부합니다. 코드 곳곳에 "사화성은 영원히 고정, 움직이지 않는다(四化星永遠固定不動)"는 주석이 박혀 있고, 비성파의 핵심 기법인 "궁간자화(宮干自化)"는 함수로 구현은 해놓되 배치 과정에서 일부러 비활성화했습니다. 학술적 입장을 코드 설계로 표현한 흔치 않은 사례입니다.
| ziwei-doushu | iztro (라이브러리) | 유료 점술 앱 | 맨몸 LLM에 질문 | |
|---|---|---|---|---|
| 명반 계산 | iztro 래핑 | 핵심 기능 | 있음(블랙박스) | 부정확 |
| 격국 자동 판정 | 41종 + 출처 | 없음 | 일부 | 환각 위험 |
| 고전 원문 데이터 | 3대 고서 내장 | 없음 | 없음 | 없음 |
| 학습용 코드 공개 | MIT 전체 | MIT 전체 | 폐쇄 | 해당 없음 |
| AI 학습 데이터 | 51만 샘플 제공 | 없음 | 없음 | 해당 없음 |
중요한 한계를 분명히 해둡니다. 일부러 불완전한 스냅샷으로 공개됐습니다. 백엔드 API(/api/interpret 등)와 인증·결제 관련 코드가 빠져 있습니다. 반면 UI 컴포넌트(ChartBoard·InsightPanel 등)와 lib/ziwei/db-analysis.ts(stub)는 포함돼 있어 npm install 후 npm run dev로 프런트는 실행됩니다. npm run build는 미완성 API import 때문에 실패할 수 있습니다.
따라서 이 레포의 가치는 "실행"이 아니라 읽고 배우는 데 있습니다 — 핵심 알고리즘·지식 라이브러리(lib/)는 온전하며, 그게 본체입니다. 커밋 이력이 소수(초기 공개 기준 12개, 대부분 README·문서 수정)이며 코드 자체는 단일 스냅샷으로 공개된 점을 감안하세요.
이 레포는 평범한 Next.js 앱처럼 보이지만, 무게중심은 화면(프론트)이 아니라 lib/ 아래의 순수 함수·데이터에 있습니다. 계층별로 끊어 보면 이렇게 됩니다.
| 계층 | 구성 요소 | 기술 / 버전 | 역할 |
|---|---|---|---|
| 프레임워크 | Next.js App Router | next ^15.5.15 | 페이지 라우팅·SSR·SEO. README엔 "14"라 적혔으나 실제 핀은 15 |
| UI | React + Tailwind + Framer Motion | react ^19.2.5 / tailwind ^3.4 / framer-motion ^11.3 | 명반 보드·랜딩 애니메이션·반응형 |
| 언어 | TypeScript | typescript ^5 | 도메인 타입(ZiweiChart, Palace, Pattern) 정의 |
| 점성 코어 ★ | iztro | iztro ^2.5.8 | 실제 명반 계산 엔진. 이 레포는 이걸 래핑 |
| 역법 변환 | lunar-javascript | lunar-javascript ^1.7.3 | 양력↔음력 변환, 간지(干支) 산출 |
| 지식(본체) | lib/ziwei/, lib/classics/, lib/nihai/ | 순수 TS 데이터·함수 | 격국 규칙·사화표·고전 원문·니하이샤 三紀 전집 |
| AI(미사용) | @anthropic-ai/sdk | ^0.27.0 | OSS엔 미사용. .env 기본값은 DeepSeek |
| 인프라(닫힘) | pg / ioredis / Vercel·Cloudflare | pg ^8.20 / ioredis ^5.10 | Postgres·Redis·배포. 백엔드는 비공개 |
| 공유/내보내기 | html2canvas | ^1.4.1 | 명반을 이미지로 캡처해 공유 |
자동차에 비유하면 — 엔진(명반 계산)은 iztro라는 기성품을 사 왔습니다. 이 레포가 직접 만든 건 그 위에 얹은 계기판과 내비게이션(타입 모델 + 격국 판정 + 고전 지식 DB)입니다. "엔진을 직접 안 만들었다"가 약점이 아니라, 검증된 엔진 위에서 도메인 가치(해석·판정·지식)에 집중한 합리적 설계입니다.
astro.bySolar('2000-1-1', 시, '남', ...) 한 줄로 12궁·별 배치를 계산합니다. lunar-javascript: 양력 날짜를 음력·간지(천간지지)로 바꾸는 역법 엔진. 자미두수는 음력·간지 기반이라 이 변환이 필수입니다.핵심 함수 generateChart()는 점성 계산을 직접 하지 않고 iztro에 위임한 뒤, iztro의 출력을 이 프로젝트만의 타입 ZiweiChart로 다시 매핑합니다. 외부 라이브러리의 데이터 모양에 앱 전체가 종속되지 않도록 "번역 막(膜)"을 한 겹 둔 것입니다. DDD에서 말하는 부패 방지 계층(Anti-Corruption Layer)의 교과서적 예시입니다.
// lib/ziwei/algorithm.ts (요지)
const astrolabe = astro.bySolar(`${y}-${m}-${d}`, hour, gender, true, 'zh-CN');
const palaces = astrolabe.palaces.map(p => ({
name: p.name, // 命宮·財帛·官祿…
branch: p.earthlyBranch,
stars: [...p.majorStars, ...p.minorStars, ...p.adjectiveStars]
.map(s => ({ name:s.name, type: mapStarType(s),
brightness: mapBrightness(s.brightness) })),
}));
// → 외부 iztro 모델을 우리 ZiweiChart 타입으로 격리 변환
자미두수 규칙은 if-else 지옥이 되기 쉽습니다. 이 레포는 대신 규칙을 데이터(배열·맵)로 표현합니다. 대표가 사화표입니다 — 천간 10개 각각에 대해 [녹·권·과·기] 4개 별을 매핑한 2차원 표 하나로, 수십 줄의 분기를 인덱싱 한 번으로 대체합니다.
// lib/ziwei/constants.ts — 四化表 (일부)
export const SI_HUA_TABLE = {
甲: ['廉貞','破軍','武曲','太陽'], // [化祿, 化權, 化科, 化忌]
庚: ['太陽','武曲','太陰','天同'],
// … 천간 10개 전부
};
// 연도 → 천간 인덱스 : 결정론적 산식
const getYearStemIndex = (year) => (((year - 4) % 10) + 10) % 10;
전통 지식은 보통 "갑년에는 염정이 녹이 되고…"처럼 표(table)로 전승됩니다. 그 표를 그대로 코드의 표로 옮기면, 원전과 코드를 1:1로 대조 검증할 수 있고 버그도 줄어듭니다. "전문가가 읽는 표 ≈ 프로그램이 읽는 표"가 되도록 만든 게 이 설계의 묘미입니다.
가장 무거운 파일 patterns.ts(1,118줄)는 작은 규칙 엔진입니다. detectPatterns(chart) 하나가 detectJunChenQingHui()(군신경회), detectShaPoLang()(살파랑) 같은 독립된 판정 함수 41개를 차례로 호출합니다. 각 격국은 동일한 타입으로 표준화됩니다.
// 격국 타입 — 모든 패턴이 같은 모양
interface Pattern {
name: string;
level: 'excellent' | 'good' | 'neutral' | 'caution'; // 上格~惡格
conditions: { required: ...; bonus: ...; breaking: ... }; // 必須/加分/破格
source: string; // 예: 《紫微斗數全書·君臣慶會格》 — 古書 출처 명시
}
// 기하 헬퍼 — 한 궁에서 三方四正 네 자리 계산
const sanFangSiZheng = (m) => [m, (m+4)%12, (m+8)%12, (m+6)%12];
각 격국 객체에 source 필드로 어느 고서 몇 장에서 나온 규칙인지를 박아둡니다. 이는 brooks-lint가 코드 진단마다 책을 인용하게 만든 것과 똑같은 발상 — "근거 없는 판정은 판정이 아니다"를 타입 차원에서 강제해, 점술의 자의성을 줄이고 검증 가능성을 높입니다.
| 개념 | 코드 표현 | 설명 |
|---|---|---|
| 12궁 | Palace[] (length 12) | 命宮·兄弟·夫妻·子女·財帛·疾厄·遷移·僕役·官祿·田宅·福德·父母 |
| 지지 인덱스 | (branch+6)%12 = 대궁 | 맞은편 궁(對宮) 계산. +4·+8은 삼합(三合) |
| 별 분류 | major/minor/lucky/sha | SHA_STARS(擎羊·陀羅·火星·鈴星…) vs LUCKY_STARS(文昌·文曲·左輔…) |
| 밝기 | bright / dim | 廟·旺 → 길하게 작용 / 陷·不 → 흉하게 작용 |
| 오행국 | 水二·木三·金四·土五·火六 | 대한(大限) 시작 나이를 정하는 기준 |
app/(화면)과 lib/(로직)가 칼같이 분리돼 있습니다. 페이지는 'use client'로 브라우저에서 generateChart()를 직접 부르고, 계산 로직은 화면을 전혀 모릅니다 — "두꺼운 도메인 라이브러리, 얇은 UI"입니다. 이 레포를 공부할 땐 app/은 건너뛰고 바로 lib/ziwei/부터 읽으세요. 거기에 90%의 배울 거리가 있습니다.
이 레포의 최고 교본 가치입니다. "운명"처럼 막연한 개념을 ZiweiChart → Palace[] → Star[], Pattern{level, conditions, source}처럼 닫힌 타입 계층으로 표현했습니다. 점술이 아니라 법률·세무·보험·게임 룰처럼 "전문가만 아는 복잡한 규칙"을 코드로 옮길 때 그대로 응용할 수 있습니다.
patterns.ts처럼 "조건 + 출처"를 가진 판정 객체 배열로 모델링해 보세요.constants.ts의 SI_HUA_TABLE, TIANKUI_TABLE, LUCUN_TABLE은 분기문을 데이터로 바꾸는 기법의 살아있는 예제입니다. 조건이 많을수록 코드가 아니라 표를 늘리는 방식이 왜 유지보수에 유리한지 체감할 수 있습니다. 원전(전문가의 표)과 코드(프로그램의 표)가 1:1 대응돼 검증도 쉽습니다.
generateChart()가 iztro를 직접 노출하지 않고 자체 타입으로 번역하는 방식은, 실무에서 결제 SDK·지도 API·LLM SDK 같은 외부 의존성을 다룰 때 그대로 쓰는 패턴입니다. 외부 모델이 바뀌어도 번역 함수 한 곳만 고치면 되는 안전망의 가치를 배울 수 있습니다.
"결정론적 엔진 → 전수 데이터 생성 → 파인튜닝/RAG"라는 파이프라인은 이 레포만의 독특한 학습거리입니다. 규칙이 명확한 도메인이라면 가능한 입력을 전수 계산해 (입력→출력) 쌍을 무한 생성할 수 있다는 발상은, 합성 데이터(synthetic data) 전략의 실전 예시입니다.
classics/index.ts의 searchClassics(query)는 DB 없이 정적 TS 객체만으로 ±40자 문맥 스니펫과 <mark> 하이라이트를 만들어냅니다(HTML 이스케이프 포함). "검색에 꼭 DB·엘라스틱서치가 필요한 건 아니다"는 걸 보여주는 가벼운 예제입니다.
자미두수의 예언 능력 자체는 과학적으로 검증된 바 없습니다. 이 자료는 그 믿음을 옹호하지 않습니다. 여기서 배울 것은 점술의 효력이 아니라, 복잡한 규칙 체계를 깔끔한 코드 구조로 옮기는 엔지니어링 기법입니다. 결과 해석을 인생 결정의 근거로 삼지는 마세요.
| 항목 | 요구사항 |
|---|---|
| 필수 | Node.js 18+ , npm/pnpm |
| 설치 | git clone → npm install (.npmrc에 legacy-peer-deps=true 설정됨, React19↔@types/react18 충돌 회피) |
| 실행 | cp .env.example .env.local → npm run dev (Next.js :3000) |
| 주의 | 미커밋 컴포넌트·API 때문에 npm run build는 실패. 코드 읽기 학습용으로 권장 |
| AI 해석 연결 시 | DeepSeek/OpenAI 호환 API 키 (직접 /api/interpret 구현 필요) |
| 데이터셋 | 51만 샘플은 Releases v3.0-samples 별도 다운로드 (5.5GB, 3분할 zip) |
| 하드웨어 | 일반 노트북이면 충분. 명반 계산은 순수 JS 연산이라 매우 가벼움 |
레포를 클론하고 lib/ziwei/algorithm.ts(7.3KB)만 끝까지 읽으세요. generateChart()가 입력을 받아 iztro를 부르고, 그 결과를 어떻게 ZiweiChart 타입으로 매핑하는지 — mapStarType·mapBrightness의 분기 기준을 종이에 옮겨 적어 보면 도메인 모델링이 한눈에 들어옵니다.
constants.ts의 SI_HUA_TABLE 10줄을, 위키백과나 자미두수 입문 자료의 "천간사화표"와 한 칸씩 비교해 보세요. 코드의 표 = 전문가의 표임을 직접 확인하면, 테이블 주도 개발이 왜 검증에 유리한지 체감됩니다. 틀린 칸이 있는지도 찾아보세요(좋은 PR 기회).
이 레포의 무거운 UI를 걷어내고, iztro와 lunar-javascript만 설치해 "생년월일시 입력 → 12궁 별 배치를 콘솔에 출력"하는 30줄짜리 Node 스크립트를 작성하세요. 그러면 이 레포가 "iztro 위에 무엇을 더했는지"가 선명해집니다.
patterns.ts의 detectShaPoLang 같은 기존 함수를 본떠, 새 격국 판정 함수를 하나 작성하고 detectPatterns 디스패처에 등록하세요. 삼방사정((m+4)%12 등) 헬퍼로 별 조합을 검사하고, source에 출처를 붙이는 것까지가 핵심 — 규칙 엔진 확장 패턴을 손에 익힙니다.
비공개인 백엔드를 직접 재구성하세요. generateChart() + detectPatterns()의 JSON 결과와 classics/의 관련 원문을 RAG 컨텍스트로 묶어 LLM에 던지고, 한국어 해석을 받아오는 API 라우트를 작성합니다. "구조화된 도메인 데이터 + 원전 근거 → LLM 해석"이라는 RAG 파이프라인 전체를 한 바퀴 돌려보는 종합 과제입니다.
| 주차 | 주제 | 할 일 |
|---|---|---|
| 1주차 | 도메인 읽기 | lib/ziwei/ 전체 통독. ZiweiChart 타입 트리를 직접 그려보고, 자미두수 입문 글 1편으로 도메인 배경 잡기 |
| 2주차 | 테이블 주도 + 규칙 엔진 | 《Code Complete》의 Table-Driven Methods 장 읽기. patterns.ts 구조를 분석해 "조건+출처" 규칙 객체 패턴 정리. 과제 4 수행 |
| 3주차 | 외부 의존성 격리 | DDD의 Anti-Corruption Layer 개념 학습. generateChart의 매핑 계층을 본떠, 임의의 외부 API를 자체 타입으로 래핑하는 연습 |
| 4주차 | 합성 데이터 + RAG | 51만 샘플 생성 원리(전수 계산) 이해. 과제 5로 "구조화 데이터 + 원전 → LLM 해석" RAG 파이프라인 구축 |
| 용어 | 뜻 |
|---|---|
| 紫微斗數 (자미두수) | 생년월일시로 12궁에 별을 배치해 운명을 읽는 동양 점성술. 천간·지지 규칙 기반이라 컴퓨터로 정확히 재현 가능 |
| 命盤 (명반) | 자미두수의 결과물. 12궁 격자에 별이 배치된 "운명 지도". 코드로는 ZiweiChart 타입 |
| 宮 (궁) / 12궁 | 명반의 12개 영역. 命宮(자아)·財帛(재물)·官祿(직업) 등 삶의 영역별로 별을 담는 칸 |
| 主星 (주성) / 14주성 | 紫微·天機·太陽·武曲 등 운명을 좌우하는 핵심 14개 별 |
| 四化 (사화) | 천간에 따라 특정 별이 화록(化祿)·화권(化權)·화과(化科)·화기(化忌)로 변하는 시스템. SI_HUA_TABLE로 구현 |
| 格局 (격국) | 특정 별 조합이 이루는 길흉 패턴. 君臣慶會·殺破狼 등. patterns.ts가 41종 판정 |
| 三方四正 (삼방사정) | 한 궁을 볼 때 함께 보는 네 자리(자기·삼합 2개·대궁). [m,(m+4)%12,(m+8)%12,(m+6)%12] |
| 大限 (대한) | 10년 단위의 운(運) 구간. 五行局에 따라 시작 나이가 정해짐 |
| 眞太陽時 (진태양시) | 출생지 경도에 따른 실제 태양 시각 보정. cities.ts가 도시 경도로 계산 |
| Table-Driven (테이블 주도) | 분기문 대신 룩업 테이블(배열·맵)로 규칙을 표현하는 기법. 이 레포 설계의 뼈대 |
| Anti-Corruption Layer | 외부 라이브러리(iztro)의 모델을 자체 타입으로 번역·격리하는 DDD 패턴 |
| iztro | 실제 명반 계산을 담당하는 오픈소스 라이브러리. 이 레포가 래핑해 사용 |
| 飛星派 (비성파) | 사화가 움직인다고 보는 자미두수 유파. 이 레포는 倪海厦 노선을 따라 의도적으로 배제 |
| 합성 데이터 (Synthetic Data) | 결정론적 엔진으로 가능한 입력을 전수 계산해 만든 학습 데이터. 51만 샘플이 그 예 |
| 링크 | 설명 |
|---|---|
| GitHub 저장소 | 소스 전체. lib/ziwei/가 핵심 읽을거리 |
| TrendShift 페이지 | 트렌딩 추이 |
| iztro (계산 엔진) | 이 레포가 래핑하는 명반 계산 라이브러리. 직접 써보면 차이가 보임 |
| lunar-javascript | 양력↔음력·간지 변환 라이브러리 |
| Next.js App Router 문서 | app/ 디렉토리 구조의 공식 가이드 |
| Anti-Corruption Layer (Fowler) | 외부 의존성 격리 패턴의 이론적 배경 |