2026년 08월 23일 | DBMS Error 가이드
이 글에서 다루는 내용
2203A 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2203A sql json member not found 는?
PostgreSQL 에러 코드 2203A는 SQL/JSON 경로 표현식에서 특정 멤버(키)를 찾을 수 없을 때 발생하는 오류입니다. 이 에러는 주로 jsonb_path_query, jsonb_path_value, JSON_VALUE, JSON_QUERY 등의 SQL/JSON 함수에서 strict 모드를 사용할 때 나타납니다. 즉, JSON 데이터 내에 요청한 키나 속성이 존재하지 않을 경우 PostgreSQL이 이 에러를 발생시키며, lax 모드와 달리 strict 모드에서는 누락된 멤버를 자동으로 무시하지 않습니다.
주요 발생 원인
- strict 모드에서 존재하지 않는 JSON 키 접근
SQL/JSON 경로 표현식에서 strict 키워드를 명시한 상태에서 JSON 객체에 존재하지 않는 멤버를 참조할 경우 에러가 발생합니다. lax 모드에서는 존재하지 않는 멤버를 조용히 빈 시퀀스로 처리하지만, strict 모드는 이를 허용하지 않고 즉시 에러를 반환합니다. 대부분의 실무 환경에서 데이터 품질 검증 목적으로 strict 모드를 사용하다가 이 에러를 처음 접하는 경우가 많습니다.
- JSON 데이터 구조의 불일치 또는 스키마 변경
애플리케이션이 발전하면서 JSON 데이터의 구조가 변경되거나, 서로 다른 버전의 데이터가 같은 컬럼에 혼재하는 경우 특정 레코드에는 해당 키가 없을 수 있습니다. 예를 들어, 초기에는 address 필드가 없었다가 나중에 추가된 경우, 오래된 레코드에 대해 strict 모드 경로 쿼리를 실행하면 이 에러가 발생합니다. 이처럼 스키마리스(Schemaless) JSON의 유연성이 오히려 독이 되는 대표적인 상황입니다.
- 동적으로 생성된 JSON 경로 표현식의 오타 또는 잘못된 키 이름
프로그래밍 방식으로 JSON 경로 문자열을 생성할 때 키 이름에 오타가 생기거나 대소문자가 맞지 않는 경우가 있습니다. JSON 키는 대소문자를 구분하기 때문에 "UserName"과 "username"은 완전히 다른 멤버로 취급됩니다. 특히 ORM이나 쿼리 빌더를 통해 경로 표현식이 자동 생성될 때 이러한 문제가 은밀하게 발생하는 경우가 많습니다.
해결 방법
원인 1 해결: lax 모드 사용 또는 에러 핸들링
가장 직접적인 해결책은 strict 모드 대신 lax 모드를 사용하거나, ON ERROR 절을 활용하여 에러 발생 시 기본값을 반환하도록 하는 것입니다.
-- 문제가 발생하는 쿼리 (strict 모드)
SELECT JSON_VALUE(
'{"name": "Alice"}'::json,
'strict $.address'
);
-- ERROR: 2203A: SQL/JSON member not found
-- 해결책 1: lax 모드 사용 (기본값)
SELECT JSON_VALUE(
'{"name": "Alice"}'::json,
'lax $.address'
);
-- 결과: NULL (에러 없이 처리)
-- 해결책 2: ON ERROR 절로 기본값 지정
SELECT JSON_VALUE(
'{"name": "Alice"}'::json,
'strict $.address'
DEFAULT 'N/A' ON ERROR
);
-- 결과: 'N/A'
-- 해결책 3: jsonb_path_exists로 사전 확인 후 접근
SELECT
CASE
WHEN jsonb_path_exists(data, '$.address')
THEN jsonb_path_query_first(data, '$.address')::text
ELSE 'No address'
END AS address_info
FROM users;
원인 2 해결: 키 존재 여부 확인 후 처리
JSON 데이터 구조가 혼재하는 경우, 조회 전에 키 존재 여부를 검사하는 방어적 쿼리를 작성합니다.
-- 테스트용 테이블 및 데이터 준비
CREATE TABLE user_profiles (
id SERIAL PRIMARY KEY,
data JSONB
);
INSERT INTO user_profiles (data) VALUES
('{"name": "Alice", "email": "alice@example.com", "address": "Seoul"}'),
('{"name": "Bob", "email": "bob@example.com"}'),
('{"name": "Charlie"}');
-- 문제: strict 모드로 전체 테이블 조회 시 에러 발생
SELECT JSON_VALUE(data::json, 'strict $.address')
FROM user_profiles;
-- ERROR: 2203A 발생 (Bob, Charlie 레코드에 address 없음)
-- 해결: ? 연산자로 키 존재 확인
SELECT
id,
data->>'name' AS name,
CASE
WHEN data ? 'address' THEN data->>'address'
ELSE '주소 없음'
END AS address
FROM user_profiles;
-- 또는 jsonb_path_query_first 활용 (NULL 안전)
SELECT
id,
jsonb_path_query_first(data, 'lax $.address') AS address
FROM user_profiles;
-- NULL 데이터를 필터링하는 경우
SELECT *
FROM user_profiles
WHERE data ? 'address'
AND data->>'address' IS NOT NULL;
원인 3 해결: 키 이름 대소문자 및 정확성 검증
-- JSON 데이터 내 실제 키 목록 확인
SELECT DISTINCT jsonb_object_keys(data)
FROM user_profiles
ORDER BY 1;
-- 대소문자 구분 문제 예시
SELECT JSON_VALUE(
'{"UserName": "Alice"}'::json,
'strict $.username' -- 소문자로 잘못 작성
);
-- ERROR: 2203A 발생
-- 올바른 키 이름 사용
SELECT JSON_VALUE(
'{"UserName": "Alice"}'::json,
'strict $.UserName' -- 정확한 대소문자 사용
);
-- 결과: 'Alice'
-- 키 존재 여부를 사전에 검사하는 함수 작성
CREATE OR REPLACE FUNCTION safe_json_value(
p_data JSONB,
p_key TEXT,
p_default TEXT DEFAULT NULL
)
RETURNS TEXT AS $$
BEGIN
IF p_data ? p_key THEN
RETURN p_data ->> p_key;
ELSE
RETURN p_default;
END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- 함수 사용 예시
SELECT
id,
safe_json_value(data, 'address', '미등록') AS address,
safe_json_value(data, 'email', '이메일 없음') AS email
FROM user_profiles;
예방 방법
- JSON 스키마 검증 및 CHECK 제약조건 적용
JSON 데이터를 INSERT/UPDATE할 때 필수 키가 반드시 포함되도록 CHECK 제약조건을 활용하거나, 트리거를 통해 스키마를 강제하는 것이 좋습니다. 이렇게 하면 데이터 입력 단계에서부터 구조적 일관성을 보장할 수 있습니다.
“`sql
— CHECK 제약조건으로 필수 키 강제
ALTER TABLE user_profiles
ADD CONSTRAINT chk_required_keys
CHECK (
data ? ‘name’ AND
data ? ’email’
);
— pg_jsonschema 확장을 사용한 고급 스키마 검증 (설치 필요)
— CREATE EXTENSION pg_jsonschema;
— ALTER TABLE user_profiles
— ADD CONSTRAINT chk_json_schema
— CHECK (jsonb_matches_schema(
— ‘{“type”: “object”, “required”: [“name”, “email”]}’,
— data
— ));
“`
- lax 모드를 기본으로 사용하고 strict는 명시적 목적에만 적용
특별한 이유가 없다면 SQL/JSON 경로 쿼리에서 lax 모드(PostgreSQL 기본값)를 사용하고, strict 모드는 데이터 품질 감사나 명시적인 무결성 검사 목적으로만 제한적으로 사용하는 정책을 수립하세요. 또한 팀 내 코딩 가이드라인에 JSON 쿼리 작성 규칙을 문서화하여 공유하는 것이 좋습니다.
“`sql
— 권장: lax 모드 + COALESCE로 안전하게 처리
SELECT
id,
COALESCE(
jsonb_path_query_first(data, ‘lax $.address’)::text,
‘기본 주소’
) AS address
FROM user_profiles;
“`
관련 에러
- 2203B (sql_json_array_not_found): JSON 경로가 배열 요소를 기대하지만 해당 위치에 배열이 없을 때 발생합니다.
- 2203C (sql_json_number_not_found): JSON 경로 결과에서 숫자 값을 기대했지만 찾지 못했을 때 발생합니다.
- 2203D (sql_json_object_not_found): JSON 경로 표현식이 객체를 기대하지만 해당 위치에 객체가 없을 때 발생합니다.
- 2203F (sql_json_scalar_required): JSON_VALUE 함수에서 스칼라 값이 아닌 배열이나 객체가 반환될 때 발생하며, 2203A와 함께 strict 모드 사용 시 자주 동반됩니다.
- 22032 (invalid_json_text): JSON 형식 자체가 잘못된 경우 발생하며, 2203A 이전에 먼저 확인해야 할 에러입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.