2026년 10월 28일 | DBMS Error 가이드
이 글에서 다루는 내용
2203E 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2203E too many json object members 는?
PostgreSQL 에러 코드 2203E는 JSON 오브젝트(Object)가 허용된 최대 멤버(키-값 쌍) 수를 초과했을 때 발생하는 에러입니다. PostgreSQL 내부적으로 JSON 처리 시 오브젝트의 멤버 수에 제한이 존재하며, 이 한계를 넘어서는 JSON 데이터를 파싱하거나 생성하려 할 때 이 에러가 트리거됩니다. 실무에서는 외부 API 응답 데이터를 그대로 JSON 컬럼에 저장하거나, json_build_object() 또는 jsonb_build_object() 함수를 사용해 동적으로 대형 JSON을 생성할 때 자주 마주치게 됩니다.
주요 발생 원인
json_build_object()/jsonb_build_object()함수의 인자 초과
PostgreSQL의 json_build_object() 함수는 내부적으로 가변 인자(variadic arguments)를 받는 방식으로 동작합니다. 이 함수에 전달할 수 있는 인자 쌍의 수는 PostgreSQL 버전 및 내부 구현에 따라 제한이 있으며, 너무 많은 키-값 쌍을 한 번에 전달하면 2203E 에러가 발생합니다. 특히 동적 SQL을 생성하거나 집계 함수와 함께 사용할 때 의도치 않게 멤버 수가 폭증하는 경우가 많습니다.
- 외부 시스템에서 유입된 비정상적으로 큰 JSON 데이터 저장 시도
외부 API, 로그 수집 파이프라인, 또는 ETL 프로세스를 통해 수신한 JSON 페이로드가 매우 많은 수의 키를 포함하고 있을 경우 PostgreSQL이 이를 파싱하는 과정에서 에러가 발생합니다. 예를 들어, 플랫(flat) 구조로 설계된 JSON이 수백 개 이상의 최상위 키를 가지고 있을 때 문제가 됩니다. 이런 경우는 데이터 설계 자체에 문제가 있는 경우가 많아 단순 회피보다 근본적인 구조 개선이 필요합니다.
json_agg()또는jsonb_object_agg()집계 시 데이터 폭발
jsonb_object_agg(key, value) 와 같은 집계 함수를 사용할 때, 집계 대상 데이터셋에 중복 제거 없이 너무 많은 고유 키가 존재하면 결과 JSON 오브젝트의 멤버 수가 한계를 초과할 수 있습니다. 특히 GROUP BY 없이 전체 테이블을 집계하거나, 카디널리티가 높은 컬럼을 키로 사용할 때 이 문제가 발생합니다. 이는 성능 문제와도 직결되기 때문에 반드시 데이터 범위를 제한하여 집계해야 합니다.
해결 방법
원인 1 해결: json_build_object() 분할 처리
너무 많은 인자를 하나의 json_build_object() 호출에 담지 말고, 여러 오브젝트를 만든 뒤 || 연산자로 병합하세요.
-- ❌ 잘못된 방식: 너무 많은 키-값 쌍을 한 번에 전달
SELECT json_build_object(
'key1', val1, 'key2', val2, 'key3', val3,
-- ... 수백 개의 키-값 쌍 ...
'key300', val300
)
FROM some_table;
-- ✅ 올바른 방식: 오브젝트를 분할 후 jsonb || 연산자로 병합
SELECT
jsonb_build_object('key1', val1, 'key2', val2, 'key3', val3)
||
jsonb_build_object('key4', val4, 'key5', val5, 'key6', val6)
AS merged_json
FROM some_table;
-- ✅ 또는 jsonb_object 함수를 활용한 배열 기반 생성
SELECT jsonb_object(
ARRAY['key1', 'key2', 'key3'],
ARRAY[val1::text, val2::text, val3::text]
)
FROM some_table;
원인 2 해결: 입력 JSON 사전 검증 및 구조 정규화
외부에서 유입된 JSON을 저장하기 전에 멤버 수를 사전에 검증하고, 필요한 키만 추출하여 저장하는 방식을 사용하세요.
-- JSON 오브젝트의 멤버 수 확인
SELECT json_object_keys('{"a":1,"b":2,"c":3}'::json)
-- 또는 집계로 개수 파악
SELECT count(*) AS member_count
FROM json_object_keys('{"a":1,"b":2,"c":3}'::json);
-- ✅ 필요한 키만 선택적으로 추출하여 저장
INSERT INTO target_table (id, filtered_data)
SELECT
id,
jsonb_build_object(
'name', raw_json->>'name',
'email', raw_json->>'email',
'status', raw_json->>'status'
)
FROM staging_table
WHERE raw_json IS NOT NULL;
-- ✅ 입력 검증 함수 생성
CREATE OR REPLACE FUNCTION validate_json_member_count(
p_json jsonb,
p_max_members int DEFAULT 100
)
RETURNS boolean
LANGUAGE plpgsql
AS $$
DECLARE
v_count int;
BEGIN
SELECT count(*) INTO v_count
FROM jsonb_object_keys(p_json);
IF v_count > p_max_members THEN
RAISE WARNING 'JSON object has % members, exceeding limit of %',
v_count, p_max_members;
RETURN false;
END IF;
RETURN true;
END;
$$;
-- 함수 사용 예시
SELECT validate_json_member_count('{"a":1,"b":2}'::jsonb, 50);
원인 3 해결: 집계 범위 제한 및 분산 처리
jsonb_object_agg() 사용 시 반드시 데이터 범위를 제한하고, 결과를 나누어 처리하세요.
-- ❌ 잘못된 방식: 전체 테이블을 하나의 JSON으로 집계
SELECT jsonb_object_agg(user_id::text, profile_data)
FROM users; -- 수백만 건이면 에러 발생 가능
-- ✅ 올바른 방식: WHERE 절로 범위 제한
SELECT jsonb_object_agg(user_id::text, profile_data)
FROM users
WHERE created_at >= now() - INTERVAL '7 days'
AND status = 'active';
-- ✅ 집계 결과를 배열로 분산 처리 (청크 단위)
WITH chunked_data AS (
SELECT
(ROW_NUMBER() OVER (ORDER BY user_id) - 1) / 100 AS chunk_id,
user_id,
profile_data
FROM users
WHERE status = 'active'
)
SELECT
chunk_id,
jsonb_object_agg(user_id::text, profile_data) AS chunk_json
FROM chunked_data
GROUP BY chunk_id
ORDER BY chunk_id;
-- ✅ 중첩 구조로 재설계하여 최상위 멤버 수 감소
SELECT jsonb_build_object(
'metadata', jsonb_build_object(
'created_at', now(),
'version', '1.0'
),
'users', jsonb_agg(
jsonb_build_object('id', user_id, 'name', username)
)
)
FROM users
WHERE status = 'active';
예방 방법
- JSON 스키마 설계 시 플랫 구조 지양 및 멤버 수 상한 정책 수립
JSON 컬럼에 저장할 데이터 구조를 설계할 때부터 최상위 오브젝트의 멤버 수를 일정 수준(예: 최대 50~100개) 이하로 유지하는 가이드라인을 팀 내에서 수립하세요. 멤버 수가 많아질 것으로 예상된다면 중첩 오브젝트(nested object) 또는 배열 구조로 계층화하여 설계하고, CHECK 제약 조건을 활용해 DB 레벨에서 강제하는 것이 좋습니다.
“`sql
— CHECK 제약 조건으로 JSON 멤버 수 제한
ALTER TABLE api_responses
ADD CONSTRAINT chk_json_member_limit
CHECK (
(SELECT count(*) FROM jsonb_object_keys(response_data)) <= 100
);
“`
- 애플리케이션 레이어에서 JSON 직렬화 전 사전 검증 및 모니터링 구축
애플리케이션에서 PostgreSQL로 JSON 데이터를 삽입하기 전, 직렬화 단계에서 키 개수를 검사하는 로직을 추가하세요. 또한 pg_stat_statements 또는 로그 분석을 통해 대형 JSON을 처리하는 쿼리를 주기적으로 모니터링하고, 임계치 초과 시 알림을 받을 수 있도록 운영 체계를 갖추는 것이 중요합니다. 이러한 사전 방어 체계가 갖춰지면 운영 환경에서의 돌발 에러를 크게 줄일 수 있습니다.
관련 에러
22032(invalid_json_text): JSON 문자열 자체가 유효하지 않은 형식일 때 발생합니다.2203E와 함께 JSON 처리 관련 에러 그룹(22032~2203F)에 속합니다.2203F(too many json array elements): JSON 배열의 요소 수가 허용 한계를 초과했을 때 발생하며,2203E의 배열 버전에 해당합니다. 두 에러 모두 JSON 데이터 크기 관리의 중요성을 시사합니다.54000(program_limit_exceeded): PostgreSQL 내부 리소스 한계 초과와 관련된 상위 에러 클래스로, JSON 멤버 수 초과 역시 이 맥락에서 이해할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.