무중단 배포에서 컬럼 이름 바꾸기 — 확장·이전·정리 5단계

컬럼 이름 하나 바꾸고 배포했을 뿐인데 운영에서 몇 분간 오류가 쏟아졌어요. 스테이징에선 멀쩡했고요. 배포 중 옛 코드와 새 스키마가 함께 도는 구간을 안전하게 넘는 다섯 단계를 담았어요.

컬럼 이름을 바꾸는 마이그레이션을 돌리고 새 코드를 배포했어요. 스테이징에서는 완벽했는데 운영에서 몇 분간 오류가 쏟아졌어요.

원인은 그 몇 분이에요. 마이그레이션은 끝났는데 배포는 진행 중이라, 옛 코드가 도는 인스턴스와 새 코드가 도는 인스턴스가 섞여 있었어요.

옛 코드는 없어진 컬럼을 찾고, 새 코드는 새 컬럼을 찾아요. 둘 중 하나는 반드시 실패해요. 그리고 그 몇 분은 트래픽이 그대로 들어오는 몇 분이라 오류 수가 적지 않아요.

스테이징에서 안 잡힌 이유도 같아요. 거기서는 배포가 순식간에 끝나고 트래픽도 없거든요. 공존 구간이 사실상 0이었던 거예요.

배포는 순간이 아니라 구간이에요. 그 구간 동안 옛 코드와 새 스키마가 공존한다는 걸 전제로 설계하지 않으면 매번 이 오류가 납니다.

왜 그런가

무중단 배포는 인스턴스를 하나씩 교체해요. 그래서 배포 중에는 반드시 두 버전이 동시에 돌아요. 몇 초일 수도 몇 분일 수도 있지만 0 은 아니에요.

그런데 DB 는 하나예요. 스키마는 한 순간에 바뀌고, 두 버전이 같은 스키마를 봐요.

여기서 조합이 생겨요. 옛 코드 + 옛 스키마(정상), 새 코드 + 새 스키마(정상), 그리고 옛 코드 + 새 스키마 또는 새 코드 + 옛 스키마. 뒤의 둘 중 하나가 배포 구간에 반드시 존재해요.

마이그레이션을 먼저 하면 옛 코드가 새 스키마를 만나요. 컬럼이 사라졌거나 이름이 바뀌었으면 옛 코드가 깨져요.

배포를 먼저 하면 새 코드가 옛 스키마를 만나요. 새 컬럼이 아직 없으니 새 코드가 깨지고요.

어느 순서든 깨져요. 그래서 순서를 고르는 문제가 아니라 양쪽 모두와 호환되는 중간 상태를 만드는 문제예요.

롤백까지 생각하면 더 분명해져요. 새 코드를 되돌려도 스키마는 안 되돌아가요. 롤백한 옛 코드가 새 스키마 위에서 돌아야 하니, 그 조합이 애초에 동작해야 합니다.

코드로 보면

한 번에 하지 말고 여러 배포로 나눈다:

코드
 배포 1회로 컬럼 이름 바꾸기
 ALTER TABLE … RENAME COLUMN a TO b;  +  새 코드
 → 배포 구간에 옛 코드가 b 를 모른다

· 확장 → 이전 → 정리, 세 번의 배포로 나눈다
 1) 확장: 새 컬럼 b 를 추가 (a 는 그대로)
 2) 배포: 코드가 a 와 b 를 둘 다 쓴다 (b 에 쓰고 a 에도 쓴다)
 3) 이전: 기존 데이터를 a → b 로 채운다 (배치)
 4) 배포: 코드가 b 만 쓴다
 5) 정리: a 를 지운다

· 각 단계 사이에 롤백이 가능해야 한다
 4단계 배포를 되돌려도 3단계 상태가 정상이어야 한다

✕ 문제 지점 · ✓ 해법 · 그대로 돌려 보기 전에 자기 환경에 맞게 고쳐 주세요.

실전에서는

표준 절차가 확장·이전·정리 세 단계예요.

확장 단계에서는 더하기만 해요. 새 컬럼을 추가하고, 새 테이블을 만들고, 기본값을 준비해요. 지우거나 이름을 바꾸는 건 하지 않아요. 그래서 옛 코드가 아무 영향을 안 받아요.

