2026년 10월 26일 | DBMS Error 가이드
이 글에서 다루는 내용
22037 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22037 non unique keys in a json object 는?
PostgreSQL 에러 코드 22037은 JSON 객체 내에서 동일한 키(key)가 두 번 이상 중복으로 사용되었을 때 발생하는 오류입니다. JSON 표준(RFC 7159)에서는 객체 내의 키가 유일해야 한다고 권고하고 있으며, PostgreSQL의 jsonb 타입은 이 규칙을 엄격하게 적용합니다. 특히 jsonb 타입으로 데이터를 저장하거나 변환하는 과정에서 중복 키가 발견되면 이 에러가 트리거됩니다.
주요 발생 원인
- 외부 애플리케이션에서 생성된 중복 키 JSON 데이터 삽입
가장 흔한 발생 원인 중 하나는 Python, JavaScript, Java 등의 외부 애플리케이션에서 생성된 JSON 데이터에 의도치 않게 중복 키가 포함되어 있는 경우입니다. 일부 언어나 라이브러리에서는 중복 키를 허용하거나 마지막 값을 덮어쓰는 방식으로 처리하기 때문에, 데이터베이스에 저장할 때 비로소 문제가 드러납니다. 예를 들어, API 응답을 그대로 jsonb 컬럼에 저장하려다 이 오류를 마주치게 됩니다.
- json 타입에서 jsonb 타입으로 마이그레이션 또는 형변환 시
PostgreSQL의 json 타입은 중복 키를 허용하며 원본 텍스트를 그대로 저장합니다. 반면 jsonb 타입은 내부적으로 이진 형태로 파싱하면서 키의 유일성을 검사합니다. 따라서 기존에 json 타입으로 저장되어 있던 데이터를 jsonb로 컬럼 타입을 변경하거나 명시적으로 ::jsonb 형변환을 시도할 때 중복 키 문제가 표면으로 드러나게 됩니다.
- 동적 SQL 또는 함수에서 JSON 문자열을 직접 조합할 때
애플리케이션 레이어나 PostgreSQL 함수 내에서 문자열 연결(concatenation) 방식으로 JSON을 동적으로 생성하는 경우 중복 키가 발생하기 쉽습니다. 특히 조건에 따라 특정 키를 추가하는 로직이 중복 실행되거나 루프 처리 과정에서 같은 키가 반복 삽입될 수 있습니다. 이런 방식은 코드 복잡도가 올라갈수록 버그를 찾기 어려워집니다.
해결 방법
원인 1: 중복 키 확인 및 정제 후 삽입
먼저 삽입하려는 JSON 데이터에 중복 키가 있는지 확인하고, PostgreSQL 내장 함수를 활용해 정제합니다.
-- 중복 키가 포함된 잘못된 예시 (jsonb로 변환 시 에러 발생)
SELECT '{"name": "Alice", "age": 30, "name": "Bob"}'::jsonb;
-- ERROR: 22037: non unique keys in a json object
-- json 타입으로는 저장 가능하지만 마지막 값이 무시될 수 있음
SELECT '{"name": "Alice", "age": 30, "name": "Bob"}'::json;
-- 해결책: jsonb_object 함수를 이용해 고유 키로만 JSON 재구성
SELECT jsonb_object(
ARRAY['name', 'age'],
ARRAY['Alice', '30']
);
-- 또는 애플리케이션에서 중복 제거 후 전달된 JSON을 사용
-- 임시 테이블을 이용해 중복 키 데이터 필터링 예시
WITH raw_data AS (
SELECT key, value
FROM json_each_text('{"name": "Alice", "age": 30, "name": "Bob"}'::json)
),
deduped AS (
SELECT DISTINCT ON (key) key, value
FROM raw_data
ORDER BY key
)
SELECT jsonb_object_agg(key, value) AS cleaned_json
FROM deduped;
원인 2: json → jsonb 마이그레이션 시 중복 키 사전 점검
-- 기존 json 컬럼에서 중복 키를 가진 행 탐지
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
data JSON
);
-- 중복 키 보유 여부 확인 쿼리
SELECT
id,
data,
key,
COUNT(*) AS key_count
FROM documents,
json_each_text(data) AS kv(key, value)
GROUP BY id, data, key
HAVING COUNT(*) > 1;
-- 중복 키 제거 후 jsonb로 변환하는 업데이트 쿼리
UPDATE documents
SET data = (
SELECT json_object_agg(key, value)
FROM (
SELECT DISTINCT ON (key) key, value
FROM json_each_text(data) AS kv(key, value)
ORDER BY key
) sub
)::json
WHERE id IN (
SELECT id
FROM documents,
json_each_text(data) AS kv(key, value)
GROUP BY id, key
HAVING COUNT(*) > 1
);
-- 안전하게 jsonb 컬럼으로 변경
ALTER TABLE documents
ALTER COLUMN data TYPE JSONB USING data::jsonb;
원인 3: 동적 JSON 생성 시 jsonb_build_object 활용
-- 잘못된 방식: 문자열 연결로 JSON 생성 (중복 위험)
-- 올바른 방식: PostgreSQL 내장 함수 사용
SELECT jsonb_build_object(
'user_id', 1001,
'username', 'postgres_master',
'role', 'admin'
);
-- 여러 레코드에서 JSON 집계 시 중복 키 방지
SELECT
user_id,
jsonb_object_agg(DISTINCT attribute_key, attribute_value) AS user_attributes
FROM user_attributes_table
GROUP BY user_id;
-- 함수 내에서 안전하게 JSON 병합
CREATE OR REPLACE FUNCTION safe_jsonb_merge(base jsonb, override jsonb)
RETURNS jsonb
LANGUAGE plpgsql AS $$
BEGIN
-- jsonb에서 || 연산자는 오른쪽 값이 우선
RETURN base || override;
END;
$$;
SELECT safe_jsonb_merge(
'{"name": "Alice", "age": 30}'::jsonb,
'{"age": 31, "city": "Seoul"}'::jsonb
);
-- 결과: {"age": 31, "city": "Seoul", "name": "Alice"}
예방 방법
- 컬럼 타입을 jsonb로 통일하고 입력 시점에 검증하기
가능하면 json 타입 대신 jsonb 타입을 사용하세요. jsonb는 저장 시점에 파싱과 유효성 검사를 수행하기 때문에 중복 키 문제를 데이터가 영구 저장되기 전에 즉시 차단할 수 있습니다. 또한 CHECK 제약 조건이나 트리거를 활용해 삽입/수정 전에 중복 키 여부를 사전 검사하는 로직을 추가하면 데이터 품질을 더욱 확실하게 보장할 수 있습니다.
“`sql
— jsonb 타입 컬럼 사용 예시 (기본 권장)
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
“`
- 애플리케이션 레이어에서 JSON 직렬화 라이브러리를 통한 표준화
JSON 데이터를 생성할 때 문자열 직접 조합 방식을 절대 사용하지 말고, 각 언어의 공식 JSON 직렬화 라이브러리(Python의 json, JavaScript의 JSON.stringify, Java의 Jackson 등)를 사용하세요. 이 라이브러리들은 대부분 Map/Dictionary 구조 기반이므로 자연스럽게 중복 키가 제거됩니다. 데이터베이스에 저장하기 전 유효성 검사 레이어(Validation Layer)를 두어 JSON 스키마 검증을 수행하는 것도 강력히 권장합니다.
관련 에러
- 22P02 (invalid_text_representation): JSON 형식 자체가 잘못된 경우 발생하며, 22037과 달리 JSON 파싱 단계에서 구조적 오류가 있을 때 나타납니다.
- 22023 (invalid_parameter_value):
jsonb_set,jsonb_insert등의 함수에 잘못된 경로나 값이 전달될 때 발생합니다. - 42804 (datatype_mismatch):
json또는jsonb타입과 호환되지 않는 타입으로 형변환을 시도할 때 발생하며, 마이그레이션 작업 시 함께 마주칠 수 있는 에러입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.