라벨이 사주명리인 게시물 표시

회고 — 타로에서 사주로, AI 협업의 진화

회고 — 타로에서 사주로, AI 협업의 진화 두 개의 프로젝트가 끝났다. 타로 마스터 20편, 사주명리 20편. 총 40편의 개발기를 쓰는 동안 AI와의 협업 방식은 계속 진화했다. 처음에는 "이게 되네?"라는 놀라움이었고, 끝에서는 "이것 없이 어떻게 했지?"라는 자연스러움이 됐다. 시리즈의 마지막 편에서 그 여정을 되돌아본다. 타로 — 속도의 발견 타로 프로젝트에서 발견한 것은 "속도"였다. AI와 함께하면 코드를 얼마나 빨리 짤 수 있는지. 78장의 카드 데이터를 수작업으로 입력하면 며칠이 걸릴 것을 AI가 몇 시간 만에 생성해줬다. 리딩 로직, UI 컴포넌트, 상태 관리 — 전통적으로 2~3주가 걸릴 기능들이 며칠 만에 동작했다. 타로에서의 AI 협업은 일종의 "터보 부스터"였다. 내가 할 줄 아는 것을 더 빠르게 해주는 도구. 기존 개발 방식의 연장선에서, 속도만 극적으로 빨라진 느낌이었다. 이 단계에서의 핵심 발견은 "AI가 반복적인 작업에서 압도적으로 효율적이다"라는 것이었다. 78장 카드 데이터 같은 대량 생성 작업, 비슷한 패턴의 컴포넌트를 여러 개 만드는 작업, 라이브러리 설정이나 보일러플레이트 코드 작성 같은 것들. 사주 — 깊이의 발견 사주 프로젝트에서 발견한 것은 "깊이"였다. AI와 함께하면 혼자서는 접근하기 어려운 도메인의 깊은 곳까지 도달할 수 있다는 것. 사주명리는 타로와 차원이 다른 도메인이었다. 수천 년의 역사, 다양한 학파, 복잡한 관계 체계. 이 도메인에 혼자 뛰어들었다면 도메인 학습에만 몇 달이 걸렸을 것이다. AI와 함께하니 도메인의 지형도를 10분 만에 그리고, 각 요소의 깊은 규칙을 구현하면서 동시에 학습할 수 있었다. 사주에서의 AI 협업은 "터보 부스터"를 넘어 "탐험 파트너"였다. 낯선 영역을 함께 탐색하는 동반자. 속도뿐 아니라 도달 가능한 깊이 자체가...

설계 문서 우선 개발 — AI 시대의 게임 체인저

설계 문서 우선 개발 — AI 시대의 게임 체인저 코드를 한 줄도 쓰기 전에 문서를 먼저 쓴다. 개발자라면 누구나 아는 원칙이지만 실제로 지키는 사람은 드물다. 그런데 AI와 협업하면서 이 원칙이 단순한 모범 사례가 아니라 생산성의 핵심이라는 것을 깨달았다. 특히 사주명리처럼 복잡한 도메인에서는 설계 문서가 있고 없고의 차이가 하늘과 땅이었다. 타로 때의 방식 — 대화에서 코드로 타로 프로젝트를 돌이켜보자. 그때의 워크플로우는 이랬다. AI와 대화를 나누면서 "78장 카드의 데이터 구조를 어떻게 잡을까?"라고 묻고, AI가 제안하면 바로 코드로 옮긴다. 코드가 동작하면 다음 기능으로 넘어간다. 문제가 생기면 대화를 이어가며 수정한다. 이 방식은 빠르고 직관적이었다. 타로의 복잡도에서는 충분히 작동했다. 78장의 카드, 고정된 해석 텍스트, 비교적 단순한 리딩 로직. 전체 구조를 머릿속에 담을 수 있는 규모였기에 대화 기반으로도 일관성을 유지할 수 있었다. 하지만 사주 프로젝트에 들어가면서 이 방식의 한계가 드러났다. 천간지지 계산, 십신 관계, 합충형파해, 12운성, 지장간, 격국, 용신. 이 모든 요소가 서로 얽혀 있는 도메인에서 대화만으로 맥락을 유지하는 것은 불가능했다. 한계를 깨닫는 순간 사주 프로젝트 초기에도 타로 때와 같은 방식을 시도했다. AI와 대화하며 "십신 계산 로직을 구현해볼까"라고 하면 AI가 코드를 생성한다. 그런데 다음 세션에서 "지장간 분석을 추가하자"고 하면 AI가 이전 세션의 십신 로직과 일관되지 않는 구조를 제안하는 경우가 생겼다. 이유는 단순했다. AI의 대화 컨텍스트에는 한계가 있다. 이전 세션에서 어떤 결정을 내렸는지, 데이터 구조를 어떻게 잡았는지, 왜 그 방식을 선택했는지 — 이런 정보가 새 세션에서는 사라진다. 코드를 읽으면 "무엇"은 알 수 있지만 "왜"는 알 수 없다. 타로에서는 이 문제가 크지 않았다. 구조가...

학파 설정이라는 차별화 — '같은 사주, 다른 해석'

