TL;DR

  • Flyway - 데이터베이스 마이그레이션의 모든 것의 핵심 개념을 빠르게 파악할 수 있다.
  • 배경과 이유를 통해 왜 필요한지 맥락을 이해할 수 있다.
  • 특징과 상세 내용을 통해 실무 적용 포인트를 확인할 수 있다.

1. 개념

Flyway - 데이터베이스 마이그레이션의 모든 것의 핵심 정의와 문제 공간을 간단히 정리한다.

2. 배경

이 주제가 등장한 기술적·조직적 배경과 기존 접근의 한계를 설명한다.

3. 이유

왜 지금 이 방식을 채택해야 하는지, 기대 효과와 트레이드오프를 함께 정리한다.

4. 특징

핵심 동작 방식, 장단점, 적용 시 주의점을 빠르게 훑을 수 있도록 요약한다.

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. HikariCP와 Flyway의 관계

9.1 Spring Boot 시작 순서 (Startup Order)

┌─────────────────────────────────────────────────────────────────┐
│         Spring Boot 애플리케이션 시작 순서                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ① HikariCP DataSource 초기화                                   │
│     ├── 커넥션 풀 생성 (minimum-idle 만큼 미리 연결)             │
│     └── DB 연결 가능 여부 확인                                   │
│              │                                                    │
│              ▼                                                    │
│  ② FlywayMigrationInitializer → Flyway.migrate() 실행           │
│     ├── flyway_schema_history 확인                               │
│     ├── 적용되지 않은 마이그레이션 스크립트 실행 (blocking)      │
│     └── 완료 전까지 다음 단계로 진행하지 않음                    │
│              │                                                    │
│              ▼                                                    │
│  ③ Hibernate/JPA EntityManagerFactory 생성                      │
│     ├── ddl-auto: validate → 현재 스키마 검증                    │
│     └── 엔티티 ↔ 테이블 매핑 확인                               │
│              │                                                    │
│              ▼                                                    │
│  ④ @PostConstruct, ApplicationRunner, CommandLineRunner          │
│     └── 비즈니스 초기화 로직 실행                                │
│              │                                                    │
│              ▼                                                    │
│  ⑤ 애플리케이션 시작 완료                                       │
│                                                                   │
│  핵심 보장 메커니즘:                                             │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  FlywayMigrationInitializerEntityManagerFactory          │    │
│  │    DependsOnPostProcessor                                │    │
│  │                                                          │    │
│  │  → Spring Boot 자동 설정이 이 클래스를 통해             │    │
│  │    EntityManagerFactory가 반드시 Flyway 완료 후          │    │
│  │    생성되도록 의존 관계를 자동으로 주입                  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  ⚠ CRITICAL EDGE CASE: 커스텀 @Bean Flyway 등록 시              │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  @Bean                                                   │    │
│  │  public Flyway flyway(DataSource ds) {  // 이렇게 하면!  │    │
│  │    return Flyway.configure().dataSource(ds).load();      │    │
│  │  }                                                       │    │
│  │                                                          │    │
│  │  → DependsOnPostProcessor 등록이 비활성화됨              │    │
│  │  → Hibernate가 Flyway보다 먼저 실행될 수 있음            │    │
│  │  → 결과: 엔티티 검증 실패 또는 빈 DB에서 validate 오류   │    │
│  │                                                          │    │
│  │  해결책 1: @Bean(initMethod = "migrate") 사용            │    │
│  │  @Bean(initMethod = "migrate")                           │    │
│  │  public Flyway flyway(DataSource ds) { ... }             │    │
│  │                                                          │    │
│  │  해결책 2: FlywayMigrationInitializer를 직접 주입        │    │
│  │  @Bean                                                   │    │
│  │  public FlywayMigrationInitializer flywayInitializer(    │    │
│  │      Flyway flyway) {                                    │    │
│  │    return new FlywayMigrationInitializer(flyway, null);  │    │
│  │  }                                                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9.2 Flyway가 사용하는 커넥션 수

