TL;DR

  • Flyway는 SQL 기반 버전드/반복 마이그레이션으로 스키마 변경을 추적한다.
  • flyway_schema_history로 적용 순서와 상태를 기록해 드리프트를 예방한다.
  • 확장-축소 패턴과 Spring Boot/CI 파이프라인과의 통합이 강점이다.

1. 개념

Flyway는 DB 스키마를 코드처럼 버전 관리하는 마이그레이션 도구다.

2. 배경

수동 변경과 환경 불일치로 장애가 반복되며 체계적 관리가 필요해졌다.

3. 이유

배포 안정성과 변경 이력 추적, 롤백 전략을 확보하기 위해서다.

4. 특징

버전드·반복·언두 마이그레이션, 히스토리 테이블, 다중 DB/CI 통합이 핵심이다.

5. 상세 내용

Flyway - 데이터베이스 마이그레이션의 모든 것

작성일: 2026-02-27 카테고리: Backend / Database / Migration 포함 내용: Flyway, Database Migration, Schema Versioning, flyway_schema_history, Versioned Migration, Repeatable Migration, Undo Migration, Spring Boot 통합, Liquibase 비교, Expand-Contract Pattern, Zero-Downtime Migration, CI/CD


1. 탄생 배경과 역사

1.1 도구 없는 데이터베이스 변경 = 재앙

┌─────────────────────────────────────────────────────────────────┐
│           도구 없이 DB 스키마를 관리하면 생기는 일                │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  시나리오: 4명의 개발자가 동시에 스키마를 변경                    │
│                                                                   │
│  개발자 A: ALTER TABLE users ADD COLUMN phone VARCHAR(20);       │
│  개발자 B: ALTER TABLE users ADD COLUMN address TEXT;            │
│  개발자 C: CREATE TABLE orders (...);                            │
│  개발자 D: ALTER TABLE users DROP COLUMN nickname;               │
│                                                                   │
│  문제점:                                                          │
│  ├── 누가 먼저 적용해야 하는지 아무도 모름                       │
│  ├── 로컬 DB와 개발 서버 DB가 서로 다른 상태                     │
│  ├── "내 컴퓨터에서는 되는데?" 반복                              │
│  ├── 스테이징에 배포하면 에러 폭발                               │
│  └── 프로덕션 배포 = 기도하고 실행하기                           │
│                                                                   │
│  실제 사고 사례:                                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  TSB Bank (2018년 영국)                                  │    │
│  │  ├── 데이터베이스 마이그레이션 실패로 서비스 장애         │    │
│  │  ├── 190만 고객이 계좌 접근 불가                          │    │
│  │  ├── 일부 고객이 타인 계좌 정보 열람                      │    │
│  │  ├── 복구에 수 주 소요                                    │    │
│  │  └── CEO 사임 + £48.65M 벌금                              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Experian 연구 (데이터 마이그레이션 프로젝트):                   │
│  ├── 64%가 예산 초과                                             │
│  ├── 46%만 일정 내 완료                                          │
│  └── 주요 원인: 체계적 도구/프로세스 부재                        │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

1.2 Schema Drift 문제

┌─────────────────────────────────────────────────────────────────┐
│              Schema Drift = 스키마 표류 현상                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  정의: 동일해야 할 여러 환경의 DB 스키마가 서로 달라지는 현상   │
│                                                                   │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐       │
│  │  로컬 DB  │  │  개발 DB  │  │ 스테이징  │  │ 프로덕션  │       │
│  │          │  │          │  │    DB    │  │    DB    │       │
│  ├──────────┤  ├──────────┤  ├──────────┤  ├──────────┤       │
│  │ users    │  │ users    │  │ users    │  │ users    │       │
│  │ + phone  │  │ + phone  │  │          │  │          │       │
│  │ + addr   │  │          │  │ + addr   │  │ + nick   │       │
│  │ orders ✓ │  │ orders ✗ │  │ orders ✓ │  │ orders ✗ │       │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘       │
│       ↑              ↑              ↑             ↑              │
│    4개 환경이 전부 다른 상태! = Schema Drift                     │
│                                                                   │
│  Schema Drift가 발생하는 원인:                                   │
│  ├── 수동 SQL 실행 (DBA가 프로덕션에서 직접 ALTER TABLE)        │
│  ├── 핫픽스로 인한 임시 변경이 다른 환경에 반영 안 됨            │
│  ├── ORM ddl-auto=update가 환경마다 다르게 동작                  │
│  └── 마이그레이션 이력 관리 부재                                 │
│                                                                   │
│  결과:                                                            │
│  ├── "이 컬럼이 왜 여기 있지?" (기원 불명)                       │
│  ├── 프로덕션에만 존재하는 인덱스 (누가 만들었는지 모름)         │
│  ├── 리포팅 DB와 운영 DB의 스키마 불일치                         │
│  └── 장애 복구 시 "원래 스키마가 뭐였지?" 혼란                   │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

1.3 Flyway의 탄생

┌─────────────────────────────────────────────────────────────────┐
│                   Flyway의 역사                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  이론적 토대:                                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  2003년: Martin Fowler                                   │    │
│  │  "Evolutionary Database Design" 발표                     │    │
│  │  → "DB 스키마도 코드처럼 점진적으로 진화해야 한다"       │    │
│  │                                                          │    │
│  │  2006년: Scott Ambler & Pramod Sadalage                  │    │
│  │  "Refactoring Databases" 출간                            │    │
│  │  → DB 리팩토링을 체계적 패턴으로 정리                    │    │
│  │                                                          │    │
│  │  핵심 주장:                                              │    │
│  │  "데이터베이스 변경도 버전 관리되어야 한다.              │    │
│  │   애플리케이션 코드와 동일한 수준의 관리가 필요하다."    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Flyway 연혁:                                                    │
│  ├── 2010년: Axel Fontaine이 Flyway 최초 공개                   │
│  │           → "DB 스키마 버전 관리" 문제를 해결하겠다!          │
│  │           → SQL 파일 기반의 단순한 접근 방식 채택             │
│  ├── 2015년: ThoughtWorks Technology Radar "Adopt" 등급          │
│  │           → 업계 최고 권위 기술 레이더에서 "도입 권장"        │
│  ├── 2019년: Redgate가 Flyway 인수 (약 $10M 추정)               │
│  │           → 데이터베이스 도구 전문 기업의 전폭적 지원         │
│  ├── 2020년+: Community / Teams / Enterprise 에디션 분리          │
│  └── 현재: JVM 생태계 DB 마이그레이션 사실상 표준                │
│                                                                   │
│  Flyway의 설계 철학:                                             │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  "Simplicity is the ultimate sophistication"             │    │
│  │  - Leonardo da Vinci                                     │    │
│  │                                                          │    │
│  │  1. SQL이 최고의 DDL 언어다 (SQL-first)                  │    │
│  │  2. 마이그레이션은 순서가 있다 (Ordered)                  │    │
│  │  3. 적용된 마이그레이션은 불변이다 (Immutable)            │    │
│  │  4. 단순함이 곧 강력함이다 (Simple > Complex)             │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

2. 핵심 개념

2.1 마이그레이션의 세 가지 유형

