PostgreSQL 22035 오류 원인과 해결 방법 완벽 가이드

22035
2026년 06월 17일 | DBMS Error 가이드

이 글에서 다루는 내용

22035 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

22035 no sql json item 는?

PostgreSQL 에러 코드 22035no_sql_json_item 에러로, SQL/JSON 경로 표현식(path expression)이 JSON 데이터 내에서 일치하는 항목을 찾지 못했을 때 발생합니다. 이 에러는 주로 jsonb_path_query(), jsonb_path_value() 등 JSON 경로 함수를 사용할 때 경로에 해당하는 데이터가 존재하지 않는 경우에 트리거됩니다. 특히 _strict 모드(엄격 모드)를 사용하는 함수에서 더욱 빈번하게 발생하며, 실무에서 복잡한 JSON 구조를 처리할 때 주의가 필요합니다.


주요 발생 원인

1. 존재하지 않는 JSON 경로 참조

가장 흔한 원인으로, JSON 데이터 내에 실제로 존재하지 않는 키 또는 인덱스를 경로 표현식으로 참조할 때 발생합니다. 예를 들어, $.user.address.city와 같은 경로를 지정했을 때 address 키 자체가 없거나 city가 누락된 경우입니다. jsonb_path_value()처럼 단일 값을 반환해야 하는 함수는 값이 없으면 에러를 발생시킵니다.

2. 배열 인덱스 범위 초과

JSON 배열에서 존재하지 않는 인덱스를 참조하는 경우에도 이 에러가 발생합니다. 예를 들어 배열에 3개의 요소가 있는데 $[5]처럼 범위를 벗어난 인덱스에 접근하면 no_sql_json_item 에러가 발생할 수 있습니다. 이는 동적으로 생성된 JSON 데이터를 처리할 때 데이터 크기를 예측하지 못하는 상황에서 자주 발생합니다.

3. strict 모드에서의 구조적 불일치

PostgreSQL의 JSON 경로 함수는 기본적으로 lax 모드로 동작하지만, strict 키워드를 명시하면 엄격하게 경로를 검증합니다. strict 모드에서는 경로가 하나라도 맞지 않으면 에러를 발생시키며, 특히 배열과 객체 구조가 섞인 복잡한 JSON 문서를 처리할 때 예상치 못한 구조 불일치로 인해 에러가 빈번하게 발생합니다.


해결 방법

원인 1: 존재하지 않는 JSON 경로 참조 해결

jsonb_path_value() 대신 jsonb_path_value_tz() 혹은 안전한 버전인 jsonb_path_query_first()를 사용하거나, 경로 존재 여부를 먼저 확인하는 방법을 사용합니다.

-- 에러 발생 예시
SELECT jsonb_path_value('{"user": {"name": "Alice"}}'::jsonb, '$.user.address.city');
-- ERROR: no SQL/JSON item

-- 해결 방법 1: 경로 존재 여부 먼저 확인
SELECT
  CASE
    WHEN jsonb_path_exists('{"user": {"name": "Alice"}}'::jsonb, '$.user.address.city')
    THEN jsonb_path_value('{"user": {"name": "Alice"}}'::jsonb, '$.user.address.city')
    ELSE NULL
  END AS city;

-- 해결 방법 2: jsonb_path_query_first 사용 (없으면 NULL 반환)
SELECT jsonb_path_query_first(
  '{"user": {"name": "Alice"}}'::jsonb,
  '$.user.address.city'
) AS city;

-- 해결 방법 3: ? 연산자로 키 존재 확인 후 처리
SELECT
  CASE
    WHEN data->'user' ? 'address'
    THEN data->'user'->'address'->>'city'
    ELSE '주소 없음'
  END AS city
FROM (
  SELECT '{"user": {"name": "Alice"}}'::jsonb AS data
) t;

원인 2: 배열 인덱스 범위 초과 해결

배열 접근 전에 배열 크기를 확인하거나, 안전한 경로 조회 함수를 활용하는 방법을 사용합니다.

-- 에러 발생 예시: 배열 크기 초과
SELECT jsonb_path_value('{"items": [1, 2, 3]}'::jsonb, '$.items[5]');
-- ERROR: no SQL/JSON item

-- 해결 방법 1: 배열 크기 확인 후 접근
DO $$
DECLARE
  v_data jsonb := '{"items": [1, 2, 3]}';
  v_index int := 5;
  v_array_size int;
BEGIN
  v_array_size := jsonb_array_length(v_data->'items');

  IF v_index < v_array_size THEN
    RAISE NOTICE '값: %', (v_data->'items'->v_index)::text;
  ELSE
    RAISE NOTICE '인덱스 % 는 배열 범위(0~%)를 초과합니다.', v_index, v_array_size - 1;
  END IF;
END;
$$;

-- 해결 방법 2: jsonb_path_query_first로 안전하게 접근
SELECT jsonb_path_query_first(
  '{"items": [1, 2, 3]}'::jsonb,
  '$.items[5]'
) AS item;  -- NULL 반환 (에러 없음)

