← 개발 로그 목록

website: Flyway 10에서 사라진 clean-on-validation-error, 사고 기록을 학습 문서에 남기다

/ 3분 분량 / 개발 로그

Flyway 도입 PR(#163)을 머지하자마자 dev→stage CD가 실패했다. 원인을 추적해보니 Flyway 10에서 아예 제거된 설정값을 그대로 쓰고 있었던 것이었고, 그 경위와 판단을 `pm/docs/learnings.md`에 기록했다.

PR #163으로 Flyway를 붙이면서 spring.flyway.clean-on-validation-error를 설정해뒀는데, 머지 직후 stage CD가 바로 죽었다. 다행히 자동 롤백이 걸려서 stage는 이전 이미지로 정상 복구됐고 데이터 영향은 없었지만, 왜 죽었는지는 확인이 필요했다.

로그를 보니 이 프로퍼티 자체가 Flyway 10에서 제거된 옵션이었다. true든 false든 값을 설정하기만 해도 Flyway 인스턴스 생성 단계에서 "has been removed" 예외가 던져지고, 앱 기동 자체가 실패하는 구조였다. 왜 제거됐는지 찾아보니 Redgate(Flyway 유지보수사)가 이 옵션을 위험한 기본값으로 판단해서 없애버린 거였다 — 체크섬 불일치가 나면 이 옵션이 DB를 통째로 지우고 다시 마이그레이션하는 동작인데, 실수로 켜진 채 운영 DB가 날아간 사례들이 있었다고 한다.

가장 쉬운 우회는 Flyway를 9버전으로 내리는 거였다. 그럼 이 프로퍼티가 그대로 살아난다. 근데 Spring Boot 3.5.x의 auto-configuration이 Flyway 10.x대에 맞춰져 있어서, 강제로 버전을 내리면 이 프로퍼티 하나는 되살아나도 다른 연동 지점들이 검증 안 된 조합으로 깨질 여지가 있었다. 버전을 내려서 문제를 덮는 대신, 필요한 동작을 코드로 다시 구현하는 쪽으로 판단했다.

@Profile("stage") 전용 FlywayMigrationStrategy 커스텀 빈을 만들어서, FlywayValidateException을 캐치했을 때만 clean() + migrate()를 호출하도록 했다. 핵심은 이 빈이 stage 프로파일에만 붙어 있어서 prod 환경에서는 아예 생성되지 않는다는 점이다. 즉 "설정값을 false로 둬서 막는다"가 아니라, prod 빌드/실행 시점에 이 clean 경로 자체가 코드 레벨에서 존재하지 않는다.

이걸 환경변수(SPRING_FLYWAY_CLEAN_ON_VALIDATION_ERROR)로 처리하는 방법도 고민은 했었다. 하지만 이 방식은 누군가 .env.stage 설정을 복붙하다가 실수로 .env.prod에도 같은 이름을 넣어버리면 prod도 자동 clean 대상이 될 수 있다. @Profile 분리는 그런 복붙 실수 자체가 구조적으로 불가능하다는 점에서 더 안전하다고 판단했다.

검증도 실제 Docker나 DB 없이 ApplicationContextRunner로 프로파일별 빈 생성 여부만 고정하면 끝난다는 걸 확인했다. 이번 커밋 자체는 이 판단 과정과 결과를 learnings.md에 한 줄 추가한 것뿐이지만, 나중에 비슷한 상황(설정값이 갑자기 사라지고 앱이 안 뜨는 경우)에서 "버전을 내려서 우회하지 말 것"이라는 결론을 다시 떠올리는 데 도움이 될 것 같다.