개발

PostgreSQL duplicate key value violates unique constraint 오류 원인과 해결 방법

Ray901104 2026. 9. 20. 00:16
반응형

PostgreSQL에서 INSERT 또는 UPDATE를 수행하다 보면 다음과 같은 오류를 만날 수 있습니다.

ERROR: duplicate key value violates unique constraint

 

처음 보면 단순히 데이터가 중복됐다는 의미처럼 보이지만, 실제로는 Primary Key(PK), UNIQUE 제약조건, Sequence 불일치 등 여러 가지 원인으로 발생할 수 있습니다.

 

이번 글에서는 이 오류가 발생하는 대표적인 원인과 해결 방법을 정리해보겠습니다.


1. duplicate key value violates unique constraint란?

예를 들어 다음과 같은 테이블이 있다고 가정하겠습니다.

CREATE TABLE member (
    member_id BIGINT PRIMARY KEY,
    email VARCHAR(100) UNIQUE,
    member_name VARCHAR(50)
);

이미 다음 데이터가 존재합니다.

member_id : 1
email     : test@test.com

이 상태에서 동일한 member_id를 INSERT하면 오류가 발생합니다.

INSERT INTO member (
    member_id,
    email,
    member_name
) VALUES (
    1,
    'new@test.com',
    '홍길동'
);

 

PostgreSQL에서는 Primary Key가 중복될 수 없기 때문에 다음과 같은 오류가 발생합니다.

ERROR: duplicate key value violates unique constraint "member_pkey"
DETAIL: Key (member_id)=(1) already exists.

 

핵심은 오류 메시지에 표시되는 constraint 이름과 DETAIL 부분을 확인하는 것입니다.


2. 가장 먼저 중복 데이터를 확인

오류 메시지가 다음과 같다고 가정하겠습니다.

DETAIL: Key (member_id)=(1) already exists.

 

그러면 실제 데이터가 존재하는지 확인합니다.

SELECT *
FROM member
WHERE member_id = 1;

 

데이터가 조회된다면 동일한 PK로 INSERT를 시도한 것이 원인입니다.

이 경우 애플리케이션에서 PK를 생성하는 로직이나 INSERT 쿼리를 확인해야 합니다.


3. UNIQUE 컬럼 중복 확인

PK뿐만 아니라 UNIQUE 제약조건 때문에 발생할 수도 있습니다.

예를 들어 email 컬럼에 UNIQUE가 설정되어 있다면

INSERT INTO member (
    member_id,
    email,
    member_name
) VALUES (
    2,
    'test@test.com',
    '김개발'
);

 

member_id는 다르더라도 이미 동일한 이메일이 존재하기 때문에 오류가 발생합니다.

ERROR: duplicate key value violates unique constraint "member_email_key"
DETAIL: Key (email)=(test@test.com) already exists.

 

이 경우 다음과 같이 확인할 수 있습니다.

SELECT *
FROM member
WHERE email = 'test@test.com';

4. 어떤 UNIQUE 제약조건이 걸려 있는지 확인

테이블에 어떤 제약조건이 설정되어 있는지 정확하게 모르는 경우도 있습니다.

 

DBeaver 같은 DB 툴에서 확인할 수도 있지만 SQL로도 확인할 수 있습니다.

SELECT
    conname,
    contype
FROM pg_constraint
WHERE conrelid = 'member'::regclass;

 

contype 값 중 대표적으로 다음과 같은 값이 있습니다.

p : Primary Key
u : UNIQUE
f : Foreign Key
c : CHECK

 

오류 메시지에 나온 constraint 이름과 비교하면 어떤 제약조건 때문에 실패했는지 찾을 수 있습니다.


728x90

5. Sequence가 실제 데이터와 맞지 않는 경우

PostgreSQL을 사용하다 보면 조금 더 애매한 경우가 있습니다.

 

Sequence를 이용해서 PK를 자동 생성하고 있는데도 PK 중복 오류가 발생하는 경우입니다.

 

예를 들어 현재 테이블의 가장 큰 ID가 100이라고 가정하겠습니다.

SELECT MAX(member_id)
FROM member;

 

결과:

100

 

그런데 Sequence가 아직 50 근처를 가리키고 있다면 문제가 발생할 수 있습니다.

 

데이터 마이그레이션이나 직접 INSERT를 수행한 이후 이런 상황이 발생할 수 있습니다.

 

Sequence 상태를 확인합니다.

SELECT last_value
FROM member_member_id_seq;

 

테이블의 실제 PK 값과 Sequence가 맞지 않는다면 Sequence를 다시 맞춰줘야 합니다.

 

