website: Flyway 10에서 사라진 clean-on-validation-error를 stage 전용 빈으로 재구현하기
Flyway를 10 버전으로 올리고 나니 `spring.flyway.clean-on-validation-error` 프로퍼티를 설정만 해도 "has been removed" 예외가 터졌다. stage 환경에서는 마이그레이션 체크섬이 안 맞을 때 자동으로 clean 후 재적용하는 동작에 의존하고 있었는데, 그 옵션 자체가 Flyway 10에서 없어져버린...
옵션이 사라졌다고 그 동작 자체를 포기할 수는 없었다. stage는 배포 전에 마이그레이션 파일을 자주 고치는 편이라 체크섬 불일치가 종종 발생하고, 그때마다 수동으로 DB를 지우고 다시 마이그레이션을 돌리는 건 번거로운 일이었다. 그래서 이 프로퍼티가 원래 하던 일 — 검증 실패 시 clean하고 다시 migrate — 을 FlywayMigrationStrategy 빈으로 직접 구현하기로 했다.
@Bean
@Profile("stage")
public FlywayMigrationStrategy stageCleanOnValidationErrorStrategy() {
return flyway -> {
try {
flyway.migrate();
} catch (FlywayValidateException e) {
log.warn("stage 마이그레이션 체크섬 불일치 — clean 후 재적용합니다(stage 전용 정책)", e);
flyway.clean();
flyway.migrate();
}
};
}
여기서 신경 쓴 부분은 이 빈을 @Profile("stage")로 명시적으로 묶은 것이다. 원래 프로퍼티 방식으로 쓸 때도 stage에만 적용했었지만, 코드로 재구현하면서 실수로 prod 환경에 이 로직이 새어 들어가면 검증 실패 시 데이터를 통째로 지워버리는 참사가 날 수 있다. 설정 파일 한 줄이 아니라 자바 코드로 옮기는 만큼, "prod에는 이 빈이 절대 생성되지 않는다"는 걸 테스트로 고정해둘 필요가 있다고 판단했다.
그래서 ApplicationContextRunner로 프로파일별 빈 생성 여부를 검증하는 테스트를 세 가지 케이스로 작성했다. stage 프로파일일 때는 빈이 생성되는지, prod일 때는 생성되지 않는지, 그리고 활성 프로파일이 아예 없을 때도 생성되지 않는지를 각각 확인했다. 세 번째 케이스를 넣은 이유는 프로파일 미지정 상태에서 예상치 못하게 빈이 등록되는 걸 놓치고 싶지 않아서였다. prod은 마이그레이션 검증에 실패하면 이 빈 없이 Spring Boot 기본 동작을 그대로 타게 되는데, 그러면 기동 자체가 실패하고 헬스체크가 떨어져서 CD가 자동으로 롤백하는 흐름으로 이어진다. clean을 자동으로 실행하는 코드 경로가 prod 쪽에는 물리적으로 존재하지 않도록 만든 셈이다.
프로퍼티 하나로 처리되던 걸 빈과 테스트로 옮기고 나니 코드량은 늘었지만(69줄 추가), 어떤 프로파일에서 어떤 동작이 일어나는지가 설정 파일이 아니라 코드와 테스트로 명확히 드러난다는 점은 나쁘지 않았다. 라이브러리가 프로퍼티를 제거하는 식으로 하위 호환을 깨는 경우, 그 프로퍼티가 실제로 하던 일이 무엇이었는지부터 정확히 파악하고 옮기는 게 먼저라는 걸 다시 확인한 작업이었다.