학파 설정이라는 차별화 — "같은 사주, 다른 해석" "이 앱에서 본 사주랑 다른 앱에서 본 사주가 달라요." 사주 앱을 만들면 반드시 마주치는 질문이다. 같은 생년월일시를 입력했는데 결과가 다르다니, 사용자 입장에서는 당연히 의아하다. 이 문제를 숨기는 대신 정면으로 드러내기로 했다. 그것이 "학파 설정"이라는 기능의 출발점이었다. 왜 같은 생년월일시에도 다른 결과가 나오는가 사주명리는 수천 년의 역사를 가진 학문이다. 그 긴 시간 동안 다양한 학파와 유파가 생겨났고, 각 학파마다 계산 방식에 미묘한 차이가 있다. 핵심적인 차이점은 크게 네 가지다. 야자시(夜子時) 처리. 밤 11시에서 자정 사이에 태어난 사람의 일주를 어떻게 정할 것인가. 한 학파는 밤 11시가 지나면 다음 날로 본다. 다른 학파는 자정이 지나야 다음 날로 본다. 이 차이 하나로 일주 전체가 바뀌고, 당연히 십신 관계도 전부 달라진다. 진태양시(真太陽時) 적용. 시계로 보는 시간과 실제 태양의 위치에 따른 시간은 다르다. 서울과 부산의 경도가 다르니 같은 시각이라도 태양의 위치가 다르다. 진태양시를 적용하는 학파는 태어난 지역의 경도에 따라 시간을 보정한다. 적용하지 않는 학파는 표준시를 그대로 사용한다. 서머타임(일광절약시간) 보정. 한국은 과거 몇 차례 서머타임을 시행한 적이 있다. 1948~1961년, 그리고 1987~1988년. 이 기간에 태어난 사람의 시간을 1시간 빼야 하는지 말아야 하는지. 서머타임 보정을 적용하는 앱과 그렇지 않은 앱에서 결과가 갈린다. 12운성 음간 순역. 12운성을 계산할 때 양간은 순행, 음간은 역행한다는 것이 전통적인 방식이다. 하지만 일부 학파에서는 음간도 순행으로 계산한다. 이 차이로 12운성 결과가 크게 달라질 수 있다. calculationSchool — 설정으로 풀다 대부분의 사주 앱은 이 중 하나의 방식을 선택해서 고정한다. 어떤 앱은 야자시를 적용하고, 어떤 앱은 적용하지...

콘텐츠로 사용자를 잡다 — 유명인 사주, MBTI, 오행 테스트

콘텐츠로 사용자를 잡다 — 유명인 사주, MBTI, 오행 테스트 사주 앱에 기능이 아무리 많아도, 사용자가 "내 사주를 본다"는 행위는 한 번이면 끝난다. 처음 방문해서 사주를 보고, 신기해하고, 그다음에는? 다시 올 이유가 없다. 이 문제의 답을 "콘텐츠"에서 찾았다. 기능과 콘텐츠의 차이 기능은 사용자가 입력하면 결과를 보여주는 것이다. 사주 분석, 궁합 분석, 일일 운세. 이런 기능들은 앱의 핵심이지만 그 자체로는 재방문 동기가 약하다. 사주는 바뀌지 않으니까. 콘텐츠는 다르다. 읽을거리, 탐색할 거리, 공유할 거리. "이 연예인의 사주가 이렇구나", "내 MBTI랑 일간이 이런 관련이 있네", "오행 중 나는 뭐가 강하지?" 같은 호기심을 자극하는 콘텐츠는 사용자를 다시 끌어들인다. 이 구분을 인식한 시점이 프로젝트의 전환점 중 하나였다. 사주 "분석 도구"에서 사주 "콘텐츠 플랫폼"으로 시야가 넓어진 순간이다. 유명인 사주 — CelebrityListPage "이 사람의 사주가 어떻길래 이렇게 성공했을까?" 사주에 관심 있는 사람이라면 누구나 한 번쯤 품는 궁금증이다. CelebrityListPage는 실제 유명인들의 생년월일시를 기반으로 사주를 분석해 보여주는 페이지다. 연예인, 기업인, 스포츠 선수, 역사적 인물 등 다양한 카테고리로 분류하고, 각 인물의 사주 원국과 핵심 특징을 정리했다. 이 기능의 기획 의도는 명확했다. 호기심 유발이 가장 크다. 자기 사주만 보는 것보다 유명인의 사주를 함께 보면 사주 분석의 설득력을 체감하기 쉽다. "이 사람이 편관격이라 리더십이 강한 거구나" 같은 연결고리가 생기면 자기 사주도 더 깊이 들여다보게 된다. 검색 유입 효과도 있다. "손흥민 사주", "아이유 사주" 같은 검색 키워드는 꾸준히 검색량이...

기능 폭발 — 궁합, 일일 운세, 토정비결까지

기능 폭발 — 궁합, 일일 운세, 토정비결까지 사주 분석 엔진이 안정되자 머릿속에 아이디어가 쏟아지기 시작했다. 궁합도 보고 싶고, 매일 운세도 보여주고 싶고, 토정비결도 넣고 싶다. 문제는 이 기능들 하나하나가 독립적인 프로젝트 규모라는 것이었다. 그런데 AI와 함께하니 이야기가 달라졌다. 핵심 엔진이 만든 확장의 토대 앞선 편들에서 다룬 룰 기반 엔진과 AI 해석 레이어, 이 하이브리드 아키텍처가 기능 확장의 핵심 토대가 됐다. 천간지지 계산, 오행 판별, 십신 분석, 합충형파해 판단 — 이 모든 로직이 모듈화되어 있었기 때문에 새로운 기능을 추가할 때 바닥부터 시작할 필요가 없었다. 궁합 분석을 예로 들면, 두 사람의 사주를 각각 분석하는 것은 기존 엔진이 이미 할 수 있는 일이다. 추가로 필요한 것은 "두 사주 간의 관계"를 분석하는 레이어뿐이었다. 일일 운세도 마찬가지다. 사용자의 사주 원국은 이미 있고, 오늘 날짜의 천간지지만 계산하면 그날의 운세를 뽑아낼 수 있다. 모듈화의 진짜 가치는 처음 설계할 때가 아니라, 확장할 때 드러난다. 이 프로젝트에서 그 교훈을 몸으로 체감했다. 궁합 분석 — CompatibilityPage 궁합은 사용자들이 가장 많이 찾는 기능 중 하나다. 연인, 부부, 비즈니스 파트너까지 두 사람의 상성을 궁금해하는 수요는 늘 존재한다. 구현의 핵심은 두 사주의 일간 관계였다. 일간끼리의 오행 상생상극 관계, 십신 관계, 천간합 여부를 기본으로 보고, 여기에 지지의 삼합이나 육합, 육충 관계까지 분석한다. 예를 들어 갑(甲)일간과 기(己)일간은 천간합이 되므로 기본적인 끌림이 있다고 본다. 반면 갑(甲)과 경(庚)은 편관 관계로 긴장감이 존재한다. AI에게 "궁합 분석에서 중요한 요소를 우선순위로 정리해줘"라고 요청했더니, 일간 관계, 용신 보완 여부, 합충 관계, 오행 균형 보완이라는 네 가지 축을 제안했다. 이 구조를 그대로 채택해 CompatibilityPage를 설계했다...