┌─────────────────────────────────────────────────────────────────┐
│              Flyway 마이그레이션 유형 3가지                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. Versioned Migration (버전 마이그레이션)                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  파일명: V{버전}__{설명}.sql                             │    │
│  │                                                          │    │
│  │  예시:                                                   │    │
│  │  ├── V1__Create_users_table.sql                          │    │
│  │  ├── V2__Add_email_to_users.sql                          │    │
│  │  ├── V3__Create_orders_table.sql                         │    │
│  │  └── V1.1__Add_phone_to_users.sql                        │    │
│  │                                                          │    │
│  │  특징:                                                   │    │
│  │  ├── 딱 한 번만 적용됨 (once and only once)              │    │
│  │  ├── 버전 순서대로 실행됨                                │    │
│  │  ├── 적용 후 checksum으로 무결성 검증                    │    │
│  │  └── 적용된 파일은 절대 수정하면 안 됨!                  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  2. Repeatable Migration (반복 마이그레이션)                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  파일명: R__{설명}.sql                                   │    │
│  │                                                          │    │
│  │  예시:                                                   │    │
│  │  ├── R__Create_user_view.sql                             │    │
│  │  ├── R__Update_stored_procedures.sql                     │    │
│  │  └── R__Refresh_materialized_views.sql                   │    │
│  │                                                          │    │
│  │  특징:                                                   │    │
│  │  ├── checksum이 변경될 때마다 재실행                     │    │
│  │  ├── 버전 번호가 없음 (R__ 로 시작)                      │    │
│  │  ├── 모든 Versioned 마이그레이션 이후에 실행             │    │
│  │  ├── View, Stored Procedure 관리에 이상적                │    │
│  │  └── 항상 CREATE OR REPLACE 형태로 작성                  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  3. Undo Migration (되돌리기 마이그레이션)                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  파일명: U{버전}__{설명}.sql                             │    │
│  │                                                          │    │
│  │  예시:                                                   │    │
│  │  ├── U2__Add_email_to_users.sql     ← V2의 역순         │    │
│  │  └── U3__Create_orders_table.sql    ← V3의 역순         │    │
│  │                                                          │    │
│  │  특징:                                                   │    │
│  │  ├── ⚠️  Enterprise Edition 전용!                        │    │
│  │  ├── 해당 버전의 Versioned Migration을 되돌림            │    │
│  │  ├── Community Edition에서는 사용 불가                    │    │
│  │  └── 대안: 새로운 Versioned Migration으로 롤백 로직 작성 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

2.2 파일 명명 규칙 (Naming Convention)

┌─────────────────────────────────────────────────────────────────┐
│              파일명 해부하기                                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  V2.1__Add_email_column.sql                                      │
│  │ │   │                  │                                       │
│  │ │   │                  └── 확장자: .sql                        │
│  │ │   │                                                          │
│  │ │   └── 설명 (Description)                                    │
│  │ │       ├── 언더스코어(_)로 단어 구분                         │
│  │ │       └── 무엇을 하는지 명확하게                            │
│  │ │                                                              │
│  │ └── __ (이중 밑줄 = 구분자)                                   │
│  │     └── 반드시 언더스코어 2개! 1개면 인식 안 됨               │
│  │                                                                │
│  └── V + 버전 번호                                               │
│      ├── V1, V2, V3 (정수)                                       │
│      ├── V1.1, V1.2 (소수점)                                     │
│      ├── V20240101 (날짜 기반)                                    │
│      └── V202401011430 (타임스탬프 기반)                          │
│                                                                   │
│  실무 팁 - 타임스탬프 기반 권장:                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  정수 방식의 문제:                                       │    │
│  │  개발자 A → V4__Add_phone.sql     (월요일)               │    │
│  │  개발자 B → V4__Add_address.sql   (화요일)               │    │
│  │  → 충돌! 같은 버전 번호!                                 │    │
│  │                                                          │    │
│  │  타임스탬프 방식:                                        │    │
│  │  개발자 A → V20260227143000__Add_phone.sql               │    │
│  │  개발자 B → V20260227150000__Add_address.sql             │    │
│  │  → 충돌 가능성 거의 제로!                                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

2.3 flyway_schema_history 테이블

┌─────────────────────────────────────────────────────────────────┐
│           flyway_schema_history = 마이그레이션 감사 로그          │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Flyway가 자동으로 생성하는 메타데이터 테이블                    │
│  → 어떤 마이그레이션이 언제, 누가, 얼마나 걸려서 적용됐는지     │
│                                                                   │
│  테이블 구조:                                                    │
│  ┌──────────────────┬──────────┬──────────────────────────┐    │
│  │     컬럼명        │  타입     │  설명                     │    │
│  ├──────────────────┼──────────┼──────────────────────────┤    │
│  │ installed_rank   │ INT      │ 적용 순서 (1, 2, 3...)    │    │
│  │ version          │ VARCHAR  │ 버전 번호 (1, 2, 2.1)    │    │
│  │ description      │ VARCHAR  │ 설명 (Add_email 등)       │    │
│  │ type             │ VARCHAR  │ SQL / JAVA / SPRING_JDBC  │    │
│  │ script           │ VARCHAR  │ 파일명                     │    │
│  │ checksum         │ INT      │ 파일 내용의 CRC32 해시    │    │
│  │ installed_by     │ VARCHAR  │ DB 접속 사용자명           │    │
│  │ installed_on     │ TIMESTAMP│ 적용 시각                  │    │
│  │ execution_time   │ INT      │ 실행 시간 (밀리초)         │    │
│  │ success          │ BOOLEAN  │ 성공 여부                  │    │
│  └──────────────────┴──────────┴──────────────────────────┘    │
│                                                                   │
│  실제 데이터 예시:                                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │ rank │ ver  │ description      │ checksum   │ success   │    │
│  ├──────┼──────┼──────────────────┼────────────┼───────────┤    │
│  │  1   │ 1    │ Create users     │ -12345678  │ true      │    │
│  │  2   │ 2    │ Add email column │  98765432  │ true      │    │
│  │  3   │ 3    │ Create orders    │ -55667788  │ true      │    │
│  │  4   │ 4    │ Add phone column │  11223344  │ false     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  핵심 원칙:                                                      │
│  ├── 이 테이블은 불변 감사 로그 (Immutable Audit Trail)          │
│  ├── 수동으로 절대 수정하지 말 것! (repair 명령어 사용)          │
│  ├── checksum으로 파일 변조 감지                                 │
│  └── success=false인 항목은 repair로 정리                        │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

2.4 마이그레이션 상태 (Migration States)

┌─────────────────────────────────────────────────────────────────┐
│              마이그레이션 라이프사이클                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  파일 시스템에 존재    flyway_schema_history에 기록               │
│                                                                   │
│  ┌──────────┐                                                    │
│  │ Pending  │ → 파일은 있지만 아직 적용 안 됨                    │
│  │ (대기)   │   (history 테이블에 없음)                          │
│  └────┬─────┘                                                    │
│       │ flyway migrate 실행                                      │
│       ▼                                                          │
│  ┌──────────┐                                                    │
│  │ Applied  │ → 성공적으로 적용됨                                │
│  │ (적용됨) │   (success=true)                                   │
│  └──────────┘                                                    │
│                                                                   │
│  ┌──────────┐                                                    │
│  │ Failed   │ → 실행 중 에러 발생                                │
│  │ (실패)   │   (success=false)                                  │
│  └──────────┘   → repair 명령어로 정리 필요                      │
│                                                                   │
│  ┌──────────────┐                                                │
│  │ Out of Order │ → 이미 높은 버전이 적용된 상태에서             │
│  │ (순서 이탈)  │   낮은 버전이 나중에 적용됨                    │
│  └──────────────┘   → outOfOrder=true 설정 필요                  │
│                                                                   │
│  ┌──────────────┐                                                │
│  │  Superseded  │ → Repeatable Migration이 새 버전으로           │
│  │  (대체됨)    │   교체되어 이전 기록이 대체된 상태              │
│  └──────────────┘                                                 │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3. 주요 명령어

3.1 7가지 핵심 명령어