이전 단계에서는 코드가 양쪽을 다 쓰게 만들어요. 읽을 때는 새 것을 우선하되 없으면 옛 것을 보고, 쓸 때는 양쪽에 다 써요. 이 상태에서 기존 데이터를 배치로 옮기고요. 이 단계가 가장 길고, 그동안 두 버전이 공존해도 문제가 없어요.

정리 단계에서는 옛 것을 지워요. 모든 인스턴스가 새 코드로 바뀌고, 데이터도 다 옮겨졌고, 롤백할 일이 없다고 확신한 뒤에요.

각 단계는 별도의 배포여야 해요. 한 배포에 두 단계를 넣으면 그 안에서 다시 순서 문제가 생겨요.

여기서 중요한 원칙이 있어요. 모든 스키마 변경은 이전 버전 코드와 호환되어야 해요. 컬럼 추가는 안전하고, 컬럼 삭제와 이름 변경은 안전하지 않아요. 제약 추가도 위험한데, 옛 코드가 그 제약을 모르고 쓰다가 실패하거든요.

되돌릴 수 없는 것도 구분해야 해요. 컬럼을 지우면 데이터가 사라져요. 롤백해도 안 돌아와요. 그래서 정리 단계는 특히 신중해야 하고, 지우기 전에 백업이나 보관 기간을 두는 게 좋습니다.

절차가 번거로워 보이지만, 이걸 안 하면 매 배포마다 짧은 장애를 감수하거나 서비스를 멈춰야 해요.

처방

  1. 스키마 변경과 코드 배포를 같은 배포에 넣지 마세요. 별도 배포로 나누세요.
  2. 확장·이전·정리 세 단계를 따르세요. 더하기 → 양쪽 사용 → 지우기 순서입니다.
  3. 모든 스키마 변경이 이전 버전 코드와 호환되는지 확인하세요. 컬럼 추가는 안전, 삭제·이름 변경은 위험합니다.
  4. 각 단계에서 롤백이 가능한지 검토하세요. 되돌려도 동작해야 합니다.
  5. 제약 추가는 두 단계로 하세요. 검증 없이 먼저 붙이고 나중에 검증하는 방식이 있습니다.
  6. 정리 단계를 잊지 마세요. 확장만 하고 정리를 안 하면 옛 컬럼이 영원히 남습니다.
  7. 지우기 전에 보관 기간을 두세요. 컬럼 삭제는 되돌릴 수 없습니다.
  8. 마이그레이션 검토를 코드 리뷰에 포함하세요. "이 변경이 옛 코드에서 동작하는가"를 묻는 항목을 만드세요.
  9. 조회할 컬럼을 명시하세요. 전체를 가져오는 코드는 컬럼 추가만으로도 깨질 수 있습니다.
  10. 스키마가 바뀔 때 캐시를 함께 처리하세요. 옛 형식으로 직렬화된 값이 남아 있으면 새 코드가 깨집니다.

지금 확인해 보세요

옛 코드에서도 동작하는가:

코드
· 마이그레이션 검토 항목
 이 변경이 현재 운영 중인 코드에서 동작하는가
 이 배포를 롤백했을 때 동작하는가
 되돌릴 수 없는 변경(삭제)이 포함돼 있는가

· 배포 중 오류율을 시각과 겹쳐 본다
 배포 구간에만 봉우리가 생기면 이 문제다

· 정리되지 않은 옛 컬럼 찾기
 코드베이스에서 참조되지 않는 컬럼을 주기적으로 훑는다
 확장만 하고 정리를 안 하면 계속 쌓인다

· 캐시에 옛 형식이 남아 있지 않은지
 스키마가 바뀌면 캐시 키에 버전을 넣거나 비운다

핵심 한 줄배포는 순간이 아니라 구간이다. 그 구간에 두 버전이 공존한다.

자주 묻는 것

컬럼 이름을 무중단으로 바꾸려면 어떻게 하나요?

한 배포로 하지 말고 나눠요. 새 컬럼 추가, 코드가 양쪽에 쓰기, 기존 데이터 옮기기, 새 컬럼만 쓰기, 옛 컬럼 삭제 순으로 하고 각 단계를 별도 배포로 해요.

브루니가 적어요

색을 바꾸며 숨는 카멜레온이에요. 운영에서 실제로 터지는 문제를 한 편씩 적어요. 같은 실수를 두 번 하지 않으려고 쓰는 기록이에요.

배포·관측 글 16편 모두 보기 →