KKflow

MAKING · 제작기

사주 플로우 제작기: 328번의 커밋으로 돌아본 두 달

2026년 8월 4일 첫 커밋부터 테스트 1,308개까지. 만세력 엔진의 1분 오차, 중국 음력과 한국 음력의 한 달 차이, 링크를 깨지 않으려고 지킨 원칙들을 커밋 기록으로 돌아봤습니다.

· KKflow

사주 플로우의 첫 커밋은 2026년 8월 4일입니다. 그 뒤로 두 달 동안 328개의 커밋이 쌓였습니다. 커밋 메시지에 무엇을 왜 고쳤는지 길게 적어 두는 편이라, 기록을 다시 읽으면 그때 어디서 막혔는지가 거의 그대로 남아 있습니다. 이 글은 그 기록을 따라가며 사주 플로우가 지금 모양이 되기까지를 정리한 제작기입니다. 개발은 Claude Code 같은 AI 코딩 도구와 함께 했고, 커밋의 절반쯤이 그렇게 만들어졌습니다.

화면보다 엔진이 먼저였다

첫날 만든 것은 화면이 아니라 만세력 엔진이었습니다. 진태양시 보정, 한국 표준시 변경 이력, 서머타임 12개 구간, 입춘과 절기 기준의 연주·월주, 율리우스 적일(JDN)로 계산하는 일주, 야자시 옵션까지 첫 커밋에 들어갔습니다. 커밋 메시지에는 "검증 테스트 91건 통과 (일주 30건·월주 20건 교차 대조 포함)"라고 적혀 있습니다. 사주 서비스는 화면이 아무리 예뻐도 여덟 글자가 틀리면 아무 의미가 없기 때문에, 계산부터 단단히 세우고 시작했습니다.

처음 기획안에는 AI가 써 주는 맞춤 문장, 회원가입, 유료 리포트와 결제가 들어 있었습니다. 하지만 바로 다음 날 커밋의 제목은 "AI 없는 전체 기능 구현"이었고, 궁합·만세력 달력·택일·신살·공유 카드가 모두 규칙 기반으로 만들어졌습니다. 지금도 사주 플로우에는 데이터베이스도, 로그인도, AI API 호출도 없습니다. 같은 입력이면 언제나 같은 결과가 나오고, 그 결과가 어떤 규칙에서 나왔는지 화면에서 확인할 수 있게 하는 것이 이후 모든 결정의 기준이 됐습니다.

존재하지 않는 날짜, 2월 31일

초기에 발견한 버그 중 하나는 날짜 입력이었습니다. 커밋 기록에는 이렇게 남아 있습니다. "2월 31일을 고를 수 있었고, JDN 계산이 다음 달로 굴러가 틀린 원국이 오류 없이 만들어졌다." 오류가 나지 않고 그럴듯한 오답이 나온다는 점이 가장 위험했습니다. 그래서 날짜 검증 모듈, 계산 함수 내부, 입력 폼의 세 곳에서 막았습니다.

1분 차이가 여덟 글자 중 넷을 바꾼다

절기가 바뀌는 시각(절입 시각)은 처음에 오픈소스 라이브러리로 계산했습니다. 그런데 이 라이브러리는 중국 표준시를 기준으로 하고 있어서, 한국천문연구원(KASI)이 발표하는 시각과 1~2분씩 어긋날 수 있었습니다. 1~2분이 작아 보여도 커밋 메시지의 표현대로 "입춘 경계에 태어난 사람은 그 1분에 연주와 월주가 함께 바뀐다 — 여덟 글자 중 넷이다."

그래서 KASI 공공데이터를 받아 표로 만들어 저장소에 넣는 파이프라인을 GitHub Actions로 만들었습니다. 빌드할 때마다 받아 오는 대신 받아 온 값을 표로 굳혀 두고, 버튼 하나로 다시 받으면 결과가 PR로 열리게 했습니다. 이 과정에서 두 번의 실수가 있었습니다.

  • 조용한 실패 — 121년치를 다 받았는데 절기가 0건이었습니다. 시각 표기를 한 가지 형식만 받도록 짜 둔 탓이었습니다. 커밋에는 "조용한 실패는 없는 것보다 나쁘다"고 적었습니다.
  • 초를 버리고 있었다 — 발표값 695건과 맞대어 보니 분 단위까지 일치하는 것이 58%뿐이었습니다. 예를 들어 2000년 처서는 실제로 03시 48분 31초인데 초를 버려 48분으로 처리하고 있었습니다. 반올림으로 바꾸자 일치율이 93%로 올라갔습니다.

중국 음력과 한국 음력은 다르다

제작 과정에서 가장 큰 발견은 8월 17일 커밋에 있습니다. 음력 변환에 쓰던 라이브러리가 중국 음력 기준이었던 것입니다. 한국 음력 라이브러리와 1900~2049년을 대조하자 1,948일이 어긋났고, 커밋 메시지에 따르면 "살아 있는 사람의 출생년만 마흔한 해가 걸렸다." 극단적인 경우로 음력 2012년 4월 1일은 중국식으로 양력 4월 21일, 한국식으로 5월 21일이었습니다. 한 달이 통째로 다릅니다.

더 아팠던 건 그다음입니다. 정작 음력을 검증하던 스크립트도 중국식 결과를 정답으로 삼고 있었습니다. 그때 남긴 문장이 "안 쓰는 것을 검증하는 검증은 통과해도 아무것도 말해 주지 않는다"입니다. 음력 변환을 한국 기준으로 교체하고, 다른 파일에서 중국식 변환을 다시 쓰지 못하게 막는 검사도 추가했습니다.

