2026년 08월 21일 | DBMS Error 가이드
이 글에서 다루는 내용
22035 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22035 no sql json item 는?
PostgreSQL 에러 코드 22035는 no_sql_json_item 오류로, SQL/JSON 경로 표현식을 사용할 때 해당 경로에서 아무런 JSON 항목도 반환되지 않을 때 발생합니다. 주로 jsonb_path_query, jsonb_path_value, JSON_VALUE, JSON_QUERY 등 SQL/JSON 함수에서 경로 표현식이 존재하지 않는 키나 배열 인덱스를 참조할 경우 이 에러가 트리거됩니다. 이 오류는 특히 PostgreSQL 12 이후 도입된 SQL/JSON 표준 함수들을 활용할 때 자주 마주치게 됩니다.
주요 발생 원인
- 존재하지 않는 JSON 경로(키)를 엄격 모드(strict mode)로 조회할 때
SQL/JSON 경로 표현식에는 lax(관대 모드)와 strict(엄격 모드) 두 가지 모드가 있습니다. strict 모드에서는 경로가 존재하지 않으면 즉시 에러를 발생시키는 반면, lax 모드는 해당 경우 빈 결과를 반환합니다. 개발자가 명시적으로 strict 키워드를 사용하거나, 함수 자체가 내부적으로 엄격 모드로 동작할 때 경로가 없으면 이 에러가 발생합니다.
- 배열 범위를 벗어난 인덱스 접근
JSON 배열에서 존재하지 않는 인덱스 번호를 strict 모드로 참조하면 22035 에러가 발생합니다. 예를 들어 배열의 원소가 3개인데 인덱스 5번을 요청하는 경우가 해당됩니다. 데이터 품질이 일정하지 않아 배열의 길이가 레코드마다 다를 때 특히 빈번하게 발생합니다.
JSON_VALUE/JSON_QUERY함수에서ERROR ON EMPTY옵션 적용 시
PostgreSQL 14 이상에서 사용할 수 있는 JSON_VALUE, JSON_QUERY 함수는 결과가 없을 때의 동작을 ON EMPTY 절로 제어할 수 있습니다. 기본적으로 또는 명시적으로 ERROR ON EMPTY를 지정한 경우, 경로 결과가 없으면 22035 에러를 발생시킵니다. 이 옵션의 동작을 제대로 이해하지 못한 채 사용하면 예기치 못한 에러가 런타임에 발생할 수 있습니다.
해결 방법
원인 1: strict 모드 대신 lax 모드 사용
경로 표현식 앞에 lax 키워드를 명시하거나, 기본값인 lax 모드를 그대로 활용하세요. lax 모드는 경로가 없을 때 에러 대신 빈 결과(또는 NULL)를 반환하므로 안전합니다.
-- 에러 발생: strict 모드에서 존재하지 않는 키 접근
SELECT jsonb_path_query('{"name": "Alice"}'::jsonb, 'strict $.age');
-- ERROR: jsonpath member accessor can only be applied to an object
-- (22035 계열 오류 유발)
-- 해결: lax 모드 사용 (기본값이므로 키워드 생략 가능)
SELECT jsonb_path_query_first('{"name": "Alice"}'::jsonb, 'lax $.age');
-- 결과: NULL (에러 없음)
-- jsonb_path_value 사용 예
SELECT jsonb_path_value(
'{"user": {"name": "Bob"}}'::jsonb,
'lax $.user.email'
);
-- 결과: NULL
원인 2: 배열 인덱스 접근 시 안전 처리
배열 접근 전 jsonb_array_length로 크기를 확인하거나, lax 모드와 함께 조건부 처리를 수행하세요.
-- 에러 발생: 존재하지 않는 배열 인덱스를 strict 모드로 접근
SELECT jsonb_path_query('{"items": [1, 2]}'::jsonb, 'strict $.items[5]');
-- ERROR: 22035
-- 해결 방법 1: lax 모드 사용
SELECT jsonb_path_query_first('{"items": [1, 2]}'::jsonb, 'lax $.items[5]');
-- 결과: NULL
-- 해결 방법 2: 배열 길이를 미리 확인 후 접근
DO $$
DECLARE
v_data jsonb := '{"items": [10, 20, 30]}';
v_len int;
v_val jsonb;
BEGIN
v_len := jsonb_array_length(v_data->'items');
IF v_len > 2 THEN
v_val := jsonb_path_query_first(v_data, 'lax $.items[2]');
RAISE NOTICE 'Value: %', v_val;
ELSE
RAISE NOTICE 'Index out of range. Array length: %', v_len;
END IF;
END;
$$;
-- 해결 방법 3: 테이블 쿼리에서 안전하게 배열 요소 추출
SELECT
id,
jsonb_path_query_first(payload, 'lax $.tags[0]') AS first_tag,
jsonb_path_query_first(payload, 'lax $.tags[1]') AS second_tag
FROM events
WHERE payload ? 'tags';
원인 3: JSON_VALUE / JSON_QUERY 의 ON EMPTY 절 수정
ERROR ON EMPTY 대신 NULL ON EMPTY 또는 DEFAULT ... ON EMPTY를 명시하여 에러 없이 처리하세요.
-- PostgreSQL 14+ 기준
-- 에러 발생: ERROR ON EMPTY (기본 동작 또는 명시)
SELECT JSON_VALUE(
'{"name": "Charlie"}'::json,
'$.age'
ERROR ON EMPTY
);
-- ERROR: 22035: no SQL/JSON item
-- 해결 방법 1: NULL ON EMPTY 사용
SELECT JSON_VALUE(
'{"name": "Charlie"}'::json,
'$.age'
NULL ON EMPTY
) AS age;
-- 결과: NULL
-- 해결 방법 2: DEFAULT 값 지정
SELECT JSON_VALUE(
'{"name": "Charlie"}'::json,
'$.age'
DEFAULT '0' ON EMPTY
) AS age;
-- 결과: '0'
-- 해결 방법 3: JSON_QUERY에서 동일 패턴 적용
SELECT JSON_QUERY(
'{"user": "Dave"}'::json,
'$.address'
NULL ON EMPTY
NULL ON ERROR
) AS address;
-- 결과: NULL
-- 실무 테이블 예제: 여러 컬럼을 안전하게 추출
SELECT
id,
JSON_VALUE(profile::json, '$.name' NULL ON EMPTY) AS name,
JSON_VALUE(profile::json, '$.email' NULL ON EMPTY) AS email,
JSON_VALUE(profile::json, '$.age' DEFAULT '0' ON EMPTY) AS age
FROM users;
예방 방법
- 항상
lax모드를 기본으로 사용하고,strict모드는 데이터 무결성이 완전히 보장된 경우에만 제한적으로 적용하세요.
운영 환경에서는 JSON 데이터의 구조가 항상 일정하다고 보장하기 어렵습니다. 따라서 SQL/JSON 경로 표현식을 작성할 때는 lax 모드를 기본으로 채택하고, 경로가 없을 경우 COALESCE나 DEFAULT ON EMPTY를 결합하여 NULL-safe한 쿼리를 작성하는 습관을 가지세요. strict 모드는 JSON 스키마 유효성 검사 목적처럼 오류 발생이 의도적일 때만 사용하세요.
“`sql
— 권장 패턴: lax + COALESCE 조합
SELECT
id,
COALESCE(
jsonb_path_query_first(data, ‘lax $.price’)::numeric,
0
) AS price
FROM products;
“`
- JSON 데이터를 저장하기 전에 스키마 검증 로직(CHECK 제약 또는 트리거)을 추가하여 필수 필드의 존재를 보장하세요.
애플리케이션 레이어나 DB 레이어에서 JSON 데이터의 필수 키 존재 여부를 사전에 검증하면, 런타임 에러를 사전에 방지할 수 있습니다. PostgreSQL의 CHECK 제약 조건과 jsonb @> 연산자를 활용하거나, 트리거를 사용해 필수 필드가 없는 레코드가 삽입되지 않도록 막는 것이 좋습니다.
“`sql
— CHECK 제약으로 필수 키 보장
ALTER TABLE orders
ADD CONSTRAINT chk_order_payload_required_keys
CHECK (
payload ? ‘order_id’
AND payload ? ‘user_id’
AND payload ? ‘items’
);
— 트리거 방식으로 필수 키 검증
CREATE OR REPLACE FUNCTION validate_order_payload()
RETURNS TRIGGER AS $$
BEGIN
IF NOT (NEW.payload ? ‘order_id’ AND NEW.payload ? ‘items’) THEN
RAISE EXCEPTION ‘payload must contain order_id and items keys’;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_validate_order_payload
BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW EXECUTE FUNCTION validate_order_payload();
“`
관련 에러
22032(invalid_json_text): JSON 문자열 자체가 파싱 불가능한 형식일 때 발생합니다.22035와 달리 경로가 아닌 JSON 형식 자체의 오류입니다.22033(invalid_sql_json_subscript): SQL/JSON 경로에서 배열 서브스크립트가 유효하지 않은 타입일 때 발생합니다.22034(more_than_one_sql_json_item): 경로 표현식이 단일 항목을 기대하는 함수(예:JSON_VALUE)에서 복수의 항목을 반환할 때 발생합니다.22035의 반대 상황으로 볼 수 있습니다.22036(no_sql_json_object): 경로에서 JSON 객체가 기대되지만 없을 때 발생하는 유사 오류입니다.22037(singleton_sql_json_item_required): 단일 JSON 항목이 요구되는 컨텍스트에서 여러 항목이 반환될 때 발생합니다.
이러한 221xx ~ 220xx 계열의 SQL/JSON 에러들은 모두 JSON 경로 처리와 밀접하게 연관되어 있으므로, 함께 이해해두면 디버깅 시 큰 도움이 됩니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.