-- 해결 방법 3: unnest를 활용한 안전한 배열 처리
SELECT
  elem,
  ordinality - 1 AS index
FROM
  jsonb_array_elements('{"items": [10, 20, 30]}'::jsonb -> 'items')
  WITH ORDINALITY AS t(elem, ordinality)
WHERE ordinality - 1 = 2;  -- 안전하게 특정 인덱스 접근

원인 3: strict 모드 구조적 불일치 해결

strict 모드 대신 lax 모드를 사용하거나, 데이터 유효성을 사전 검증하는 방법을 적용합니다.

-- strict 모드에서 에러 발생
SELECT jsonb_path_value(
  '{"orders": {"id": 1}}'::jsonb,
  'strict $.orders[*].id'
);
-- ERROR: no SQL/JSON item (strict 모드에서 배열이 아닌 객체를 배열처럼 접근)

-- 해결 방법 1: lax 모드 사용 (기본값)
SELECT jsonb_path_query(
  '{"orders": {"id": 1}}'::jsonb,
  'lax $.orders[*].id'
) AS order_id;

-- 해결 방법 2: 데이터 구조 사전 검증 함수 작성
CREATE OR REPLACE FUNCTION safe_json_path_value(
  p_data jsonb,
  p_path text
) RETURNS jsonb AS $$
BEGIN
  RETURN jsonb_path_query_first(p_data, p_path::jsonpath);
EXCEPTION
  WHEN sqlstate '22035' THEN
    RETURN NULL;
  WHEN OTHERS THEN
    RAISE;
END;
$$ LANGUAGE plpgsql;

-- 사용 예시
SELECT safe_json_path_value(
  '{"user": {"name": "Bob"}}'::jsonb,
  '$.user.email'
) AS email;  -- NULL 반환

-- 해결 방법 3: 실무에서 자주 쓰는 패턴 - 기본값 처리
SELECT COALESCE(
  jsonb_path_query_first(
    '{"profile": {"age": 25}}'::jsonb,
    '$.profile.nickname'
  )::text,
  '"anonymous"'
) AS nickname;

예방 방법

1. jsonb_path_query_first() 및 jsonb_path_exists()를 기본으로 사용하기

실무에서 JSON 경로 쿼리를 작성할 때는 항상 값이 없을 수 있다는 가능성을 염두에 두고, jsonb_path_value() 대신 NULL을 반환하는 jsonb_path_query_first()를 기본 함수로 채택하는 것이 좋습니다. 또한 복잡한 경로 접근 전에는 반드시 jsonb_path_exists()로 경로 존재 여부를 먼저 검증하고, 이를 프로젝트 내 JSON 처리 코딩 컨벤션으로 문서화하여 팀 전체가 일관되게 따를 수 있도록 합니다.

-- 권장 패턴: 항상 존재 확인 후 값 조회
SELECT
  jsonb_path_query_first(data, '$.user.name') AS name,
  jsonb_path_query_first(data, '$.user.email') AS email,
  jsonb_path_query_first(data, '$.user.phone') AS phone
FROM user_profiles;

2. JSON 스키마 검증 로직을 데이터 입력 단계에서 적용하기

데이터가 DB에 저장되기 전에 JSON 구조를 검증하는 CHECK 제약 조건이나 트리거를 설정하면 런타임 에러를 사전에 방지할 수 있습니다. PostgreSQL 16 이상에서는 IS JSON 술어와 결합한 제약 조건을 활용할 수 있으며, 필수 키의 존재 여부를 강제하는 방식으로 데이터 품질을 유지합니다.

-- CHECK 제약 조건으로 필수 JSON 구조 강제
CREATE TABLE user_profiles (
  id SERIAL PRIMARY KEY,
  data jsonb NOT NULL,
  CONSTRAINT chk_required_fields CHECK (
    (data ? 'user') AND
    (data->'user' ? 'name') AND
    (data->'user' ? 'email')
  )
);

-- 삽입 테스트
INSERT INTO user_profiles (data)
VALUES ('{"user": {"name": "Alice", "email": "alice@example.com"}}');  -- 성공

INSERT INTO user_profiles (data)
VALUES ('{"user": {"name": "Bob"}}');  -- 실패: email 누락

관련 에러

  • 22032 (invalid_json_text): JSON 문자열 자체가 올바른 형식이 아닐 때 발생하는 에러로, 22035보다 먼저 처리해야 하는 파싱 단계 에러입니다.
  • 22033 (impossible_power): JSON 경로 연산 과정에서 수학적으로 불가능한 연산이 발생할 때 나타납니다.
  • 22034 (invalid_argument_for_sql_json_datetime_function): JSON 날짜/시간 처리 함수에 잘못된 인수가 전달되었을 때 발생하며, datetime 관련 JSON 경로를 처리할 때 함께 주의해야 합니다.
  • 22036 (null_value_not_allowed): JSON 경로가 NULL을 반환했는데 NULL이 허용되지 않는 컨텍스트에서 사용될 때 발생하며, 22035와 유사한 상황에서 함께 나타날 수 있습니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기