예를 들어 다음과 같이 처리할 수 있습니다.

SELECT setval(
    'member_member_id_seq',
    (SELECT MAX(member_id) FROM member)
);

 

이후 다음 Sequence 호출에서는 현재 데이터의 최대값 이후 번호가 사용됩니다.

 

※ 실제 Sequence 이름은 테이블마다 다르므로 반드시 확인한 뒤 실행해야 합니다.


6. INSERT가 아니라 UPDATE에서도 발생할 수 있다

이 오류는 INSERT에서만 발생하는 것이 아닙니다.

 

다음 데이터가 있다고 가정해보겠습니다.

member_id | email
----------+----------------
1         | a@test.com
2         | b@test.com

 

여기서 다음 UPDATE를 실행합니다.

UPDATE member
SET email = 'a@test.com'
WHERE member_id = 2;

 

a@test.com은 이미 다른 데이터에서 사용하고 있습니다.

 

따라서 email에 UNIQUE 제약조건이 있다면 UPDATE 역시 실패합니다.

즉,

duplicate key value violates unique constraint

 

오류가 발생했다고 해서 무조건 INSERT 쿼리만 확인하면 안 됩니다.


7. 중복이면 UPDATE, 없으면 INSERT하고 싶은 경우

비즈니스 로직에 따라 동일한 키가 존재하면 UPDATE하고, 존재하지 않으면 INSERT하고 싶은 경우도 있습니다.

 

PostgreSQL에서는 ON CONFLICT를 사용할 수 있습니다.

예를 들어 다음과 같습니다.

INSERT INTO member (
    member_id,
    email,
    member_name
) VALUES (
    1,
    'test@test.com',
    '홍길동'
)
ON CONFLICT (member_id)
DO UPDATE
SET
    email = EXCLUDED.email,
    member_name = EXCLUDED.member_name;

 

member_id가 존재하지 않으면 INSERT되고, 이미 존재한다면 UPDATE됩니다.

 

하지만 단순히 오류를 없애기 위해 모든 INSERT에 ON CONFLICT를 사용하는 것은 권장하지 않습니다.

 

중복 데이터가 들어오는 것 자체가 잘못된 비즈니스 로직이라면 해당 로직을 수정하는 것이 먼저입니다.


8. MyBatis에서 발생한다면?

Spring + MyBatis 환경에서도 결국 확인해야 할 부분은 동일합니다.

 

예를 들어 다음과 같은 INSERT가 있다고 가정하겠습니다.

<insert id="insertMember">
    INSERT INTO member (
        member_id,
        email,
        member_name
    ) VALUES (
        #{memberId},
        #{email},
        #{memberName}
    )
</insert>

이 경우 다음 항목을 확인합니다.

  • memberId가 이미 존재하는 값인지
  • Java에서 PK를 중복 생성하고 있지 않은지
  • Sequence를 정상적으로 호출하고 있는지
  • UNIQUE 컬럼에 기존 값이 들어가고 있지 않은지
  • 반복문에서 동일한 객체를 여러 번 INSERT하고 있지 않은지

특히 여러 건을 저장하는 로직에서는 JavaScript → Controller → Service → MyBatis로 넘어오는 데이터를 로그로 확인하는 것이 좋습니다.

 

DB 오류만 보면 PostgreSQL 문제처럼 보이지만 실제 원인은 애플리케이션에서 동일한 데이터를 두 번 전달하고 있는 경우도 있기 때문입니다.


정리

PostgreSQL에서

duplicate key value violates unique constraint

 

오류가 발생했다면 무작정 제약조건을 삭제하기보다는 다음 순서로 확인하는 것이 좋습니다.

  1. 오류 메시지의 constraint 이름 확인
  2. DETAIL에 표시된 중복 컬럼과 값 확인
  3. 실제로 동일한 데이터가 존재하는지 SELECT
  4. PK 또는 UNIQUE 제약조건 확인
  5. Sequence를 사용한다면 현재 값 확인
  6. INSERT뿐만 아니라 UPDATE 쿼리도 확인
  7. 애플리케이션에서 동일한 데이터가 중복 전달되는지 확인

대부분은 오류 메시지의 DETAIL 부분만 제대로 확인해도 원인을 빠르게 찾을 수 있습니다.

 

특히 운영 중인 시스템에서는 오류를 해결하기 위해 UNIQUE나 PK 제약조건부터 제거하면 데이터 무결성 문제가 발생할 수 있으므로, 왜 중복된 값이 저장되려고 했는지를 먼저 확인하는 것이 중요합니다.

반응형