메인 콘텐츠로 건너뛰기
생성된 Migration 파일을 데이터베이스에 적용하는 방법입니다.

실행 명령어

migrate run

모든 Migration 실행DB 최신 상태로

migrate status

실행 상태 확인pending 목록

migrate latest

최신까지 실행run과 동일

migrate up

한 번에 하나씩단계별 실행

기본 실행

모든 Migration 실행

pnpm sonamu migrate run
실행 결과:
Batch 1 run: 3 migrations
✅ 20251220143022_create__users.ts
✅ 20251220143100_create__posts.ts
✅ 20251220143200_foreign__posts__user_id.ts
migrate run은 아직 실행되지 않은 모든 Migration을 순서대로 실행합니다.

실행 상태 확인

pnpm sonamu migrate status
출력 예시:
Executed migrations:
  ✅ 20251220143022_create__users.ts
  ✅ 20251220143100_create__posts.ts

Pending migrations:
  ⏳ 20251220143200_alter_users_add1.ts
  ⏳ 20251220143300_alter_posts_add1.ts

실행 순서

Migration은 파일명의 타임스탬프 순서로 실행됩니다:
1. 20251209160740_create__companies.ts
2. 20251209160741_create__departments.ts
3. 20251209160742_create__employees.ts
4. 20251209160750_foreign__departments__company_id.ts
5. 20251209160751_foreign__employees__user_id.ts

단계별 실행

한 번에 하나씩

pnpm sonamu migrate up
첫 번째 실행:
Batch 1 run: 1 migration
✅ 20251220143022_create__users.ts
두 번째 실행:
Batch 2 run: 1 migration
✅ 20251220143100_create__posts.ts
migrate up은 pending 중 가장 오래된 Migration 하나만 실행합니다.

Batch 시스템

Batch란?

함께 실행된 Migration 그룹입니다:
SELECT * FROM knex_migrations ORDER BY id;
idnamebatchmigration_time
120251220143022_create__users.ts12025-12-20 14:30:22
220251220143100_create__posts.ts12025-12-20 14:30:22
320251220143200_alter_users_add1.ts22025-12-20 15:00:00

Batch의 역할

  • 롤백 단위: 같은 batch의 Migration은 함께 롤백됩니다
  • 실행 그룹: migrate run으로 한 번에 실행하면 같은 batch
# Batch 1: 한 번에 2개 실행
pnpm sonamu migrate run
 20251220143022_create__users.ts     (batch 1)
 20251220143100_create__posts.ts     (batch 1)

# Batch 2: 나중에 1개 더 실행
pnpm sonamu migrate run
 20251220143200_alter_users_add1.ts  (batch 2)

환경별 실행

개발 환경

# .env.development
DATABASE_URL=postgresql://user:pass@localhost:5432/dev_db

pnpm sonamu migrate run

스테이징 환경

# .env.staging
DATABASE_URL=postgresql://user:pass@staging-db:5432/staging_db

NODE_ENV=staging pnpm sonamu migrate run

프로덕션 환경

# .env.production
DATABASE_URL=postgresql://user:pass@prod-db:5432/prod_db

NODE_ENV=production pnpm sonamu migrate run
프로덕션 실행 전 주의사항:
  1. 스테이징에서 먼저 테스트
  2. 백업 완료 확인
  3. 다운타임 계획
  4. 롤백 계획 준비

트랜잭션

자동 트랜잭션

각 Migration은 별도 트랜잭션으로 실행됩니다:
// 20251220143022_create__users.ts
export async function up(knex: Knex): Promise<void> {
  // 이 함수 전체가 하나의 트랜잭션
  await knex.schema.createTable("users", (table) => {
    table.increments().primary();
    table.string("email", 255).notNullable();
  });
  
  // 에러 발생 시 위 CREATE TABLE도 롤백됨
  await knex.schema.createTable("profiles", (table) => {
    table.increments().primary();
    table.integer("user_id").notNullable();
  });
}

트랜잭션 비활성화