┌─────────────────────────────────────────────────────────────────┐
│                 Flyway 핵심 명령어 7가지                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. migrate (마이그레이트)                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  역할: Pending 상태의 마이그레이션을 순서대로 적용       │    │
│  │  사용: flyway migrate                                    │    │
│  │                                                          │    │
│  │  동작 흐름:                                              │    │
│  │  1) flyway_schema_history 테이블 확인 (없으면 생성)      │    │
│  │  2) 적용된 마이그레이션 목록 조회                        │    │
│  │  3) Pending 마이그레이션을 버전 순서대로 실행             │    │
│  │  4) 각 마이그레이션의 checksum 기록                      │    │
│  │  5) 실행 결과 (성공/실패) 기록                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  2. clean (클린) ⚠️  위험!                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  역할: 스키마의 모든 객체를 삭제 (테이블, 뷰, 인덱스)   │    │
│  │  사용: flyway clean                                      │    │
│  │                                                          │    │
│  │  ⚠️  절대 프로덕션에서 실행하지 말 것!                   │    │
│  │  → Flyway 9.0+: cleanDisabled=true 기본값               │    │
│  │  → 개발/테스트 환경에서만 사용                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  3. info (인포)                                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  역할: 모든 마이그레이션의 상태를 보여줌                 │    │
│  │  사용: flyway info                                       │    │
│  │                                                          │    │
│  │  출력 예시:                                              │    │
│  │  +---------+-----+----------------+----------+           │    │
│  │  | Version | Desc| Type           | State    |           │    │
│  │  +---------+-----+----------------+----------+           │    │
│  │  | 1       | Init| SQL            | Applied  |           │    │
│  │  | 2       | Add | SQL            | Applied  |           │    │
│  │  | 3       | Tbl | SQL            | Pending  |           │    │
│  │  +---------+-----+----------------+----------+           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  4. validate (검증)                                              │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  역할: 적용된 마이그레이션과 로컬 파일의 일치 여부 확인  │    │
│  │  사용: flyway validate                                   │    │
│  │                                                          │    │
│  │  검증 항목:                                              │    │
│  │  ├── checksum 일치 여부 (파일이 수정됐는지)              │    │
│  │  ├── 누락된 마이그레이션 (파일이 삭제됐는지)             │    │
│  │  └── 버전 순서 정합성                                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  5. baseline (베이스라인)                                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  역할: 기존 DB에 Flyway를 도입할 때 시작점 설정          │    │
│  │  사용: flyway baseline -baselineVersion=3               │    │
│  │                                                          │    │
│  │  시나리오:                                               │    │
│  │  ├── 이미 운영 중인 DB에 Flyway를 처음 적용할 때         │    │
│  │  ├── V1~V3까지의 변경은 이미 수동 적용된 상태            │    │
│  │  ├── baselineVersion=3으로 설정하면                      │    │
│  │  └── V4부터 Flyway가 관리 시작                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  6. repair (복구)                                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  역할: flyway_schema_history 테이블의 문제 해결          │    │
│  │  사용: flyway repair                                     │    │
│  │                                                          │    │
│  │  수행 작업:                                              │    │
│  │  ├── 실패한 마이그레이션 기록 제거 (success=false)       │    │
│  │  ├── checksum 재정렬 (파일 수정 후 동기화)               │    │
│  │  └── 누락된 마이그레이션 정보 업데이트                   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  7. undo (되돌리기) ⚠️  Enterprise 전용                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  역할: 마지막으로 적용된 마이그레이션을 되돌림           │    │
│  │  사용: flyway undo                                       │    │
│  │                                                          │    │
│  │  Community Edition 대안:                                 │    │
│  │  → 새로운 V 마이그레이션으로 롤백 SQL 작성               │    │
│  │  예: V5__Rollback_add_phone.sql                          │    │
│  │      ALTER TABLE users DROP COLUMN phone;                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

3.2 명령어 실행 방법

┌─────────────────────────────────────────────────────────────────┐
│              Flyway 실행 방법 4가지                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. CLI (Command Line Interface)                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  $ flyway -url=jdbc:postgresql://localhost/mydb \        │    │
│  │          -user=admin -password=secret migrate            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  2. Java API                                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Flyway flyway = Flyway.configure()                      │    │
│  │      .dataSource(url, user, password)                    │    │
│  │      .load();                                            │    │
│  │  flyway.migrate();                                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  3. Spring Boot (자동 실행)                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  # application.yml                                       │    │
│  │  spring:                                                 │    │
│  │    flyway:                                               │    │
│  │      enabled: true   ← 이것만으로 앱 시작 시 자동 실행  │    │
│  │      locations: classpath:db/migration                   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  4. Gradle / Maven 플러그인                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  # Gradle                                                │    │
│  │  $ ./gradlew flywayMigrate                               │    │
│  │                                                          │    │
│  │  # Maven                                                 │    │
│  │  $ mvn flyway:migrate                                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

4. 어떤 상황에서 효과적인가?

4.1 Flyway가 빛나는 상황

┌─────────────────────────────────────────────────────────────────┐
│              Flyway를 써야 하는 6가지 상황                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. 팀 개발 (Team Development)                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  여러 개발자가 동시에 스키마 변경 작업                    │    │
│  │  → 각자 V 파일 생성 → merge 시 순서 자동 정렬            │    │
│  │  → "이거 적용했어?" 물어볼 필요 없음                     │    │
│  │  → flyway info 한 번이면 현재 상태 파악 완료             │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  2. CI/CD 파이프라인 통합                                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Build → Test → flyway migrate → Deploy                  │    │
│  │  → 배포 시 자동으로 DB 스키마 업데이트                    │    │
│  │  → 수동 SQL 실행 제로                                    │    │
│  │  → 롤백 시 어떤 버전으로 돌아가야 하는지 명확             │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  3. 마이크로서비스 아키텍처                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  각 서비스가 자체 DB 스키마를 독립적으로 관리             │    │
│  │  ├── user-service   → src/main/resources/db/migration/  │    │
│  │  ├── order-service  → src/main/resources/db/migration/  │    │
│  │  └── payment-service→ src/main/resources/db/migration/  │    │
│  │  → 서비스별 독립적 스키마 진화                            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  4. 규정 준수 / 감사 요구                                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  flyway_schema_history = 불변 감사 추적                   │    │
│  │  ├── 누가 언제 어떤 변경을 적용했는지 기록                │    │
│  │  ├── 금융, 의료, 공공 분야의 규제 충족                    │    │
│  │  └── Git 이력 + Flyway 이력 = 완전한 변경 추적            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  5. 기존 데이터베이스 도입 (Legacy Adoption)                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  이미 운영 중인 DB에 Flyway 도입                          │    │
│  │  → baseline 명령어로 현재 상태를 기준점 설정              │    │
│  │  → 이후 변경부터 Flyway로 관리                            │    │
│  │  → 점진적 도입 가능 (Big Bang 불필요)                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  6. Blue-Green / Canary 배포                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  여러 환경의 DB 스키마를 동일하게 유지                    │    │
│  │  ├── Blue 환경: V1~V10 적용 상태                          │    │
│  │  ├── Green 환경: V1~V10 적용 + V11 Pending                │    │
│  │  └── 전환 시 flyway migrate → V11 자동 적용               │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

4.2 Flyway가 적합하지 않은 상황

┌─────────────────────────────────────────────────────────────────┐
│              Flyway를 쓰지 않아도 되는 경우                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. 빠른 프로토타이핑 / PoC                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  → 스키마가 하루에도 수십 번 바뀌는 초기 개발             │    │
│  │  → Hibernate ddl-auto=update가 더 편리                    │    │
│  │  → 스키마가 안정화된 후 Flyway 도입 권장                  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  2. 스키마 없는 데이터베이스 (NoSQL)                             │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  → MongoDB, DynamoDB 등 Schema-less DB                    │    │
│  │  → 스키마 마이그레이션 자체가 불필요                      │    │
│  │  → 다만 MongoDB도 Mongock 같은 도구가 있긴 함             │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  3. 1인 개발 + 단순 프로젝트                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  → 스키마 변경이 거의 없는 소규모 프로젝트                │    │
│  │  → 오버엔지니어링이 될 수 있음                            │    │
│  │  → 단, 프로덕션 배포 시에는 규모와 관계없이 권장          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

5. 12가지 흔한 트러블 상황

5.1 Checksum Mismatch (체크섬 불일치)

┌─────────────────────────────────────────────────────────────────┐
│           트러블 #1: Checksum Mismatch                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  상황: 이미 적용된 마이그레이션 파일을 수정했을 때               │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  V2__Add_email.sql (원본, 이미 적용됨)                   │    │
│  │  ALTER TABLE users ADD COLUMN email VARCHAR(100);        │    │
│  │  → checksum: 12345678                                    │    │
│  │                                                          │    │
│  │  개발자가 "아, 255로 바꿔야지" 하고 수정:                │    │
│  │  ALTER TABLE users ADD COLUMN email VARCHAR(255);        │    │
│  │  → checksum: 87654321 (달라짐!)                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  에러 메시지:                                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Migration checksum mismatch for migration version 2    │    │
│  │  -> Applied to database : 12345678                       │    │
│  │  -> Resolved locally    : 87654321                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  해결:                                                            │
│  ├── 방법 1: 원본 파일로 되돌리고, V3로 변경 사항 작성           │
│  ├── 방법 2: flyway repair (checksum 재동기화, 주의 필요!)       │
│  └── 원칙: 적용된 마이그레이션은 절대 수정하지 않는다!           │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

