운영 중인 테이블을 바꾸는 순서 — 컬럼 삭제는 마지막에
스키마 변경과 배포 순서가 어긋나면 장애가 난다. 무중단으로 진행하는 단계별 절차.
배포 순간에 두 버전이 함께 돈다
무중단 배포에서는 구버전 인스턴스와 신버전 인스턴스가 잠시 공존한다. 그래서 스키마 변경을 한 번에 하면 한쪽이 깨진다.
```
컬럼명 변경: user_name → username
구버전 코드: user_name 참조 → 컬럼 없음 → 오류
```
원칙은 하나다. 모든 중간 상태가 양쪽 버전과 호환돼야 한다.
컬럼 이름 변경 — 4단계
한 번에 바꾸지 않고 나눈다.
1단계: 새 컬럼 추가 (nullable)
```sql
ALTER TABLE users ADD COLUMN username VARCHAR(255);
```
구버전은 이 컬럼을 모르므로 영향 없음.
2단계: 양쪽 쓰기 배포
코드가 두 컬럼에 모두 쓴다. 읽기는 아직 옛 컬럼.
3단계: 데이터 이관 + 읽기 전환
```sql
UPDATE users SET username = user_name WHERE username IS NULL;
```
이후 읽기를 새 컬럼으로 바꾼 코드 배포.
4단계: 옛 컬럼 삭제
충분한 기간(보통 한 배포 주기 이상) 뒤에 삭제한다.
```sql
ALTER TABLE users DROP COLUMN user_name;
```
4단계를 서두르면 롤백이 불가능해진다. 3단계 배포에 문제가 생겨 되돌려야 할 때, 옛 컬럼이 남아 있어야 구버전이 동작한다.
NOT NULL 컬럼 추가
기존 행에 값이 없으므로 바로 NOT NULL을 걸 수 없다.
```
- nullable로 추가
- 코드가 새 행에 값을 넣도록 배포
- 기존 행을 배치로 채움 (큰 테이블은 나눠서)
- NOT NULL 제약 추가
```
3번을 한 번의 UPDATE로 처리하면 큰 테이블에서 락이 오래 걸린다. 배치 크기를 나눠 반복하는 편이 안전하다.
인덱스 추가
큰 테이블에 인덱스를 만들면 그동안 쓰기가 막힐 수 있다. DB에 따라 온라인 생성 옵션이 있다.
```sql
-- PostgreSQL
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
```
온라인 생성은 느리지만 락을 오래 잡지 않는다. 운영 중이라면 이쪽을 쓴다.
타입 변경
가장 위험한 축에 속한다. 대개 컬럼 이름 변경과 같은 4단계 방식으로 처리한다 — 새 타입의 컬럼을 추가하고, 양쪽 쓰기, 이관, 전환, 삭제.
VARCHAR(50) → VARCHAR(100)처럼 확장 방향은 대체로 안전하지만, 축소나 타입 자체 변경은 데이터 손실 위험이 있어 단계를 나눈다.
체크리스트
변경 전에 확인:
- [ ] 이 변경이 구버전 코드와 호환되는가
- [ ] 롤백이 가능한가 (코드만 되돌려도 동작하는가)
- [ ] 락이 얼마나 걸리는가 (큰 테이블인가)
- [ ] 배치가 필요하다면 크기를 나눴는가
- [ ] 삭제 단계를 충분히 미뤘는가
특히 두 번째가 핵심이다. 롤백 불가능한 변경은 배포 시간대를 조정하거나 별도 계획이 필요하다.
기록
마이그레이션은 되돌리기 어려운 변경이므로 근거를 남긴다.
```markdown
2026-08-28 users.user_name → username
- 이유: 코드 컨벤션 통일
- 단계: 4단계 진행 중 (현재 2단계)
- 옛 컬럼 삭제 예정: 9월 셋째 주 (2주 관찰 후)
- 롤백: 3단계까지는 코드 롤백만으로 가능
```
여러 배포에 걸치므로, 지금 어느 단계인지가 팀에 보여야 한다.
최종 수정 2026-08-28