LabHub

블로그

DB 마이그레이션 도구 2026 — Atlas / Flyway / Liquibase / Bytebase / pgroll / Squawk / gh-ost 심층 비교

한국어English日本語

프롤로그 — "마이그레이션 한 줄로 5분 다운"

2026년 어느 팀의 사후 보고서.

23:14, 평소처럼 배포. ALTER TABLE orders ADD COLUMN status_v2 VARCHAR(32) NOT NULL DEFAULT 'pending'. 23:14:08, 프라이머리 락. 23:14:09, 모든 쿼리가 큐에 쌓이기 시작. 23:14:42, 백엔드가 connection pool 고갈, 5xx 시작. 23:19:31, 마이그레이션 종료. 23:19:34, 큐 소진, 정상화. 5분 22초 다운. 테이블에는 4억 행이 있었다.

이게 2026년에도 흔한 풍경이다. 도구는 충분히 많다 — Flyway, Liquibase, Atlas, Bytebase, pgroll, Squawk, gh-ost, pt-osc, Reshape, dbmate, golang-migrate, Diesel, Alembic, Prisma Migrate, Drizzle Kit. 그런데 "어떤 마이그레이션이 안전한가"를 도구가 알려주지 않으면, 결국 5분 다운은 반복된다.

이 글은 2026년 DB 마이그레이션 도구의 지도를 그린다. 누가 무엇을 잘하고, 누가 무엇을 못 하고, 우리 팀은 무엇을 골라야 하는지까지.


1장 · 2026년 DB 마이그레이션 지도 — 선언적 vs 명령적 vs 온라인 변경

먼저 큰 그림. 모든 마이그레이션 도구는 세 축 중 어디에 서느냐로 갈린다.

축 1: 선언적(declarative) vs 명령적(imperative)

선언적은 Terraform이 인프라에 한 일을 DB 스키마에 한다. 장점: 스키마가 코드로 한눈에 보인다. 단점: 도구가 만든 ALTER가 항상 안전하다는 보장이 없다(컬럼 rename을 drop+add로 풀어버리는 등).

축 2: 온라인(online) vs 오프라인(offline)

작은 테이블(수십만 행 이하)은 오프라인이 단순하다. 큰 테이블(수억 행, 트래픽 많음)에서는 온라인이 필수.

축 3: forward-only vs reversible

2026년 시니어들의 합의: forward-only + 짧고 안전한 변경의 연속이 reversible보다 현실적이다. down은 "방금 만든 빈 컬럼 drop" 정도까지만 의미가 있다.

한 장 요약

도구모델온라인?언어/대상권장 사용처
Atlas선언적직접 ❌, 통합 ✅다중 DB선언적 스키마 + CI
Flyway 11명령적Java 친화엔터프라이즈 표준
Liquibase 4.30명령적 + 메타Java·XML/YAML대기업·다중 DB
Sqitch명령적 + DAGDB 중립의존성 명시형
Bytebase 3둘 다 + 워크플로통합 ✅다중 DBDB DevOps·리뷰
Squawk린터n/aPostgresCI 게이트
pgroll선언적 + expand/contractPostgres무중단 Postgres
gh-ostonline changeMySQL대규모 MySQL
pt-osconline changeMySQL클래식 MySQL
Reshapeonline changePostgresRust·무중단
dbmate명령적다중 DB·Go CLI가벼운 CLI
golang-migrate명령적Go·다중 DBGo 표준
Diesel명령적 + ORMRustRust 친화
Alembic명령적 + 오토젠Python·SQLAlchemyPython 표준
Prisma Migrate양방향TypeScriptNode 친화
Drizzle Kit선언적 + 명령적TypeScriptTS·경량

이게 2026년 지도. 이제 한 칸씩 들어가본다.


2장 · Expand-Contract 패턴 + 흔한 발자국들

도구를 고르기 전에 패턴을 먼저 알아야 한다. 도구가 좋아도 잘못된 패턴으로 쓰면 락이 걸린다.

Expand-Contract (Parallel Change)

무중단 마이그레이션의 핵심 패턴. 세 단계로 쪼갠다.

  1. Expand: 이전 + 새 스키마가 공존하도록 추가만 한다. drop 없음.
  2. Migrate: 코드가 새 스키마로 쓰기/읽기를 함께. backfill로 과거 데이터를 새 스키마로 복사.
  3. Contract: 모든 코드가 새 스키마만 쓰는 게 확인되면, 옛 스키마를 drop.