5.2 Version Conflict (버전 충돌)

┌─────────────────────────────────────────────────────────────────┐
│           트러블 #2: 동일 버전 번호 충돌                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  상황: 두 개발자가 같은 버전 번호로 마이그레이션 생성            │
│                                                                   │
│  개발자 A (feature/user-phone 브랜치):                           │
│  └── V4__Add_phone_to_users.sql                                  │
│                                                                   │
│  개발자 B (feature/user-address 브랜치):                         │
│  └── V4__Add_address_to_users.sql                                │
│                                                                   │
│  merge 후: 같은 V4가 2개! → Flyway 에러                         │
│                                                                   │
│  에러 메시지:                                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Found more than one migration with version 4           │    │
│  │  Offenders:                                              │    │
│  │  -> V4__Add_phone_to_users.sql                           │    │
│  │  -> V4__Add_address_to_users.sql                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  예방:                                                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  타임스탬프 기반 버전 번호 사용!                          │    │
│  │                                                          │    │
│  │  V20260227143000__Add_phone_to_users.sql    (A)          │    │
│  │  V20260227150000__Add_address_to_users.sql  (B)          │    │
│  │                                                          │    │
│  │  → 시간이 다르면 절대 충돌 안 함                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

5.3 Out-of-Order Migration (순서 이탈)

┌─────────────────────────────────────────────────────────────────┐
│           트러블 #3: Out-of-Order Migration                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  상황: V5가 이미 적용된 상태에서 V4가 나중에 추가됨              │
│                                                                   │
│  타임라인:                                                       │
│  1) main 브랜치에 V3, V5 적용됨                                 │
│  2) feature 브랜치에서 만든 V4가 뒤늦게 merge                    │
│  3) Flyway가 V4를 발견 → 기본 동작: 무시 또는 에러!             │
│                                                                   │
│  해결:                                                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  # application.yml                                       │    │
│  │  spring:                                                 │    │
│  │    flyway:                                               │    │
│  │      out-of-order: true    ← 순서 이탈 허용              │    │
│  │                                                          │    │
│  │  # 또는 CLI                                              │    │
│  │  flyway -outOfOrder=true migrate                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  주의: out-of-order를 항상 켜두면                                │
│  → 의도치 않은 순서 이탈도 허용되므로                            │
│  → 팀 규칙으로 관리하는 것이 중요                                │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

5.4 Large Table ALTER Lock (대형 테이블 잠금)

┌─────────────────────────────────────────────────────────────────┐
│           트러블 #4: ALTER TABLE이 프로덕션을 멈춤               │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  상황: 수백만 행 테이블에 ALTER TABLE 실행                       │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  -- V10__Add_index_to_orders.sql                         │    │
│  │  ALTER TABLE orders ADD INDEX idx_user_id (user_id);     │    │
│  │                                                          │    │
│  │  orders 테이블: 5천만 행                                  │    │
│  │  → MySQL에서 이 작업: 테이블 락 발생!                    │    │
│  │  → 모든 INSERT/UPDATE 대기 → 서비스 장애                 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  해결 전략:                                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  MySQL:                                                  │    │
│  │  ├── pt-online-schema-change (Percona Toolkit)           │    │
│  │  ├── gh-ost (GitHub Online Schema Migration)             │    │
│  │  └── ALTER ... ALGORITHM=INPLACE, LOCK=NONE;            │    │
│  │                                                          │    │
│  │  PostgreSQL:                                             │    │
│  │  ├── CREATE INDEX CONCURRENTLY (비차단 인덱스 생성)      │    │
│  │  └── ALTER TABLE ... SET NOT NULL 대신                   │    │
│  │      CHECK constraint + NOT VALID → VALIDATE 분리 실행   │    │
│  │                                                          │    │
│  │  공통 원칙:                                              │    │
│  │  → 대형 테이블 변경은 Flyway SQL 안에서                  │    │
│  │    DB별 비차단(non-blocking) 전략 사용                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

5.5 Failed Migration Dirty State (실패 후 오염 상태)

┌─────────────────────────────────────────────────────────────────┐
│           트러블 #5: 마이그레이션 실패 후 DB 상태                │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  DB별 트랜잭션 DDL 지원 차이:                                    │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  PostgreSQL: DDL도 트랜잭션 안에서 롤백 가능!            │    │
│  │  ┌─────────────────────────────────────────────────┐    │    │
│  │  │  BEGIN;                                          │    │    │
│  │  │  CREATE TABLE temp (...);  -- 성공               │    │    │
│  │  │  ALTER TABLE users ADD ... ; -- 실패!            │    │    │
│  │  │  ROLLBACK;  ← temp 테이블도 롤백됨              │    │    │
│  │  │  → 깨끗한 상태 유지!                             │    │    │
│  │  └─────────────────────────────────────────────────┘    │    │
│  │                                                          │    │
│  │  MySQL: DDL은 자동 커밋! 롤백 불가!                      │    │
│  │  ┌─────────────────────────────────────────────────┐    │    │
│  │  │  BEGIN;                                          │    │    │
│  │  │  CREATE TABLE temp (...);  -- 성공 → 자동 커밋! │    │    │
│  │  │  ALTER TABLE users ADD ... ; -- 실패!            │    │    │
│  │  │  ROLLBACK;  ← temp 테이블은 이미 커밋됨!        │    │    │
│  │  │  → 절반만 적용된 오염 상태!                      │    │    │
│  │  └─────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  MySQL에서의 대응:                                               │
│  ├── 마이그레이션 파일당 하나의 DDL만 작성 (원자성 확보)         │
│  ├── 실패 시 flyway repair → 수동으로 잔여 DDL 정리              │
│  └── 테스트 환경에서 반드시 사전 실행 검증                       │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

5.6 나머지 7가지 트러블

┌─────────────────────────────────────────────────────────────────┐
│           트러블 #6~#12: 빠르게 짚어보기                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  #6. Baseline 설정 실수                                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  잘못된 baselineVersion 설정 → 필요한 마이그레이션 건너뜀│    │
│  │  예: V1~V5가 있는데 baselineVersion=3으로 설정            │    │
│  │  → V4, V5가 이미 적용된 것으로 간주되어 실행 안 됨       │    │
│  │  → 해결: baseline 전 반드시 flyway info로 상태 확인       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  #7. 데이터 마이그레이션 복잡성                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  스키마 변경과 데이터 변환을 같은 파일에 혼합             │    │
│  │  → 실패 시 어디까지 적용됐는지 파악 어려움                │    │
│  │  → 해결: 스키마 변경(V3)과 데이터 변환(V4) 분리           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  #8. Hibernate ddl-auto와 충돌                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ddl-auto=validate가 Flyway migrate보다 먼저 실행되면    │    │
│  │  → "테이블이 없어!" 에러 발생                             │    │
│  │                                                          │    │
│  │  해결:                                                   │    │
│  │  spring.jpa.hibernate.ddl-auto=none                      │    │
│  │  spring.flyway.enabled=true                              │    │
│  │  → Flyway가 먼저 실행되고, Hibernate는 스키마 건드리지 X │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  #9. 환경 간 Schema Drift                                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  개발/스테이징/프로덕션에 서로 다른 마이그레이션 적용     │    │
│  │  → 해결: 모든 환경에서 동일한 마이그레이션 파일 사용      │    │
│  │  → 환경별 차이는 Flyway placeholder로 처리                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  #10. Community Edition 롤백 한계                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Community: undo 명령어 없음                              │    │
│  │  → 대안: "Forward-only migration" 전략 채택               │    │
│  │  → 롤백이 필요하면 새로운 V 파일로 역변경 작성            │    │
│  │  → 예: V6__Rollback_V5_changes.sql                        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  #11. 인코딩 이슈 (UTF-8 BOM)                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Windows 메모장이 UTF-8 BOM (0xEF, 0xBB, 0xBF) 삽입     │    │
│  │  → SQL 파서가 첫 문자 인식 실패                           │    │
│  │  → "Incorrect syntax near '?'" 에러                       │    │
│  │  → 해결: UTF-8 without BOM으로 저장                       │    │
│  │  → IntelliJ: File → File Properties → UTF-8 (no BOM)     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  #12. Placeholder 미해석 오류                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  SQL 안에 ${schema_name} 같은 변수가 있을 때              │    │
│  │  → Flyway가 placeholder로 인식하지만 값이 없으면 에러    │    │
│  │                                                          │    │
│  │  해결:                                                   │    │
│  │  # application.yml                                       │    │
│  │  spring:                                                 │    │
│  │    flyway:                                               │    │
│  │      placeholders:                                       │    │
│  │        schema_name: my_schema                            │    │
│  │      placeholder-replacement: false  ← 또는 기능 끄기    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

