설계·개발

DB 스키마 마이그레이션 안전하게 하기: 운영 중인 서비스에서 컬럼 추가·삭제 체크리스트

운영 중인 서비스의 DB 스키마를 무중단에 가깝게 변경하는 추가·백필·전환·삭제 순서와 롤백 가능한 설계 체크리스트를 정리했습니다.

4분 읽기조코헌트 운영팀
목차

왜 컬럼 변경은 배포보다 어렵나

코드 배포는 이전 버전으로 되돌리면 끝나는 것처럼 보이지만, DB 스키마는 한 번 바뀌면 이미 저장된 데이터와 연결됩니다. 애플리케이션 서버가 여러 대라면 새 코드와 옛 코드가 잠시 동시에 실행될 수도 있습니다.

읽기와 쓰기 호환성

마이그레이션의 첫 질문은 “새 구조가 옳은가?”보다 “구버전 코드가 실행 중이어도 깨지지 않는가?”입니다. 새 컬럼을 추가할 때는 기존 쿼리가 계속 동작하는지, 기본값과 NULL 허용 여부가 예상과 맞는지 확인하세요. 반대로 컬럼을 바로 삭제하거나 이름을 바꾸면 아직 옛 컬럼을 읽는 코드가 실패할 수 있습니다.

API 계약이나 데이터 접근 계층이 여러 곳에 흩어져 있다면 모놀리식으로 시작하라에서 다룬 것처럼 변경 지점을 한곳에 모으는 편이 추적하기 쉽습니다.

롤백의 기준

롤백은 “마이그레이션을 취소하는 명령”만을 뜻하지 않습니다. 이미 새 컬럼에 기록된 값, 백필된 데이터, 새 코드가 만든 외부 효과까지 원상복구할 수 있는지까지 봐야 합니다. 그래서 운영 마이그레이션은 작은 단계로 나누고, 각 단계 사이에 이전 버전 코드가 계속 읽을 수 있는 상태를 유지하는 것이 안전합니다.

무중단 마이그레이션 4단계

컬럼 추가부터 옛 구조 삭제까지 이어지는 4단계 DB 마이그레이션 흐름도

한 번에 구조를 갈아엎기보다 호환되는 중간 상태를 거치는 방식이 실전에서 관리하기 쉽습니다.

1. 컬럼 추가

먼저 새 컬럼이나 새 테이블을 추가합니다. 기존 컬럼은 그대로 둡니다. 새 컬럼이 당장 모든 행에 값을 가져야 한다면, 테이블 규모와 DB 엔진의 동작을 확인한 뒤 잠금 시간이 길어지지 않는 방식을 선택하세요. 운영 DB에서 대규모 기본값 변경을 한 번에 실행하는 것은 특히 주의가 필요합니다.

2. 백필

기존 데이터를 새 구조로 옮깁니다. 전체 행을 한 번에 처리하기보다 ID 범위나 시간 단위로 나누고, 각 묶음이 끝날 때 진행 상황과 실패 지점을 기록하세요. 백필 중에도 새 데이터가 들어오므로, 애플리케이션 쓰기 로직을 먼저 보강하거나 변경분을 다시 처리하는 전략이 필요합니다.

3. 애플리케이션 전환

새 코드가 새 컬럼에 쓰고 읽도록 전환합니다. 가능하면 처음에는 기존 컬럼에도 함께 기록하는 이중 쓰기를 두어 데이터 차이를 비교하세요. 이후 일정 기간 읽기 경로를 새 컬럼으로 옮기고, 오류율과 NULL 비율을 확인합니다. 코드 변경 전후의 규칙은 AI 코딩 도구에 컨텍스트 잘 주는 법처럼 문서화해 두면 자동화 도구가 구·신 필드를 섞어 쓰는 실수를 줄일 수 있습니다.

4. 옛 구조 삭제