예시 — 컬럼 rename(emailemail_address).

pgroll·Reshape·gh-ost가 이걸 기본 모델로 자동화한다. 다른 도구는 사람이 마이그레이션을 3개로 쪼개야 한다.

흔한 발자국 (foot-guns)

1) NOT NULL 추가 (큰 테이블)

Postgres 11+에서 ALTER TABLE ... ADD COLUMN x INT NOT NULL DEFAULT 0은 메타데이터만 바꾸고 끝(빠름). 하지만 ALTER TABLE ... ALTER COLUMN x SET NOT NULL은 전체 테이블을 스캔하면서 ACCESS EXCLUSIVE 락. 4억 행이면 분 단위로 락.

2) 컬럼 rename

3) 타입 변경 (INT → BIGINT)

4) 인덱스 추가

5) 디폴트 값 변경 (Postgres 11 이전 / 일부 타입)

6) Foreign key 추가

7) 큰 DELETE / UPDATE

핵심: 마이그레이션 도구는 "스크립트를 순서대로 돌리는 무언가"일 뿐, 안전성을 보장하지 않는다. 안전성은 위 패턴 + (Squawk 같은) 린터 + 리뷰에서 나온다.


3장 · Atlas (Ariga) — 선언적 스키마 관리의 대표

Atlas(ariga.io/atlas)는 2022년부터 시작해서 2024-2025년에 빠르게 점유율을 늘렸다. HashiCorp의 Terraform을 DB 스키마에 한 도구.

모델

schema.hcl(또는 SQL/JSON) 파일에 원하는 스키마를 적는다. Atlas가 현재 DB와 비교해서 diff를 계산하고 ALTER 문을 생성.

schema "public" {}

table "users" {
  schema = schema.public
  column "id" {
    type = bigint
    null = false
  }
  column "email" {
    type = varchar(255)
    null = false
  }
  primary_key { columns = [column.id] }
  index "idx_users_email" {
    columns = [column.email]
    unique  = true
  }
}

CLI는 두 모드를 지원.

강점

약점

권장 사용처

2026년 기준 Atlas는 선언적 진영의 사실상 표준 위치.


4장 · Flyway 11 — Java 진영의 클래식

Flyway는 2010년부터, JBoss/Spring Boot 생태계의 표준. 2024년에 Redgate가 인수, 11.x는 모듈 정리와 새 데이터베이스 어댑터 중심.

모델

V{version}__{description}.sql 명명 규약. 단순 명령적.

db/migration/
  V1__create_users.sql
  V2__add_email_index.sql
  V3__add_orders.sql
  R__create_view_active_users.sql  # Repeatable
  U2__rollback_email_index.sql      # Undo (Teams)

DB의 flyway_schema_history 테이블이 어떤 V가 돌았는지 추적. flyway migrate로 미적용분만 순서대로 적용.

강점

약점

권장 사용처

2026년에도 JVM 진영의 디폴트. 다만 새 프로젝트가 Flyway만 쓰는 일은 줄고, Atlas/Squawk와 조합하는 경우가 늘었다.


5장 · Liquibase 4.30 — XML/YAML 친화

Liquibase는 Flyway의 라이벌. 2006년부터, 4.30(2025-2026)은 changelog 포맷·DB 지원·UX 개선 중심.

모델

changelog 파일에 changeSet을 나열. 포맷은 XML·YAML·JSON·SQL 모두 가능.

databaseChangeLog:
  - changeSet:
      id: 1
      author: alice
      changes:
        - createTable:
            tableName: users
            columns:
              - column:
                  name: id
                  type: BIGINT
                  autoIncrement: true
                  constraints: { primaryKey: true, nullable: false }
              - column:
                  name: email
                  type: VARCHAR(255)
                  constraints: { nullable: false, unique: true }
      rollback:
        - dropTable: { tableName: users }

강점

약점

Flyway vs Liquibase 한 줄

2026년 기준 둘은 거의 동률, 다만 신규 프로젝트는 Atlas로 옮겨가는 추세가 분명함.


6장 · Sqitch — 의존성 기반의 오래된 학파

Sqitch(sqitch.org)는 2012년에 David Wheeler가 만든, "Git 같은 마이그레이션 도구". 버전 번호가 아니라 의존성 그래프로 마이그레이션을 추적.

모델