6. Flyway vs Liquibase 비교

6.1 핵심 차이점

┌─────────────────────────────────────────────────────────────────┐
│              Flyway vs Liquibase 상세 비교                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌────────────────┬──────────────────┬──────────────────┐       │
│  │     항목        │     Flyway       │    Liquibase     │       │
│  ├────────────────┼──────────────────┼──────────────────┤       │
│  │ 첫 릴리스      │ 2010년           │ 2006년            │       │
│  │ 마이그레이션   │ SQL 파일 기반    │ XML/YAML/JSON/SQL│       │
│  │ 변경 정의      │ 직접 SQL 작성    │ 추상화된 changeset│       │
│  │ 학습 곡선      │ 낮음 (SQL만)     │ 중간 (DSL 학습)  │       │
│  │ DB 추상화      │ 없음 (DB별 SQL)  │ 있음 (DB 독립적) │       │
│  │ 롤백           │ Enterprise만     │ 모든 에디션       │       │
│  │ 이력 테이블    │ flyway_schema_   │ databasechange   │       │
│  │               │   history        │   log             │       │
│  │ Spring Boot   │ 자동 설정 내장   │ 자동 설정 내장    │       │
│  │ 커뮤니티       │ 활발             │ 활발              │       │
│  │ 상용 지원      │ Redgate          │ Liquibase Inc.   │       │
│  │ 철학           │ SQL-first        │ Abstraction-first│       │
│  └────────────────┴──────────────────┴──────────────────┘       │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

6.2 각각의 마이그레이션 파일 비교

┌─────────────────────────────────────────────────────────────────┐
│              같은 작업, 다른 방식                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  작업: users 테이블에 phone 컬럼 추가                            │
│                                                                   │
│  Flyway (SQL 직접 작성):                                         │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  -- V4__Add_phone_to_users.sql                           │    │
│  │  ALTER TABLE users ADD COLUMN phone VARCHAR(20);         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Liquibase (XML changeset):                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  <!-- changelog.xml -->                                  │    │
│  │  <changeSet id="4" author="developer">                   │    │
│  │    <addColumn tableName="users">                         │    │
│  │      <column name="phone" type="VARCHAR(20)"/>           │    │
│  │    </addColumn>                                          │    │
│  │  </changeSet>                                            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Liquibase (YAML changeset):                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  databaseChangeLog:                                      │    │
│  │    - changeSet:                                          │    │
│  │        id: 4                                             │    │
│  │        author: developer                                 │    │
│  │        changes:                                          │    │
│  │          - addColumn:                                    │    │
│  │              tableName: users                            │    │
│  │              columns:                                    │    │
│  │                - column:                                 │    │
│  │                    name: phone                           │    │
│  │                    type: VARCHAR(20)                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  비교 분석:                                                      │
│  ├── Flyway: 1줄 SQL로 끝 → 단순, 직관적                        │
│  ├── Liquibase XML: 5줄 → 타이핑 많지만 DB 독립적               │
│  ├── Liquibase YAML: 9줄 → 읽기 쉬우나 들여쓰기 실수 위험       │
│  └── 핵심: Flyway는 "SQL을 잘 아는 팀"에 최적화                 │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

6.3 어떤 걸 선택해야 하는가?

┌─────────────────────────────────────────────────────────────────┐
│              선택 가이드                                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Flyway를 선택하라:                                              │
│  ├── SQL에 익숙한 팀                                             │
│  ├── 단일 DB 벤더 (PostgreSQL만, MySQL만)                        │
│  ├── 단순함을 선호하는 팀                                        │
│  ├── Spring Boot + JPA/Hibernate 기반 프로젝트                   │
│  └── "마이그레이션 도구 학습에 시간 쓰기 싫다"                   │
│                                                                   │
│  Liquibase를 선택하라:                                           │
│  ├── 멀티 DB 지원 필요 (Oracle + PostgreSQL 동시)                │
│  ├── 모든 에디션에서 롤백이 필요                                 │
│  ├── 복잡한 changelog 관리가 필요                                │
│  ├── DB 변경을 SQL이 아닌 선언적으로 정의하고 싶다               │
│  └── 비개발자(DBA)도 변경 내역을 쉽게 읽어야 한다               │
│                                                                   │
│  결론:                                                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  JVM + 단일 DB + 단순함 선호 → Flyway                    │    │
│  │  멀티 DB + 롤백 필수 + 선언적 접근 → Liquibase            │    │
│  │                                                          │    │
│  │  사실 둘 다 훌륭한 도구. "아무거나 하나만 쓰세요"가      │    │
│  │  "도구 없이 수동 관리"보다 1000배 낫다.                   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

7. 프로덕션 운영 베스트 프랙티스

7.1 마이그레이션 작성 원칙

┌─────────────────────────────────────────────────────────────────┐
│              프로덕션 마이그레이션 작성 5대 원칙                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  원칙 1: 적용된 파일은 절대 수정하지 않는다                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  적용된 V 파일 수정 → checksum mismatch → 장애           │    │
│  │  수정이 필요하면 → 새로운 V 파일로 변경 사항 작성         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 2: 스키마 변경과 데이터 변경을 분리한다                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Bad:  V5에 ALTER TABLE + UPDATE 1000만 행 같이 작성     │    │
│  │  Good: V5 = ALTER TABLE (스키마), V6 = UPDATE (데이터)   │    │
│  │  → 실패 시 어디서 실패했는지 명확                         │    │
│  │  → 재실행 범위가 최소화됨                                 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 3: 마이그레이션 파일도 코드 리뷰한다                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  SQL 파일을 Git에 커밋 → PR 리뷰 필수                    │    │
│  │  리뷰 체크리스트:                                        │    │
│  │  ├── 인덱스 전략이 적절한가?                              │    │
│  │  ├── 대형 테이블 변경 시 락 전략이 있는가?                │    │
│  │  ├── 롤백 계획이 있는가?                                  │    │
│  │  └── 기존 데이터에 영향은 없는가?                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 4: 트랜잭션으로 감싸라 (지원되는 경우)                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Flyway 기본 설정: 각 마이그레이션을 트랜잭션으로 실행    │    │
│  │  ├── PostgreSQL: DDL 트랜잭션 지원 → 안전                 │    │
│  │  ├── MySQL: DDL 자동 커밋 → 트랜잭션 무의미               │    │
│  │  └── 대형 마이그레이션은 group=true로 묶기 가능           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 5: 프로덕션 데이터 복사본에서 테스트한다                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  프로덕션 DB 복사 → 마이그레이션 적용 → 검증              │    │
│  │  ├── 실행 시간 측정 (10분 넘으면 전략 변경 필요)          │    │
│  │  ├── 락 발생 여부 확인                                    │    │
│  │  └── 애플리케이션 호환성 테스트                            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

7.2 Zero-Downtime Migration: Expand-Contract 패턴

