배포 전 DB 마이그레이션 검증 게이트를 만들고 환경 스키마 드리프트를 수렴한 사례

인프라·2026-09-03 기준
항목기록
문제DB 마이그레이션이 batch 앱 부팅 시 자동 적용되는 구조라 실패하면 앱이 무한 재시도하며 뜨지 않았고, MySQL DDL은 트랜잭션이 없어 반쯤 적용된 채 멈췄다. 과거 수동으로 스키마를 맞춘 이력 때문에 dev·staging·prod 가 저장소 마이그레이션의 결과와 어긋나 있어, 코드에서 잘 되던 마이그레이션이 특정 환경에서만 실패하는 일이 반복됐다.
기존 방식과 비용마이그레이션 성공 여부를 배포 전에 확인하는 단계가 없었다. 배포 워크플로는 zip 업로드까지만 책임져서 실행 183회 중 실패 6회로 보였지만, 마이그레이션 실패는 그 뒤 서버에서 일어나 CI 에 잡히지 않았다. 원인 추적은 서버 로그와 수동 DB 조회로 했고, 이미 적용된 마이그레이션 파일을 나중에 고치는 수정이 30/249 (12.0%) 누적돼 환경 간 차이를 키웠다.
제안/판단Terraform 의 plan → 확인 → apply 처럼 마이그레이션도 “실제 환경 스키마 위에서 먼저 돌려 보고 배포”하는 게이트가 필요하다고 판단했다. 환경을 재생성하는 방식은 prod 에 쓸 수 없어 배제하고, 현재 상태를 기준선으로 받아들여 새 드리프트만 막으면서 기존 차이는 멱등 마이그레이션으로 하나씩 수렴하는 방식을 택했다. 검증 도구로 Atlas 도 검토했으나 실데이터 검증이 유료 기능이고 마이그레이션 실행기 교체 비용이 커서 자체 도구로 갔다.
기술 선택환경 스키마는 SHOW CREATE TABLE 로 DDL 만 추출해(데이터 없음, 수백 KB) 로컬 MySQL 8.0.32 컨테이너에 복원하고, 같은 지점까지의 마이그레이션 기준 스키마와 information_schema 단위로 대조한다. 그 복제본 위에 아직 적용되지 않은 마이그레이션을 실제로 실행해 실패를 재현한다. GitHub 호스팅 runner 는 VPC 안 DB 에 닿지 않아, 검사 도구를 zip 으로 올리고 기존 SourceDeploy 배포 경로로 대상 서버에서 실행하도록 했다. 실 DB 계정은 REFERENCES 와 마이그레이션 기록 테이블 SELECT 만 가져 데이터를 읽을 수 없다. PR 코드는 서버에서 실행하지 않고 lint 만 돈다.
구현 범위대조·리허설·정적 lint·기준선 등록을 갖춘 CLI, 서버 실행 스크립트, GitHub Actions 워크플로(PR·dev push·schedule·수동), test·prod 배포 워크플로의 needs 게이트와 비상 우회 입력, 사유·만료·상태 고정(expect)을 갖는 예외 목록, 수렴 마이그레이션 6개(작성·리허설 완료, prod 실측 후 별도 적용), dev 정리 SQL 이력. 기존 배포 스크립트의 Object Storage 클라이언트에 다운로드를 추가했다.
내 기여문제 진단(환경별 드리프트 실측, 유령 마이그레이션 원인 특정), 접근 방식 결정(bastion·self-hosted runner·릴레이 대안 비교 후 SourceDeploy 경유 채택), 도구·워크플로·예외 정책 설계, 구현 검증과 리뷰 반영, dev 정리 SQL 작성, 팀 공유용 동작 안내 문서.
정량 결과최초 실측 드리프트 staging 20건 (실패 20), dev 65건 (실패 36, 경고 29). 정리 후 실패 기준 dev 실패 0건 (예외 13, 경고 9), staging 실패 0건 (예외 18, 경고 2). 새 마이그레이션 6/6 리허설 성공, 실패 재현 1건. 2026-09-03 기준이며 prod 는 미측정.
연결 데이터환경 스키마 드리프트 (DATA-IMPROVEMENT-SCHEMA-DRIFT) · 마이그레이션 규모 (DATA-SCALE-MIGRATIONS) · 리허설 성공 (DATA-QUALITY-REHEARSAL) · prod 드리프트 (DATA-PENDING-PROD-DRIFT)
공개 주의사항테이블·컬럼·마이그레이션 클래스명, 호스트·계정·시나리오 ID, 예외 목록 원문은 공개하지 않는다. 공개 자료에는 분류별 건수와 추상화한 흐름만 사용한다.
남은 검증prod 실측과 게이트 실행 검증이 남아 있다. 예외 목록은 부채이며 만료(2026-10-31, 2026-11-02) 전에 수렴해야 한다. 복제본에는 데이터가 없어 데이터 의존 실패(NOT NULL, UNIQUE, 고아 FK)와 대형 테이블 ALTER 시간은 검증 범위 밖이다. 마이그레이션 적용 자체를 배포 단계로 옮기는 작업은 후속이다.