┌─────────────────────────────────────────────────────────────────┐
│         Flyway 버전별 사용 커넥션 수 (PostgreSQL 기준)           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌─────────────────────┬──────────┬───────────────────────────┐ │
│  │ Flyway 버전         │ 커넥션 수 │ 비고                      │ │
│  ├─────────────────────┼──────────┼───────────────────────────┤ │
│  │ < 7.3.2             │    1     │ 단일 커넥션으로 모든 작업  │ │
│  │ 9.1.0+ (PostgreSQL) │    2     │ 트랜잭션 advisory lock    │ │
│  │ 11.16.0             │    3     │ 회귀 버그 (11.17.1에서 수정)│ │
│  └─────────────────────┴──────────┴───────────────────────────┘ │
│                                                                   │
│  Flyway 9.1.0 PostgreSQL 변경 사항:                              │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  변경 전 (< 9.1.0): Session-level Advisory Lock         │    │
│  │  ├── pg_try_advisory_lock(hash) → 커넥션 1개로 충분     │    │
│  │  └── 커넥션이 끊어지면 락 자동 해제                      │    │
│  │                                                          │    │
│  │  변경 후 (>= 9.1.0): Transactional Advisory Lock        │    │
│  │  ├── pg_try_advisory_xact_lock(hash)                    │    │
│  │  ├── 트랜잭션 범위 내에서만 유효                         │    │
│  │  ├── 락 전용 커넥션(A) + 마이그레이션 커넥션(B) 필요    │    │
│  │  └── 최소 2개 커넥션 필요                                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  maximum-pool-size가 너무 작으면 발생하는 문제:                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  예: maximum-pool-size: 2, Flyway 9.1+, PostgreSQL      │    │
│  │                                                          │    │
│  │  Flyway가 커넥션 A, B를 모두 점유                        │    │
│  │  → HikariCP 풀이 고갈됨                                  │    │
│  │  → Flyway 자신이 다음 커넥션을 기다리며 deadlock 유발    │    │
│  │                                                          │    │
│  │  에러 메시지:                                            │    │
│  │  HikariPool-1 - Connection is not available,            │    │
│  │  request timed out after 30000ms                        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  권장 최소 maximum-pool-size: 5                                  │
│  ├── Flyway 9.1+ PostgreSQL: 2개 사용                           │
│  └── 앱 시작 시 나머지 3개 여유분 확보                          │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9.3 Connection Timeout과 장시간 마이그레이션

┌─────────────────────────────────────────────────────────────────┐
│         Flyway 이중 커넥션 아키텍처와 장시간 마이그레이션 문제   │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Flyway 이중 커넥션 구조:                                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  커넥션 A (메타데이터 락)                                │    │
│  │  ├── flyway_schema_history 테이블 락 보유                │    │
│  │  ├── 마이그레이션 전체 기간 동안 유지                    │    │
│  │  └── SQL 실행 없이 유휴 상태 (idle)                      │    │
│  │                        │                                 │    │
│  │  커넥션 B (실제 실행)   │                                 │    │
│  │  ├── 실제 마이그레이션 SQL 실행                          │    │
│  │  └── ALTER TABLE, CREATE INDEX 등                        │    │
│  │                                                          │    │
│  │  문제: 수백만 행 테이블의 ALTER TABLE 실행 시            │    │
│  │  → 커넥션 B: 수 분~수십 분 동안 SQL 실행 중             │    │
│  │  → 커넥션 A: 유휴 상태로 같은 시간 대기                 │    │
│  │  → 방화벽/프록시가 유휴 커넥션을 강제 종료!             │    │
│  │  → 커넥션 A 끊어짐 → 락 해제 → 마이그레이션 무결성 훼손 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  문제가 되는 중간 계층:                                          │
│  ├── PgBouncer (connection pooler)                               │
│  ├── AWS NLB / ALB (idle timeout 기본 60초~350초)               │
│  ├── 방화벽 (stateful inspection timeout)                        │
│  └── 클라우드 DB 프록시 (RDS Proxy, Cloud SQL Auth Proxy)       │
│                                                                   │
│  해결책: HikariCP keepalive-time 설정                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  spring:                                                 │    │
│  │    datasource:                                           │    │
│  │      hikari:                                             │    │
│  │        keepalive-time: 300000  # 5분마다 ping            │    │
│  │        # 방화벽 idle timeout보다 짧게 설정               │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  관련 HikariCP 속성 정리:                                        │
│  ┌──────────────────────┬──────────────┬────────────────────┐   │
│  │ Property             │ 기본값       │ 설명               │   │
│  ├──────────────────────┼──────────────┼────────────────────┤   │
│  │ connectionTimeout    │ 30,000ms     │ 풀 커넥션 대기 시간 │   │
│  │ maxLifetime          │ 1,800,000ms  │ 커넥션 최대 수명   │   │
│  │                      │ (30분)       │                    │   │
│  │ keepaliveTime        │ 0 (비활성)   │ 유휴 커넥션 ping   │   │
│  │ idleTimeout          │ 600,000ms    │ 유휴 커넥션 제거   │   │
│  │                      │ (10분)       │                    │   │
│  └──────────────────────┴──────────────┴────────────────────┘   │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9.4 전용 마이그레이션 커넥션 (spring.flyway.url)

