← 전체 글로 돌아가기

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
);

가장 오래된 레코드만 남기고 나머지를 삭제한다.

애플리케이션 레벨 확인

데이터베이스에는 데이터가 없는데 에러가 난다면, 애플리케이션 코드를 봐야 한다.

  • 트랜잭션이 제대로 롤백되지 않았을 수 있다.
  • 캐시된 데이터를 사용 중일 수 있다.
  • 다른 세션이 같은 값을 쓰려고 하고 있을 수 있다.

특히 동시성 문제가 있다면, 트랜잭션 격리 수준을 확인해야 한다.

확인 순서

  1. 에러 메시지에서 테이블, 제약, 컬럼 파악
  2. 실제로 중복된 데이터가 있나 확인
  3. Unique 제약 정의 확인 (단일 vs composite)
  4. 데이터 정리 (필요시)
  5. 애플리케이션 캐시 초기화
  6. 다시 시도

Unique 제약 에러는 예상 밖의 데이터 상태를 의미한다. 데이터를 직접 보고 원인을 파악하면 쉽게 해결된다.