배포와 인프라 — $0으로 실서비스 운영하기

배포와 인프라 — $0으로 실서비스 운영하기 월 서버비 $0의 현실 사이드 프로젝트의 가장 큰 적은 서버 비용이다. 아이디어가 좋아도, 매달 나가는 호스팅비와 API 비용이 부담되면 결국 서비스를 내린다. "만들고 싶은 것"과 "운영할 수 있는 것" 사이에는 비용이라는 장벽이 있다. 이 사주 앱의 총 운영 비용은 $0이다. 도메인 비용을 제외하면, 호스팅, API, CDN, SSL 모든 것이 무료다. 어떻게 가능한지, 그리고 그 대가로 무엇을 포기했는지를 솔직하게 정리한다. 아키텍처: 정적 + 서버리스 전체 인프라는 두 축으로 구성된다. 정적 호스팅은 GitHub Pages가 담당한다. React 앱을 빌드한 결과물(HTML, CSS, JS, 이미지)을 GitHub Pages로 서빙한다. GitHub Pages는 공개 레포지토리에 대해 무료이고, CDN이 기본 제공되며, HTTPS도 자동 설정된다. AI API 프록시는 Cloudflare Workers가 담당한다. 클라이언트(브라우저)에서 직접 Groq API를 호출하면 API 키가 노출된다. Cloudflare Workers가 중간에서 프록시 역할을 한다. 클라이언트가 Workers에 요청을 보내면, Workers가 API 키를 붙여서 Groq API를 호출하고, 스트리밍 응답을 클라이언트에 전달한다. 이 구조에서 서버가 없다. 전통적인 의미의 백엔드 서버, 데이터베이스, 파일 스토리지 모두 없다. 사주 계산은 클라이언트 브라우저에서 실행되고(룰 엔진이 순수 JS), AI 해석은 서버리스 함수(Workers)를 통해 외부 API로 위임된다. GitHub Pages: 정적 호스팅의 선택 GitHub Pages를 선택한 이유는 단순하다. 무료이고, 안정적이고, 배포가 간단하다. 빌드 결과물을 특정 브랜치(gh-pages)에 푸시하면 자동으로 배포된다. GitHub Actions로 CI/CD 파이프라인을 구성하면, main 브랜치에 푸시할 때마다 빌드 → 사...

SEO와 콘텐츠 전략 — 사주 도메인의 검색 최적화

SEO와 콘텐츠 전략 — 사주 도메인의 검색 최적화 앱은 완성되었지만 분석 엔진, AI 해석, UI. 사주 앱의 핵심 기능이 모두 갖춰졌다. 그런데 한 가지 치명적인 문제가 있었다. 이 앱은 Single Page Application(SPA)이다. React로 만든 SPA는 기본적으로 빈 HTML에 자바스크립트가 모든 것을 렌더링한다. 구글 크롤러가 이 페이지를 방문하면 빈 페이지를 보게 된다. 아무리 좋은 사주 풀이 엔진을 만들어도, 검색에서 찾을 수 없으면 사용자는 오지 않는다. SPA의 SEO 한계 SPA의 SEO 문제는 잘 알려져 있지만, 실제로 직면하면 생각보다 까다롭다. 구글봇이 자바스크립트를 렌더링할 수 있다는 것은 사실이다. 하지만 모든 페이지를 렌더링해주지는 않는다. 크롤링 예산(crawl budget)이 있고, JS 렌더링은 추가 비용이 든다. 특히 새로 만든 작은 사이트에는 관대하지 않다. 메타 태그도 문제다. 사주 앱에서 "갑자(甲子) 일주 상세" 페이지와 "을축(乙丑) 일주 상세" 페이지는 각각 다른 title, description, og:image가 필요하다. SPA에서 이것을 클라이언트 사이드에서 동적으로 바꿔봐야, 소셜 미디어 크롤러나 검색 엔진 크롤러는 초기 HTML의 메타 태그만 읽는 경우가 많다. 세 가지 전략으로 이 문제를 해결했다. 전략 1: 프리렌더링 빌드 시점에 주요 페이지를 미리 렌더링해서 정적 HTML로 저장하는 방식이다. 사용자가 접속하면 정적 HTML이 먼저 로드되고, 이후 React가 hydration을 통해 인터랙티브 앱으로 전환된다. 프리렌더링 대상은 사용자가 직접 방문하는 "입구" 역할을 하는 페이지들이다. 메인 페이지, 가이드 페이지들, 칼럼 페이지들, 그리고 상세 정보 페이지들. 이 페이지들이 각각 고유한 title, description, og:image를 가진 완전한 HTML로 생성된다. 프리렌더링은 빌드 파이프라인의 일부...

오행 색상이 UI를 만든다 — 동양 미학과 모던 디자인

오행 색상이 UI를 만든다 — 동양 미학과 모던 디자인 색상이 먼저였다 사주 앱의 UI를 설계할 때 가장 먼저 결정한 것은 레이아웃이 아니라 색상이었다. 정확히 말하면 색상 시스템이었다. 오행(五行)에는 각각 고유한 색이 있다. 木은 녹색, 火는 적색, 土는 황색, 金은 은색(또는 백색), 水는 청색(또는 흑색). 이 다섯 색상이 사주명리의 모든 요소에 연결된다. 천간 갑(甲)은 목(木)이니 녹색, 지지 오(午)는 화(火)니 적색. 이 색상 체계가 수천 년간 이어져 온 것이다. 이것을 앱의 디자인 시스템으로 그대로 가져왔다. 타로 앱에서 카드의 메이저/마이너 구분이 색상으로 표현되었듯이, 사주 앱에서는 오행이 색상으로 표현된다. 다만 차이가 있다. 타로의 색상은 "분류"를 위한 것이었고, 사주의 색상은 "의미"를 담고 있다. 다섯 색상의 구체적 설계 원색 그대로 쓰면 눈이 아프다. 빨강, 초록, 노랑이 한 화면에 뒤섞이면 축제 현수막이 된다. 사주 앱에 어울리는 것은 절제된 동양적 색감이다. 각 오행의 색상을 모던한 앱에 맞게 조정했다. 木의 녹색은 에메랄드 계열로, 자연의 생동감을 살리되 눈에 편안하게. 火의 적색은 코랄에 가깝게, 강렬하지만 공격적이지 않게. 土의 황색은 따뜻한 엠버(amber) 톤으로, 흙의 안정감을 표현. 金의 은색은 쿨 그레이에 미세한 금속 광택을 더해, 차분한 기품을 표현. 水의 청색은 인디고에 가까운 깊은 파랑으로, 물의 깊이와 지혜를 연상. 이 다섯 색상에 대해 각각 메인 컬러, 배경용 연한 버전, 텍스트용 진한 버전, 보더용 중간 버전을 정의했다. 하나의 오행 색상이 네 가지 변형을 가지는 셈이다. 이 색상 시스템이 정의되자, 이후의 모든 UI 컴포넌트는 이 시스템 위에서 자연스럽게 만들어졌다. 명식표(命式表): 사주의 얼굴 명식표는 사주 앱에서 가장 중요한 시각적 요소다. 네 개의 기둥(연주, 월주, 일주, 시주)이 나란히 서 있고, 각 기둥에 천간(天干), 지지(地支), ...

