DB 스키마 마이그레이션 도구, 어떻게 고를까

DB 스키마 마이그레이션 도구, 어떻게 고를까

MSA·DDD 환경에서 Flyway·Liquibase·Bytebase·Atlas를 비교하고, 상황별로 무엇을 선택할지 정리했습니다.

image1.png

1. 스키마는 멈추지 않고 바뀐다

DDD와 MSA, 클라우드 네이티브 환경에서 DB 스키마는 고정 자산이 아닙니다. 서비스를 운영하는 내내 계속 바뀝니다.

이유는 도메인에 있습니다. 엔티티와 애그리거트는 도메인 이해가 깊어질수록 함께 변합니다. 속성이 붙고, 한 덩어리였던 개념이 둘로 갈라지고, 컬럼이던 값이 별도 테이블로 승격됩니다. 모델이 진화하면 스키마도 진화합니다.

이걸 도구 없이 손으로 ALTER만 날리면 환경마다 스키마가 어긋납니다. 마이그레이션 도구는 변경을 버전으로 관리해, 어떤 환경이든 같은 순서로 같은 상태에 도달하도록 보장합니다.

MSA는 여기에 분산을 더합니다. 서비스마다 자기 DB를 갖는 Database per Service에서는 마이그레이션도 서비스 단위로 일어납니다. 중앙에서 DDL을 한 번에 던지는 방식이 아니라, 여러 서비스가 각자 속도로 스키마를 바꾸는 상황을 통제해야 합니다. 무중단 배포까지 요구되면 "테이블 잠그고 잠깐 멈췄다 바꾸기"는 선택지에서 빠집니다.

도구를 고르기 전에: 두 가지 접근 방식

방식

설명

대표 도구

변경(Migration) 기반

스키마를 바꾸는 절차를 V1, V2, V3로 쌓는다. 변경의 누적이 곧 현재 상태.

Flyway, Liquibase

상태(선언형) 기반

목표 스키마를 선언하면 도구가 현재와의 차이를 계산해 스크립트를 생성한다.

Atlas

변경 기반은 의도가 명시적이고 추적이 쉽고, 선언형은 형상 관리가 깔끔하고 자동화에 유리합니다. 이 차이가 도구 선택의 출발점입니다.

2. 네 가지 도구, 한눈에

Flyway — 가장 단순하고 가장 많이 쓰인다

V1__create_member.sql처럼 버전과 설명을 붙인 SQL 파일을 순서대로 적용합니다. 작성한 SQL이 그대로 실행되니 무엇이 돌지 예측하기 쉽고, Spring Boot와 통합이 매끄러워 기동 시 자동 적용 패턴을 많이 씁니다. 단, 자동 롤백·스키마 diff는 상용 영역입니다.

-- V2__add_email_to_member.sql 
ALTER TABLE member
   ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';

CREATE INDEX idx_member_email ON member (email);

작성한 SQL이 그대로 적용되고, flyway migrate 한 번이면 끝입니다.

Liquibase — 추상화와 롤백, 규제 증적

XML·YAML·JSON·SQL changelog로 변경을 기술합니다. DB 종류에 묶이지 않는 추상화 덕분에 여러 DBMS를 함께 지원하거나 롤백을 명시적으로 관리할 때 유리합니다. 상용(Liquibase Secure)에서는 정책 검사와 변경·롤백 감사 증적을 SOX·PCI DSS·DORA 같은 규제에 매핑해, 규제가 엄격한 환경에서 자주 선택됩니다.

# db/changelog/002-add-email.yaml
databaseChangeLog:
  - changeSet:
      id: 002-add-email
      author: dev
      changes:
        - addColumn:
            tableName: member
            columns:
              - column: { name: email, type: varchar(255) }
      rollback:
        - dropColumn:
            tableName: member
            columnName: email

rollback 블록을 명시하면 liquibase rollback이 그대로 되돌립니다. 위처럼 단순한 변경은 생략해도 역연산을 자동 생성합니다. 표현력이 좋은 만큼 SQL을 직접 쓰는 Flyway보다 진입 장벽은 조금 높습니다.

규제 약어 정리

- SOX(Sarbanes–Oxley Act) — 미국 상장사 회계·내부통제 규제. 재무 데이터 변경의 통제·증적을 요구.

- PCI DSS — 카드 결제 데이터 보안 표준. 변경 통제·감사 로그를 요구.

