데이터베이스 스키마 변경 관리의 기본
Source: Dev.to
릴리스된 애플리케이션을 업데이트할 때, 종종 이러한 스키마를 수정해야 합니다.
이러한 변경을 안전하고 효율적으로 관리하는 것은 기본적인 엔지니어링 과제입니다.
이 글에서는 스키마 관리에 대한 나의 접근 방식, 그 트레이드‑오프, 그리고 신뢰성을 높이기 위한 확장 전략을 설명합니다. 솔로프리뉴어로서 나는 단순함과 생산성을 우선시합니다. 나는 Java와 JMigrate 를 사용하지만, 이러한 개념은 Flyway, Liquibase, MyBatis Migrations 같은 모든 라이브러리와 Rails와 같은 언어 및 프레임워크에도 적용됩니다.
Source:
데이터베이스 스키마 변경 문제
user 테이블을 만든다고 가정해 보겠습니다:
CREATE TABLE "jmigrate_test_user" (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL,
hashed_password TEXT NOT NULL,
password_expired_at TIMESTAMP
);
일주일 후에 last_login 컬럼을 추가해야 한다면, 1999년식으로 SQL을 수동으로 실행할 수 있습니다. 하지만 수동 업데이트는 두 가지 치명적인 문제를 야기합니다:
- 버전 관리 부재 – 변경 사항이 추적되지 않습니다. 새 컬럼을 지원하도록 소스 코드가 언제부터 변경되었는지 파악할 수 없으며, 쉽게 되돌릴 수도 없습니다.
- 팀 동기화 실패 – 모든 팀원의 로컬 환경에 대해 수동 업데이트를 조율해야 하는데, 이는 오류가 발생하기 쉽고 비효율적입니다.
동료가 동시에 age 컬럼을 추가하려고 하면, 자동화가 없기 때문에 스키마 충돌이 발생하고 이를 해결하기가 어렵습니다.
해결 방법
모든 데이터베이스 변경은 Git에 커밋해야 합니다.
- 마이그레이션 파일을 전용 폴더에 저장합니다 (예:
migrations/). - 순차적인 파일명을 사용합니다:
1.sql,2.sql,3.sql, …
스키마를 수정하려면 순서대로 다음 파일을 추가하면 됩니다 (예:4.sql).
각 마이그레이션 스크립트는 두 개의 섹션을 포함합니다:
| 섹션 | 목적 |
|---|---|
| Up | 데이터베이스 스키마를 진전시키는 SQL |
| Down | Up 섹션에서 만든 변경을 되돌리는 SQL |
Note: 프로덕션 환경에서는 데이터 손실을 방지하기 위해 Down 스크립트를 금지해야 하지만, 개발 환경에서는 민첩성을 위해 실행하는 것이 필수적입니다.
JMigrate, Flyway, Liquibase, MyBatis Migrations와 같은 도구가 이 과정을 자동화합니다. JMigrate를 사용하면 애플리케이션 시작 시 JMigrate.migrate() 를 한 번 호출하여 모든 대기 중인 마이그레이션을 처리합니다.
실제 작동 방식
시나리오 1 – 마이그레이션 스크립트 반복
BIGINT(epoch 밀리초) 형식의 last_login 컬럼을 추가하고 싶습니다. 5.sql을 만들어요:
# --- !Ups
ALTER TABLE "user" ADD COLUMN "last_login" BIGINT;
# --- !Downs
ALTER TABLE "user" DROP COLUMN "last_login";
스크립트를 실행한 뒤 TIMESTAMP 형식이 더 적합하다는 것을 깨달았습니다. 5.sql을 수정합니다:
# --- !Ups
ALTER TABLE "user" ADD COLUMN "last_login" TIMESTAMP;
# --- !Downs
ALTER TABLE "user" DROP COLUMN "last_login";
개발 환경에서 JMigrate는 수정 사항을 감지합니다. 이전 Down 스크립트(DROP COLUMN)를 자동으로 실행한 뒤, 수정된 Up 스크립트(ADD COLUMN … TIMESTAMP)를 실행해 로컬 데이터베이스를 코드와 동기화합니다.
시나리오 2 – 두 개발자가 동시에 마이그레이션 수행
- 마지막으로 적용된 마이그레이션이
4.sql이었으므로5.sql을 추가합니다. - 동료도
5.sql을 추가하고 먼저 병합합니다.
Git이 충돌을 보고합니다. 파일 이름을 6.sql로 바꿔 충돌을 해결합니다.
각 개발자의 로컬 환경에서는 JMigrate가 자동으로 5.sql(동료의 파일) 뒤에 6.sql(본인의 파일)을 차례대로 실행하므로, 데이터베이스가 수동 개입 없이 동기화됩니다.
시나리오 3 – 과거 마이그레이션을 실수로 수정했을 때
이미 배포된 3.sql을 편집했습니다. “프로덕션에서 다운 스크립트 실행”이 비활성화되어 있기 때문에 JMigrate는 예외를 발생시키고 배포를 중단합니다. 이는 가장 안전한 결과이며, 알림을 받고 잘못된 변경을 되돌린 뒤 올바른 수정을 만들어 재배포할 수 있습니다.
마이그레이션을 더 신뢰할 수 있게 만들기
대규모 환경에서 자동 마이그레이션 프로세스를 진행하면 비역호환성 변경이 다운타임을 초래한다는 결함이 드러납니다.
컬럼 이름을 바꾸는 것은 전형적인 예입니다. name을 full_name으로 바꾸면, 재배포될 때까지 기존 애플리케이션 인스턴스는 계속해서 name을 조회하게 되고, 이로 인해 런타임 예외가 발생합니다.
많은 엔지니어(예: Stripe, Google)는 이름 변경을 전혀 하지 않고 “잘못된” 이름을 유지하는 방식을 선호합니다. 이름 변경이 불가피할 경우, 다단계 배포를 사용해 가용성을 유지합니다:
- 새 컬럼을 추가하고 배포합니다.
- 구 컬럼과 새 컬럼 모두에 쓰기를 수행하고 배포합니다.
- 구 컬럼의 데이터를 새 컬럼으로 백필합니다(대용량 테이블의 경우 며칠이 걸릴 수 있음).
- 새 컬럼만 읽도록 전환하고 배포합니다.
- 구 컬럼을 삭제하고 배포합니다.
JMigrate: Java용 간단한 데이터베이스‑스키마 마이그레이션 라이브러리
저는 더 큰 도구들에 대한 가벼운 대안을 제공하기 위해 JMigrate를 만들었습니다.
| 기능 | JMigrate | 대안 (Flyway, Liquibase, MyBatis) |
|---|---|---|
| 단순성 | ✅ | ❌ |
| 설정 없음 | ✅ | ❌ |
| Git 친화적인 순차 스크립트 | ✅ | ✅ |
| 스크립트 수정 자동 감지 (개발 전용) | ✅ | ❌ |
| 내장 “up/down” 섹션 | ✅ | ✅ |
| 프로덕션 안전 (down 스크립트 비활성화) | ✅ | ✅ |
(표는 필요에 따라 추가 행을 확장할 수 있습니다.)
TL;DR
- 스키마 변경을 모두 번호가 매겨진 SQL 파일로 버전 관리에 저장합니다.
- 각 파일에 up 및 down 섹션을 유지합니다.
- 시작 시 자동으로 마이그레이션을 실행합니다 (예:
JMigrate.migrate()). - 비역방향 호환 변경의 경우 다단계 배포를 사용합니다.
이 패턴을 따르면 언어와 프레임워크에 관계없이 데이터베이스 스키마를 신뢰할 수 있고, 감사 가능하며, 협업적인 방식으로 진화시킬 수 있습니다.
마이그레이션 도구 개요
| 기능 | JMigrate | Flyway | Liquibase |
|---|---|---|---|
| 함수 호출 | 단일 함수 호출로 모든 마이그레이션을 처리합니다. | 종종 복잡한 구성이 필요합니다. | 종종 복잡한 구성이 필요합니다. |
| 통합 | 순수 Java; 애플리케이션 내에서 실행됩니다. | 별도의 CLI가 필요하며, Heroku 및 Render.com과 같은 플랫폼에서 제한될 수 있습니다. | 별도의 CLI가 필요하며, Heroku 및 Render.com과 같은 플랫폼에서 제한될 수 있습니다. |
| 크기 | 14 KB | 800 KB (Flyway) | 3 MB (Liquibase) |
사용 사례 가이드
- JMigrate는 파일 크기가 최소이고 아키텍처가 단순해야 하는 데스크톱 및 자체 호스팅 애플리케이션에 이상적입니다.
- 대규모 서버 측 애플리케이션—팀이 자체 배포를 관리하는 경우—는 일반적으로 방대한 기능 세트를 우선시하므로 Flyway 또는 Liquibase가 더 적합합니다.
요약
데이터베이스 스키마 마이그레이션을 관리하는 것은 기본적인 엔지니어링 책임입니다. 최신 라이브러리들이 모범 사례를 자동화해 주지만, 엔지니어는 마이그레이션 실패와 같은 예외 상황을 해결하기 위해 기본 메커니즘을 이해해야 합니다.
현재 표준은 동시 개발, 철저한 테스트, 그리고 원활한 배포를 지원합니다. JMigrate, Flyway, Liquibase, 또는 MyBatis 중 어느 것을 선택하든, 이제 자신 있게 스키마 변경을 관리할 수 있습니다.