┌─────────────────────────────────────────────────────────────────┐
│           Expand-Contract (Parallel Change) 패턴                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  목표: 서비스 중단 없이 스키마 변경                              │
│                                                                   │
│  예시: users.name → users.first_name + users.last_name 분리     │
│                                                                   │
│  Step 1: EXPAND (확장) - 새 컬럼 추가                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  -- V10__Expand_add_name_columns.sql                     │    │
│  │  ALTER TABLE users ADD COLUMN first_name VARCHAR(100);   │    │
│  │  ALTER TABLE users ADD COLUMN last_name VARCHAR(100);    │    │
│  │                                                          │    │
│  │  테이블 상태:                                            │    │
│  │  users: [id, name, email, first_name(null), last_name(null)]│ │
│  │  → 기존 앱은 name 컬럼만 사용 → 정상 동작               │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Step 2: MIGRATE (데이터 이관)                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  -- V11__Backfill_name_columns.sql                       │    │
│  │  UPDATE users SET                                        │    │
│  │    first_name = SPLIT_PART(name, ' ', 1),                │    │
│  │    last_name = SPLIT_PART(name, ' ', 2)                  │    │
│  │  WHERE first_name IS NULL;                               │    │
│  │                                                          │    │
│  │  → 배치로 나눠서 실행 (한 번에 1만 건씩)                 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Step 3: SWITCH (앱 전환)                                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  앱 코드 변경: name → first_name + last_name 사용         │    │
│  │  양쪽 컬럼 모두에 쓰는 과도기 코드 배포                   │    │
│  │  → 새 앱은 first_name/last_name 읽기/쓰기                │    │
│  │  → 구 앱은 여전히 name 읽기/쓰기 (호환성 유지)           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Step 4: CONTRACT (축소) - 이전 컬럼 제거                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  -- V12__Contract_remove_name_column.sql                 │    │
│  │  ALTER TABLE users DROP COLUMN name;                     │    │
│  │                                                          │    │
│  │  → 모든 앱이 새 컬럼을 사용하는 것 확인 후 실행          │    │
│  │  → 최소 1~2주 대기 후 contract 실행 권장                  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  전체 흐름:                                                      │
│  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐               │
│  │ EXPAND │→│MIGRATE │→│ SWITCH │→│CONTRACT│               │
│  │ 새 컬럼 │  │데이터   │  │앱 전환  │  │구 컬럼  │               │
│  │ 추가   │  │이관     │  │        │  │제거     │               │
│  └────────┘  └────────┘  └────────┘  └────────┘               │
│                                                                   │
│  핵심: 어느 단계에서든 롤백 가능하고, 서비스 중단 없음           │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

7.3 명명 규칙 전략

┌─────────────────────────────────────────────────────────────────┐
│              팀을 위한 명명 규칙 가이드                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  전략 1: 순차 번호 (소규모 팀, 1~3명)                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  V1__Create_users_table.sql                              │    │
│  │  V2__Add_email_to_users.sql                              │    │
│  │  V3__Create_orders_table.sql                             │    │
│  │                                                          │    │
│  │  장점: 단순, 직관적                                      │    │
│  │  단점: 팀원 충돌 가능                                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  전략 2: 타임스탬프 (중규모 팀, 4~15명) ← 권장!                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  V20260227143000__Create_users_table.sql                 │    │
│  │  V20260227150000__Add_email_to_users.sql                 │    │
│  │  V20260228090000__Create_orders_table.sql                │    │
│  │                                                          │    │
│  │  장점: 충돌 거의 없음, 생성 시점 파악 가능               │    │
│  │  단점: 파일명이 길어짐                                   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  전략 3: 하이브리드 (대규모 팀, 15명+)                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  V20260227_001__Create_users_table.sql                   │    │
│  │  V20260227_002__Add_email_to_users.sql                   │    │
│  │  V20260228_001__Create_orders_table.sql                  │    │
│  │                                                          │    │
│  │  장점: 날짜별 그룹핑 + 순서 명확                         │    │
│  │  단점: 관리 규칙 합의 필요                               │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  설명(Description) 작성 규칙:                                    │
│  ├── 동사로 시작: Create_, Add_, Drop_, Alter_, Fix_             │
│  ├── 대상 포함: _users_table, _to_orders, _index_on_email        │
│  ├── 축약 금지: Crt_usr (X) → Create_users_table (O)             │
│  └── 영어 사용: 한글은 인코딩 문제 위험                          │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

8. Spring Boot 통합과 고급 패턴

8.1 Spring Boot 자동 설정

┌─────────────────────────────────────────────────────────────────┐
│              Spring Boot + Flyway 통합                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Spring Boot는 Flyway를 자동으로 구성한다!                       │
│                                                                   │
│  의존성 추가만 하면 끝:                                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  # build.gradle.kts                                      │    │
│  │  dependencies {                                          │    │
│  │      implementation("org.flywaydb:flyway-core")          │    │
│  │      // MySQL 사용 시                                    │    │
│  │      implementation("org.flywaydb:flyway-mysql")         │    │
│  │  }                                                       │    │
│  │                                                          │    │
│  │  <!-- pom.xml -->                                        │    │
│  │  <dependency>                                            │    │
│  │      <groupId>org.flywaydb</groupId>                     │    │
│  │      <artifactId>flyway-core</artifactId>                │    │
│  │  </dependency>                                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  기본 동작:                                                      │
│  ├── 앱 시작 시 자동으로 flyway migrate 실행                     │
│  ├── 마이그레이션 파일 위치: src/main/resources/db/migration/    │
│  ├── DataSource 빈을 자동으로 사용                               │
│  └── Hibernate보다 먼저 실행됨 (FlywayMigrationInitializer)      │
│                                                                   │
│  실행 순서:                                                      │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐               │
│  │ DataSource │→│  Flyway    │→│ Hibernate  │               │
│  │ 초기화     │  │  migrate   │  │ validate   │               │
│  └────────────┘  └────────────┘  └────────────┘               │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

8.2 주요 설정 옵션

# application.yml - Flyway 상세 설정
spring:
  flyway:
    # 기본 설정
    enabled: true                          # Flyway 활성화 (기본: true)
    locations: classpath:db/migration      # 마이그레이션 파일 경로
    baseline-on-migrate: true              # 기존 DB에 자동 baseline
    baseline-version: 0                    # baseline 버전

    # 검증 설정
    validate-on-migrate: true              # migrate 전 validate 실행
    validate-migration-naming: true        # 파일명 규칙 검증
    out-of-order: false                    # 순서 이탈 허용 여부

    # 안전 설정
    clean-disabled: true                   # clean 명령어 비활성화 (프로덕션!)

    # Placeholder 설정
    placeholder-replacement: true          # placeholder 치환 활성화
    placeholders:
      schema_name: my_schema
      table_prefix: app_

    # 고급 설정
    encoding: UTF-8                        # 파일 인코딩
    table: flyway_schema_history           # 이력 테이블명 (커스터마이징 가능)
    schemas: public                        # 관리할 스키마
    default-schema: public                 # 기본 스키마

  # Hibernate와 함께 사용할 때
  jpa:
    hibernate:
      ddl-auto: none                       # 반드시 none! Flyway가 관리

8.3 환경별 설정 분리

┌─────────────────────────────────────────────────────────────────┐
│              환경별 Flyway 설정 전략                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  application-local.yml (로컬 개발)                      │     │
│  │  ┌──────────────────────────────────────────────────┐   │     │
│  │  │  spring:                                          │   │     │
│  │  │    flyway:                                        │   │     │
│  │  │      clean-disabled: false  ← 로컬에서만 clean 허용│  │     │
│  │  │      locations:                                   │   │     │
│  │  │        - classpath:db/migration                   │   │     │
│  │  │        - classpath:db/testdata  ← 테스트 데이터   │   │     │
│  │  └──────────────────────────────────────────────────┘   │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                   │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  application-staging.yml (스테이징)                      │     │
│  │  ┌──────────────────────────────────────────────────┐   │     │
│  │  │  spring:                                          │   │     │
│  │  │    flyway:                                        │   │     │
│  │  │      clean-disabled: true                         │   │     │
│  │  │      validate-on-migrate: true                    │   │     │
│  │  └──────────────────────────────────────────────────┘   │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                   │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  application-prod.yml (프로덕션)                        │     │
│  │  ┌──────────────────────────────────────────────────┐   │     │
│  │  │  spring:                                          │   │     │
│  │  │    flyway:                                        │   │     │
│  │  │      clean-disabled: true   ← 절대 clean 불가     │   │     │
│  │  │      validate-on-migrate: true                    │   │     │
│  │  │      baseline-on-migrate: false ← 실수 방지       │   │     │
│  │  │      out-of-order: false    ← 순서 강제            │   │     │
│  │  └──────────────────────────────────────────────────┘   │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

