Flyway Migration 문제

임도현·2026년 7월 20일

마이그레이션이란?

정의 : 기존의 데이터, 소프트웨어, 운영체제(OS) 등의 시스템 환경을 손실 없이 새로운 환경이나 플랫폼으로 안전하게 옮기는 과정

보통 마이그레이션은 정의에서 알 수 있듯이, 하위 계층의 구현사항이 바뀌게 될때 예를들어 Mysql 에서 Postgre 로의 이동이 일어날때 마이그레이션 한다고 말할 수 있을것이다.

하지만 이번 포스트에서 다루어 볼 것은 이러한 의미의 마이그레이션은 아니고, 개발과정에서의 DB변경사항을 본래 DB의 데이터들은 유지하면서도 변경사항을 적용시키는 것을 말한다.

데이터베이스를 설계하며 , 데이터베이스를 수정해야하는 경우가 많이 생긴다.

마이그레이션은 민감하게 설정해줘야한다.

이미 운영 DB에 많은 데이터들이 들어있을 것이며, 관계형 데이터베이스에서는 복잡하게 데이터들간의 관계가 형성되어있어 더더욱 민감하게 그리고 안전하게 마이그레이션을 진행해야한다.

왜 자꾸 마이그레이션이 엉키지?? 😩

Springboot 프로젝트를 절반정도 진행되고 있는 와중에,

생각보다 초기 DB로부터 수정사항들이 많이 생겨났고, 마이그레이션 파일이 29개나 생겨버렸다..

이 수많은 마이그레이션 파일을 보면 초기에 데이터베이스 설계를 잘못했나 ? 라는 생각이 저절도 들게된다 😅

사실 초반에는 많은 문제가 없었다. 당연히 수정사항이 적고, 운영 DB에 테스트 데이터들도 적으니 문제가 없었으나,

여러가지 원인들로 인해 Flyway Migration validation 이 계속 오류가 터졌다.

운영 DB에 마이그레이션 history, 즉 여태까지 마이그레이션 진행이 되었던 이력을 보고, 변경사항이 있는 경우 마이그레이션을 진행하게 되는데, 마이그레이션이 실패하게되면 Springboot 서버가 run 하지 않는다..

그래서 마이그레이션이 실패하게 되면 ec2에서 서버가 무한히 재시작하는 상황이 발생된다.
(당연하게도 swagger도 열리지 않아 프론트엔드에서 연동도 못하는 불상사가 일어난다🥹.)

이제까지 일어났던 마이그레이션 오류의 원인들을 나열해보자면 ,,

마이그레이션 버전 문제

다른 프로젝트, 그리고 다른 개발자들도 가장 흔하게 겪어볼 수 있는 오류이지 않을까 싶다

이 버전 문제의 원인은 !! 이미 운영 DB에 더 높은 , 더 최신 버전의 마이그레이션이 먼저 반영이 되어있고, 더 낮은 버전, 더 옛날 버전의 마이그레이션이 반영이 안되어있는 경우이다.

보통, 마이그레이션 파일의 네이밍으로 버전을 구분하는데,

V1__init.sql

V2__create_user.sql

V3__add_index.sql

V4__add_display.sql

이 방식처럼 직관적으로 네이밍하는 방법이 있다.
이는 여러명의 개발자가 동시에 많은 마이그레이션을 하게되면 당연히 동일한 버전으로 마이그레이션을 올려,충돌도 많이 나는 문제가 있다.

V202607010001__init.sql
V202607010002__user.sql
V202607150001__display.sql

날짜와 마이그레이션 내용을 합해 네이밍하는 방식이 있다.
이는 위의 방식보다 당연히 충돌이 안난다는 장점이 있으나, 이 날짜 순서대로 마이그레이션을 진행해야하는 제약이 생긴다.

즉, 26일에 생성된 PR의 마이그레이션이 코드리뷰가 늦어져, merge가 늦어진 이후, 28일에 생성된 PR이 먼저 merge되면 26일에 생성된 PR이 옛날 버전이 되어서 Flyway Migration 오류가 터진다!!

운영 DB와의 차이

Flyway는 “모든 환경이 동일한 Migration을 동일한 순서로 실행했다”는 것을 전제로 동작한다.

하지만 운영 DB가 이 전제를 벗어나면 Schema Drift(스키마 불일치) 가 발생하고, Migration 실행 중 오류가 발생할 수 있다.

  1. Hibernate가 운영 DB를 자동으로 변경한 경우

운영 환경에서 ddl-auto=update 또는 create가 설정되어 있으면 Hibernate가 엔티티를 기준으로 테이블을 직접 수정한다.

이 과정에서 FK, Index 등의 이름이 Hibernate 규칙으로 자동 생성될 수 있으며, 이후 Flyway가 예상하는 이름과 달라질 수 있다.

Flyway 예상 FK
FK_ARCHIVEARTIST_CREATOR
실제 운영 DB FK
FK8dfk29a8sd...

결과적으로

ALTER TABLE ArchiveArtist
DROP FOREIGN KEY FK_ARCHIVEARTIST_CREATOR;