// knexfile.ts에서 설정
export default {
  migrations: {
    disableTransactions: true,  // 트랜잭션 비활성화
  },
};
일부 DDL 작업은 트랜잭션을 지원하지 않을 수 있습니다.

에러 처리

실행 중 에러

pnpm sonamu migrate run
에러 발생:
Batch 1 run: 3 migrations
✅ 20251220143022_create__users.ts
✅ 20251220143100_create__posts.ts
❌ 20251220143200_alter_users_add1.ts
   Error: column "phone" already exists
처리 방법:
  1. Migration 파일 수정
  2. 다시 실행 시도

부분 실패

Batch 1 run: 2 migrations (1 successful, 1 failed)
✅ 20251220143022_create__users.ts     (committed)
❌ 20251220143100_create__posts.ts     (rolled back)
Migration이 실패하면:
  • 해당 Migration만 롤백
  • 이전 Migration은 유지
  • knex_migrations 테이블에 기록 안 됨

실행 로그 확인

상세 로그

pnpm sonamu migrate run --verbose
출력:
Running migration: 20251220143022_create__users.ts
SQL: CREATE TABLE "users" (
  "id" serial primary key,
  "email" varchar(255) not null
)
✅ Completed in 45ms

Running migration: 20251220143100_create__posts.ts
SQL: CREATE TABLE "posts" (
  "id" serial primary key,
  "title" varchar(200) not null
)
✅ Completed in 32ms

Total time: 77ms

실행 기록 조회

-- 모든 실행 기록
SELECT * FROM knex_migrations 
ORDER BY migration_time DESC;

-- 최근 batch
SELECT * FROM knex_migrations 
WHERE batch = (SELECT MAX(batch) FROM knex_migrations);

-- 특정 Migration 확인
SELECT * FROM knex_migrations 
WHERE name LIKE '%users%';

CI/CD 통합

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  migrate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      
      - name: Install dependencies
        run: pnpm install
      
      - name: Run migrations
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: pnpm sonamu migrate run

Docker

# Dockerfile
FROM node:20-alpine

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install

COPY . .

# Migration 실행
CMD ["pnpm", "sonamu", "migrate", "run"]

다운타임 최소화

Blue-Green Deployment

# 1. Blue 환경 (현재 운영 중)
DATABASE_URL=postgresql://blue-db pnpm start

# 2. Green 환경에 Migration 적용
DATABASE_URL=postgresql://green-db pnpm sonamu migrate run

# 3. Green 환경 테스트
DATABASE_URL=postgresql://green-db pnpm test

# 4. 트래픽 전환 (Blue → Green)
# 5. Blue 환경 업데이트
DATABASE_URL=postgresql://blue-db pnpm sonamu migrate run

호환성 유지

// Step 1: 새 컬럼 추가 (nullable)
export async function up(knex: Knex): Promise<void> {
  await knex.schema.alterTable("users", (table) => {
    table.string("new_field").nullable();  // ← nullable
  });
}

// Step 2: 애플리케이션 배포 (new_field 사용)

// Step 3: 데이터 마이그레이션

// Step 4: NOT NULL 제약 추가
export async function up(knex: Knex): Promise<void> {
  await knex.raw(
    `ALTER TABLE users ALTER COLUMN new_field SET NOT NULL`
  );
}

실전 팁

1. 실행 전 확인

# 상태 확인
pnpm sonamu migrate status

# pending Migration 검토
cat src/migrations/20251220143200_alter_users_add1.ts

# 스테이징에서 테스트
NODE_ENV=staging pnpm sonamu migrate run

2. 백업

# PostgreSQL 백업
pg_dump -U postgres -d mydb > backup_$(date +%Y%m%d_%H%M%S).sql

# Migration 실행
pnpm sonamu migrate run

# 문제 발생 시 복원
psql -U postgres -d mydb < backup_20251220_143000.sql

3. 모니터링

// Migration 실행 시간 측정
const startTime = Date.now();

await knex.migrate.latest();

const duration = Date.now() - startTime;
console.log(`Migration completed in ${duration}ms`);

다음 단계