8.4 Flyway Callback 활용

┌─────────────────────────────────────────────────────────────────┐
│              Flyway Callback = 마이그레이션 이벤트 후킹           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  사용 가능한 Callback 시점:                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  beforeMigrate    → 마이그레이션 시작 전                  │    │
│  │  beforeEachMigrate→ 각 마이그레이션 파일 실행 전          │    │
│  │  afterEachMigrate → 각 마이그레이션 파일 실행 후          │    │
│  │  afterMigrate     → 마이그레이션 완료 후                  │    │
│  │  beforeValidate   → 검증 시작 전                          │    │
│  │  afterValidate    → 검증 완료 후                          │    │
│  │  beforeClean      → clean 실행 전                         │    │
│  │  afterClean       → clean 실행 후                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  활용 예시 - SQL Callback:                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  파일: db/migration/afterMigrate__refresh_views.sql      │    │
│  │                                                          │    │
│  │  -- 모든 마이그레이션 후 뷰 갱신                         │    │
│  │  REFRESH MATERIALIZED VIEW CONCURRENTLY user_stats;     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  활용 예시 - Java Callback:                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  @Component                                              │    │
│  │  public class FlywayCallbackConfig                       │    │
│  │      implements Callback {                               │    │
│  │                                                          │    │
│  │    @Override                                              │    │
│  │    public boolean supports(Event event, Context ctx) {   │    │
│  │      return event == Event.AFTER_MIGRATE;                │    │
│  │    }                                                     │    │
│  │                                                          │    │
│  │    @Override                                              │    │
│  │    public void handle(Event event, Context ctx) {        │    │
│  │      log.info("마이그레이션 완료! 캐시 초기화...");       │    │
│  │      cacheManager.clearAll();                            │    │
│  │    }                                                     │    │
│  │  }                                                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

8.5 Kubernetes / Docker 환경

┌─────────────────────────────────────────────────────────────────┐
│              컨테이너 환경에서의 Flyway 전략                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  전략 1: Init Container (권장)                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  # Kubernetes Deployment                                 │    │
│  │  apiVersion: apps/v1                                     │    │
│  │  kind: Deployment                                        │    │
│  │  spec:                                                   │    │
│  │    template:                                             │    │
│  │      spec:                                               │    │
│  │        initContainers:                                   │    │
│  │          - name: flyway-migrate                          │    │
│  │            image: flyway/flyway:10                        │    │
│  │            command: ["flyway", "migrate"]                │    │
│  │            env:                                          │    │
│  │              - name: FLYWAY_URL                          │    │
│  │                value: jdbc:postgresql://db:5432/myapp    │    │
│  │            volumeMounts:                                 │    │
│  │              - name: migrations                          │    │
│  │                mountPath: /flyway/sql                    │    │
│  │        containers:                                       │    │
│  │          - name: app                                     │    │
│  │            image: myapp:latest                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  장점:                                                           │
│  ├── 마이그레이션과 앱 실행이 분리됨                             │
│  ├── 마이그레이션 실패 시 앱이 시작되지 않음 (안전)              │
│  └── 여러 Pod 중 하나만 마이그레이션 실행 (경쟁 방지)            │
│                                                                   │
│  전략 2: 앱 내장 (Spring Boot 기본)                              │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  앱 시작 시 자동 실행 (기본 동작)                         │    │
│  │                                                          │    │
│  │  주의: 여러 Pod 동시 시작 시                              │    │
│  │  → Flyway가 DB 레벨 락으로 보호 (한 번만 실행)           │    │
│  │  → 나머지 Pod는 마이그레이션 완료까지 대기                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  전략 3: 별도 Job (CI/CD에서)                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  # Kubernetes Job                                        │    │
│  │  apiVersion: batch/v1                                    │    │
│  │  kind: Job                                               │    │
│  │  metadata:                                               │    │
│  │    name: flyway-migrate-v10                              │    │
│  │  spec:                                                   │    │
│  │    template:                                             │    │
│  │      spec:                                               │    │
│  │        containers:                                       │    │
│  │          - name: flyway                                  │    │
│  │            image: flyway/flyway:10                        │    │
│  │            command: ["flyway", "migrate"]                │    │
│  │        restartPolicy: Never                              │    │
│  │    backoffLimit: 3                                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

8.6 Multi-Tenancy 패턴

┌─────────────────────────────────────────────────────────────────┐
│              멀티테넌시 + Flyway                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  패턴 1: Database-per-Tenant                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  각 테넌트가 별도의 데이터베이스를 가짐                   │    │
│  │                                                          │    │
│  │  ┌────────┐  ┌────────┐  ┌────────┐                    │    │
│  │  │tenant_a│  │tenant_b│  │tenant_c│                    │    │
│  │  │   DB   │  │   DB   │  │   DB   │                    │    │
│  │  └────────┘  └────────┘  └────────┘                    │    │
│  │       ↑            ↑            ↑                        │    │
│  │       └────────────┼────────────┘                        │    │
│  │                    │                                      │    │
│  │        동일한 마이그레이션을 각각 적용                    │    │
│  │                                                          │    │
│  │  구현: 루프로 각 DB에 Flyway 실행                        │    │
│  │  tenants.forEach(tenant -> {                             │    │
│  │    Flyway.configure()                                    │    │
│  │      .dataSource(tenant.getUrl(), ...)                   │    │
│  │      .load()                                             │    │
│  │      .migrate();                                         │    │
│  │  });                                                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  패턴 2: Schema-per-Tenant                                      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  하나의 DB에서 테넌트별 스키마 분리                       │    │
│  │                                                          │    │
│  │  ┌─────────────── 하나의 DB ──────────────┐             │    │
│  │  │ ┌─────────┐ ┌─────────┐ ┌─────────┐   │             │    │
│  │  │ │schema_a │ │schema_b │ │schema_c │   │             │    │
│  │  │ └─────────┘ └─────────┘ └─────────┘   │             │    │
│  │  └────────────────────────────────────────┘             │    │
│  │                                                          │    │
│  │  구현: schemas 설정으로 각 스키마에 적용                  │    │
│  │  Flyway.configure()                                      │    │
│  │    .schemas("tenant_a")                                  │    │
│  │    .load()                                               │    │
│  │    .migrate();                                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9. 모던 대안과 비교

9.1 DB 마이그레이션 도구 생태계

┌─────────────────────────────────────────────────────────────────┐
│              2026년 DB 마이그레이션 도구 비교                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌──────────────┬────────────┬─────────────┬────────────┐      │
│  │     도구      │   생태계    │    접근 방식   │  특징       │      │
│  ├──────────────┼────────────┼─────────────┼────────────┤      │
│  │ Flyway       │ JVM        │ SQL-first   │ 단순, 강력  │      │
│  │ Liquibase    │ JVM        │ Changelog   │ DB 독립적   │      │
│  │ Atlas        │ Go/범용    │ Declarative │ 자동 diff   │      │
│  │ Alembic      │ Python     │ Migration   │ SQLAlchemy  │      │
│  │ Prisma Migrate│ Node.js   │ Declarative │ TypeScript  │      │
│  │ golang-migrate│ Go        │ SQL-first   │ 경량        │      │
│  │ sqitch       │ 범용       │ Change mgmt │ ORM 독립    │      │
│  │ Knex.js      │ Node.js    │ Code-first  │ 쿼리빌더    │      │
│  │ Django       │ Python     │ Code-first  │ 내장        │      │
│  └──────────────┴────────────┴─────────────┴────────────┘      │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9.2 주요 대안 상세 비교