와 같은 Migration이 실패하게 된다.

운영 환경에서는 반드시

spring.jpa.hibernate.ddl-auto=validate

이 원인때문에 운영 DB와 마이그레이션 파일의 FK네이밍이 맞지 않아서 DROP FK 명령어가 실행되지 않았던 것이다.


  1. 오래된 Build 또는 Docker Image가 배포된 경우

Git에는 최신 Migration이 존재하지만

  • 오래된 JAR
  • Docker Cache
  • Clean Build 누락

등으로 인해 이전 Migration이 포함된 이미지가 운영에 배포될 수 있다.

이를 방지하기 위해

./gradlew clean build

를 수행하고

배포되는 JAR 안의 Migration 파일도 검증하는 것이 좋다.

이 경우가 가장 디버깅이 어려운 경우이다. 나는 분명히 수정해서 이미지 빌드 이후 푸시했는데, 왜 오류가 수정이 안될까라는 생각이 들때가 있는데 이 때 이전 이미지가 배포되지는 않았는지 이전 배포의 이미지를 사용하고 있지는 않은지 , clean build 를 안하고 있는것은 아닌지 확인해야할 것이다.


  1. 운영 데이터 상태가 다른 경우

이 경우에는 운영 DB에 예를 들어 UNIQUE CONSTARINT 를 추가하려고 하는 상황속에, 이미 운영 DB에 중복된 데이터가 많은 경우, 마이그레이션이 실패한다.

그 이외에도 NOT NULL 칼럼을 추가할때에도 모든 존재하는 데이터에 NOT NULL이기 때문에 값들을 추가해줘야하는 것이다.
즉, 다시말해서 NOT NULL 컬럼을 기존 테이블에 추가하는 경우, 이미 존재하는 모든 Row는 해당 컬럼의 값을 반드시 가져야 한다. 따라서 기존 데이터에 대한 값 채우기(Backfill) 과정 없이 NOT NULL 컬럼을 추가하면 Migration이 실패할 수 있다.

일반적으로 NULL 허용 컬럼 추가 → 기존 데이터 Backfill → NOT NULL 변경의 순서로 Migration을 수행하여 운영 환경에서의 Migration 실패를 방지

해결책

기존 CD 파이프라인에서는 새로운 Docker 이미지를 배포한 뒤, 애플리케이션이 시작되면서 운영 DB에 Flyway Migration을 바로 수행하는 구조였다. 이 경우 운영 DB의 실제 스키마와 Migration이 기대하는 스키마가 조금이라도 다르면 Migration이 실패하고, flyway_schema_history에 success = 0이 기록되면서 애플리케이션이 정상적으로 기동하지 못하는 문제가 발생할 수 있다.

이를 해결하기 위해 운영 DB에 Migration을 실행하기 전에 운영 DB와 동일한 스키마를 가진 임시 MySQL 환경에서 Migration을 먼저 검증하는 단계를 CD 파이프라인에 추가하였다.

새로운 배포 흐름은 다음과 같다.

Build
        ↓
Migration Precheck
(운영 DB Schema 복제 + Flyway Migrate 테스트)
        ↓
Production Migration
        ↓
Application Deploy
  1. Migration Precheck 단계 추가

운영 DB의 Schema와 flyway_schema_history만 복제하여 임시 MySQL을 구성한 뒤, 해당 환경에서 실제 Flyway migrate를 수행한다.

이를 통해 다음과 같은 문제를 운영 배포 전에 사전에 발견할 수 있다.

  • Foreign Key 이름 불일치
  • 존재하지 않는 Column 또는 Index 삭제
  • 이미 존재하는 Constraint 추가
  • NOT NULL 변경 실패
  • UNIQUE Constraint 충돌
  • MySQL 버전 또는 문법 차이
  1. Migration 성공 시에만 운영 DB 변경

Precheck가 성공한 경우에만 운영 DB에서 Migration을 수행하도록 변경하였다.

만약 사전 검증에서 Migration이 실패하면 운영 DB에는 어떠한 변경도 발생하지 않으며, 이후 Application Deploy도 진행되지 않는다.

  1. 애플리케이션 배포와 DB Migration 분리

기존에는 Spring Boot가 시작되면서 Flyway Migration도 함께 수행하였다.

개선 이후에는 Migration을 CD 파이프라인의 독립된 단계로 분리하여, Migration이 성공한 이후에만 새로운 애플리케이션을 배포하도록 변경하였다.

  1. Migration 파일 보호

추가적으로 CD에서 다음 사항을 검증하도록 구성하였다.

  • 이미 적용된 Migration 파일 수정 또는 삭제 금지
  • Migration Version 중복 검사
  • Migration 파일명 규칙 검사
  • 위험한 DDL(DROP COLUMN, DROP FOREIGN KEY 등) 감지

이를 통해 운영 환경과 Git Repository의 Migration 이력이 항상 동일하게 유지되도록 하였다.

profile
성장을 즐기는 사람 🧍

0개의 댓글