2026년 08월 16일 | DBMS Error 가이드
이 글에서 다루는 내용
22003 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22003 numeric value out of range 는?
PostgreSQL 에러 코드 22003은 numeric value out of range 로, 숫자형 데이터 타입이 허용하는 범위를 초과하는 값을 저장하거나 연산하려 할 때 발생합니다. 예를 들어 SMALLINT 컬럼에 32,767을 초과하는 값을 삽입하거나, NUMERIC(5,2) 타입에 999.99를 넘는 값을 넣으려 할 때 이 에러가 발생합니다. 이 에러는 데이터 마이그레이션, 배치 처리, 또는 외부 시스템으로부터 데이터를 적재할 때 특히 자주 나타나며, 운영 환경에서 갑작스럽게 발생하면 서비스 장애로 이어질 수 있어 반드시 원인 파악과 사전 예방이 필요합니다.
주요 발생 원인
1. 데이터 타입의 범위 한계 초과
가장 흔한 원인으로, 컬럼에 정의된 데이터 타입의 최대/최솟값을 벗어나는 값을 삽입하거나 업데이트할 때 발생합니다. PostgreSQL의 정수형 타입별 허용 범위는 다음과 같습니다: SMALLINT는 -32,768 ~ 32,767, INTEGER는 약 -21억 ~ 21억, BIGINT는 약 -922경 ~ 922경입니다. 서비스 초기 설계 시 SMALLINT나 INTEGER로 정의한 기본 키(PK) 또는 카운터 컬럼이 데이터가 누적됨에 따라 한계치에 도달하는 경우가 실무에서 매우 빈번합니다.
2. NUMERIC/DECIMAL 타입의 정밀도(Precision) 초과
NUMERIC(precision, scale) 타입에서 precision은 전체 유효 자릿수를, scale은 소수점 이하 자릿수를 의미합니다. 예를 들어 NUMERIC(6, 2)로 정의된 컬럼에는 최대 9999.99까지 저장할 수 있으며, 이를 초과하는 값(예: 10000.00)을 삽입하면 즉시 22003 에러가 발생합니다. 금융, 회계 시스템에서 금액이나 환율 데이터를 다룰 때 초기 설계된 자릿수가 부족해지는 상황에서 자주 목격됩니다.
3. 산술 연산 또는 집계 함수 결과값 오버플로우
단순 삽입뿐 아니라 SUM(), COUNT(), 곱셈 연산 등의 결과가 대상 컬럼이나 표현식의 타입 범위를 초과할 때도 동일한 에러가 발생합니다. 특히 대용량 테이블에서 INTEGER 타입 컬럼의 SUM() 연산 결과가 INTEGER 범위를 넘어설 때, 또는 두 NUMERIC 값을 곱한 결과의 자릿수가 지정된 precision을 초과할 때 이 에러가 발생합니다. 배치 프로세스나 리포트 생성 쿼리에서 예상치 못하게 나타나는 경우가 많아 디버깅이 어렵습니다.
해결 방법
원인 1 해결: 데이터 타입 변경 (ALTER TABLE)
컬럼의 데이터 타입을 더 넓은 범위의 타입으로 변경합니다. 운영 중인 대용량 테이블의 경우 ALTER TABLE이 테이블 잠금을 유발할 수 있으므로 점검 시간이나 pg_repack 같은 도구 활용을 권장합니다.
-- 문제 상황: INTEGER 한계 초과 직전의 시퀀스 확인
SELECT last_value FROM your_sequence_name;
-- SMALLINT -> INTEGER 변경
ALTER TABLE orders
ALTER COLUMN status_code TYPE INTEGER;
-- INTEGER -> BIGINT 변경 (PK 포함 연관 테이블 FK도 함께 변경 필요)
ALTER TABLE users
ALTER COLUMN user_id TYPE BIGINT;
-- 변경 전 현재 최대값 확인
SELECT MAX(user_id), pg_typeof(MAX(user_id)) FROM users;
-- SERIAL 대신 BIGSERIAL 사용 (신규 테이블 설계 시)
CREATE TABLE new_orders (
order_id BIGSERIAL PRIMARY KEY,
amount NUMERIC(15, 2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
원인 2 해결: NUMERIC 정밀도 확장
-- 현재 컬럼 타입 확인
SELECT column_name, data_type, numeric_precision, numeric_scale
FROM information_schema.columns
WHERE table_name = 'products' AND column_name = 'price';
-- NUMERIC(6,2) -> NUMERIC(12,2) 로 확장
ALTER TABLE products
ALTER COLUMN price TYPE NUMERIC(12, 2);
-- 삽입 전 범위 유효성 검사 (애플리케이션 레벨 보조)
-- CHECK 제약 조건으로 사전 방어
ALTER TABLE products
ADD CONSTRAINT chk_price_range
CHECK (price >= 0 AND price < 10000000000.00);
-- 문제가 되는 데이터 사전 탐지
SELECT *
FROM products
WHERE price > 9999.99; -- 기존 NUMERIC(6,2) 한계
원인 3 해결: 연산 결과 타입 캐스팅
-- 문제 쿼리: INTEGER SUM 오버플로우
-- SELECT SUM(quantity) FROM order_items; -- 위험
-- 해결: BIGINT로 명시적 캐스팅
SELECT SUM(quantity::BIGINT) AS total_quantity
FROM order_items;
-- NUMERIC 곱셈 오버플로우 방지
SELECT
(unit_price::NUMERIC(15,4) * quantity::NUMERIC(15,4))::NUMERIC(20,4) AS line_total
FROM order_items;
-- 집계 전 범위 초과 데이터 사전 확인
SELECT
COUNT(*) AS total_rows,
SUM(quantity::BIGINT) AS safe_sum,
MAX(quantity) AS max_value,
MIN(quantity) AS min_value
FROM order_items;
-- 안전한 캐스팅 함수 활용 예시
CREATE OR REPLACE FUNCTION safe_int_to_bigint(val INTEGER)
RETURNS BIGINT AS $$
BEGIN
RETURN val::BIGINT;
EXCEPTION WHEN numeric_value_out_of_range THEN
RAISE WARNING 'Value % is out of range, returning NULL', val;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
예방 방법
1. 초기 설계 시 데이터 타입을 넉넉하게 선택하고 시퀀스 사용량을 주기적으로 모니터링하세요
신규 테이블 설계 시 기본 키(PK)와 카운터 컬럼은 특별한 이유가 없는 한 처음부터 BIGINT / BIGSERIAL을 사용하는 것을 표준으로 삼으세요. 또한 아래 쿼리를 주기적(예: 매일 1회)으로 실행하여 시퀀스 소진율을 모니터링하고, 80% 이상 사용 시 알람을 받을 수 있도록 모니터링 시스템에 통합하는 것을 강력히 권장합니다.
-- 시퀀스 소진율 모니터링 쿼리
SELECT
sequencename,
last_value,
max_value,
ROUND((last_value::NUMERIC / max_value::NUMERIC) * 100, 2) AS usage_pct
FROM pg_sequences
ORDER BY usage_pct DESC;
2. 데이터 적재 전 CHECK 제약 조건과 트리거로 이중 방어선을 구축하세요
애플리케이션 레벨의 유효성 검사만 믿지 말고, 데이터베이스 레벨에서 CHECK 제약 조건을 활용하여 범위를 초과하는 값이 절대 저장되지 않도록 강제하세요. 대규모 배치 데이터 적재 시에는 먼저 스테이징 테이블에 데이터를 넣고 범위 검사를 수행한 후 본 테이블로 이동하는 2단계 방식을 채택하면 운영 장애를 사전에 예방할 수 있습니다.
-- 스테이징 -> 본 테이블 안전 이관 예시
INSERT INTO products (product_id, price)
SELECT product_id, price
FROM staging_products
WHERE price BETWEEN 0 AND 99999999999.99 -- NUMERIC(13,2) 한계 이내
AND price IS NOT NULL;
관련 에러
- 22001
string_data_right_truncation: 문자열 데이터가 컬럼의 최대 길이를 초과할 때 발생하며, 22003과 함께 데이터 타입 범위 관련 에러의 대표적인 쌍입니다. - 22P02
invalid_text_representation: 문자열을 숫자형으로 변환 시 형식이 맞지 않을 때 발생하며, 외부 데이터 적재 시 22003과 함께 자주 등장합니다. - 23514
check_violation:CHECK제약 조건 위반으로, 범위 초과 방어를 위해 CHECK를 설정했을 때 22003 대신 이 에러가 발생할 수 있습니다. - 54000
program_limit_exceeded: 시스템 수준의 한계 초과로, 22003보다 더 심각한 수준의 오버플로우 상황에서 나타납니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.