각 변경은 이름이 있는 change. sqitch add appchanges로 만들면 세 파일이 생김.

deploy/appchanges.sql    -- 적용 SQL
revert/appchanges.sql    -- 되돌리기 SQL
verify/appchanges.sql    -- 검증 SELECT

sqitch.plan에 의존성을 명시.

add_users 2026-05-16T12:00:00Z alice
add_orders [add_users] 2026-05-16T13:00:00Z alice  -- add_users에 의존

sqitch deploy는 의존성을 만족하는 순서로 적용. sqitch verify는 verify SQL을 돌려서 실제로 들어갔는지 확인.

강점

약점

권장 사용처

2026년 Sqitch는 틈새지만 충성도 높은 도구. PostgreSQL의 일부 코어 컨트리뷰터들이 여전히 선호.


7장 · Bytebase 3 — DB DevOps + Review

Bytebase(bytebase.com)는 2021년 시작, 2025년 3.x로 점프. GitLab/GitHub for DBs 컨셉.

모델

웹 UI + GitOps. 마이그레이션 SQL을 PR로 올리면 Bytebase가:

  1. 자동 lint (SQL Review, Squawk-style 룰셋).
  2. 승인 워크플로(DBA가 리뷰).
  3. stage별 자동 배포(dev → staging → prod).
  4. schema drift 감지(Bytebase가 모르는 변경이 prod에 들어가면 알람).
  5. 데이터 마스킹·access control(누가 어떤 컬럼을 볼 수 있는지).

강점

약점

권장 사용처

2026년 Bytebase는 DB DevOps의 사실상 선두. PlanetScale·Neon 같은 서버리스 DB가 자체 UI를 잘 만들어서 경쟁이 있지만, on-prem/multi-cloud는 Bytebase 우위.


8장 · Squawk — Postgres 마이그레이션 린터

Squawk(squawkhq.com)는 2020년에 Steve Dignam이 만든 작은 OSS 도구. 마이그레이션 SQL을 읽고 위험한 패턴을 잡는다. 마이그레이션 도구가 아니라 린터.

모델

squawk migration.sql

위험한 패턴을 발견하면 에러/경고.

migration.sql:1:1: warning: adding-not-nullable-field
  ALTER TABLE "user" ADD COLUMN "email" TEXT NOT NULL
  Adding a NOT NULL field requires a table rewrite ...
  Use ADD COLUMN ... DEFAULT NULL, backfill, then ALTER ... SET NOT NULL.

검출 룰 (Postgres 중심, 30+)

강점

약점

권장 사용처

2026년 시니어들의 합의: Postgres 쓰면 Squawk는 디폴트. 도구 선택과 무관하게 CI에 넣는다.


9장 · pgroll (Xata) — 무중단 Postgres 마이그레이션

pgroll(github.com/xataio/pgroll)은 Xata가 2023-2024년에 오픈소스로 공개. expand-contract를 도구가 자동화한다는 야심.

모델

JSON으로 마이그레이션을 정의. pgroll이 자동으로 view + trigger를 만들어서 옛 버전 + 새 버전 스키마를 동시에 노출.

{
  "name": "rename_email_column",
  "operations": [
    {
      "rename_column": {
        "table": "users",
        "column": "email",
        "to": "email_address"
      }
    }
  ]
}

pgroll start로 시작 → 두 버전 공존(view로 노출) → pgroll complete로 옛 컬럼 제거. 중간에 pgroll rollback도 가능.

작동 원리

  1. pgroll start가 새 컬럼/테이블/인덱스 추가 + view 생성.
  2. 옛 view = 옛 스키마, 새 view = 새 스키마. 코드는 SET search_path로 어느 view를 쓸지 고름.
  3. trigger가 양방향으로 데이터를 동기화(옛 컬럼 쓰면 새 컬럼에도, 새 컬럼 쓰면 옛 컬럼에도).
  4. backfill이 백그라운드로 옛 데이터를 새 컬럼으로 복사.
  5. 모든 트래픽이 새 view로 옮겨가면 pgroll complete로 옛 컬럼 drop.

강점

약점

권장 사용처

2026년 pgroll은 Postgres 무중단 마이그레이션의 best bet. 다만 안정성은 gh-ost(MySQL)에 비하면 아직 어림.


10장 · gh-ost / pt-online-schema-change / Reshape — 온라인 스키마 변경

