2026년 06월 18일 | DBMS Error 가이드
이 글에서 다루는 내용
22038 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22038 singleton sql json item required 는?
PostgreSQL 에러 코드 22038 (singleton sql json item required)은 SQL/JSON 경로 표현식(path expression)이 단일 항목(singleton)을 반환해야 하는 컨텍스트에서 여러 개의 JSON 항목을 반환하거나, 또는 반대로 아무런 값도 반환하지 않을 때 발생합니다. 이 에러는 주로 JSON_VALUE(), JSON_QUERY() 함수나 IS JSON 조건절, 또는 JSON 경로 연산자를 사용할 때 나타납니다. PostgreSQL 15 이상에서 SQL 표준 JSON 함수들이 도입되면서 이 에러를 접하는 경우가 늘어났으며, JSON 데이터를 엄격하게 다루는 환경에서는 반드시 이해하고 있어야 하는 에러입니다.
주요 발생 원인
JSON_VALUE()함수에서 경로 표현식이 다수의 값을 반환하는 경우
JSON_VALUE() 함수는 반드시 스칼라 단일 값 하나만 반환해야 합니다. JSON 배열이나 중첩 오브젝트에서 경로 표현식이 복수의 결과를 생성하면 PostgreSQL은 즉시 이 에러를 발생시킵니다. 예를 들어, 배열의 모든 요소를 [*] 와일드카드로 조회하면 단일 값이 아니라 여러 값이 나오므로 에러가 발생합니다.
JSON_QUERY()함수에서WITHOUT WRAPPER옵션과 함께 다중 결과가 반환되는 경우
JSON_QUERY() 함수 역시 기본적으로 단일 JSON 오브젝트나 배열을 반환하도록 설계되어 있습니다. WITHOUT ARRAY WRAPPER 옵션이 지정된 상태에서 경로 표현식이 여러 항목을 생성하게 되면 단일 항목 요건을 충족하지 못하여 에러가 발생합니다. 이 경우 WITH ARRAY WRAPPER 옵션을 활용하면 문제를 해결할 수 있습니다.
STRICT모드의 JSON 경로 표현식에서 배열 요소 전체를 참조하는 경우
PostgreSQL의 JSON 경로 연산자(@@, @?)나 jsonb_path_query_first() 계열 함수와 달리, SQL 표준 JSON 함수들은 기본적으로 엄격한 단일 항목 규칙을 적용합니다. $.items[*].price와 같이 배열 전체 요소를 순회하는 표현식을 단일 값 반환 함수에 넣으면 이 에러가 발생합니다. 특히 데이터 마이그레이션이나 ETL 작업 중 기존 jsonb_path_query 로직을 SQL 표준 함수로 전환할 때 자주 마주치는 상황입니다.
해결 방법
원인 1 해결: JSON_VALUE()에서 단일 값 경로 지정
문제 상황:
-- 에러 발생: 배열 [*]로 인해 다수의 값 반환
SELECT JSON_VALUE('{"prices": [100, 200, 300]}', '$.prices[*]');
-- ERROR: 22038: singleton SQL/JSON item required
해결 방법 1 – 특정 인덱스로 단일 값 지정:
-- 첫 번째 요소만 가져오기
SELECT JSON_VALUE('{"prices": [100, 200, 300]}', '$.prices[0]');
-- 결과: 100
해결 방법 2 – DEFAULT 절로 에러 대신 기본값 처리:
-- 다중 결과 시 에러 대신 NULL 또는 기본값 반환
SELECT JSON_VALUE(
'{"prices": [100, 200, 300]}',
'$.prices[*]'
DEFAULT NULL ON ERROR
);
-- 결과: NULL (에러 없이 처리)
해결 방법 3 – 배열 전체를 다뤄야 한다면 JSON_QUERY() 사용:
-- JSON_QUERY는 배열 자체를 반환 가능
SELECT JSON_QUERY('{"prices": [100, 200, 300]}', '$.prices');
-- 결과: [100, 200, 300]
원인 2 해결: JSON_QUERY()에서 WITH ARRAY WRAPPER 옵션 활용
문제 상황:
-- 에러 발생: WITHOUT WRAPPER 기본 설정 + 다중 결과
SELECT JSON_QUERY(
'{"items": [{"id":1}, {"id":2}]}',
'$.items[*]'
WITHOUT ARRAY WRAPPER
);
-- ERROR: 22038: singleton SQL/JSON item required
해결 방법:
-- WITH ARRAY WRAPPER로 결과를 배열로 감싸기
SELECT JSON_QUERY(
'{"items": [{"id":1}, {"id":2}]}',
'$.items[*]'
WITH ARRAY WRAPPER
);
-- 결과: [{"id": 1}, {"id": 2}]
실무 예시 – 테이블 컬럼에 적용:
-- 주문 테이블에서 상품 ID 목록 추출
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_data JSONB NOT NULL
);
INSERT INTO orders (order_data) VALUES
('{"order_no": "A001", "products": [{"pid": "P01", "qty": 2}, {"pid": "P02", "qty": 1}]}'),
('{"order_no": "A002", "products": [{"pid": "P03", "qty": 5}]}');
-- 올바른 방식: WITH ARRAY WRAPPER 사용
SELECT
order_id,
JSON_VALUE(order_data::json, '$.order_no') AS order_no,
JSON_QUERY(order_data::json, '$.products[*].pid' WITH ARRAY WRAPPER) AS product_ids
FROM orders;
원인 3 해결: 배열 순회가 필요하면 jsonb_path_query 계열 함수 사용
기존 PostgreSQL 방식 (jsonb_path_query)으로 안전하게 처리:
-- jsonb_path_query_array: 다중 결과를 배열로 반환
SELECT jsonb_path_query_array(
'{"scores": [85, 92, 78, 95]}'::jsonb,
'$.scores[*]'
);
-- 결과: [85, 92, 78, 95]
-- jsonb_path_query: SET 형태로 각 행 반환
SELECT jsonb_path_query(
'{"scores": [85, 92, 78, 95]}'::jsonb,
'$.scores[*]'
) AS score;
-- 결과: 각각 85, 92, 78, 95 행으로 반환
-- jsonb_path_query_first: 첫 번째 값만 반환
SELECT jsonb_path_query_first(
'{"scores": [85, 92, 78, 95]}'::jsonb,
'$.scores[*]'
);
-- 결과: 85
SQL 표준 함수와 혼용하는 실무 패턴:
-- 조건에 맞는 JSON 배열 요소 필터링 후 집계
SELECT
order_id,
jsonb_path_query_array(order_data, '$.products[*].pid') AS all_pids,
JSON_VALUE(order_data::json, '$.order_no') AS order_no
FROM orders
WHERE order_data @? '$.products[*] ? (@.qty > 1)';
예방 방법
ON ERROR절을 활용한 방어적 코딩 습관화
SQL/JSON 함수를 사용할 때는 항상 ON ERROR 절을 명시적으로 작성하는 것이 좋습니다. DEFAULT <값> ON ERROR 또는 NULL ON ERROR를 지정하면 예상치 못한 JSON 데이터 구조 변화에도 쿼리가 중단되지 않고 안전하게 실행됩니다. 특히 외부 API나 사용자 입력으로부터 유입되는 JSON 데이터를 처리할 때는 이 습관이 장애를 예방하는 핵심이 됩니다.
“`sql
— 방어적 코딩 패턴
SELECT JSON_VALUE(
event_payload::json,
‘$.user.id’
RETURNING INTEGER
NULL ON EMPTY
DEFAULT -1 ON ERROR
)
FROM event_log;
“`
- 함수 선택 기준을 명확히 정의하고 팀 내 가이드라인으로 공유
JSON_VALUE (단일 스칼라), JSON_QUERY (단일 오브젝트/배열), jsonb_path_query_array (다중 결과 배열), jsonb_path_query (다중 행 반환)의 차이를 명확히 구분하여 사용 목적에 맞는 함수를 선택해야 합니다. 팀 내 코드 리뷰 체크리스트에 “JSON 경로 표현식이 반환하는 항목 수와 함수의 요건이 일치하는가?”를 추가하면 배포 전 사전에 이 에러를 차단할 수 있습니다.
관련 에러
- 22032 (
invalid_json_text): JSON 문자열 자체가 유효하지 않은 형식일 때 발생하며, 22038 이전 단계에서 먼저 마주칠 수 있습니다. - 22033 (
impossible_json_subscript): JSON 경로에서 존재하지 않는 키나 인덱스에 접근할 때 발생하며,STRICT모드에서 자주 함께 나타납니다. - 22034 (
invalid_sql_json_subscript): 잘못된 형식의 JSON 서브스크립트(인덱스) 사용 시 발생하며, 배열 경로 지정 오류와 관련이 깊습니다. - 22035 (
non_numeric_sql_json_item): 숫자형을 기대하는 컨텍스트에서 비숫자 JSON 값이 반환될 때 발생합니다. - 22036 (
non_unique_keys_in_a_json_object): JSON 오브젝트 내 중복 키가 존재할 때 발생하며, 엄격 모드 JSON 처리 시 함께 점검해야 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.