9분과 32분: 출생지를 넓힌 이유

사주의 시주는 2시간 단위라 몇십 분이 결과를 바꿉니다. 한국 표준시는 동경 135도 기준인데 서울은 약 127도라 실제 해의 시각과 30분 남짓 차이가 납니다. 이 보정은 출생지 경도에 따라 달라집니다. 처음에는 출생지 선택지가 좁았는데, 커밋 메시지에 남긴 예시처럼 "안동 사람이… 맨 위의 서울을 고르고, 그 순간 9분이 어긋난다." 그래서 시군구 경도 데이터를 붙여 출생지를 넓혔습니다.

골든 테스트를 만들다가 거꾸로 제 쪽 실수를 찾은 적도 있습니다. 서울 밤 11시 30분 출생을 자시라고 적어 둔 테스트 케이스가 있었는데, 경도 보정 −32분을 적용하면 22시 58분, 즉 해시입니다. 엔진이 아니라 테스트가 틀렸던 것입니다.

링크는 절대 깨지면 안 된다

사주 플로우는 회원가입 대신 입력값을 압축해 결과 주소에 담습니다. 그래서 한 번 퍼진 링크가 다른 결과를 보여 주는 일은 무엇보다 피해야 했습니다.

  • 출생지 목록의 순서를 바꾸자 이미 공유된 링크의 출생지가 바뀌었습니다. "1번이 부산이었는데 인천이 됐다." 화면에 보여 주는 목록과 링크에 쓰는 번호표를 분리해서 해결했습니다.
  • 코드 주석에는 "34비트로 압축"이라고 적혀 있었는데 실제로는 38비트, 체크섬까지 42비트였습니다. 주석을 고치면서 이것이 압축일 뿐 암호화가 아니라는 점도 분명히 적었습니다.
  • 타로는 Math.random()을 쓰지 않습니다. 무작위로 섞으면 공유한 링크가 남에게 다른 카드를 보여 주기 때문입니다. 생년월일시와 날짜로 섞인 덱에서 뽑습니다.
  • 타로 배열을 네 종류에서 세 장 배열 하나로 줄일 때도, 엔진에는 네 가지를 모두 남겼습니다. 예전 링크가 계속 열려야 하기 때문입니다.
  • 성능을 이유로 링크 형식을 바꾸자는 의견도 검토했지만, 재 보니 계산 몫은 20ms 중 1ms였습니다. 커밋 메시지의 결론은 "공유 링크가 전부 깨지는 일을 20ms 때문에 하지 않는다."

만들었다가 덜어낸 것들

기록을 보면 추가한 것만큼 덜어낸 것도 많습니다.

  • 적중 통계 — 운세가 맞았는지 매기는 기능을 만들었다가 몇 번 모양을 바꾼 끝에 전부 철거했습니다. 테스트 수가 721개에서 685개로 줄었습니다.
  • 손금 사진 분석 — 사진을 분석하는 방식은 입력값을 저장하지 않는다는 원칙과 맞지 않아 택하지 않았고, 손 그림을 보고 고르는 방식으로 만들었습니다.
  • 디자인 — 로고는 수채 태극에서 시작해 오행 오각형, 네 기둥, 물결, 놋 원판까지 여섯 번 바뀌었고, 테마는 회색과 보라에서 "새벽 장미", "흙과 놋"을 거쳐 9월에 "백자"로 정착했습니다. 백자로 바꾼 커밋의 이유는 "바탕에 색이 있으면 그 위의 글이 계속 눌린다"였습니다.
  • 문체 — 화면 문장의 어미 1,880개를 해요체로 한꺼번에 바꿨습니다.

테스트 91개에서 1,308개까지

날짜테스트 수그때 있었던 일
08-0491만세력 엔진 첫 커밋
08-05140AI 없는 전체 기능 구현
08-07237디자인 개편 중
08-29678첫 로드 용량 절반 감축
09-151,000골든 테스트 52건 추가
09-291,308신년운세·결혼 좋은 날 추가

9월 중순에 만든 골든 테스트 52건에는 원칙이 하나 있었습니다. "기대값은 한 건도 지어내지 않았다." 모든 기대값에 교차 대조, KASI 발표값, 명리 규칙 중 어디서 왔는지 근거를 적었습니다. 52건 모두 통과했고 엔진 버그는 나오지 않았지만, 앞에서 말한 것처럼 제가 세운 테스트 케이스 4건이 틀려 있었습니다.

성능 쪽에서는 홈 화면에 천문 계산 라이브러리 100KB가 딸려 오던 문제를 찾아 첫 로드 용량을 250KB에서 119KB로 줄였고, 같은 일이 다시 생기지 않도록 번들 크기를 검사하는 테스트를 붙였습니다.

돌아보며

두 달의 기록을 다시 읽으며 남은 교훈은 세 가지입니다.

  1. 오류 없이 틀리는 것이 가장 위험하다. 2월 31일, 0건 수집, 중국 음력 모두 오류 메시지 없이 그럴듯한 결과를 냈습니다.
  2. 검증의 기준부터 검증해야 한다. 중국식을 정답으로 삼은 검증, 틀린 테스트 케이스처럼 기준이 틀리면 통과는 아무 의미가 없습니다.
  3. 한 번 내보낸 것은 약속이다. 공유된 링크가 계속 같은 결과를 보여 주도록 지키는 일이 기능 하나를 더하는 것보다 중요했습니다.

사주 원국을 읽는 법이 궁금하다면 사주 원국 처음 읽는 법을, 가입 없이 동작하는 구조가 궁금하다면 회원가입 없는 웹 서비스를 만드는 네 가지 방법을 함께 읽어 보세요.