DB
DB unique 제약이 걸릴 때 확인 순서
프로젝트에서 데이터베이스 unique 제약 오류가 날 때 먼저 확인해야 할 것들입니다.
새 레코드를 저장하려는데 갑자기 "Duplicate key" 에러가 난다. 아직 그 값을 저장한 적이 없는데, 뭐가 문제일까.
먼저 에러 메시지 읽기
ORDER BY 또는 데이터베이스 에러 메시지는 매우 구체적이다. 어떤 컬럼이 중복되었는지, 어떤 값인지 보여준다.
ERROR: duplicate key value violates unique constraint "users_email_key"
DETAIL: Key (email)=([email protected]) already exists.
この메시지에서:
- 테이블: users
- 제약 이름: users_email_key
- 컬럼: email
- 중복된 값: [email protected]
이 정보만으로도 대부분 문제를 찾을 수 있다.
실제로 중복된 데이터가 있나
에러 메시지가 말하는 값이 정말 데이터베이스에 있는지 확인한다.
SELECT * FROM users WHERE email = '[email protected]';
결과가 1개면 정상, 2개 이상이면 데이터가 중복되어 있다는 뜻이다.
혹은 전체 중복을 찾으려면:
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;
어떤 값들이 중복되었는지 한눈에 본다.
제약 정의 확인
Unique 제약이 정말 그 컬럼에만 걸려있는지 확인한다.
\d users
PostgreSQL에서는 이 명령으로 테이블 구조를 본다. 제약 목록이 나타난다.
"users_email_key" UNIQUE, btree (email)
만약 composite unique 제약이 있다면:
"users_email_org_key" UNIQUE, btree (email, org_id)
즉, 같은 조직 내에서만 이메일이 unique하다는 뜻이다. 다른 조직이면 중복이 가능하다.
NULL 값 처리
Unique 제약에서 NULL은 특별하다. 많은 데이터베이스에서 NULL != NULL이므로, NULL 값은 여러 개 있을 수 있다.
SELECT * FROM users WHERE email IS NULL;
이메일을 설정하지 않은 사용자가 여러 명이라면 에러가 날 수 있다.
Unique 제약을 강제하려면:
ALTER TABLE users ADD CONSTRAINT users_email_key UNIQUE (email) WHERE email IS NOT NULL;
SQL 문법에 따라 다르다 (PostgreSQL은 partial index 사용).
마이그레이션이나 데이터 정리
만약 이미 중복된 데이터가 있다면, Unique 제약을 추가하기 전에 정리해야 한다.
DELETE FROM users WHERE id NOT IN (
SELECT MIN(id) FROM users GROUP BY email
);
가장 오래된 레코드만 남기고 나머지를 삭제한다.
애플리케이션 레벨 확인
데이터베이스에는 데이터가 없는데 에러가 난다면, 애플리케이션 코드를 봐야 한다.
- 트랜잭션이 제대로 롤백되지 않았을 수 있다.
- 캐시된 데이터를 사용 중일 수 있다.
- 다른 세션이 같은 값을 쓰려고 하고 있을 수 있다.
특히 동시성 문제가 있다면, 트랜잭션 격리 수준을 확인해야 한다.
확인 순서
- 에러 메시지에서 테이블, 제약, 컬럼 파악
- 실제로 중복된 데이터가 있나 확인
- Unique 제약 정의 확인 (단일 vs composite)
- 데이터 정리 (필요시)
- 애플리케이션 캐시 초기화
- 다시 시도
Unique 제약 에러는 예상 밖의 데이터 상태를 의미한다. 데이터를 직접 보고 원인을 파악하면 쉽게 해결된다.