왜 필요했는지: 실측 근거

근거값산출 방법의미
마이그레이션 파일249개 (2024-08~2026-09)배포 기준 브랜치 파일 수검증 없이 누적된 변경의 규모
적용 후 수정된 파일30/249 (12.0%)파일별 커밋 수 2 이상, 최다 11회환경과 파일이 어긋나는 직접 원인
마이그레이션 관련 fix 커밋8건메시지 기준, 2024-08~2026-08반복된 사후 대응. 2건은 부팅 시 경합·락·재시도 수정
CI 에 보인 배포 실패6/183배포 워크플로 실행 상태마이그레이션 실패가 CI 밖에서 일어났다는 근거
파일 규칙 위반파일명 위반 23/241 · timestamp 중복 7그룹 · name 공백 4파일명 정규식, timestamp 중복, name 공백정적 lint 가 필요한 이유
staging 드리프트20건 (실패 20)DDL 복제본 vs 마이그레이션 기준 스키마와 대조환경에만 6 · 없음 1 · 다름 6 · 기록에 파일 없음 7
dev 드리프트65건 (실패 36, 경고 29)같은 방법실패 36 · 경고 29, 기록 268 vs 파일 249
유령 마이그레이션20건기록은 있으나 원격 어느 브랜치에도 파일 없음피처 브랜치에서 개발 앱 부팅 시 공용 DB 에 자동 적용된 흔적

동작 흐름

  1. GitHub Actions 가 새 마이그레이션 파일만 lint 하고 검사 도구를 zip 으로 Object Storage 에 올린 뒤 SourceDeploy 시나리오를 실행한다.
  2. 대상 서버가 실 DB 에서 DDL 스냅샷을 뜨고, 로컬 MySQL 컨테이너에 복원한다.
  3. 빈 DB 에 저장소 마이그레이션을 환경의 최신 적용 지점까지 돌려 마이그레이션 기준 스키마를 만든다.
  4. 두 스키마를 컬럼·인덱스·FK·테이블 옵션 단위로 대조하고, 예외 목록을 적용해 남은 차이가 있으면 실패한다.
  5. 복제본 위에 아직 적용되지 않은 마이그레이션을 실제로 실행한다. 실패하면 이름·오류·소요시간을 남기고 실패한다.
  6. 리포트를 Object Storage 로 돌려보내고, GitHub 이 요약·아티팩트·Slack 으로 게시한다. test·main 배포는 이 결과가 성공일 때만 진행된다.

근거

지표는 포트폴리오 정본 data/metrics.yml(2026-08-31 기준)의 DATA-* 항목이다. 원본 응답·행 데이터는 저장하지 않는다.

검증 대기

기존 기록에서 찾았지만 같은 조건의 실행 기록을 다시 확인하지 못한 값이다. 확정 지표와 섞지 않는다.

측정값

항목이전이후비고
staging 스키마 드리프트20건 (실패 20)실패 0건 (예외 18, 경고 2)실패 20 → 0, 기준선 20건 등록
dev 스키마 드리프트65건 (실패 36, 경고 29)실패 0건 (예외 13, 경고 9)실패 36 → 0

예외 등록이며 스키마 해소가 아니다. 수렴 마이그레이션 6개 배포 후 재측정 필요. 정리 SQL 로 43건 해소, 13건은 기준선 예외(만료 2026-11-02), 경고 9건은 새 마이그레이션 적용 시 소멸. 측정일 2026-09-03 기준. 정본: 포트폴리오 data/metrics.yml.

공개 범위

이 글은 포트폴리오 정본의 공개 요약이다. 원본 업무 데이터와 회사·약국·서버·공급사 식별정보는 뺐다. 테이블·컬럼·마이그레이션 클래스명, 호스트·계정·시나리오 ID, 예외 목록 원문은 공개하지 않는다. 공개 자료에는 분류별 건수와 추상화한 흐름만 사용한다.