- DORA(Digital Operational Resilience Act) — EU 금융기관 운영 복원력 규제. 변경 이력과 롤백 능력 입증을 요구.

Bytebase — DB DevOps 플랫폼

앞의 둘이 라이브러리·CLI라면 Bytebase는 플랫폼입니다. 웹 GUI에서 변경 요청 → 리뷰 → 승인 → 적용 흐름을 돌리고, SQL 리뷰 규칙·변경 이력·접근 통제를 한곳에서 다룹니다. 여러 팀이 다수의 DB를 운영하며 거버넌스와 감사 추적이 중요한 조직에 맞습니다. 대신 도입이 무겁습니다.

Atlas — 선언형 + GitOps

원하는 스키마를 HCL·SQL·ORM으로 적어두면 현재 상태와의 차이를 자동 계산해 마이그레이션을 만듭니다. 스키마를 코드로 선언하고 Git에서 관리하는 GitOps 친화성이 강점입니다. 선언형이 익숙하지 않으면 사고방식의 전환이 필요합니다.

# schema.hcl — "최종 상태"를 선언한다
table "member" {
  schema = schema.public
  column "id" { type = bigint }
  column "email" { type = varchar(255) }
  primary_key { columns = [column.id] }
}

email 컬럼을 선언에 추가하고 atlas schema apply를 실행하면, Atlas가 현재 DB와 비교해 ADD COLUMN을 계산하고 적용합니다.

비교표

항목

Flyway

Liquibase

Bytebase

Atlas

접근 방식

변경 기반

변경 기반

변경 기반 + 플랫폼

선언형 중심

작성 형식

SQL

XML/YAML/JSON/SQL

SQL(+GUI)

HCL/SQL/ORM

지원 DBMS

30+

60+ (크로스 DB)

25개 엔진

주요 오픈소스 RDBMS

롤백

수동(자동은 상용)

다수 자동·raw SQL은 명시

지원

계획·되돌리기

자동 diff

없음

부분

부분

강점

리뷰·승인

외부 CI

외부 CI

내장(GUI)

CI 연계

감사 증적

제한적

Secure에서 SOX/PCI/DORA

변경·승인 로그

변경 계획·이력

라이선스

OSS + 상용

OSS + Pro·Secure

OSS + Enterprise

OSS + Pro

잘 맞는 상황

단순·SQL 친화

멀티 DB·롤백·규제

팀 거버넌스

선언형·GitOps

이식성도 DBMS 개수만큼 중요합니다. PostgreSQL·MySQL·Oracle·MS-SQL은 같은 변경이라도 DDL 문법이 다른데, Flyway는 raw SQL이라 제품별 차이를 직접 처리해야 합니다. Liquibase는 추상화된 changelog가 대상 DBMS에 맞는 SQL을 생성해 하나의 정의로 여러 제품군을 함께 지원하기 쉽습니다. 고객사마다 DBMS가 다르다면 이 차이가 선택을 가릅니다.

3. 어떻게 쓰나 - Flyway로 보는 동작과 GitOps

마이그레이션 파일은 네이밍 규칙만 지키면 됩니다.

db/migration/
├── V1__create_member.sql
├── V2__add_email_to_member.sql
└── V3__create_post.sql

V{버전}__{설명}.sql 형식이고, 버전 순서대로 한 번씩만 적용됩니다. 자주 쓰는 명령은 네 개입니다.

migrate — 아직 적용되지 않은 마이그레이션을 순서대로 실행

info — 적용 이력과 대기 중인 변경을 표시

validate — 적용된 파일이 변조되지 않았는지 체크섬으로 검증

baseline — 운영 중인 DB에 처음 도입할 때 기준점을 설정

validate는 생각보다 자주 사고를 막습니다. 이미 적용된 파일을 누군가 뒤늦게 수정하면 체크섬 불일치로 마이그레이션이 멈춥니다. 원칙은 하나입니다. 적용된 마이그레이션은 수정하지 말고, 새 버전을 추가합니다.

마이그레이션은 어느 모듈에 두나

한 서비스를 도메인·부트 등 여러 Gradle 모듈로 나눴다면, 마이그레이션은 실제 DB에 연결되어 기동되는 부트(애플리케이션) 모듈에 둡니다.

member-service/
├── member-domain/ ← 엔티티·도메인 로직
└── member-boot/ ← 애플리케이션 부트·설정
└── src/main/resources/db/migration/ ← Flyway 마이그레이션