궁통보감의 마법 — 프롬프트 엔지니어링과 참조 데이터

궁통보감의 마법 — 프롬프트 엔지니어링과 참조 데이터 "잘 써달라"는 통하지 않는다 룰 엔진이 완성되자, 구조화된 분석 데이터가 JSON 형태로 출력되기 시작했다. 오행 비율, 십신 관계, 용신, 합충형파해. 정확한 데이터다. 이제 이것을 AI에게 넘겨서 "사람이 읽을 수 있는 해석"으로 바꿔야 한다. 처음에는 단순하게 생각했다. 데이터를 넘기고 "잘 해석해줘"라고 하면 되지 않을까. 결과는 실망스러웠다. "목(木)이 강합니다. 성격이 곧고 직선적입니다." 틀린 말은 아니지만, 누구에게나 해당될 수 있는 일반적인 이야기였다. 사주 전문 앱에서 기대하는 깊이의 해석과는 거리가 멀었다. 분석 데이터에서 프롬프트로 처음 개선한 것은 프롬프트의 구조였다. AI에게 넘기는 정보를 체계적으로 정리했다. 프롬프트에 포함되는 핵심 정보는 다음과 같다. 사주 네 기둥의 천간과 지지 전체, 일간(日干)이 누구인지, 각 위치의 십신 관계, 지장간에 숨어 있는 오행과 십신, 오행 비율(퍼센트 단위), 판단된 용신과 그 근거, 주요 합충형파해 관계, 12운성 배치, 성별. 원칙은 하나였다. AI에게는 "계산"을 절대 시키지 않는다. 오행 비율이 木 30%, 火 20%, 土 15%, 金 25%, 水 10%라는 것은 이미 계산된 결과로 넘긴다. AI에게는 "이 비율이 이 사람의 삶에서 무엇을 의미하는지 설명해달라"고만 요청한다. 이렇게 구조화된 데이터를 넘기자 해석의 정확성이 올라갔다. "편재가 2개이고 정재가 1개, 용신이 금(金)"이라는 구체적 데이터가 있으니, AI도 "사업적 기질이 있지만 안정적 수입도 병행하는 것이 좋겠다"는 맥락 있는 해석을 내놓기 시작했다. 하지만 여전히 깊이가 부족했다. 시스템 프롬프트: 40년 경력의 전문가 그다음 개선한 것은 시스템 프롬프트다. AI의 "역할"을 명확하게 설정했...

AI에만 맡기면 안 되는 이유 — 하이브리드 아키텍처의 핵심

AI에만 맡기면 안 되는 이유 — 하이브리드 아키텍처의 핵심 같은 질문, 다른 답 "1990년 5월 15일 오전 6시에 태어난 남성의 사주를 풀어줘." 프로젝트 초기에 이 질문을 AI에게 세 번 던졌다. 세 번 모두 그럴듯한 해석이 돌아왔다. 문제는 세 번의 답에서 일주(日柱)가 두 가지로 갈렸다는 것이다. 한 번은 갑진(甲辰), 두 번은 을사(乙巳). 사주의 출발점인 일주가 다르면 그 위에 쌓이는 모든 해석이 무너진다. 이 실험 하나로 "사주 계산을 AI에 직접 맡기면 안 된다"는 결론이 나왔다. AI 블랙박스의 위험성 왜 이런 일이 생기는 걸까. 사주 기둥을 세우는 과정은 단순한 날짜 변환이 아니다. 연주(年柱)는 양력 1월 1일이 아니라 입춘(立春) 기준으로 바뀐다. 2024년 입춘은 2월 4일 17시 27분이다. 2024년 2월 3일에 태어난 사람은 양력으로는 2024년이지만, 사주에서는 아직 2023년(계묘년)이다. 이 절기 경계를 분 단위로 정확히 판정해야 한다. 월주(月柱)도 마찬가지다. 양력 월이 아니라 절기를 기준으로 월이 바뀐다. 소한(小寒)부터 입춘 전까지가 축월(丑月), 입춘부터 경칩 전까지가 인월(寅月). 24절기 중 12개의 "절(節)"이 월의 경계를 결정한다. 이 경계 역시 시간 단위로 정확해야 한다. 시주(時柱)에서는 진태양시(眞太陽時) 보정이라는 함정이 있다. 한국 표준시는 동경 135도 기준이지만, 서울의 실제 경도는 약 127도다. 이 차이를 보정하지 않으면 시주가 달라질 수 있다. 특히 자시(子時)와 축시(丑時)의 경계, 묘시(卯時)와 진시(辰時)의 경계처럼 시간대 전환점에 태어난 사람은 보정 여부에 따라 시주 자체가 바뀐다. AI에게 "1990년 5월 15일 오전 6시 태어난 사람의 사주"를 물었을 때, AI 내부에서 이 절기 경계 판정과 진태양시 보정을 정확히 수행했는지 확인할 방법이 없다. 이것이 블랙박스의 핵심 문제다. 결과가 맞는...

테스트와 검증 — 정답이 여러 개인 도메인에서의 품질 보증

