Flyway
Flyway는 데이터베이스 스키마 변경 이력을 코드로 관리하고, 배포 시점에 아직 적용되지 않은 migration SQL을 순서대로 실행하는 DB migration 도구다.
애플리케이션 코드를 Git으로 버전 관리하듯이, 테이블 생성, 컬럼 추가, 인덱스 변경 같은 DB 스키마 변경도 파일로 남겨 환경 간 차이를 줄이는 것이 핵심이다.
왜 필요한가
서비스를 운영하다 보면 로컬, 개발, 스테이징, 운영 DB의 스키마가 조금씩 달라지기 쉽다. 누군가는 수동으로 컬럼을 추가하고, 누군가는 같은 SQL을 빼먹고, 운영 반영 순서가 꼬이면 애플리케이션은 특정 환경에서만 실패한다.
Flyway는 이런 문제를 줄이기 위해 migration 파일을 정해진 순서대로 적용하고, 어떤 migration이 이미 실행되었는지 DB 안에 기록한다.
기본 동작
Flyway는 보통 db/migration 같은 디렉터리에 있는 SQL 파일을 읽는다. 파일 이름은 버전과 설명을 포함하는 규칙을 따른다.
db/migration/
├── V1__init.sql
├── V2__create_config_resource.sql
└── V3__add_unique_key_to_config_resource_name.sql
여기서 V1, V2, V3는 migration version이고, __ 뒤는 사람이 읽기 위한 설명이다.
애플리케이션 시작 또는 배포 과정에서 Flyway가 실행되면 다음 순서로 동작한다.
- DB의 현재 migration 상태를 확인한다.
- 아직 적용되지 않은 migration 파일을 찾는다.
- 버전 순서대로 SQL을 실행한다.
- 실행 결과를
flyway_schema_history테이블에 기록한다.
flyway_schema_history
Flyway는 DB 안에 flyway_schema_history 테이블을 만들어 migration 적용 이력을 관리한다.
이 테이블에는 대략 다음 정보가 남는다.
installed_rank
version
description
type
script
checksum
installed_by
installed_on
success
이 기록 덕분에 Flyway는 V1__init.sql이 이미 적용되었는지, V2__...sql을 새로 실행해야 하는지 판단할 수 있다.
중요한 규칙
이미 운영이나 공유 환경에 적용된 migration 파일은 수정하지 않는 것이 원칙이다.
예를 들어 V2__create_config_resource.sql이 이미 적용된 뒤 컬럼 정의를 바꾸고 싶다면, 기존 V2 파일을 고치는 대신 새 파일을 추가한다.
V3__alter_config_resource_id_to_bigint.sql
V4__add_unique_key_to_config_resource_name.sql
이렇게 해야 각 환경의 migration 이력이 일관되게 유지된다. 이미 적용된 파일을 수정하면 checksum이 달라져 Flyway가 검증 오류를 낼 수 있고, 환경마다 실제 스키마가 달라질 위험이 생긴다.
Java/Kotlin에서 사용
Java/Kotlin, 특히 Spring Boot에서는 Flyway를 의존성에 추가하면 애플리케이션 시작 시 자동으로 migration을 실행하는 방식이 흔하다.
Gradle 예시:
dependencies {
implementation("org.flywaydb:flyway-core")
runtimeOnly("org.flywaydb:flyway-mysql") // MySQL 사용 시
}
기본 migration 위치는 다음과 같다.
src/main/resources/db/migration/
├── V1__init.sql
└── V2__add_config_resource.sql
Spring Boot 설정 예시:
spring:
flyway:
enabled: true
locations: classpath:db/migration
이렇게 두면 애플리케이션이 DB에 연결한 뒤, 아직 적용되지 않은 V*.sql 파일을 Flyway가 자동으로 적용한다.
Go에서 사용
Go에서는 Flyway를 애플리케이션 라이브러리로 직접 붙이기보다는, Docker 이미지나 CLI를 배포 파이프라인에서 실행하는 방식이 단순하다.
예를 들어 migration 파일을 db/migration에 두고 Flyway Docker 이미지를 실행할 수 있다.
docker run --rm \
-v "$(pwd)/db/migration:/flyway/sql" \
flyway/flyway \
-url="jdbc:mysql://localhost:3306/app" \
-user="app" \
-password="secret" \
migrate
Go 애플리케이션 자체는 DB 접속과 비즈니스 로직에 집중하고, migration은 CI/CD, init job, 배포 스크립트에서 별도 단계로 실행하는 식이다. Go 코드 안에서 migration을 강하게 통합하고 싶다면 golang-migrate, goose 같은 Go 생태계 도구를 선택하는 경우도 많다.
장점
- DB 스키마 변경 이력이 코드 리뷰 대상이 된다.
- 로컬, 개발, 운영 환경의 스키마 동기화 누락을 줄일 수 있다.
- 새 환경을 만들 때 migration을 처음부터 순서대로 적용해 같은 스키마를 재현할 수 있다.
- 배포 과정에서 DB 변경을 자동화할 수 있다.
주의할 점
- 큰 테이블에
ALTER TABLE을 실행하면 운영 DB에서 lock이 오래 잡힐 수 있다. - migration 파일은 적용 후 수정하지 말고 새 migration으로 보정한다.
- 실패한 migration을 수동으로 고쳤다면
flyway_schema_history상태와 실제 DB 상태가 일치하는지 확인해야 한다. - DDL이 transaction으로 묶이는지는 DB마다 다르므로, 실패 시 롤백 동작을 DB별로 확인해야 한다.
한 줄 요약
Flyway는 DB 스키마 변경을 SQL 파일로 버전 관리하고, flyway_schema_history를 기준으로 누락된 migration만 순서대로 적용해 환경 간 스키마 차이를 줄이는 도구다.
Written by GPT-5.5 Codex