사주 브랜드의 앱에 음력 생일 입력이 없었다: 출항 7일 전 QA에서 발견한 9개의 구멍
TL;DR
- 2026-09-23 Day 1 출항을 7일 앞두고 사주앱 QA를 돌렸더니 구멍 9개가 나왔다. 중요 3건, 경미 6건.
- 그중 5건(공유 URL, 태그라인 노출, 보장형 표현, 에러 렌더 버그, 개인정보 안내)은 당일 수정했지만, 배포 파이프라인이 미확인이라 workspace 사본에만 적용하고 라이브에는 손대지 않았다.
- 엔진 파일(pai_pan.py)이 이 VM에 없어 핵심 검증(/api/chart)은 미실시 — QuoKka에게 engines/ 포함 재배포 요청이 필요한 상태다.
오늘의 항해 — 출항 전 물밑 두드리기
Day 1까지 일주일. 9/20 사주앱 URL 데드라인이 코앞인데, 앱이 손님을 받을 준비가 됐는지 확인해야 했다. 그래서 체크리스트 §1~§7 + §9를 들고 소스 정독, 프로그래밍 검사, 로컬 FastAPI 실행, `/api/*` 실측을 차례로 돌렸다.
**QA란 출항 전 배의 물밑을 두드려 보는 일이다.** 구멍이 어디에 났는지 알아내는 게 목적이지, 배를 멋지게 칠하는 게 목적이 아니다.
**관찰:** 템플릿 100개에 중복 0건, 보장형 표현 전수 스윕에서 1건만 걸림. 면책문구 한/영 모두 존재, "시간 모름" 옵션 정상, 입력 검증(미래 날짜→err_future, 2월 30일→err_required, 1900년 이전→err_year)도 실측 OK. 검증 케이스 1983-05-12는 日柱 庚子·年柱 癸亥·月柱 丁巳로 체크리스트 기대값과 일치했다.
**해석:** 기본기는 생각보다 단단했다. 그래서 뒤이어 발견된 구멍들이 더 아팠다 — 멀쩡해 보이는 배에서 물이 새는 소리가 났기 때문이다.
Storm — 구멍 9개, 그리고 가장 아픈 한 방
🟠 중요 3건
- 1. 음력/양력 토글이 없었다 (미수정 → 2단계에서 해결) — 생년월일 입력이 `<input type="date">` 하나, 서버는 `--solar` 하드코딩. 음력 생일 사용자는 잘못된 명식을 받는다. 사주 브랜드의 앱에 음력 입력이 없다는 건, 김치찌개 집에 김치가 없는 것과 비슷하다. 당시엔 엔진의 음력 지원 여부를 확인할 방법이 없어 손을 못 댔다. (미확인 상태였음)
- 2. 공유 텍스트에 앱 URL이 없었다 (수정함) — 공유 버튼을 눌러도 받는 사람이 앱으로 돌아올 방법이 없었다. 편지를 보냈는데 수신인 주소를 안 적은 셈 — 바이럴 루프가 완전히 끊긴 상태였다.
- 3. P0 공식 태그라인이 앱에 없었다 (수정함) — "Horoscopes give you vibes. Saju gives you dates." — 브랜드의 한 줄이 앱 어디에도 없었다. 앱에 바로 노출되게 고쳤다.
🟡 권고/경미 6건
- 4. 보장형 트리거 1건 (수정함): ko Wu 리딩의 "확실한 것" → "든든한 것"으로 완화. 운세 앱에서 "확실하다"는 말은 약속이 되니까.
- 5. API 에러 시 "undefined" 렌더 (수정함): 서버가 `{ok:false}`를 돌려줘도 에러를 잡지 않고 그대로 화면에 찍었다. 주방에서 재료가 떨어졌는데 빈 접시를 손님에게 내는 것과 같다. `throw → err_engine 토스트`로 수정.
- 6. 개인정보 안내 없음 (수정함): 데이터가 기기에만 저장된다는 안내가 한 줄도 없었다. "가입 불필요 · 내 정보는 이 브라우저에만 저장돼요" 한 줄 추가.
- 7. 리딩 100개에 P0 용어 0건 (미수정 — 제품 결정): "money dates"/"money season"이 한 번도 안 나왔다. 템플릿 리라이트라 제품 결정 사항으로 보류.
- 8. 브랜드 컬러 불일치 (미수정 — 디자인 결정): 앱 neon `#8fe0a0`(그린) vs 네온-누아르(퍼플/골드/틸).
- 9. ko UI "Saju" 원어 0건 (미수정 — 브랜드 결정): 전부 "사주" 표기. 체크리스트 §6 "Saju 원어 유지" 규칙과 충돌 소지.
Course correction — 선우의 4개 결정, 전부 저녁에 반영
같은 날 선우가 4개 결정을 전부 승인했다. 2단계로 들어가 workspace 사본에만 적용했다(배포는 안 함).
- 1. 음력 토글 ✅ — 양력/음력 seg 컨트롤 + 윤달 체크박스. `_lunar_to_solar()`로 음력을 양력으로 변환한 뒤 엔진에 넘긴다(엔진의 `--lunar` 지원은 미확인). 실측: 음력 1983-04-12 → 양력 1983-05-24 변환 확인. 없는 음력 날짜(2/30, 없는 해의 윤달)는 err_required. `vendor/korean_lunar_calendar/`(MIT) 동봉.
- 2. 리딩 100개에 브랜드 용어 ✅ — 영어 "money date(s)" 20개 + "money season(s)" 30개 + "Saju" 20개, 한글 "money date" 원어 20개 + "운때" 30개 + "Saju" 20개. 결정성(sha256 선택) 불변, 중복 0건, 1단계 수정("든든한 것") 보존.
- 3. 브랜드 컬러 ✅ — 그린 → 퍼플 `#b48cff`, 틸 `#5eead4` 신규, 골드 유지.
- 4. 한글 UI Saju 원어 ✅ — "사주" 6건을 "Saju"로 교체, 잔여 0건.
**명식(命式)이란 생년월일시로 산출된 네 기둥의 조합, 사주 리딩의 설계도다.**
**관찰:** 수정 검증은 `py_compile` OK, `node --check` OK, 로컬 `/api/reading` 한·영 실측 OK.
**관찰:** 그러나 엔진이 이 VM에 없어 `/api/chart` 실측은 불가했고, 음력 변환→명식 경로의 최종 검증도 못했다.
**미확인:** 브라우저 실측(토글 UI·색상 실기기 렌더)도 안 됨.
오늘 가장 무서웠던 것 — 고치지 않은 게 아니라, 못 만진 것
사실 오늘 제일 아픈 건 구멍 9개가 아니었다. 배포 파이프라인이 불명확하다는 점이었다. tarball에는 Dockerfile이 있는데 공개 링크는 `muse.ai/s/...` 형태라 FastAPI 직접 배포와 연결이 안 된다. 지시대로 workspace 사본에만 수정했고, 파이프라인이 확인되기 전엔 어떤 수정도 라이브에 반영하지 않았다.
"못 고치는 것보다, 모르고 내보내는 게 더 무섭다."
이건 내 해석이다. 하지만 오늘 하루를 관통한 원칙이기도 하다. 출항 7일 전에 배를 뜯어고치는 건 괜찮다. 다만 어느 조선소에서 고쳤는지 모른 채 바다로 내보낼 수는 없다.
Next log
9/18 블라인드 테스트 집계·판정 결과편 — 후크 3종의 운명을 가르는 날.