← 개발 로그 목록

website: dev→main(prod) 배포 통과 기준 초안 — 첫 Flyway 컷오버를 앞두고

/ 6분 분량 / 개발 로그

dev→main 승격 기준이 그동안 문서로 정리된 적이 없어서, 첫 Flyway 컷오버를 앞두고 그 기준을 문서로 만들어 논의에 부친 PR이다.

발단은 단순한 확인이었다. main과 dev가 얼마나 벌어져 있나 실측해봤더니 193개 커밋 차이가 나왔다. 그 자체로도 꽤 큰 격차인데, 더 걸리는 부분은 따로 있었다. prod는 아직 ddl-auto: update로 돌고 있는데, dev는 #133에서 이미 Flyway(ddl-auto: validate)로 전환을 끝낸 상태였다. 즉 다음번 dev→main 승격은 그냥 "밀린 커밋 반영"이 아니라, prod가 태어나서 처음으로 Flyway 체제로 넘어가는 컷오버라는 뜻이었다. V1__baseline.sql부터 쌓인 마이그레이션 10개가 그 배포 한 번에 실제 prod DB 위에서 전부 실행되는 상황이다.

이 정도 규모의 승격을 "CI 통과했으니 머지" 수준으로 넘기기엔 불안했다. 그래서 통과 기준을 문서로 정리하기로 했다.

기준을 정리하면서 참고한 과거 기록들

기준을 새로 만드는 김에 근거 없이 나열하고 싶지는 않아서, 레포에 이미 있는 기록들을 다시 찾아봤다.

backend/.claude/skills/db-man/SKILL.md를 보면 V1 baseline은 추정으로 만든 게 아니라 실제 prod DB 사본을 받아서 검증한 결과였다. main이 2026-07-24 이후 재배포된 적이 없으니 그 사이 드리프트는 없을 걸로 보고는 있지만, 이번에 다시 실측 확인을 한 건 아니라서 이 부분은 가정으로 남겨뒀다.

pm/docs/learnings.md에 있는 V6 사고 기록도 다시 봤다. enum CHECK 마이그레이션이 CI는 green이었는데 stage에서 실패한 사고였는데, 원인이 마이그레이션 SQL 자체가 아니라 업그레이드 테스트 시드가 "이 컬럼에 대해 과거 모든 마이그레이션이 허용했던 값 전체"를 담지 않았던 것이었다. 당시 stage/prod 실 데이터를 CI에 들이는 방안은 PII 노출 때문에 기각됐고, 대신 과거 마이그레이션 SQL의 CHECK 값 합집합을 정적으로 시드하는 방식으로 정리됐다는 게 확인됐다. 이번 문서에서도 이 규칙을 재확인 대상으로 그대로 가져왔다.

그리고 CI가 실제로 스펙 위반 버그를 잡은 사례가 이 레포 기록엔 없다는 것도 짚어뒀다. CI가 잡은 건 Flyway 버전 충돌이나 migration-guard 같은 기계적인 것들이었지, "의도한 대로 동작하는가"는 CI가 애초에 검증 대상으로 삼지 않는다. 이 지점이 QA verification을 별도 기준으로 넣게 된 이유이기도 하다.

다섯 개 기준으로 정리한 이유

문서에는 CI 통과, stage 체류, QA verification, 마이그레이션 안전성, 롤백 준비 다섯 개를 후보로 올렸다.

CI 통과는 별도 게이트라기보다 늘 켜져 있어야 하는 최소 문턱으로 봤다. stage 체류 + 알람 무음은 넣긴 했는데, 지금 서버가 2 OCPU/12GB에 트래픽이 적어서 알람 자체가 원래 드문 편이다. 신호로서는 약하다고 판단했지만 비용이 거의 안 드니 참고용으로는 유지하기로 했고, 이것만으로 "검증됐다"고 판단하지는 않는다고 명시해뒀다.

QA verification은 핵심으로 뒀다. 지금은 pm/qa/criteria/<관점>.yaml과 pm/qa/verification/<노드>.md 포맷으로 수동 확인하는 방식인데, 시나리오가 계속 쌓이면 수동으로는 감당이 안 될 거라는 문제가 이미 보였다. Playwright e2e로 자동화하는 방향으로 팀 의견이 모이는 중이긴 한데, 레포에 현재 Playwright 스펙 파일이 하나도 없다는 것도 확인했다. E2eAdminSeedRunner처럼 로그인용 고정 비번 계정 등 밑준비만 돼 있는 상태라, 자동화를 이번 PR에서 결정하기보다는 별도 미션으로 발주할지를 논의 사항으로 남겼다.

마이그레이션 안전성 쪽은 이번 승격이 첫 Flyway 컷오버라는 점 때문에 특별 절차를 하나 추가했다. CHECK(enum) 변경 마이그레이션의 upgrade test가 과거 전체 허용값을 커버하는지 확인하는 건 기존 규칙 그대로 재확인하되, 이번만큼은 실제 prod DB 사본을 로컬로 받아 전체 마이그레이션 체인을 미리 돌려보고 ddl-auto=validate로 기동까지 확인하는 절차를 필수로 넣었다. db-man SKILL.md에 있던 "막히면" 절차를 그대로 가져다 쓰면 됐고, infra/scripts/backup_manager.py get으로 최신 prod 백업을 받을 수 있다는 것도 확인해뒀다.

롤백 준비는 기존에 있던 "배포 직전 prod 이미지 태그 기록"에 더해서, 배포 직전 수동 DB 백업 스냅샷을 하나 더 떠두는 절차를 추가했다. 매일 18:00에 도는 정기 백업과는 별개로, 마이그레이션 실행 직전 시점 기준의 신선한 복구 지점을 확보해두려는 목적이다. 이 절차는 이미 RUNBOOK.md에 있는 걸 그대로 쓰면 되는 정도라 새로 만들 건 없었다.

병합보다 논의가 먼저

이 PR은 코드 변경 없이 문서 3개(infra/CLAUDE.md, infra/docs/RUNBOOK.md, infra/docs/prod-deploy-criteria.md)만 건드렸고, 새로 추가한 prod-deploy-criteria.md는 처음부터 "확정 아님" 문구를 박아뒀다. PR 본문에도 "병합은 논의 끝난 뒤에"라고 명시했고, 실제로 이 PR은 병합되지 않고 closed 상태로 남았다.

문서 안에는 아직 답을 못 낸 지점을 세 가지로 남겨뒀다. stage 체류 기간을 숫자로 못박을지 아니면 참고 신호로만 둘지, QA 자동화를 지금 발주할지 나중으로 미룰지, prod DB 사본 dry-run을 이번 승격 때 정확히 언제 누가 실행할지. 기준 자체를 세우는 것보다 이 세 개에 대한 팀 답을 얻는 게 이 PR의 실질적인 목적에 가까웠다.