테스트와 검증 — 정답이 여러 개인 도메인에서의 품질 보증 "맞다"의 기준이 하나가 아니다 일반적인 소프트웨어에서 테스트는 명확하다. 1 + 1의 결과가 2인지 확인하면 된다. 정답은 하나이고, 맞거나 틀리거나 둘 중 하나다. 사주 앱의 테스트는 이것과 근본적으로 다른 성격을 갖고 있었다. 같은 생년월일시를 입력해도, 진태양시 보정을 적용하느냐 안 하느냐, 야자시설을 채택하느냐 안 하느냐에 따라 "정답"이 달라진다. 전문 앱 A와 전문 앱 B의 결과가 다를 때, 둘 중 하나가 틀린 것이 아니라 서로 다른 설정을 적용하고 있는 것일 수 있다. 이 상황에서 "우리 앱이 정확하다"를 어떻게 증명할 것인가. 이 질문이 테스트 전략의 출발점이었다. 전문 앱들과의 교차 검증 가장 먼저 한 것은 기존 전문 앱들과 결과를 비교하는 것이었다. 1편에서 벤치마킹했던 앱들 — "만세력 천을귀인", "고전 사주", 네이버 만세력 — 에 동일한 생년월일시를 입력하고 우리 앱의 결과와 대조했다. 검증용 테스트 케이스는 세 가지 유형으로 나눠서 준비했다. "안전한" 케이스 : 절기 경계에서 충분히 떨어진 날짜와 시각으로, 어떤 설정을 적용해도 결과가 같아야 하는 케이스다. 기본적인 만세력 정확도를 확인하는 용도다. 절기 경계 케이스 : 입춘 당일, 경칩 당일처럼 절기 경계에 가까운 출생일이다. 절기 시각의 정밀도가 검증된다. 보정 민감 케이스 : 야자시 시간대, 서머타임 기간, 시주 경계 근처의 출생 시각이다. 설정에 따라 결과가 달라질 수 있는 케이스다. "안전한" 케이스에서 결과가 다르면 심각한 문제다. 기본적인 만세력 로직이 틀렸다는 뜻이기 때문이다. 실제 검증에서 이 유형에서는 모든 앱의 결과가 일치했고, 우리 앱도 일치했다. 기본 만세력 파이프라인이 올바르게 작동한다는 것을 확인한 순간이었다. 절기 경계 케이스에서 흥미로운 차이가 ...

분석 엔진 만들기 — 용신 판단과 신살 22종

분석 엔진 만들기 — 용신 판단과 신살 22종 기둥은 세워졌다, 이제 읽어야 한다 6편에서 8편까지 만세력과 시간 보정을 다뤘다. 사주 네 기둥(연주, 월주, 일주, 시주)이 정확하게 계산되는 상태가 됐다. 하지만 네 기둥은 그 자체로는 "데이터"일 뿐이다. 이 데이터를 "의미"로 바꾸는 것이 분석 엔진의 역할이다. 분석 엔진이 수행하는 작업은 크게 네 가지다. 오행 비율 분석, 십신 관계 파악, 용신 판단, 그리고 합충형파해 검출. 여기에 12운성 배치와 신살 판정, 대운/세운 계산이 추가된다. 이번 편에서는 이 중 가장 핵심적인 용신 판단과, 가장 많은 논의를 거친 신살 구현을 중심으로 이야기한다. 용신(用神): 사주의 처방전 용신은 사주 해석의 핵심 개념이다. 한 마디로 말하면, 사주에서 가장 필요로 하는 오행(또는 십신)이 무엇인가를 판단하는 것이다. 의학에 비유하면 진단 후 처방을 내리는 과정과 같다. 사주의 오행 구성을 진단하고, 균형을 맞추기 위해 무엇이 필요한지를 처방하는 것이다. 용신 판단법은 크게 세 가지 학파가 있다. 억부법(抑扶法)은 일간(日干, "나 자신")이 강한지 약한지를 판단하고, 강하면 억제하고 약하면 도와주는 오행을 용신으로 삼는다. 조후법(調候法)은 태어난 계절을 중시하여, 추운 계절에 태어났으면 따뜻한 오행을, 더운 계절에 태어났으면 시원한 오행을 용신으로 삼는다. 통변법(通變法)은 사주 전체의 흐름과 구조를 종합적으로 읽어 용신을 판단한다. 우리 앱에서는 억부법을 기본으로 채택했다. 이유는 세 가지다. 첫째, 가장 체계적으로 규칙화되어 있어 코드로 구현하기에 적합하다. 둘째, 현대 명리학에서 가장 널리 사용되는 방법이다. 셋째, 벤치마킹한 전문 앱들도 대부분 억부법을 기본으로 채택하고 있었다. 억부법의 알고리즘화 억부법을 코드로 구현하는 과정을 간략히 설명하면 이렇다. 먼저 일간의 강약을 판단한다. 일간과 같은 오행(비견, 겁재)이 사주에 얼마나 있는지...

시주를 바꾸는 보정들 — 진태양시, 서머타임, 야자시

시주를 바꾸는 보정들 — 진태양시, 서머타임, 야자시 출생 시각이 "진짜" 시각이 아닐 수 있다 사주에서 시주(時柱)는 출생 시각으로 결정된다. 하루를 12시진(時辰)으로 나누고, 각 시진은 2시간 단위다. 자시(子時)는 23:00~01:00, 축시(丑時)는 01:00~03:00, 이런 식이다. 단순해 보인다. 출생 시각을 2시간 단위로 나누면 될 것 같다. 그런데 출생 시각이 "진짜 태양 시간"과 다를 수 있다는 문제가 있다. 한국의 시계가 가리키는 시각과 서울에서 해가 남중하는 실제 시각 사이에 최대 48분의 차이가 존재한다. 2시간 단위인 시주에서 48분은 시주 자체를 바꿀 수 있는 크기다. 이 문제를 무시하고 출생 신고 시각을 그대로 사용할 것인가, 아니면 보정을 적용할 것인가. 그리고 보정을 적용한다면 어디까지 할 것인가. 이것이 시주 구현에서 만난 가장 큰 의사결정이었다. 진태양시: 32분 + 16분 = 최대 48분의 차이 한국 표준시(KST)는 동경 135도를 기준으로 한다. 이 기준 경선은 일본 효고현 아카시시를 지나간다. 서울의 경도는 약 127도다. 이 8도의 차이가 시간으로 환산하면 약 32분이다. 경도 1도가 4분의 시간 차이를 만들기 때문이다. 쉽게 말하면, 한국 시계가 12시 정오를 가리킬 때 서울에서는 아직 태양이 남중하지 않았다는 뜻이다. 서울의 "진짜 정오"는 시계 시각보다 약 32분 늦다. 사주에서 시주를 정할 때는 시계 시각이 아니라 이 "진짜 태양 시각"을 써야 한다는 것이 진태양시(True Solar Time) 보정의 핵심이다. 여기에 균시차(Equation of Time)라는 변수가 추가된다. 지구 공전 궤도가 완전한 원이 아니라 타원이고, 자전축이 기울어져 있기 때문에, 태양의 남중 시각은 매일 미세하게 달라진다. 이 차이가 최대 약 16분이다. 2월 중순에는 태양이 시계보다 약 14분 빠르게 남중하고, 11월 초에는 약 16분 늦게 ...