┌─────────────────────────────────────────────────────────────────┐
│              대안 도구 상세 분석                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Atlas (by Ariga) - 선언적 스키마 관리                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  특징:                                                   │    │
│  │  ├── "원하는 상태"를 선언하면 자동으로 마이그레이션 생성  │    │
│  │  ├── 현재 DB 상태와 원하는 상태를 자동 diff               │    │
│  │  ├── HCL(HashiCorp Configuration Language) 사용          │    │
│  │  └── Terraform처럼 DB 스키마를 "선언적"으로 관리          │    │
│  │                                                          │    │
│  │  예시:                                                   │    │
│  │  schema "public" {                                       │    │
│  │    table "users" {                                       │    │
│  │      column "id" { type = int }                          │    │
│  │      column "email" { type = varchar(255) }              │    │
│  │      primary_key { columns = [column.id] }               │    │
│  │    }                                                     │    │
│  │  }                                                       │    │
│  │  → atlas schema apply --auto-approve                     │    │
│  │  → 자동으로 ALTER TABLE 생성!                            │    │
│  │                                                          │    │
│  │  Flyway와 비교:                                          │    │
│  │  ├── Flyway: 개발자가 직접 ALTER SQL 작성 (명시적)       │    │
│  │  └── Atlas: 도구가 자동으로 ALTER SQL 생성 (편리)        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Alembic (Python / SQLAlchemy)                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Python 생태계의 표준 마이그레이션 도구               │    │
│  │  ├── SQLAlchemy 모델에서 자동으로 마이그레이션 감지       │    │
│  │  ├── alembic revision --autogenerate                     │    │
│  │  └── FastAPI, Flask 프로젝트에서 필수                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Prisma Migrate (Node.js / TypeScript)                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Prisma Schema에서 자동으로 마이그레이션 생성         │    │
│  │  ├── TypeScript 타입 자동 생성 (DX 최고)                  │    │
│  │  ├── prisma migrate dev → 개발, prisma migrate deploy    │    │
│  │  └── Next.js, NestJS 프로젝트에서 인기                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  golang-migrate                                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Go 생태계의 경량 마이그레이션 도구                   │    │
│  │  ├── Flyway와 유사하게 SQL 파일 기반                      │    │
│  │  ├── CLI + Go 라이브러리 모두 지원                        │    │
│  │  └── Docker, Kubernetes 친화적                            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Flyway를 고수해야 할 때:                                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── JVM 생태계 (Spring Boot, Kotlin, Java)              │    │
│  │  ├── SQL-first 철학 공감                                  │    │
│  │  ├── 엔터프라이즈 지원이 필요 (Redgate)                   │    │
│  │  ├── 이미 Flyway를 사용 중 (전환 비용 > 이점)            │    │
│  │  └── 팀이 SQL에 능숙하고 자동 생성을 불신                 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

10. 소프트웨어 엔지니어를 위한 교훈

10.1 핵심 원칙

┌─────────────────────────────────────────────────────────────────┐
│              DB 마이그레이션에서 배우는 엔지니어링 원칙           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  원칙 1: "Database changes should be as reviewable              │
│           as code changes"                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  SQL 파일 = 코드 파일                                     │    │
│  │  ├── Git으로 버전 관리                                    │    │
│  │  ├── PR에서 코드 리뷰                                     │    │
│  │  ├── CI에서 자동 테스트                                   │    │
│  │  └── 프로덕션 배포 파이프라인에 통합                      │    │
│  │                                                          │    │
│  │  더 이상 "DBA가 프로덕션에서 직접 ALTER TABLE" 하는       │    │
│  │  시대는 끝났다.                                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 2: Evolutionary Database Design                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Martin Fowler의 핵심 주장:                               │    │
│  │                                                          │    │
│  │  "완벽한 스키마를 미리 설계하려 하지 마라.                │    │
│  │   요구사항이 진화하듯, 스키마도 진화한다.                 │    │
│  │   중요한 것은 안전하게 진화할 수 있는 인프라다."          │    │
│  │                                                          │    │
│  │  ├── Big Design Up Front (X)                              │    │
│  │  ├── Iterative Schema Evolution (O)                       │    │
│  │  ├── 작은 변경을 자주, 안전하게                           │    │
│  │  └── Flyway = 안전한 진화를 가능하게 하는 인프라          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 3: Schema as Code                                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Infrastructure as Code (IaC)의 DB 버전:                  │    │
│  │                                                          │    │
│  │  IaC        : Terraform → 인프라 상태를 코드로 관리       │    │
│  │  Schema as  : Flyway → DB 스키마 상태를 코드로 관리       │    │
│  │    Code                                                   │    │
│  │                                                          │    │
│  │  공통 원칙:                                              │    │
│  │  ├── 선언적 또는 절차적으로 상태 정의                     │    │
│  │  ├── 버전 관리 시스템에 저장                              │    │
│  │  ├── 자동화된 적용 (수동 개입 최소화)                     │    │
│  │  └── 감사 가능한 변경 이력                                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 4: Forward-Only Migration                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  롤백 문화 vs 전진 문화:                                  │    │
│  │                                                          │    │
│  │  전통적: 문제 발생 → 이전 버전으로 롤백                   │    │
│  │  현대적: 문제 발생 → 수정 버전을 새로 배포 (전진)         │    │
│  │                                                          │    │
│  │  Flyway Community Edition이 undo를 제공하지 않는 이유:    │    │
│  │  ├── 완벽한 롤백은 대부분 불가능 (데이터 손실)            │    │
│  │  ├── 롤백에 의존하면 마이그레이션 품질이 떨어짐           │    │
│  │  └── "고장난 것을 고치는 게 되돌리는 것보다 안전하다"     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  원칙 5: 마이그레이션 파일은 불변 프로덕션 아티팩트이다          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Docker Image처럼:                                        │    │
│  │  ├── 한번 빌드되면 수정하지 않는다                        │    │
│  │  ├── 태그(버전)로 식별한다                                │    │
│  │  └── 문제 시 새 버전을 만든다                             │    │
│  │                                                          │    │
│  │  Flyway Migration 파일도:                                 │    │
│  │  ├── 한번 적용되면 수정하지 않는다                        │    │
│  │  ├── 버전 번호로 식별한다                                 │    │
│  │  └── 문제 시 새 마이그레이션 파일을 만든다                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

10.2 최종 요약

┌─────────────────────────────────────────────────────────────────┐
│              Flyway 한눈에 정리                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Flyway란?                                                       │
│  → SQL 기반의 데이터베이스 스키마 버전 관리 도구                 │
│                                                                   │
│  핵심 가치:                                                      │
│  ├── 단순함: SQL만 알면 된다                                     │
│  ├── 신뢰성: checksum으로 무결성 보장                            │
│  ├── 추적성: flyway_schema_history로 완전한 감사 이력            │
│  └── 자동화: CI/CD와 완벽한 통합                                 │
│                                                                   │
│  핵심 명령어:                                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  migrate  → 마이그레이션 적용                             │    │
│  │  info     → 상태 확인                                     │    │
│  │  validate → 무결성 검증                                   │    │
│  │  baseline → 기존 DB에 Flyway 도입                         │    │
│  │  repair   → 문제 복구                                     │    │
│  │  clean    → 전체 삭제 (개발 전용!)                        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  베스트 프랙티스:                                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  적용된 파일 수정 금지                                    │    │
│  │  타임스탬프 기반 버전 번호 사용                            │    │
│  │  스키마 변경과 데이터 변경 분리                            │    │
│  │  Hibernate ddl-auto=none 설정                             │    │
│  │  Expand-Contract 패턴으로 Zero-Downtime 달성              │    │
│  │  프로덕션 데이터 복사본에서 사전 테스트                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  한 줄 요약:                                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  "데이터베이스 변경을 코드처럼 관리하라.                  │    │
│  │   Flyway는 그것을 가장 단순하게 실현하는 도구다."         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

관련 키워드

Flyway, Database Migration, 스키마 마이그레이션, flyway_schema_history, Versioned Migration, Repeatable Migration, Undo Migration, migrate, clean, info, validate, baseline, repair, checksum, Schema Drift, Expand-Contract Pattern, Zero-Downtime Migration, Liquibase, Atlas, Alembic, Prisma Migrate, Spring Boot, Hibernate ddl-auto, CI/CD, Kubernetes Init Container, Multi-Tenancy, Flyway Callback, Forward-Only Migration, Schema as Code, Evolutionary Database Design, Martin Fowler, Axel Fontaine, Redgate, SQL-first