┌─────────────────────────────────────────────────────────────────┐
│         Flyway 전용 DataSource 분리                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  기본 동작 (spring.flyway.url 미설정):                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  HikariCP Pool ←──── Flyway (풀에서 커넥션 빌림)        │    │
│  │                ←──── 애플리케이션 (동일 풀 사용)         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  spring.flyway.url/user/password 설정 시:                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  HikariCP Pool ←──── 애플리케이션 (앱 전용)              │    │
│  │  Plain JDBC    ←──── Flyway (별도 커넥션, HikariCP 아님) │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  활용 사례 1: 최소 권한 원칙 (Least Privilege)                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  마이그레이션 유저: CREATE, ALTER, DROP, REFERENCES      │    │
│  │  애플리케이션 유저: SELECT, INSERT, UPDATE, DELETE만     │    │
│  │                                                          │    │
│  │  # application.yml                                       │    │
│  │  spring:                                                 │    │
│  │    datasource:                                           │    │
│  │      url: jdbc:postgresql://db:5432/myapp                │    │
│  │      username: app_user        # 앱 전용 (제한된 권한)   │    │
│  │      password: ${APP_PASSWORD}                           │    │
│  │    flyway:                                               │    │
│  │      url: jdbc:postgresql://db:5432/myapp                │    │
│  │      user: migration_user      # 마이그레이션 전용       │    │
│  │      password: ${MIGRATION_PASSWORD}                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  활용 사례 2: @FlywayDataSource 어노테이션                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  @Bean                                                   │    │
│  │  @FlywayDataSource                                       │    │
│  │  public DataSource flywayDataSource() {                  │    │
│  │    return DataSourceBuilder.create()                     │    │
│  │        .url("jdbc:postgresql://db:5432/myapp")           │    │
│  │        .username("migration_user")                       │    │
│  │        .password(migrationPassword)                      │    │
│  │        .build();                                         │    │
│  │  }                                                       │    │
│  │  // @Primary DataSource는 앱 유저로 HikariCP 사용        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  ⚠ 알려진 버그: spring.flyway.init-sqls 무시 문제               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Spring Boot < ~2.5 에서:                               │    │
│  │  spring.flyway.init-sqls 설정이 spring.flyway.url이     │    │
│  │  함께 설정되지 않으면 조용히(silently) 무시됨            │    │
│  │                                                          │    │
│  │  참고: Spring Boot Issue #23392                          │    │
│  │  해결: spring.flyway.url도 함께 설정하거나              │    │
│  │        Spring Boot 버전 업그레이드                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9.5 프로덕션 통합 설정 (Production Configuration)

# application.yml - HikariCP + Flyway 프로덕션 통합 설정
spring:
  datasource:
    url: jdbc:postgresql://db:5432/myapp
    username: ${DB_APP_USER}
    password: ${DB_APP_PASSWORD}
    hikari:
      maximum-pool-size: 10          # Flyway 2개 + 앱 8개 여유
      minimum-idle: 5                # 최소 유지 커넥션
      keepalive-time: 300000         # 5분마다 유휴 커넥션 ping
      connection-timeout: 30000      # 30초 대기 후 실패
      max-lifetime: 1800000          # 30분 커넥션 최대 수명
      idle-timeout: 600000           # 10분 유휴 후 제거

  flyway:
    url: jdbc:postgresql://db:5432/myapp
    user: ${DB_MIGRATION_USER}       # 마이그레이션 전용 유저
    password: ${DB_MIGRATION_PASSWORD}
    clean-disabled: true             # 프로덕션 clean 절대 금지
    validate-on-migrate: true        # 마이그레이션 전 무결성 검증
    connect-retries: 5               # DB 연결 재시도 횟수
    connect-retries-interval: 10     # 재시도 간격 (초), Docker 환경 필수

  jpa:
    hibernate:
      ddl-auto: validate             # Flyway가 관리, Hibernate는 검증만
