2026년 08월 23일 | DBMS Error 가이드
이 글에서 다루는 내용
2203B 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2203B sql json number not found 는?
PostgreSQL 에러 코드 2203B는 SQL/JSON 경로 표현식(SQL/JSON Path Expression)을 사용할 때 숫자(number) 타입의 값이 예상된 위치에 존재하지 않을 경우 발생하는 에러입니다. 이 에러는 주로 jsonb_path_query, jsonb_path_exists, @?, @@ 연산자 등 JSON 경로 함수를 사용하면서 숫자 값을 추출하거나 연산하려 할 때 나타납니다. 예를 들어 JSON 데이터 내에서 숫자여야 할 필드가 문자열이거나 null이거나 아예 키가 없는 경우에 이 에러가 트리거됩니다.
주요 발생 원인
1. JSON 경로가 가리키는 값이 숫자가 아닌 다른 타입인 경우
가장 흔한 원인으로, JSON 필드에 저장된 값이 숫자라고 가정하고 SQL/JSON 경로 표현식에서 산술 연산이나 비교 연산을 수행할 때, 실제 값이 문자열("123")이거나 불리언(true/false), 혹은 배열/객체인 경우 PostgreSQL은 숫자를 찾지 못했다고 판단해 이 에러를 발생시킵니다. 데이터 수집 단계에서 타입 통일이 되지 않은 경우 특히 자주 발생합니다.
2. JSON 경로가 가리키는 키가 존재하지 않거나 null인 경우
SQL/JSON 경로 표현식이 특정 키를 참조했지만 해당 키 자체가 JSON 문서에 없거나, 값이 명시적으로 null로 저장되어 있는 경우에도 숫자를 찾지 못한 것으로 처리됩니다. 특히 동적으로 생성된 JSON 데이터를 다루는 API 연동 환경이나 ETL 파이프라인에서 컬럼 누락이 발생할 때 이 상황이 빈번하게 나타납니다.
3. 경로 표현식의 필터 또는 메서드 사용 오류
.abs(), .floor(), .ceiling() 등 숫자 전용 SQL/JSON 메서드를 사용할 때, 해당 경로에 숫자가 아닌 값이 오면 에러가 발생합니다. 또한 경로 표현식의 필터 조건(? (@.price > 100))에서 대상 값이 숫자가 아닌 경우에도 동일한 에러가 발생할 수 있으며, 이는 경로 표현식 설계 시 타입 안정성을 고려하지 않은 것이 원인입니다.
해결 방법
원인 1 해결: 타입 확인 후 처리
jsonb_typeof() 함수나 SQL/JSON 경로의 is unknown 조건을 활용해 숫자인지 먼저 확인한 뒤 연산을 수행합니다.
-- 문제 상황: price 필드가 문자열로 저장된 경우 에러 발생
SELECT jsonb_path_query('{"price": "150"}', '$.price.abs()');
-- ERROR: 2203B: sql json number not found
-- 해결책 1: jsonb_typeof로 타입 먼저 확인
SELECT
data,
jsonb_typeof(data -> 'price') AS price_type
FROM products
WHERE jsonb_typeof(data -> 'price') = 'number';
-- 해결책 2: 안전한 경로 표현식 사용 (IS UNKNOWN 처리)
SELECT jsonb_path_query_first(
'{"price": "150"}',
'$.price ? (@ is number)'
);
-- 숫자가 아니면 NULL 반환 (에러 없음)
-- 해결책 3: 문자열을 숫자로 변환 후 처리
SELECT (data ->> 'price')::numeric AS price
FROM products
WHERE data ->> 'price' ~ '^\d+(\.\d+)?$';
원인 2 해결: 키 존재 여부 및 null 처리
-- 문제 상황: 키가 없거나 null인 경우
SELECT jsonb_path_query('{"name": "item"}', '$.price + 10');
-- ERROR: 2203B: sql json number not found
-- 해결책 1: 키 존재 여부 먼저 확인
SELECT *
FROM orders
WHERE data ? 'amount'
AND jsonb_typeof(data -> 'amount') = 'number';
-- 해결책 2: jsonb_path_query_first로 NULL 안전하게 처리
SELECT COALESCE(
jsonb_path_query_first(data, '$.price')::numeric,
0
) AS safe_price
FROM products;
-- 해결책 3: 기본값과 함께 경로 표현식 사용
SELECT jsonb_path_query_first(
'{"name": "item"}',
'strict $.price ? (exists(@))'
);
-- strict 모드: 키 없으면 에러, lax 모드(기본): NULL 반환
-- lax 모드 명시적 사용
SELECT jsonb_path_query_first(
'{"name": "item"}',
'lax $.price'
);
원인 3 해결: 숫자 전용 메서드 사용 시 타입 가드
-- 문제 상황: .abs() 사용 시 숫자가 아닌 값
SELECT jsonb_path_query('{"delta": "unknown"}', '$.delta.abs()');
-- ERROR: 2203B: sql json number not found
-- 해결책 1: 조건 필터로 숫자인 경우만 메서드 적용
SELECT jsonb_path_query(
'{"delta": -50}',
'$.delta ? (@ is number).abs()'
);
-- 해결책 2: 배치 처리 시 타입 안전한 쿼리 작성
SELECT
id,
CASE
WHEN jsonb_typeof(data -> 'score') = 'number'
THEN jsonb_path_query_first(data, '$.score.floor()')::integer
ELSE NULL
END AS safe_score
FROM student_records;
-- 해결책 3: 필터 조건에서 타입 안전성 확보
SELECT *
FROM transactions
WHERE jsonb_path_exists(data, '$.amount ? (@ is number && @ > 1000)');
예방 방법
1. JSON 데이터 저장 시 스키마 검증 적용
PostgreSQL의 CHECK 제약조건과 jsonb_typeof() 또는 JSON Schema 검증을 활용해 데이터 입력 단계에서 타입을 강제합니다. 이렇게 하면 잘못된 타입의 데이터가 DB에 저장되는 것을 원천 차단할 수 있습니다.
-- CHECK 제약조건으로 숫자 필드 타입 강제
ALTER TABLE products
ADD CONSTRAINT chk_price_is_number
CHECK (
data -> 'price' IS NULL
OR jsonb_typeof(data -> 'price') = 'number'
);
-- 도메인을 활용한 검증
CREATE OR REPLACE FUNCTION validate_product_json(data jsonb)
RETURNS boolean AS $$
BEGIN
IF jsonb_typeof(data -> 'price') != 'number' THEN
RAISE EXCEPTION '가격(price)은 숫자여야 합니다. 현재 타입: %',
jsonb_typeof(data -> 'price');
END IF;
RETURN true;
END;
$$ LANGUAGE plpgsql;
2. SQL/JSON 경로 표현식 작성 시 항상 lax 모드와 타입 필터 병행 사용
운영 환경에서 JSON 경로 쿼리를 작성할 때는 반드시 lax 모드(PostgreSQL 기본값)를 명시하고, ? (@ is number) 같은 타입 필터를 경로 표현식에 포함시키는 것을 팀 코딩 컨벤션으로 정착시킵니다. 이를 통해 예기치 않은 데이터 타입 변화에도 쿼리가 안전하게 동작하도록 보장할 수 있습니다.
-- 팀 컨벤션: 안전한 JSON 숫자 추출 패턴
-- BAD: 타입 확인 없이 메서드 호출
SELECT jsonb_path_query(data, '$.amount.abs()') FROM ledger;
-- GOOD: 항상 타입 필터 포함
SELECT jsonb_path_query(data, 'lax $.amount ? (@ is number).abs()')
FROM ledger;
-- GOOD: 집계 시에도 안전하게
SELECT
AVG(
jsonb_path_query_first(data, 'lax $.score ? (@ is number)')::numeric
) AS avg_score
FROM evaluations;
관련 에러
22032(invalid_json_text): JSON 문자열 자체가 파싱 불가능한 형식일 때 발생. JSON 경로 연산 전 단계에서 실패하는 에러입니다.2203A(sql_json_array_not_found): 숫자 대신 배열이 기대되는 경로에서 배열을 찾지 못했을 때 발생하며,2203B와 유사한 패턴으로 처리할 수 있습니다.2203C(sql_json_object_not_found): 객체가 예상된 위치에 없을 때 발생하며,2203B와 함께 JSON 경로 표현식의 타입 안전성 문제를 나타내는 에러 계열입니다.2203F(sql_json_scalar_required): 스칼라 값이 필요한 위치에 배열이나 객체가 왔을 때 발생하며, JSON 경로 결과 처리 시 함께 고려해야 합니다.22033(invalid_sql_json_subscript): JSON 배열 접근 인덱스가 잘못되었을 때 발생하는 에러로, 동적 경로 생성 시 함께 주의해야 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.