웹 개발
배포 전에 데이터베이스 제약을 꼭 확인하자
unique 제약이나 foreign key 때문에 배포 후 데이터 입력이 실패할 수 있다. 배포 전 마이그레이션을 검토해야 한다.
데이터베이스에 새로운 제약을 추가하고 배포했는데, 갑자기 에러가 난다고 하면 보통 기존 데이터가 그 제약을 만족하지 않아서다.
어떤 제약이 추가될 예정인지 확인하기
먼저 마이그레이션 파일을 본다.
# 대기 중인 마이그레이션 확인
pg_dump -s mydb | grep CONSTRAINT
# 또는 마이그레이션 파일 직접 확인
cat migrations/20240630_add_unique_email.sql
마이그레이션 파일에 ADD CONSTRAINT 또는 ADD UNIQUE 같은 명령이 있다면, 기존 데이터와 충돌할 수 있다.
기존 데이터가 제약을 만족하는지 확인하기
# unique 제약을 추가하려는데, 중복된 값이 몇 개나 있는가?
SELECT email, COUNT(*) as cnt FROM users GROUP BY email HAVING COUNT(*) > 1;
# NOT NULL 제약을 추가하는데, NULL 값이 있는가?
SELECT COUNT(*) FROM users WHERE age IS NULL;
# foreign key 제약을 추가하는데, 존재하지 않는 참조가 있는가?
SELECT user_id FROM orders WHERE user_id NOT IN (SELECT id FROM users);
만약 중복 이메일이 있다면, ADD CONSTRAINT UNIQUE (email) 명령은 실패한다.
마이그레이션을 두 단계로 나누기
한 번의 마이그레이션으로 제약을 추가하면, 기존 데이터 때문에 롤백될 수 있다. 대신 두 단계로 나눈다:
Step 1: 데이터 정리 (배포 전)
-- 중복된 이메일은 유지하고 나머지는 삭제
DELETE FROM users
WHERE id NOT IN (
SELECT MIN(id) FROM users GROUP BY email
);
Step 2: 제약 추가 (배포)
ALTER TABLE users ADD CONSTRAINT unique_email UNIQUE(email);
이렇게 하면 기존 데이터도 보존하면서 제약을 추가할 수 있다.
테스트 환경에서 먼저 시뮬레이션하기
# 프로덕션 데이터를 테스트 환경에 복사
pg_dump production_db | psql test_db
# 마이그레이션 실행
python manage.py migrate
# 에러가 나는가?
에러가 난다면 원인을 파악해서 마이그레이션을 수정한다. 프로덕션에 배포하기 전에 test 환경에서 검증하는 게 중요하다.
롤백 계획 세우기
만에 하나 마이그레이션이 실패하면 어떻게 할지 미리 정한다.
# 데이터베이스 백업
pg_dump mydb > backup_20240630.sql
# 마이그레이션 실패 시 복구
psql mydb < backup_20240630.sql
또는 롤백 마이그레이션을 준비한다.
-- down 마이그레이션
ALTER TABLE users DROP CONSTRAINT unique_email;
배포 후 확인
# 제약이 실제로 추가됐는가?
SELECT constraint_name FROM information_schema.table_constraints
WHERE table_name = 'users';
# 새 데이터 입력이 제약을 지키는가?
INSERT INTO users (name, email) VALUES ('Test', '[email protected]');
INSERT INTO users (name, email) VALUES ('Test2', '[email protected]');
-- Unique violation 에러가 나야 정상
제약이 제대로 작동하는지 확인해야 나중에 버그를 줄일 수 있다.
앞으로의 마이그레이션 습관
제약을 추가할 때는:
- 기존 데이터가 제약을 만족하는지 먼저 확인
- 필요하면 데이터를 정리하는 마이그레이션을 먼저 실행
- 테스트 환경에서 검증
- 롤백 계획을 준비
- 배포 후 제약이 제대로 작동하는지 확인
이 단계들을 거치면 예상치 못한 배포 오류를 줄일 수 있다.