┌─────────────────────────────────────────────────────────────────┐
│         프로덕션 배포 체크리스트                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. 마이그레이션 유저와 앱 유저 분리                              │
│     └── 최소 권한 원칙: 앱 유저는 DML만, 마이그레이션 유저는 DDL  │
│                                                                   │
│  2. maximum-pool-size >= 5 (Flyway 9.1+ PostgreSQL)              │
│     └── Flyway 2개 + 앱 기동 시 필요한 여유분 확보               │
│                                                                   │
│  3. clean-disabled: true 설정 확인                               │
│     └── 프로덕션 DB 전체 삭제 사고 방지                          │
│                                                                   │
│  4. ddl-auto: validate 설정                                      │
│     └── Hibernate가 스키마를 직접 변경하지 않도록 차단           │
│                                                                   │
│  5. keepalive-time 설정 (장시간 마이그레이션 시)                 │
│     └── 유휴 커넥션 A가 방화벽에 의해 끊기지 않도록 유지         │
│                                                                   │
│  6. connect-retries + connect-retries-interval 설정              │
│     └── 컨테이너 환경에서 DB 기동 전 앱이 먼저 뜰 때 대비       │
│                                                                   │
│  7. 프로덕션 데이터 볼륨으로 마이그레이션 사전 테스트            │
│     └── 개발 DB (소량)와 프로덕션 DB (수백만 행)의 실행 시간 차이 │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

9.6 알려진 버그와 주의사항

┌─────────────────────────────────────────────────────────────────┐
│         Flyway + HikariCP 알려진 버그 및 주의사항               │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  Bug 1: Flyway 11.16.0 - 3 커넥션 회귀 버그                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  증상: maximum-pool-size: 2 이하 환경에서 기동 hang       │    │
│  │  원인: 커넥션을 3개 획득하도록 회귀                       │    │
│  │  수정: Flyway 11.17.1 업그레이드                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Bug 2: Flyway 9.1.0 PostgreSQL - 트랜잭션 락 2 커넥션           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  증상: Connection is not available, timed out after Nms  │    │
│  │  원인: session-level → transactional advisory lock 변경  │    │
│  │  수정: maximum-pool-size >= 5 설정                        │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Bug 3: Spring Boot < 2.5 - init-sqls 조용히 무시               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  증상: spring.flyway.init-sqls 설정이 적용 안 됨         │    │
│  │  원인: spring.flyway.url 없이는 init-sqls 무시 (Issue   │    │
│  │         #23392)                                          │    │
│  │  수정: spring.flyway.url 함께 설정 또는 버전 업그레이드  │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Bug 4: 커스텀 @Bean Flyway - DependsOnPostProcessor 비활성      │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  증상: Hibernate가 Flyway보다 먼저 실행되어 validate 실패 │    │
│  │  원인: 자동 설정 우회로 의존 순서 보장 메커니즘 해제      │    │
│  │  수정: FlywayMigrationInitializer Bean 수동 등록         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Bug 5: Flyway 7.15+ Docker - connect-retries 재시도 너무 빠름   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  증상: Docker Compose 환경에서 DB 기동 전 재시도 모두 소진│    │
│  │  원인: connect-retries 기본 재시도 간격이 너무 짧음       │    │
│  │  수정: connect-retries-interval: 10 (초) 이상 설정       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
│  Bug 6: 장시간 마이그레이션 - 유휴 메타데이터 커넥션 끊김        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  증상: 수십 분 마이그레이션 도중 커넥션 오류 발생         │    │
│  │  원인: 방화벽/NLB가 유휴 커넥션 A를 idle timeout으로 종료 │    │
│  │  수정: hikari.keepalive-time을 방화벽 timeout보다 짧게   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

10. 모던 대안과 비교

10.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  │ 내장        │      │
│  └──────────────┴────────────┴─────────────┴────────────┘      │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

10.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에 능숙하고 자동 생성을 불신                 │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

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

11.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 파일도:                                 │    │
│  │  ├── 한번 적용되면 수정하지 않는다                        │    │
│  │  ├── 버전 번호로 식별한다                                 │    │
│  │  └── 문제 시 새 마이그레이션 파일을 만든다                │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

11.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, HikariCP, Connection Pool, maximum-pool-size, keepaliveTime, connectionTimeout, FlywayMigrationInitializer, DependsOnPostProcessor, @FlywayDataSource, init-sqls, connect-retries