본문 바로가기

Programming/Database

Flyway로 끝내는 Spring Boot DB 마이그레이션 실전 가이드

추천캐릭터 2026. 7. 11. 08:00
728x90

ddl-auto: update로 운영 DB를 관리하다가 컬럼 하나가 조용히 사라지는 경험, 한 번쯤 있으실 겁니다. JPA의 자동 DDL은 편하지만 "언제, 누가, 무엇을" 바꿨는지 기록이 남지 않습니다. 코드에는 Git이 있는데 DB 스키마에는 버전 관리가 없는 셈이죠. 이 공백을 메우는 도구가 바로 Flyway입니다.

Flyway가 하는 일

Flyway는 SQL 마이그레이션 파일을 버전 순서대로 실행하고, 실행 이력을 flyway_schema_history 테이블에 기록합니다. 애플리케이션이 시작될 때 아직 적용되지 않은 버전만 골라 실행하므로, 어느 환경에서든 스키마가 동일한 상태로 수렴합니다.

핵심 개념은 세 가지입니다.

  • Versioned Migration (V): 한 번만 실행되는 변경. V1__create_member.sql처럼 버전 번호를 갖습니다.
  • Repeatable Migration (R): 파일 체크섬이 바뀔 때마다 재실행. 뷰나 프로시저 정의에 적합합니다.
  • 체크섬 검증: 이미 적용된 파일이 수정되면 기동 시점에 에러를 내며 멈춥니다. 스키마 이력의 무결성을 지키는 안전장치입니다.

Spring Boot 적용하기

Gradle(Kotlin DSL) 기준 의존성은 두 줄이면 됩니다. MariaDB/MySQL은 전용 모듈이 별도로 필요하다는 점만 주의하세요.

// build.gradle.kts
dependencies {
    implementation("org.flywaydb:flyway-core")
    implementation("org.flywaydb:flyway-mysql") // MariaDB/MySQL 필수
}

설정은 application.yml에서 JPA의 DDL 생성을 끄고 검증만 맡기는 것이 핵심입니다.

spring:
  jpa:
    hibernate:
      ddl-auto: validate   # 스키마 생성은 Flyway, JPA는 검증만
  flyway:
    enabled: true
    locations: classpath:db/migration
    baseline-on-migrate: true   # 기존 운영 DB에 처음 도입할 때

첫 마이그레이션 파일을 src/main/resources/db/migration/V1__create_member.sql로 만듭니다.

-- V1__create_member.sql
CREATE TABLE member (
    id          BIGINT AUTO_INCREMENT PRIMARY KEY,
    email       VARCHAR(100) NOT NULL UNIQUE,
    password    VARCHAR(255) NOT NULL,
    role        VARCHAR(20)  NOT NULL DEFAULT 'USER',
    status      VARCHAR(20)  NOT NULL DEFAULT 'ACTIVE',
    created_at  DATETIME     NOT NULL,
    updated_at  DATETIME     NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

이후 컬럼 추가가 필요하면 기존 파일을 고치는 게 아니라 새 버전을 추가합니다.

-- V2__add_member_nickname.sql
ALTER TABLE member ADD COLUMN nickname VARCHAR(30) NULL AFTER email;

애플리케이션을 실행하면 로그에 적용 내역이 출력되고, flyway_schema_history 테이블에서 버전·체크섬·실행 시각을 확인할 수 있습니다.

실무 팁과 주의사항

1. 적용된 파일은 절대 수정하지 않는다. 체크섬 불일치로 기동이 실패합니다. 오타가 있어도 새 버전(V3)으로 수정하는 것이 원칙입니다. 로컬에서 이미 꼬였다면 ./gradlew flywayRepair로 체크섬을 재계산할 수 있지만, 운영에서는 신중해야 합니다.

2. 파일명 규칙: 언더스코어 두 개. V1_create.sql(하나)은 인식되지 않아 조용히 무시됩니다. V1__create.sql이 맞습니다. 의외로 가장 흔한 실수입니다.

3. 기존 운영 DB 도입은 baseline부터. 이미 테이블이 있는 DB에는 baseline-on-migrate: true를 켜고 현재 상태를 V1(baseline)으로 선언한 뒤, 이후 변경만 V2부터 관리합니다.

4. 테스트 환경 분리. H2로 테스트한다면 MariaDB 전용 문법(AFTER, ENGINE=InnoDB 등)이 깨질 수 있습니다. spring.flyway.locations를 프로파일별로 나누거나, Testcontainers로 실제 MariaDB를 띄우는 편이 안전합니다.

5. 롤백은 공짜가 아니다. 무료 버전에는 undo 기능이 없습니다. 되돌림이 필요하면 보상 마이그레이션(예: V3__drop_nickname.sql)을 새로 작성하는 방식으로 전진만 합니다. 그래서 파괴적 변경(컬럼 삭제 등)은 "컬럼 추가 → 코드 전환 → 다음 배포에서 삭제"처럼 단계를 나누는 습관이 중요합니다.

마무리

Flyway를 도입하면 스키마 변경이 코드 리뷰의 대상이 되고, 신규 환경 구축이 ./gradlew bootRun 한 번으로 끝납니다. ddl-auto: update의 편리함과 이별하는 대신, 운영 DB에 대한 예측 가능성을 얻는 거래입니다. 개인 프로젝트라도 배포를 염두에 둔다면 첫 테이블부터 Flyway로 시작하는 것을 권합니다.


728x90