KASI 데이터와 절기 정밀도 — 분 단위가 사주를 바꾼다

KASI 데이터와 절기 정밀도 — 분 단위가 사주를 바꾼다 절기의 "날짜"가 아니라 "시각"이 필요하다 6편에서 사주가 절기력이라는 독자적인 달력 체계를 사용한다는 것을 다뤘다. 이번에는 그 절기력의 핵심 재료인 절기 데이터를 어디서, 어떻게 확보했는지 이야기한다. 이 과정에서 "데이터의 출처가 곧 앱의 신뢰도"라는 교훈을 얻었다. 절기 데이터가 필요하다는 것은 처음부터 알고 있었다. 문제는 어떤 수준의 데이터가 필요한가였다. "입춘은 대략 2월 4일쯤"이라는 수준으로는 부족하다. 6편에서 봤듯이 입춘 당일 출생자의 연주를 판정하려면, "2024년 입춘은 2월 4일 16시 27분"이라는 분 단위 정밀도가 필요하다. 연간 12개 절기(절) 각각에 대해, 1920년부터 2050년까지 130년간의 정밀 시각이 필요하다는 계산이 나온다. 이 데이터를 어디서 가져올 것인가. 인터넷에 떠도는 절기 표를 복사해서 쓸 수도 있다. 하지만 1편에서 벤치마킹할 때 이미 확인한 사실이 있었다. 정확도에서 차별화되는 전문 앱들은 예외 없이 한국천문연구원(KASI) 데이터를 사용한다. 출처가 불분명한 데이터로 만든 앱은, 아무리 분석 로직이 정교해도 기반이 흔들리는 것이다. 한국천문연구원(KASI) 데이터를 선택한 이유 한국천문연구원(Korea Astronomy and Space Science Institute)은 대한민국의 공식 천문 기관이다. 절기 시각을 포함한 천문 데이터를 공공 데이터로 제공하고 있으며, 이 데이터의 정밀도는 천문학적 계산에 기반한다. 개인이 만든 만세력 테이블이나, 출처를 알 수 없는 인터넷 데이터와는 신뢰도의 차원이 다르다. KASI 데이터를 선택한 결정적 이유는 명확했다. 공신력 면에서, 정부 산하 연구 기관의 공식 데이터이므로 앱의 정확도에 대한 신뢰 근거가 된다. 정밀도 면에서, 절기 시각을 분 단위까지 제공하므로 사주 계산에 필요한 수준을 충족한다....

만세력이라는 거대한 벽 — 양력도 음력도 아닌 절기력

만세력이라는 거대한 벽 — 양력도 음력도 아닌 절기력 달력이 세 개 필요한 프로젝트 사주 앱을 만든다고 하면 대부분 "생년월일 입력받아서 계산하면 되는 거 아닌가"라고 생각한다. 나도 처음에는 그랬다. 그런데 실제로 구현에 들어가자 첫 번째 벽이 나타났다. 사주는 양력도 음력도 아닌, 절기력(節氣曆)이라는 전혀 다른 달력 체계를 기준으로 한다. 양력은 태양의 공전을 기준으로 한 달력이고, 음력은 달의 위상을 기준으로 한 달력이다. 절기력은 태양의 황경(黃經)을 기준으로 한 달력이다. 셋 다 "태양"과 관련이 있지만, 날짜를 나누는 기준이 전부 다르다. 사주 앱을 만든다는 것은, 양력 날짜를 입력받아서 음력으로 변환하고, 다시 절기력으로 변환해야 한다는 뜻이다. 이 "세 겹의 달력 변환"이 만세력(萬歲曆) 모듈의 본질이다. 그리고 이것이 사주 앱 개발에서 가장 까다로운 부분이라는 것을, 구현을 시작하고 나서야 제대로 실감했다. 연주(年柱): 1월 1일이 아니라 입춘에 바뀐다 우리가 일상에서 쓰는 "몇 년생"은 양력 1월 1일에 바뀐다. 음력으로는 설날에 바뀐다. 그런데 사주에서 연주(年柱)는 입춘(立春)에 바뀐다. 입춘은 보통 양력 2월 3일에서 5일 사이인데, 매년 날짜가 다를 뿐 아니라 시각까지 달라진다. 여기서 실제 구현의 어려움이 드러난다. 2024년 입춘은 2월 4일 16시 27분이다. 이 시각을 기준으로, 같은 날 16시 26분에 태어난 사람과 16시 28분에 태어난 사람의 연주가 다르다. 1분 차이로 갑진년(甲辰年)과 계묘년(癸卯年)이 갈린다. 그 사람의 띠가 용인지 토끼인지가 1분으로 결정되는 것이다. 이것은 단순히 "2월 4일이면 새해"라고 하드코딩할 수 없다는 뜻이다. 매년 입춘의 정확한 날짜와 시각을 알아야 하고, 입력된 출생 시각과 분 단위로 비교해야 한다. "대략 2월 초"가 아니라 "2024년 2월 4일 16...

관계의 그물망 — 오행, 십신, 합충형파해를 코드로