서비스가 여럿이고 저장소가 분리돼 있다면, 각 서비스가 자기 저장소에서 같은 패턴을 따르고 각 부트 모듈이 자기 DB의 마이그레이션을 소유합니다(Database per Service).

현실적인 CI/CD 파이프라인

클라우드 네이티브에서는 Git을 단일 진실 원천(SSOT)으로 두는 GitOps가 표준입니다. "PR 올리면 CD가 적용한다"로 요약되지만, 엔터프라이즈의 실제 흐름은 더 촘촘합니다.

버전 관리 + 리뷰 — 테이블·인덱스·시드 데이터까지 모든 변경을 코드처럼 PR로 올리고 리뷰

자동 검증 — CI에서 임시 DB에 적용해 문법·제약·인덱스를 점검하고, 위험한 명령(조건 없는 DROP 등)을 정적 검사로 차단. 실행 계정은 최소 권한

하위 환경 재현 테스트 — 스테이징에 적용 후 단위·통합·회귀·성능 테스트. 특히 구버전 코드 + 신버전 스키마의 하위 호환을 확인

승인 게이트 — 운영 반영 전 MR 승인 또는 ITSM 티켓 승인. 위험도(저/중/고)에 따라 단계가 달라지고 티켓 번호가 남음

백업 확보 — 운영 적용 직전 백업, 백업도 주기적으로 복구 테스트

단계적 적용 — 멱등(idempotent) 스크립트로 마이그레이션을 먼저 적용하고 그에 의존하는 코드를 배포. Expand–Contract와 결합하면 순서 의존성이 사라짐

모니터링 + 기능 플래그 — 적용 직후 응답시간·커넥션·에러율 관찰. 기능 플래그로 신규 기능을 스키마와 분리

롤백 검증 — 하위 환경에서 미리 검증된 롤백 경로만 사용

여기서 빠지지 않는 원칙이 권한 분리입니다. 개발자는 하위 환경에서 자율적으로 작업하되, 운영 반영은 DBA·보안의 감독을 거칩니다. 그리고 최근 가이드들은 상태 기반 diff보다 명시적 versioned 마이그레이션을 권합니다. 변경이 예측 가능하고 감사·재현이 쉽기 때문입니다.

롤백은 "되돌리기"로 끝나지 않는다

규제 환경에서는 되돌렸다는 사실과 내용이 증적으로 남는가까지 요구됩니다.

Flyway — undo는 U1__...sql 같은 역방향 스크립트를 직접 작성해야 하고, 자동 undo는 상용 기능입니다. 롤백도 "또 하나의 마이그레이션"이라 검증·기록 대상입니다.

Liquibase — changelog 변경 다수는 롤백을 자동 생성, raw SQL은 명시적 rollback 블록을 답니다. Secure에서는 실행 SQL을 별도 테이블에 적재하고 롤백 리포트(누가·언제·무엇을·어떻게)를 산출합니다.

핵심은, 감사 대상 시스템에서는 롤백을 그때그때 손으로 짜서 운영에 바로 실행하면 안 된다는 것입니다. 사전에 검증되고 이력이 남는 경로로만 실행해야 합니다.

생성과 적용을 분리하는 모델

CD 자동 적용의 반대편에는 도구는 SQL만 생성하고, 적용은 DBA가 변경 윈도우에 직접 실행하는 모델이 있습니다. 운영 DB 접근을 소수에게만 주는 금융·공공 환경에서 흔합니다.

세 도구 모두 "적용 없이 SQL만 뽑는" 명령을 제공합니다.

Flyway — flyway migrate -dryRunOutput=deploy.sql

Liquibase — liquibase update-sql(적용), liquibase rollback-sql(롤백)

Atlas — atlas migrate diff <name>로 versioned SQL 생성

장점은 분명합니다. 사람이 SQL을 직접 확인하니 통제·감사·권한 분리가 자연스럽고, 위험한 변경을 통제된 시간대에 실행할 수 있습니다.

대신 비용이 있습니다. 자동 배포가 끊겨 리드타임이 길어지고, 테넌트가 많아지면 수작업이 병목이 됩니다. 가장 큰 함정은 드리프트입니다. DBA가 생성된 스크립트를 운영에서 손으로 고쳐 실행하면 Git의 SSOT와 실제 적용분이 어긋납니다. 원칙은 생성된 스크립트는 수정 없이 그대로 실행하고, 고칠 게 있으면 소스를 고쳐 다시 생성하는 것입니다.