큰 테이블 + 라이브 트래픽 = 온라인 스키마 변경 필요. 세 도구.

gh-ost (GitHub, MySQL)

2016년에 GitHub이 만들고 OSS화. trigger 없이, binlog 기반으로 동작.

작동:

  1. shadow 테이블을 새 스키마로 만듦.
  2. 옛 테이블의 행을 chunk 단위로 shadow에 INSERT.
  3. binlog를 읽어서 진행 중 발생한 변경(INSERT/UPDATE/DELETE)을 shadow에도 적용.
  4. cutover: 짧은 락으로 옛 테이블 ↔ shadow를 atomic swap.
gh-ost \
  --user="root" --password="..." \
  --host=primary.db \
  --database="app" --table="orders" \
  --alter="ADD COLUMN status_v2 VARCHAR(32) NOT NULL DEFAULT 'pending'" \
  --execute

pt-online-schema-change (Percona, MySQL)

Percona Toolkit의 일부. trigger 기반.

작동:

  1. shadow 테이블 + 옛 테이블에 INSERT/UPDATE/DELETE trigger 설치.
  2. chunk 단위로 옛 → shadow 복사.
  3. cutover: RENAME TABLE 두 번으로 swap.

Reshape (Postgres, Rust)

2022년에 Fabian Lindfors가 만든 Rust 도구. Postgres용 expand-contract.

작동: pgroll과 비슷. SQL이 아니라 TOML로 마이그레이션 정의 + view + trigger로 옛/새 공존.

[[actions]]
type = "add_column"
table = "users"
column = "email_address"
data_type = "text"
nullable = true

셋의 비교

gh-ostpt-oscReshapepgroll
DBMySQLMySQLPostgresPostgres
trigger
binlog
활발도 (2026)중간낮음낮음높음
권장MySQL 대규모gh-ost 안 되는 케이스(회피)Postgres

2026년 합의: MySQL은 gh-ost가 디폴트, fallback이 pt-osc. Postgres는 pgroll이 신흥 표준.


11장 · dbmate / golang-migrate / Diesel / Alembic — 언어별 옵션

각 언어 진영의 디폴트.

dbmate (Go, 다중 DB)

amacneil/dbmate. 한 파일 짜리 Go 바이너리, CLI 디자인이 깔끔.

dbmate new add_users
# db/migrations/20260516120000_add_users.sql 생성

파일은 up/down 두 섹션.

-- migrate:up
CREATE TABLE users (id BIGINT PRIMARY KEY, email TEXT NOT NULL);

-- migrate:down
DROP TABLE users;

golang-migrate (Go, 다중 DB)

golang-migrate/migrate. CLI + Go 라이브러리.

migrations/
  000001_create_users.up.sql
  000001_create_users.down.sql

dbmate와 golang-migrate 사이 — 2024-2025년에 dbmate로 옮긴 팀이 늘었음. 새 Go 프로젝트는 dbmate 디폴트 (또는 Atlas).

Diesel migrations (Rust)

Diesel ORM의 일부. diesel migration generate add_usersup.sql/down.sql.

Alembic (Python, SQLAlchemy)

SQLAlchemy의 마이그레이션 도구. Python 코드로 마이그레이션을 정의.

def upgrade():
    op.add_column('users', sa.Column('email', sa.String(255), nullable=False))

def downgrade():
    op.drop_column('users', 'email')

2026년에도 Python/Django는 Django migrations, Python/FastAPI/SQLAlchemy는 Alembic이 디폴트.


12장 · Prisma Migrate / Drizzle Kit — TypeScript 진영

Node/TypeScript는 분열 상태. 둘이 양강.

Prisma Migrate

Prisma ORM의 일부. schema.prisma에 모델 적고, prisma migrate dev로 마이그레이션 생성/적용.

model User {
  id    BigInt  @id @default(autoincrement())
  email String  @unique
}

Drizzle Kit

Drizzle ORM의 일부. 코드(TypeScript)로 스키마 정의 → drizzle-kit generate로 SQL diff 생성.

export const users = pgTable("users", {
  id: bigserial("id").primaryKey(),
  email: varchar("email", { length: 255 }).notNull().unique(),
});

2026년 TS 합의:


13장 · 누가 무엇을 골라야 하나 — 결정 트리

