website: 엔티티만 고쳐선 스키마가 안 바뀐다 — Flyway 레포에서 놓칠 뻔한 것
며칠 전 학번+기수 복합 유니크 관련 학습 기록을 남겼는데, 그게 애플리케이션 레벨만 보고 쓴 불완전한 기록이었다는 걸 나중에 발견해서 이어 붙이는 글이다.
경위는 이렇다. 멤버 관리 기능(#145, PR #155)에서 studentId 단일 컬럼에 유니크 제약을 걸어놨던 걸, 리뷰에서 오프보딩 후 재입부 케이스가 막힌다는 지적을 받고 학번+기수 복합키로 넓히기로 했다. 여기까지는 지난 기록에 이미 썼던 내용이다. 문제는 그 다음이었다. 우진님이 리뷰에서 "테스트가 실제 시나리오대로인지 검증해봤냐"고 재지적을 했는데, 처음엔 엔티티 애노테이션만 고치고 테스트가 통과하니 끝난 줄 알았다.
그런데 이 레포는 #133부터 Flyway + ddl-auto: validate로 스키마를 관리하고 있었다. 엔티티 애노테이션을 아무리 고쳐도 db/migration/에 SQL을 넣지 않으면 실제 stage/prod DB에는 전혀 반영이 안 되는 구조였다. 이걸 놓치고 있었던 이유를 찾아보니, src/test/resources/application.yml이 create-drop + :memory:로 되어 있어서 테스트를 돌릴 때마다 스키마를 엔티티 그대로 새로 만들고 있었다. 즉 매번 그린필드 상태에서 검증하는 거라, "이미 데이터가 있는 실제 DB 위에 새 마이그레이션을 얹었을 때" 벌어지는 문제는 테스트가 원리적으로 잡아낼 수 없는 구조였다.
이걸 확인하려고 git으로 옛 커밋을 체크아웃해서 실제 파일 기반 SQLite DB로 재현해봤다. 그리고 SQLite는 ALTER TABLE로 기존 컬럼의 unique 제약을 못 바꾼다는 것도 다시 확인했다(이건 db-man 스킬에 이미 경고돼 있던 내용이었는데, 실제로 부딪히니 단순 ALTER 마이그레이션이 아니라 테이블 재생성 패턴으로 가야 했다).
이 문제를 다음에 또 수동으로 재현하지 않으려고 MigrationUpgradeHarness를 만들었다. Flyway를 특정 target 버전까지만 올리고 raw SQL로 구버전 데이터를 심어둔 다음, 그 위에 새 마이그레이션을 얹어서 검증하는 방식이다. 이걸 db-man 스킬의 표준 절차에 편입시켜서, 앞으로 스키마 변경이 있을 때마다 반복해서 확인할 수 있게 해뒀다.
정리하면, "엔티티를 수정했다"와 "스키마가 바뀌었다"가 같은 말이 아닌 레포(Flyway로 관리하는 레포)에서는 코드 리뷰만으로 스키마 변경의 실효성을 판단하면 안 된다. 실제 마이그레이션 파일이 있는지, 그리고 그린필드가 아닌 업그레이드 경로까지 확인해야 한다는 걸 이번에 다시 새겼다. 지난 기록이 절반짜리였던 셈이라, 이번엔 그 뒷부분을 마저 이어 붙였다.