그래서 최근의 절충안은 "DBA가 검토·승인하고, 파이프라인이 변경 윈도우에 적용"하는 게이트 모델입니다. 사람의 통제는 유지하되 실행은 자동화해 증적과 재현성을 함께 잡습니다. Bytebase, Liquibase Secure가 이 모델에 잘 맞습니다.

4. 실전 - 무중단과 대용량

도구 사용법보다 어려운 건 운영 중인 시스템을 멈추지 않고 스키마를 바꾸는 일입니다.

Expand–Contract 패턴

무중단의 기본기입니다. 변경을 한 번에 하지 않고 세 단계로 나눕니다.

확장(Expand) — 새 컬럼·테이블 추가. 기존 코드는 영향 없음

전환(Migrate & Dual-write) — 신·구 양쪽을 함께 쓰도록 배포하고 기존 데이터를 백필

수축(Contract) — 트래픽이 전부 새 구조를 쓰는 걸 확인한 뒤 낡은 컬럼·테이블 제거

이렇게 하면 배포 중 어느 순간에도 애플리케이션과 스키마가 서로 호환됩니다. 컬럼 이름 변경처럼 단순해 보이는 작업도 롤링 배포 중 구버전 인스턴스가 옛 컬럼을 참조하므로, 한 번의 RENAME으로는 무중단이 깨집니다. "추가 → 양쪽 쓰기 → 제거"로 풀어야 합니다.

되돌릴 수 없는 변경

컬럼 삭제, 타입 변경, NOT NULL 추가는 한 번에 하면 위험합니다. 항상 하위 호환을 한 단계 남기는 방향으로 분해합니다. NOT NULL은 (1) 컬럼 추가 → (2) 기존 행 백필 → (3) 제약 추가 순으로 나눕니다.

데이터가 많을 때

테이블에 수천만 행이 쌓이면 평범한 ALTER TABLE 한 줄이 장애가 됩니다. 많은 DB에서 스키마 변경이 테이블에 잠금(lock)을 걸어 그동안 읽기·쓰기가 막히기 때문입니다. 작은 테이블에서 순식간이던 인덱스 추가가, 큰 테이블에서는 수십 초간 락을 잡으며 API 지연과 커넥션 풀 포화로 번집니다.

대용량에서는 다음 기법을 씁니다.

온라인 스키마 변경 — MySQL 계열이면gh-ost, pt-online-schema-change. 원본 대신 그림자 테이블을 만들어 데이터를 조금씩 복사하고, 변경분을 동기화한 뒤 마지막에 순간 교체합니다.

배치 백필 — 수억 행을 UPDATE 한 번에 채우면 트랜잭션이 비대해지고 복제 지연이 생깁니다. 수천~수만 행씩 끊어 부하를 보며 채웁니다.

대용량 인덱스 — PostgreSQL의 CREATE INDEX CONCURRENTLY처럼 쓰기를 막지 않는 옵션을 활용합니다. 트랜잭션 밖에서 실행돼야 하므로 마이그레이션 도구의 트랜잭션 분리 옵션을 함께 챙깁니다.

원칙은 하나입니다. 큰 테이블의 변경은 한 번에가 아니라 잘게 나누어 점진적으로.

국내 사례 둘

① Flyway 도입 — JPA ddl-auto에 의존하던 팀이 로컬·테스트·운영 스키마가 어긋나는 문제, "운영에서 테이블 DROP은 위험하다"는 현실에 부딪혀 Flyway를 도입했습니다. 운영에는 ddl-auto: validate를 두어 엔티티-스키마 불일치 시 기동 자체를 막고, 변경은 versioned 스크립트로만 반영합니다. flyway_schema_history의 체크섬으로 변조를 감지하고, 적용된 스크립트는 수정하지 않고 새 버전 추가를 운영 규칙으로 굳혔습니다. 앞서 본 validate·체크섬 원칙이 그대로 자리 잡은 예입니다. (Flyway 적용기)

