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

2203C
2026년 10월 27일 | DBMS Error 가이드

이 글에서 다루는 내용

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

2203C sql json object not found 는?

PostgreSQL 에러 코드 2203C는 SQL/JSON 경로 표현식(path expression)을 사용하여 JSON 데이터를 조회할 때, 지정한 경로에 해당하는 객체나 값이 존재하지 않을 경우 발생하는 에러입니다. 이 에러는 주로 JSON_VALUE(), JSON_QUERY(), JSON_EXISTS(), JSONB_PATH_QUERY() 등의 SQL/JSON 함수에서 ERROR ON EMPTY 옵션을 사용하거나, 엄격한(strict) 경로 모드를 사용할 때 나타납니다. 쉽게 말해, JSON 문서 안에서 찾으려는 키나 배열 요소가 없는데 “반드시 있어야 한다”는 조건으로 조회했을 때 PostgreSQL이 이 에러를 던집니다.


주요 발생 원인

1. 존재하지 않는 JSON 키를 strict 모드로 접근

SQL/JSON 경로 표현식에는 lax(느슨한) 모드와 strict(엄격한) 모드가 있습니다. 기본값인 lax 모드는 경로가 존재하지 않아도 빈 시퀀스를 반환하지만, strict 모드는 경로가 반드시 존재해야 하며 없을 경우 에러를 발생시킵니다. 따라서 jsonb_path_query(data, 'strict $.nonexistent_key')처럼 존재하지 않는 키를 strict 모드로 탐색하면 2203C 에러가 발생합니다.

2. ERROR ON EMPTY 옵션과 함께 빈 결과 반환

JSON_VALUE() 또는 JSON_QUERY() 함수에서 ERROR ON EMPTY 옵션을 명시적으로 설정한 경우, 경로 표현식이 아무런 값도 반환하지 않으면 에러를 발생시킵니다. 이 옵션은 데이터의 완결성을 보장하기 위해 의도적으로 사용하는 경우도 있지만, 실제 데이터가 항상 해당 키를 포함하지 않을 때 예기치 않은 에러를 유발할 수 있습니다.

3. 배열 인덱스 범위 초과

JSON 배열에서 존재하지 않는 인덱스를 strict 모드로 접근하거나, 배열의 크기보다 큰 인덱스를 지정할 때도 이 에러가 발생합니다. 예를 들어, 배열에 요소가 2개뿐인데 $[5]처럼 6번째 요소를 접근하려 할 경우, strict 모드에서는 해당 인덱스를 찾을 수 없다는 에러를 반환합니다.


해결 방법

원인 1 해결: lax 모드 사용 또는 경로 존재 여부 확인

strict 모드 대신 lax 모드를 사용하거나, 먼저 경로가 존재하는지 확인한 후 접근하는 방식으로 해결할 수 있습니다.

-- 문제가 되는 쿼리 (strict 모드)
SELECT jsonb_path_query('{"name": "Alice"}'::jsonb, 'strict $.age');
-- ERROR: SQL/JSON object not found

-- 해결 방법 1: lax 모드 사용 (기본값)
SELECT jsonb_path_query_first('{"name": "Alice"}'::jsonb, 'lax $.age');
-- 결과: NULL (에러 없음)

-- 해결 방법 2: jsonb_path_exists로 사전 확인
SELECT 
    CASE 
        WHEN jsonb_path_exists('{"name": "Alice"}'::jsonb, '$.age')
        THEN jsonb_path_query_first('{"name": "Alice"}'::jsonb, '$.age')::text
        ELSE 'N/A'
    END AS age_value;

-- 해결 방법 3: -> 연산자 활용 (NULL 반환)
SELECT ('{"name": "Alice"}'::jsonb) -> 'age';
-- 결과: NULL

원인 2 해결: ERROR ON EMPTY 대신 DEFAULT 사용

JSON_VALUE() 함수에서 ERROR ON EMPTY 대신 DEFAULT ... ON EMPTY를 사용하면 값이 없을 때 기본값을 반환하도록 설정할 수 있습니다.

-- 문제가 되는 쿼리
SELECT JSON_VALUE('{"name": "Bob"}'::json, '$.age' ERROR ON EMPTY);
-- ERROR: SQL/JSON object not found

-- 해결 방법 1: DEFAULT 값 지정
SELECT JSON_VALUE('{"name": "Bob"}'::json, '$.age' DEFAULT '0' ON EMPTY);
-- 결과: 0

-- 해결 방법 2: NULL ON EMPTY (명시적 NULL 반환)
SELECT JSON_VALUE('{"name": "Bob"}'::json, '$.age' NULL ON EMPTY);
-- 결과: NULL

