애플리케이션이 발전하면서 DB 스키마도 계속 변경된다. 협업을 하다보면 이러한 스키마 수정 값들이 제대로 반영되지 않아 문제가 발생할 수 있다.
테이블 추가, 컬럼 변경, 인덱스 추가 등의 스키마를 코드처럼 버전 관리하는 것이 DB 마이그레이션이다.
"DB 변경 이력을 Git처럼 관리하자"
수동으로 SQL을 실행하면 생기는 문제들:
SpringBoot에서 사용하는 대표적인 마이그레이션 툴 Flyway와 LiquiBase에 대해 알아보도록 하자.
V{버전}__{설명}.sql
V1__create_user_table.sql
V2__add_email_column.sql
V3__create_order_table.sql
V+ 버전번호 +__(언더바 2개) + 설명 +.sql
build.gradle.kts
dependencies {
implementation("org.flywaydb:flyway-core")
implementation("org.flywaydb:flyway-mysql") // DB에 맞게
}
application.yml
spring:
flyway:
enabled: true
locations: classpath:db/migration # SQL 파일 위치
baseline-on-migrate: true # 기존 DB가 있을 때
파일 위치
src/main/resources/
└── db/
└── migration/
├── V1__create_user_table.sql
├── V2__add_email_column.sql
└── V3__create_order_table.sql
V1__create_user_table.sql
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Flyway는 flyway_schema_history 테이블을 자동 생성해서 적용된 마이그레이션을 추적합니다.
| version | description | installed_on | success |
|---------|---------------------|---------------------|---------|
| 1 | create user table | 2024-01-01 10:00:00 | true |
| 2 | add email column | 2024-01-02 11:00:00 | true |
changeset 단위로 변경 관리build.gradle.kts
dependencies {
implementation("org.liquibase:liquibase-core")
}
application.yml
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yaml
src/main/resources/
└── db/
└── changelog/
├── db.changelog-master.yaml # 마스터 파일 (진입점)
├── 2024/
│ ├── 01-create-user.yaml
│ └── 02-add-email.yaml
└── 2025/
└── 01-create-order.yaml
db.changelog-master.yaml
databaseChangeLog:
- include:
file: db/changelog/2024/01-create-user.yaml
- include:
file: db/changelog/2024/02-add-email.yaml
01-create-user.yaml
databaseChangeLog:
- changeSet:
id: 1
author: gildong
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: BIGINT
autoIncrement: true
constraints:
primaryKey: true
- column:
name: name
type: VARCHAR(100)
constraints:
nullable: false
- column:
name: created_at
type: DATETIME
defaultValueComputed: CURRENT_TIMESTAMP
rollback:
- dropTable:
tableName: users
# CLI로 롤백
liquibase rollback --tag=v1.0
# 특정 changeset 수만큼 롤백
liquibase rollbackCount 2
| 항목 | Flyway | Liquibase |
|---|---|---|
| 형식 | SQL 중심 | XML / YAML / JSON / SQL |
| 롤백 | 수동 작성 필요 | 공식 지원 |
| 조건부 실행 | 미지원 | 지원 (preconditions) |
| 가독성 | SQL 그대로라 직관적 | 설정 파일 구조 |
| 엔터프라이즈 기능 | 유료 플랜 | 유료 플랜 |
| 적합한 규모 | 소~중규모 | 중~대규모 |
현재 에러 섹션을 보면 이렇게 3단계로 나뉘어 있어요.
1. 주 에러 상황 표 (원인 + 설명만)
2. 공통 + 툴별 해결법 표
3. Flyway / Liquibase 상황별 해결법 표 (2번과 중복)
2번과 3번을 합치고 1번은 제거하면 깔끔해져요. 이렇게요:
이러한 DB 마이그레이션 툴을 처음 사용하다보면 에러 상황에 직면할 때가 많다.
| 원인 | 설명 | 공통 해결법 | Flyway | Liquibase |
|---|---|---|---|---|
| 파일 수정 | 이미 적용된 마이그레이션 파일을 수정 | 체크섬 초기화 후 재적용 | flyway repair | liquibase clearCheckSums |
| 파일 삭제 | 적용된 마이그레이션 파일을 삭제 | 히스토리 테이블에서 해당 버전 기록 삭제 | flyway_schema_history 에서 해당 행 DELETE | DATABASECHANGELOG 에서 해당 행 DELETE |
| 수동 SQL | 누군가 DB에 직접 SQL 실행 | 히스토리 테이블에 실행된 것처럼 수동 기록 | flyway_schema_history 에 행 INSERT | liquibase changelogSync 로 자동 동기화 |
| 버전 충돌 | 브랜치 병합 후 같은 버전 파일 충돌 | 충돌 파일 버전 변경 후 재정렬 | out-of-order: true 로 순서 무시 허용 | changeset id 중복이 없으므로 충돌 자체가 드묾 |
- Flyway — 히스토리 테이블을 직접 SQL로 조작하는 경우가 많다
- Liquibase —
changelogSync,clearCheckSums등 전용 명령어로 대부분 해결할 수 있다
추가로 아래는 각 툴에서만 발생하는 케이스다.
| 상황 | 해결법 |
|---|---|
| 실패한 마이그레이션 잔존 | flyway_schema_history에서 success = false 행 삭제 후 재실행 |
| 기존 DB에 처음 도입 | baseline-on-migrate: true 설정 |
| 상황 | 해결법 |
|---|---|
| 특정 시점으로 되돌리기 | 미리 작성한 rollback 태그로 liquibase rollback |
| 기존 DB에 처음 도입 | liquibase changelogSync 로 현재 상태를 기준점으로 등록 |