② 무중단 대용량 전환 — 좋아요 기능을 PostgreSQL → MySQL로 옮기며 스키마까지 바뀌었고, 데이터가 많아 전환에 오랜 시간이 필요했습니다. 중단이 불가능했기에 이중 쓰기를 택했습니다. 원장은 기존 V1으로 두고 V1의 변경을 SQS(FIFO)로 V2에 흘려보내 순서와 일관성을 지켰습니다. 초기 데이터는 스크립트로 옮기고, 그사이 변경분은 SQS에 쌓았다가 V2 배포 후 소비해 따라잡았습니다. 조회는 V1·V2를 함께 제공하다 점진적으로 V2로 옮기는 스트랭글러 방식이었고, SQS 구독을 멱등하게 구현해 마지막 이벤트가 최종 상태가 되도록 보장했습니다. 저자가 꼽은 핵심은 "마이그레이션 중에도 계속 업데이트되는 데이터를 어떻게 동기화하느냐"였습니다. (마이그레이션 후기)

5. 그래서 무엇을 골라야 하나

같은 도구라도 어디에 쓰느냐에 따라 답이 달라집니다.

설치형 SaaS 플랫폼 → Flyway

만약 SaaS 플랫폼이 별도의 앱 유통 시스템을 통해 구독 → 설치 → 해지 → 정리까지 자동으로 흐른다면 어떻게 될까요?

이 생애주기가 요구하는 건 두 가지입니다.

설치 시 스키마 자동 생성, 구독 취소 시 자동 정리 — 사람 개입 없이 앱 단위로 스키마가 깔리고 지워져야 합니다. 마이그레이션이 앱에 내장돼 코드와 함께 배포되는 모델이 적합합니다.

모듈별 독립성 — 모듈마다 마이그레이션이 분리되고 각자의 버전 이력을 갖습니다.

⚠️ 구독 취소 시 정리가 마이그레이션 이력 테이블까지 비우지 못하면, 재설치 때 "이미 적용된 버전"으로 오인돼 초기 스키마가 생성되지 않을 수 있습니다.
설치·정리 흐름을 설계할 때 이력의 생애주기까지 함께 고려해야 합니다.

이 조건에는 Flyway가 잘 맞습니다. 앱 기동 시 자동 적용이 매끄럽고, 모듈별 SQL 마이그레이션을 애플리케이션 구조에 그대로 얹을 수 있으며, 설치·정리 파이프라인에 단순하게 통합됩니다. 형상 관리와 자동화를 한 단계 더 끌어올리려면 Atlas + GitOps 결합도 선택지입니다.

사이트 프로젝트라면 → 산업군별로

산업군

요구

추천

금융·은행권

리뷰·승인 감사 + 롤백 증적이 필수. 가장 엄격.

Liquibase(Secure) — 정책 검사로 비규정 변경 차단, SOX·PCI·DORA 매핑. 승인을 GUI로 강제하려면 Bytebase를 승인 레이어로 추가

의료

개인정보·규제 준수, 데이터 무결성·이력 추적.

Liquibase — 명시적 changelog와 롤백 관리. 승인 거버넌스가 더 필요하면 Bytebase 보완

내부 인트라넷

규제 부담 낮고 운영 인력 제한. 단순함 우선.

Flyway — 가벼운 SQL 운영

정리하면, 고객사에서는 안정성과 감사 증적(롤백 포함)을 얼마나 강하게 요구하느냐에 비례해 Flyway → Liquibase → Bytebase 쪽으로 무게가 옮겨갑니다.

빠른 체크리스트

• 마이그레이션이 앱과 함께 배포되어야 하나? → Flyway

여러 DBMS, 명시적 롤백, 규제 증적(SOX·PCI·DORA)이 중요한가? → Liquibase(Secure)

리뷰·승인 워크플로를 GUI로 강제해야 하나? → Bytebase

선언형 형상 관리와 GitOps 자동화를 지향하나? → Atlas

6. 마치며

도구 선택에서 가장 중요한 건 도구 자체가 아니라 그것을 둘러싼 워크플로입니다. 모델이 진화하는 한 스키마도 진화하고, 그 변화를 추적 가능하고 안전하게, 되돌릴 수 있게 만드는 절차가 갖춰져 있으면 어떤 도구를 쓰든 절반은 해결됩니다.

자동화된 생애주기를 가진 플랫폼은 단순하고 내장하기 좋은 도구를, 금융·의료처럼 감사와 통제가 중요한 고객사는 거버넌스가 강한 도구를 맥락에 맞춰 고르면 됩니다. 도입을 검토한다면 변경이 가장 잦은 서비스 하나를 골라 작은 파이프라인부터 시작하는 편이 안전합니다.

dnine

Site footer