컬럼 이름을 바꾸는 배포 한 번으로 서비스가 잠깐 죽었던 이유
문제 발생
컬럼 이름을 user_name에서 username으로 바꾸는 마이그레이션과, 그 새 컬럼명을 쓰는 애플리케이션 코드를 한 번의 배포로 함께 내보냈는데, 배포 도중(여러 인스턴스가 순차적으로 교체되는 롤링 배포 구간) 일부 요청이 실패했습니다.
원인 분석
롤링 배포는 여러 서버 인스턴스를 한 번에 전부 교체하지 않고, 하나씩 순차적으로 새 버전으로 바꿔갑니다 — 그 과정 중에는 구 버전 코드와 신 버전 코드가 동시에 실행되는 구간이 반드시 존재합니다.
마이그레이션이 ALTER TABLE ... RENAME COLUMN처럼 컬럼을 즉시 제거하고 새 이름으로 바꾸는 방식이라면, 이 전환 구간 동안 아직 교체되지 않은 구 버전 코드는 여전히 user_name을 참조하려 하지만 그 컬럼은 이미 사라진 뒤입니다 — 쿼리가 실패합니다. 신 버전이 배포되기 전에 마이그레이션이 먼저 실행되는 것이 일반적인 순서이므로, 이 창(window)은 배포 전략과 무관하게 거의 항상 존재합니다.
해결 방안
스키마 변경을 한 번에 끝내지 않고 여러 단계로 나눠, 각 단계마다 신구 버전 코드가 모두 정상 동작하도록 만듭니다 — 이를 흔히 expand-contract 패턴이라 부릅니다.
- Expand(확장): 기존 컬럼은 그대로 두고 새 컬럼을 추가만 합니다. 이 시점에는 구/신 버전 코드 둘 다 각자 원하는 컬럼을 그대로 쓸 수 있습니다.
ALTER TABLE users ADD COLUMN username VARCHAR(255);- 이중 쓰기(dual write) 또는 백필: 애플리케이션 코드가 두 컬럼에 동시에 쓰도록 하거나, 별도 배치로 기존 데이터를 새 컬럼에 채웁니다.
- 코드 전환: 애플리케이션을 새 컬럼(
username)만 읽도록 배포합니다 — 이 시점에는 이미 두 컬럼 다 최신 값을 갖고 있으므로 안전합니다. - Contract(축소): 모든 인스턴스가 새 버전으로 완전히 교체된 것을 확인한 뒤에야, 별도의 다음 배포로 옛 컬럼(
user_name)을 제거합니다.
ALTER TABLE users DROP COLUMN user_name; -- 충분히 시간이 지난 뒤, 별도 배포로- **핵심은 "스키마 변경과 코드 변경이 항상 한 단계씩 어긋나도 괜찮게 만드는 것"**입니다 — 어느 시점에 마이그레이션과 배포 중 무엇이 먼저 끝나든, 그 사이 구간에서 구버전이든 신버전이든 정상 동작해야 합니다.
- 컬럼 추가/삭제뿐 아니라 NOT NULL 제약 추가, 타입 변경도 같은 원칙이 적용됩니다 — 기존 데이터/코드와 즉시 호환되지 않는 변경은 예외 없이 단계로 나누는 것을 우선 검토합니다.
- 이 프로젝트처럼
drizzle-kit generate로 마이그레이션을 만드는 워크플로우에서도, 생성된 SQL이 파괴적인 변경(컬럼/테이블 삭제, NOT NULL 즉시 추가 등)을 포함하는지 항상 리뷰하고, 필요하면 여러 개의 작은 마이그레이션으로 직접 나눕니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.