관계의 그물망 — 오행, 십신, 합충형파해를 코드로 사주명리의 진짜 복잡도는 22개의 글자 자체가 아니라, 글자들 사이의 "관계"에 있다. 오행의 상생상극, 십신의 10가지 분류, 합충형파해의 수십 가지 조합. 이 관계의 그물망을 코드로 옮기는 과정은 프로젝트에서 가장 도전적이면서도 가장 흥미로운 부분이었다. 오행 상생상극: 두 가지 구현 방식 오행의 상생상극은 사주 분석의 가장 기본적인 축이다. 목은 화를 생하고, 화는 토를 생하고, 토는 금을 생하고, 금은 수를 생하고, 수는 목을 생한다. 상극은 목이 토를 극하고, 토가 수를 극하고, 수가 화를 극하고, 화가 금을 극하고, 금이 목을 극한다. 이 관계를 코드로 구현하는 방법은 크게 두 가지다. 매핑 테이블 방식은 "목이 화를 생한다"는 관계를 직접 테이블로 작성한다. 명시적이고 읽기 쉽다. 코드를 보면 도메인 관계가 바로 눈에 들어온다. 수정이나 검증도 쉽다. 인덱스 연산 방식은 다른 접근이다. 오행을 목(0), 화(1), 토(2), 금(3), 수(4)로 인덱싱하면, 상생은 (현재 + 1) % 5이고, 상극은 (현재 + 2) % 5다. 간결하고 수학적으로 우아하다. 오행의 순환 구조를 코드의 모듈러 연산으로 표현한 것이다. 두 방식 모두 장점이 있어서 프로젝트에서는 둘 다 유지하기로 했다. 매핑 테이블은 데이터 참조와 검증에, 인덱스 연산은 런타임 계산에 사용한다. 중복처럼 보일 수 있지만, 실제로는 각자의 역할이 다르다. 매핑 테이블은 "이 관계가 맞는가"를 확인할 때 쓴다. 코드 리뷰에서, 테스트에서, 디버깅에서 테이블을 보면 바로 확인할 수 있다. 인덱스 연산은 분석 엔진 내부에서 빠른 판별이 필요할 때 쓴다. 두 방식의 결과가 항상 일치하는지를 테스트로 검증해두면, 어느 쪽을 써도 안전하다. 십신 분석: 일간 기준의 10가지 관계 십신(十神)은 사주 해석에서 가장 중요한 분석 체계 중 하나다. 일주의 천간, 즉 일간을 기준으로 ...

천간 10개, 지지 12개 — 사주학을 TypeScript로 번역하기

천간 10개, 지지 12개 — 사주학을 TypeScript로 번역하기 개발자가 사주명리를 처음 코드로 옮기려 하면, 가장 먼저 마주치는 질문이 있다. 이 한자들을 어떤 데이터 구조로 표현해야 하는가? 22개의 글자에 담긴 음양오행 시스템을 TypeScript 타입으로 표현하는 과정은, 생각보다 깊은 설계 고민을 요구했다. 22개의 글자라는 출발점 사주명리의 모든 것은 22개의 글자에서 시작한다. 천간(天干) 10개와 지지(地支) 12개. 천간은 甲(갑), 乙(을), 丙(병), 丁(정), 戊(무), 己(기), 庚(경), 辛(신), 壬(임), 癸(계). 지지는 子(자), 丑(축), 寅(인), 卯(묘), 辰(진), 巳(사), 午(오), 未(미), 申(신), 酉(유), 戌(술), 亥(해). 각 글자에는 음양과 오행이 배속돼 있다. 甲은 양목, 乙은 음목, 丙은 양화, 丁은 음화. 이런 식으로 천간 10개가 목화토금수 다섯 오행에 양과 음 두 개씩 배치된다. 지지도 마찬가지로 각각 오행과 음양이 정해져 있다. 이 배속은 수천 년 전에 정해진 것이고, 바뀌지 않는다. 甲이 내일 갑자기 음목이 되는 일은 없다. 이 불변성이 TypeScript로 표현할 때 핵심적인 설계 포인트가 됐다. as const로 불변성 보장하기 TypeScript에는 as const라는 키워드가 있다. 값을 읽기 전용 리터럴 타입으로 고정하는 기능이다. 이것이 사주명리의 데이터 특성과 완벽하게 맞아떨어졌다. 사주명리의 기본 데이터는 "영원히 바뀌지 않는 매핑"이다. 甲은 양목이고, 乙은 음목이다. 子는 양수이고, 丑은 음토다. 이 관계는 참조 데이터(reference data)다. 런타임에 변경될 일이 없고, 변경돼서도 안 된다. as const를 사용하면 이 불변성이 타입 수준에서 보장된다. 실수로 매핑을 변경하는 코드를 쓰면 컴파일 단계에서 에러가 난다. "수천 년 전 데이터가 지금도 그대로"라는 도메인의 특성이 코드의 타입 시스템에 반영된 ...

코드 한 줄 쓰기 전에 — 설계 문서 우선 개발

코드 한 줄 쓰기 전에 — 설계 문서 우선 개발 사이드 프로젝트에서 설계 문서를 먼저 쓴다고? 과한 것 아닌가? 대부분의 개발자는 그렇게 생각할 것이다. 나도 타로 프로젝트 때는 그랬다. 그런데 사주 프로젝트에서는 코드 한 줄 쓰기 전에 docs/ 폴더에 4개의 설계 문서를 먼저 작성했다. 그리고 이 결정이 AI 협업의 판도를 완전히 바꿨다. 타로에서는 바로 코드였다 타로 마스터 프로젝트를 떠올려보자. 1편에서 아이디어를 던지고, 도메인을 파악하고, 바로 코드를 쓰기 시작했다. 그리고 그것이 잘 작동했다. 타로는 구조가 비교적 단순해서, AI와 대화하면서 바로 구현해도 큰 문제가 없었다. 그런데 사주 프로젝트 초반, AI와 대화하면서 도메인을 파악하는 과정에서 느낀 것이 있다. 이 도메인은 한 번의 대화 세션에 담기지 않는다는 것이다. 천간지지, 오행, 십신, 지장간, 합충형파해, 12운성. 하나하나가 독립적인 체계이면서 서로 얽혀 있다. 대화 한 세션에서 이 모든 것을 다루면 컨텍스트가 흐려진다. 더 근본적인 문제는 세션이 바뀔 때 생긴다. AI와의 대화는 세션 단위다. 새 세션을 시작하면 이전 대화의 맥락이 사라진다. 타로에서는 이게 큰 문제가 아니었다. 78장 카드 구조 정도는 몇 줄로 다시 설명할 수 있으니까. 하지만 사주의 도메인 지식을 매 세션마다 처음부터 설명하는 건 현실적이지 않다. 설계 문서라는 해법 이 문제의 해법이 설계 문서였다. 코드 대신 문서를 먼저 작성하면, 그 문서가 AI를 위한 "영구적 컨텍스트"로 기능할 수 있다. 아이디어는 단순했다. 도메인 지식과 설계 결정을 문서로 정리해두면, 새 대화 세션에서 @docs/ 한 번으로 AI가 전체 맥락을 이해한 상태에서 시작할 수 있다. 매번 "이 프로젝트는 사주명리 분석 웹앱이고, 하이브리드 아키텍처를 쓰고, 진태양시 보정이 있고..." 이런 설명을 반복하지 않아도 된다. 이것은 단순히 "편의성"의 문제가 아니었다. AI ...