-- 해결 방법 3: COALESCE와 함께 사용
SELECT COALESCE(
    JSON_VALUE('{"name": "Bob"}'::json, '$.age' NULL ON EMPTY),
    '알 수 없음'
) AS age_display;

원인 3 해결: 배열 접근 시 안전한 방법 사용

배열 인덱스 접근 시 먼저 배열의 크기를 확인하거나, lax 모드와 jsonb_path_query_array를 활용합니다.

-- 예시 데이터
WITH sample AS (
    SELECT '{"items": ["apple", "banana"]}'::jsonb AS data
)

-- 문제가 되는 쿼리 (존재하지 않는 인덱스)
SELECT jsonb_path_query(data, 'strict $.items[5]') FROM sample;
-- ERROR: SQL/JSON object not found

-- 해결 방법 1: 배열 크기 확인 후 접근
SELECT 
    CASE 
        WHEN jsonb_array_length(data->'items') > 5
        THEN (data->'items'->5)::text
        ELSE '인덱스 범위 초과'
    END AS item
FROM sample;

-- 해결 방법 2: lax 모드로 안전하게 접근
SELECT jsonb_path_query_first(data, 'lax $.items[5]') FROM sample;
-- 결과: NULL (에러 없음)

-- 해결 방법 3: 전체 배열을 가져온 후 애플리케이션에서 처리
SELECT jsonb_path_query_array(data, '$.items[*]') FROM sample;
-- 결과: ["apple", "banana"]

예방 방법

1. JSON 데이터 접근 시 항상 lax 모드를 기본으로, strict는 꼭 필요할 때만 사용하세요

실무에서 JSON 데이터는 항상 완벽한 구조를 보장하기 어렵습니다. 특히 외부 API로부터 수신하거나 사용자 입력을 저장한 JSON 데이터는 키가 누락되거나 구조가 다를 수 있습니다. 기본적으로 lax 모드를 사용하고, 데이터 무결성이 100% 보장된 경우에만 strict 모드를 사용하는 것이 안전합니다.

-- 추천: 안전한 JSON 접근 함수 래퍼 생성
CREATE OR REPLACE FUNCTION safe_json_value(
    p_data jsonb,
    p_path text,
    p_default text DEFAULT NULL
)
RETURNS text AS $$
BEGIN
    RETURN COALESCE(
        jsonb_path_query_first(p_data, p_path::jsonpath)::text,
        p_default
    );
EXCEPTION WHEN OTHERS THEN
    RETURN p_default;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- 사용 예시
SELECT safe_json_value('{"name": "Alice"}'::jsonb, '$.age', '0');
-- 결과: 0

2. JSON 스키마 검증 및 NOT NULL 제약 조건으로 데이터 품질 관리

데이터가 테이블에 저장될 때부터 필수 키의 존재를 강제하면, 조회 시점에 에러가 발생할 가능성을 줄일 수 있습니다. PostgreSQL의 CHECK 제약 조건과 jsonb_path_exists()를 조합하면 효과적입니다.

-- JSON 필수 키 존재를 강제하는 CHECK 제약 조건
CREATE TABLE user_profiles (
    id SERIAL PRIMARY KEY,
    profile_data JSONB NOT NULL,
    CONSTRAINT chk_required_fields CHECK (
        jsonb_path_exists(profile_data, '$.name') AND
        jsonb_path_exists(profile_data, '$.email')
    )
);

-- 유효하지 않은 데이터 삽입 시 에러 발생
INSERT INTO user_profiles (profile_data) 
VALUES ('{"name": "Alice"}'::jsonb);
-- ERROR: new row for relation "user_profiles" violates check constraint

-- 올바른 데이터 삽입
INSERT INTO user_profiles (profile_data)
VALUES ('{"name": "Alice", "email": "alice@example.com"}'::jsonb);
-- INSERT 성공

관련 에러

  • 22032 (invalid_json_text): JSON 형식 자체가 잘못된 경우 발생하며, 2203C와는 달리 파싱 단계에서 실패합니다.
  • 2203F (sql_json_array_not_found): 배열이 아닌 곳에서 배열 연산을 시도할 때 발생하는 에러로, 2203C와 유사한 SQL/JSON 계열 에러입니다.
  • 2203W (sql_json_scalar_required): JSON_VALUE 함수에서 스칼라 값이 아닌 객체나 배열이 반환될 때 발생하며, 함께 자주 만나게 되는 에러입니다.
  • 22033 (invalid_sql_json_subscript): JSON 경로 표현식의 서브스크립트가 잘못된 경우 발생하며, 배열 인덱스 관련 문제에서 2203C와 함께 확인해야 할 에러입니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기