새 코드가 안정적으로 동작하고 더 이상 옛 컬럼을 읽거나 쓰는 경로가 없을 때 삭제를 검토합니다. 삭제는 가장 마지막 단계입니다. 먼저 코드 검색, 쿼리 로그, 배치 작업, 관리자 도구까지 확인한 뒤 별도 배포로 실행하세요.

마이그레이션 파일을 실행하기 전 체크

DB 마이그레이션 실행 전 확인할 호환성·백필·잠금·롤백 체크 항목 도식

데이터와 쿼리 점검

다음 질문에 답할 수 있어야 합니다.

  • NULL이 허용되는가? 기존 행에는 어떤 값이 들어가는가?
  • 새 컬럼을 읽는 코드와 옛 컬럼을 읽는 코드가 동시에 실행되어도 괜찮은가?
  • 인덱스 추가가 쓰기 성능과 잠금에 미칠 영향은 무엇인가?
  • 백필을 중단했다가 다시 실행해도 중복이나 데이터 손상이 없는가?
  • 외래 키, 유니크 제약, 트리거, 배치 작업이 함께 영향을 받는가?

스키마 변경 전후의 쿼리를 비교하고, 개발 DB에서만 성공한 것이 아니라 운영과 비슷한 데이터 분포에서도 처리 시간을 확인하세요. DB 선택과 확장 시점은 데이터베이스 PostgreSQL vs MySQL vs SQLite 같은 기준표를 참고해 현재 스택에 맞게 판단하면 됩니다.

운영 시간과 관측

트래픽이 낮은 시간에 실행하더라도 잠금과 부하가 사라지는 것은 아닙니다. 실행 전에는 백업 상태, 디스크 여유, 연결 수, 오류 알림을 확인하고, 실행 중에는 쿼리 시간·잠금 대기·에러 로그를 관찰하세요. 중단 기준도 미리 정합니다. 예를 들어 오류가 특정 수준을 넘거나 지연이 계속 증가하면 다음 단계로 진행하지 않는 식입니다.

흔한 함정과 예방책

첫째, 마이그레이션 파일을 수정해 이미 실행된 이력을 바꾸는 실수입니다. 실행된 파일은 보존하고, 수정이 필요하면 새 마이그레이션을 추가하세요. 둘째, 로컬에서는 빠른 DDL이 운영에서도 빠를 것이라고 가정하는 경우입니다. 테이블 크기와 동시 접속에 따라 결과가 달라질 수 있습니다. 셋째, 백필을 애플리케이션 요청 안에서 처리하는 방식입니다. 요청 지연과 재시도 중복이 생길 수 있으므로 별도 작업으로 분리하는 편이 낫습니다.

바로 쓰는 실행 체크리스트

스키마 변경을 안전하게 진행하기 위한 최종 점검 게이트

  • 변경 목적과 영향을 받는 테이블·쿼리를 문서화했다.
  • 구버전 코드와 신버전 코드가 공존하는 시간을 고려했다.
  • 추가, 백필, 전환, 삭제를 별도 단계로 나눴다.
  • 백필을 재실행해도 안전한지 확인했다.
  • 운영과 비슷한 데이터 규모로 실행 시간을 확인했다.
  • 오류·잠금·지연을 관찰할 지표와 중단 기준을 정했다.
  • 옛 컬럼을 참조하는 코드와 작업을 모두 검색했다.
  • 삭제 전 복구 방법과 보존 기간을 정했다.

가장 좋은 마이그레이션은 빠른 변경이 아니라, 문제가 생겨도 어느 단계에서 멈췄는지 알고 다시 진행할 수 있는 변경입니다. 스키마를 데이터와 코드의 공동 계약으로 보고, 한 단계씩 호환성을 확인하면 1인 개발자도 운영 중인 서비스를 비교적 안전하게 진화시킬 수 있습니다.

이 글 공유하기

같은 주제를 다룬 다른 글도 살펴보세요.