'정답이 여러 개인 도메인' — 기존 사주 앱 벤치마킹

"정답이 여러 개인 도메인" — 기존 사주 앱 벤치마킹 같은 생년월일시를 넣었는데, 앱마다 사주 결과가 다르다. 시주가 다르고, 월주가 다르고, 심지어 일주까지 다른 경우가 있다. 도대체 뭐가 맞는 걸까? 이 글은 사주명리 프로젝트를 시작하기 전에 기존 앱들을 벤치마킹하면서 발견한 충격적인 현실과, 그로부터 도출한 설계 원칙의 이야기다. 벤치마킹의 출발점 프로젝트를 시작하기 전에 먼저 기존 서비스를 파악하는 것은 당연한 수순이다. 타로 프로젝트 때도 그랬다. 기존 타로 앱을 몇 개 써보고, 좋은 점과 아쉬운 점을 파악한 후 방향을 잡았다. 사주에서도 같은 접근을 했다. 구글 플레이와 앱스토어에서 인기 있는 사주 앱 몇 개를 설치하고, 웹 기반 만세력 사이트도 여러 곳을 찾았다. 그리고 동일한 생년월일시를 입력해서 결과를 비교했다. 여기서 문제가 시작됐다. 앱마다 결과가 다르다 같은 사람의 생년월일시를 넣었는데 사주 팔자 자체가 다르게 나온다. 처음에는 내가 입력을 잘못한 줄 알았다. 다시 확인하고, 다시 넣어봤다. 결과는 똑같았다. 앱 A와 앱 B의 시주가 달랐다. 앱 C는 월주까지 달랐다. 사주명리에서 "팔자"는 연주, 월주, 일주, 시주 네 개의 기둥이다. 각 기둥은 천간과 지지 한 쌍으로 이루어진다. 이 여덟 글자가 사주 분석의 모든 출발점이다. 그런데 이 출발점 자체가 앱마다 다르다니? 이건 단순한 UI 차이가 아니라 근본적인 계산 차이다. 개발자로서 이 현상이 당혹스러우면서도 흥미로웠다. 같은 입력에 같은 출력이 나와야 하는 것은 프로그래밍의 기본 전제 아닌가. 그런데 사주 도메인에서는 그 전제가 성립하지 않는 것처럼 보였다. 차이의 원인: 진태양시 보정 가장 큰 차이를 만드는 요소는 진태양시 보정이었다. 사주에서 "시(時)"를 결정할 때, 우리가 일상적으로 사용하는 시계 시간과 실제 태양의 위치가 다르다는 문제가 있다. 한국 표준시는 동경 135도를 기준으로 한다. 하지만 서...

타로 다음, 사주 — '이건 차원이 다른 복잡도다'

타로 다음, 사주 — "이건 차원이 다른 복잡도다" "혹시 사주에 필요한 데이터를 가지고 있어? 사주 프로그램을 만들고 싶어." 타로 마스터 프로젝트를 끝낸 직후였다. 20편의 개발기를 쓸 만큼 충분히 배운 프로젝트였고, AI 협업 개발이라는 방식에 대한 확신도 생겼다. 자연스럽게 다음 주제가 떠올랐다. 그런데 프로젝트를 시작하자마자 한 가지 사실이 분명해졌다. 이건 타로와는 차원이 다른 복잡도라는 것이다. 왜 사주였나 타로 마스터를 완성하고 나니, 비슷한 영역에서 더 도전적인 주제를 찾고 싶었다. 동양 철학 기반의 분석 도구. 관심 분야를 떠올리다 보니 사주명리가 자연스럽게 후보에 올랐다. 사주는 개인적으로도 흥미가 있는 주제였다. 생년월일시만으로 사람의 성향과 운의 흐름을 분석한다는 체계. 수천 년간 축적된 동양의 분류 시스템이 현대의 코드로 옮겨질 수 있을까? 그 궁금증이 프로젝트의 출발점이었다. 타로 프로젝트에서 경험한 AI 협업의 효용도 한몫했다. 타로에서는 78장 카드의 해석 데이터를 AI가 생성하고, 리딩 로직을 함께 설계했다. 그 경험이 좋았기에, 더 복잡한 도메인에서도 같은 방식이 통할지 시험해보고 싶었다. 타로와 사주의 근본적 차이 타로는 복잡한 프로젝트였지만, 구조 자체는 비교적 단순했다. 78장의 카드가 있고, 각 카드에 정방향과 역방향 해석이 있고, 리딩 방식에 따라 카드를 배치한다. 데이터 모델로 보면 카드 78개, 해석 텍스트 156개, 리딩 레이아웃 3~4가지. 이게 전부다. 사주는 출발선부터 다르다. 천간 10개, 지지 12개. 여기까지는 단순해 보인다. 그런데 이 22개의 글자가 만들어내는 조합과 관계의 그물망이 문제다. 60갑자 — 천간과 지지의 조합으로 만들어지는 60개의 간지. 오행 — 목화토금수 다섯 가지 기운과 상생상극 관계. 십신 — 일간을 기준으로 나머지 글자들과의 10가지 관계. 지장간 — 지지 안에 숨어 있는 천간. 12운성 — 오행의 에너지가 태어나서 죽고 ...