상황추천
단일 앱, Postgres, 트래픽 적음dbmate 또는 golang-migrate + Squawk
단일 앱, MySQL, 트래픽 적음Flyway 또는 dbmate
Java/Spring BootFlyway (또는 Liquibase) + Squawk
Node/TypeScript, 풀스택Prisma + Atlas(scale 시)
Node/TypeScript, edgeDrizzle Kit + Squawk
Python/DjangoDjango migrations
Python/FastAPIAlembic
RustDiesel 또는 SQLx + dbmate
Go, 새 프로젝트Atlas (선언적) 또는 dbmate (단순)
마이크로서비스 50+Bytebase + 각 서비스가 선택한 도구
대규모 Postgres, 무중단 필수pgroll + Squawk + Atlas
대규모 MySQL, 무중단 필수gh-ost + Flyway + Squawk-MySQL 대체
DBA 팀 있음, 리뷰 워크플로Bytebase 중심
Cross-DB (Postgres + MySQL + Oracle)Liquibase 또는 Bytebase
마이그레이션 1000+ 누적Sqitch (의존성 그래프)

흔한 안티패턴


14장 · 한국 / 일본 사례 — 토스 / 카카오 / Mercari

토스 (Toss)

토스는 2023년 컨퍼런스(SLASH)에서 gh-ost를 MySQL에서 광범위하게 사용한다고 공개. 결제 도메인은 24/7이라 락은 곧 매출 손실. gh-ost로 chunk 크기·throttle을 도메인별로 튜닝.

2025-2026년 토스의 일부 신규 Postgres 서비스에서는 pgroll 검토 중이라는 발표. Squawk는 모든 신규 마이그레이션 PR의 필수 CI 단계.

카카오

카카오는 메신저·페이·뱅크 각각 DB 전략이 다름. 카카오뱅크는 Oracle 기반이라 Liquibase + 자체 워크플로. 카카오 페이의 일부 신규 서비스는 Flyway + gh-ost(MySQL) 조합. 카카오 메신저는 자체 분산 storage라 일반 마이그레이션 도구 영역 밖.

2024년 카카오의 일부 팀이 Atlas를 도입해서 Go 백엔드를 통일했다는 이야기가 컨퍼런스에서 나왔음.

Mercari (일본)

Mercari는 micro-service 100+ 환경. 각 서비스가 도구 선택은 자율, 다만 lint(Squawk·자체 룰)는 공통 CI. Spanner를 쓰는 서비스가 많아서 Spanner CLI + 자체 마이그레이션 프레임워크.

Mercari Engineering blog에서 "Schema migration as code" 컨셉을 자주 언급 — Atlas/Bytebase 비슷한 선언적 모델을 사내에서 구현.

LY Corporation (LINE + Yahoo Japan)

2023년 합병 후 LINE 코어 백엔드의 DB 마이그레이션은 Flyway 중심으로 표준화 중. 일부 신규 Go 서비스는 Atlas. Squawk-MySQL 대체로 자체 룰을 추가한 sql-lint 도구를 사내에서 운영.

공통 패턴:

이게 2026년 한국·일본 빅테크의 흐름.


결론 — 도구는 많고, 패턴은 적다

긴 글을 한 장으로:

  1. 도구는 충분히 많다. Atlas, Flyway, Liquibase, Sqitch, Bytebase, pgroll, Squawk, gh-ost, pt-osc, Reshape, dbmate, golang-migrate, Diesel, Alembic, Prisma, Drizzle. 새로 하나 더 만들 필요 없음.
  2. 선택은 언어/팀 친화도로 시작. Java면 Flyway, Python이면 Alembic, Go면 Atlas/dbmate, TS면 Prisma/Drizzle.
  3. 무중단이 필요하면 패턴이 도구를 결정한다. expand-contract → pgroll(Postgres) / gh-ost(MySQL).
  4. Squawk(또는 동등 린터)는 디폴트. Postgres 쓰는 한 CI에 무조건.
  5. 포지티브 down은 환상. forward-only + 짧고 안전한 변경의 연속이 reversible보다 현실.
  6. 마이그레이션 도구는 안전을 보장 안 한다. expand-contract 패턴 + 린트 + 리뷰가 안전.

마지막: 5분 다운의 원인은 도구가 아니었다. ALTER COLUMN ... SET NOT NULL을 4억 행 테이블에 한 트랜잭션으로 돌린 패턴이 원인. 도구는 그대로 두고, 패턴을 expand-contract로 바꿨다면 5분이 0초가 됐을 것이다.


참고 / References

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다