2026년 10월 20일 | DBMS Error 가이드
이 글에서 다루는 내용
22003 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22003 numeric value out of range 는?
PostgreSQL 에러 코드 22003(numeric_value_out_of_range)은 컬럼에 정의된 데이터 타입이 허용하는 범위를 초과하는 숫자 값을 삽입하거나 연산 결과가 해당 타입의 한계를 벗어날 때 발생합니다. 예를 들어 SMALLINT 컬럼에 40,000을 저장하려 하거나, NUMERIC(5,2) 컬럼에 1000.00 이상의 값을 넣으려 할 때 이 에러가 발생합니다. 특히 대용량 데이터 마이그레이션, 집계 연산, 또는 외부 시스템에서 데이터를 수신할 때 예기치 않게 마주치는 경우가 많아 실무에서 반드시 이해해야 할 에러 중 하나입니다.
주요 발생 원인
1. 컬럼 데이터 타입의 저장 범위 초과
가장 흔한 원인으로, 테이블 설계 시 충분한 범위를 고려하지 않고 SMALLINT나 INTEGER 같은 작은 정수 타입을 선택한 경우입니다. SMALLINT는 -32,768 ~ 32,767, INTEGER는 약 -21억 ~ 21억까지만 저장할 수 있으므로, 애플리케이션이 성장하면서 데이터 값이 커질 경우 이 에러가 발생합니다. 특히 자동 증가하는 시퀀스 기반의 PK가 컬럼 범위의 최대치에 도달했을 때 가장 치명적인 상황이 됩니다.
2. NUMERIC/DECIMAL 타입의 정밀도(precision) 초과
NUMERIC(p, s) 타입에서 p는 전체 유효 숫자 수, s는 소수점 이하 자릿수를 의미합니다. NUMERIC(6,2)로 정의된 컬럼은 최대 9999.99까지만 저장 가능하며, 이 범위를 초과하는 값이 입력되면 22003 에러가 발생합니다. 실무에서는 환율 계산, 금액 누적 합산, 또는 통계 집계 과정에서 예상보다 큰 값이 도출될 때 자주 나타납니다.
3. 수치 연산(산술 연산) 중 오버플로우 발생
두 컬럼의 곱셈이나 합산 등 산술 연산의 결과가 결과를 저장할 컬럼이나 캐스팅된 타입의 범위를 넘어설 때 발생합니다. 예를 들어 INTEGER 타입 두 컬럼을 곱한 결과가 21억을 초과하거나, SUM() 집계 함수의 결과가 컬럼 타입보다 클 때 이 에러가 트리거됩니다. 배치 처리나 리포팅 쿼리에서 특히 빈번하게 발생합니다.
해결 방법
원인 1 해결: 컬럼 타입을 더 넓은 범위로 변경
SMALLINT 또는 INTEGER 컬럼이 한계에 도달했다면, ALTER TABLE을 사용해 더 큰 타입으로 변경합니다.
-- 문제 상황: SMALLINT 컬럼에 범위 초과 값 삽입 시도
CREATE TABLE products (
id SMALLINT PRIMARY KEY, -- 최대 32,767까지만 가능
quantity SMALLINT
);
-- 에러 발생 예시
INSERT INTO products (id, quantity) VALUES (40000, 100);
-- ERROR: smallint out of range (SQLSTATE 22003)
-- 해결: 컬럼 타입을 INTEGER 또는 BIGINT로 변경
ALTER TABLE products ALTER COLUMN id TYPE INTEGER;
ALTER TABLE products ALTER COLUMN quantity TYPE INTEGER;
-- SERIAL 타입 PK가 한계에 다다른 경우
ALTER TABLE products ALTER COLUMN id TYPE BIGINT;
-- 시퀀스도 함께 변경
ALTER SEQUENCE products_id_seq AS BIGINT;
원인 2 해결: NUMERIC 타입의 정밀도 확장
-- 문제 상황: NUMERIC(6,2) 컬럼에 범위 초과 값 삽입
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
total_price NUMERIC(6, 2) -- 최대 9999.99까지만 저장 가능
);
-- 에러 발생 예시
INSERT INTO orders (total_price) VALUES (100000.50);
-- ERROR: numeric field overflow (SQLSTATE 22003)
-- 해결: 정밀도(precision)를 더 크게 조정
ALTER TABLE orders ALTER COLUMN total_price TYPE NUMERIC(12, 2);
-- 삽입 전 범위 검증 후 처리하는 방법
INSERT INTO orders (total_price)
SELECT 100000.50
WHERE 100000.50 < 9999999999.99; -- 사전 조건 체크
-- NUMERIC 최대 허용 범위 확인 쿼리
SELECT pg_typeof(NUMERIC(12,2)),
(10^(12-2) - 0.01) AS max_value;
원인 3 해결: 연산 시 명시적 캐스팅 및 안전한 집계
-- 문제 상황: INTEGER 컬럼 간 곱셈 오버플로우
CREATE TABLE sales (
unit_price INTEGER,
quantity INTEGER
);
INSERT INTO sales VALUES (500000, 5000);
-- 에러 발생 예시
SELECT unit_price * quantity AS total FROM sales;
-- ERROR: integer out of range (SQLSTATE 22003)
-- 해결 1: 명시적 BIGINT 캐스팅
SELECT unit_price::BIGINT * quantity::BIGINT AS total FROM sales;
-- 해결 2: NUMERIC으로 캐스팅하여 정밀한 계산
SELECT CAST(unit_price AS NUMERIC) * CAST(quantity AS NUMERIC) AS total
FROM sales;
-- 해결 3: SUM 집계 시 오버플로우 방지
SELECT SUM(unit_price::BIGINT * quantity::BIGINT) AS grand_total
FROM sales;
-- 집계 전 오버플로우 가능성 확인
SELECT MAX(unit_price::BIGINT * quantity::BIGINT) AS max_possible
FROM sales;
현재 컬럼 타입 및 한계 범위 확인 쿼리
-- 테이블의 컬럼 타입과 최대값 한계 확인
SELECT
column_name,
data_type,
numeric_precision,
numeric_scale,
CASE data_type
WHEN 'smallint' THEN '32,767'
WHEN 'integer' THEN '2,147,483,647'
WHEN 'bigint' THEN '9,223,372,036,854,775,807'
WHEN 'numeric' THEN CONCAT('10^(', numeric_precision - numeric_scale, ') - 1')
ELSE 'N/A'
END AS max_value
FROM information_schema.columns
WHERE table_name = 'your_table_name'
AND table_schema = 'public'
ORDER BY ordinal_position;
예방 방법
1. 초기 설계 단계에서 충분한 데이터 타입 여유 확보
테이블 설계 시 현재 데이터 규모뿐 아니라 향후 3~5년의 성장을 예상하여 데이터 타입을 선택해야 합니다. 특히 PK 컬럼에는 SERIAL 대신 BIGSERIAL을, 금액이나 수량 컬럼에는 INTEGER 대신 BIGINT를 기본으로 사용하는 내부 표준을 수립하는 것을 권장합니다. NUMERIC 타입의 경우도 현재 예상되는 최대값의 최소 10배 이상의 정밀도를 부여하는 습관을 들이면 나중에 발생할 수 있는 장애를 예방할 수 있습니다.
-- 권장 테이블 설계 예시
CREATE TABLE safe_orders (
id BIGSERIAL PRIMARY KEY, -- SERIAL 대신 BIGSERIAL
customer_id BIGINT NOT NULL, -- 충분한 범위의 FK
total_price NUMERIC(15, 2) NOT NULL, -- 넉넉한 precision
quantity INTEGER NOT NULL,
CHECK (total_price >= 0 AND total_price < 1e13) -- 범위 제약 명시
);
2. 애플리케이션 레벨에서 입력값 사전 검증 및 DB 제약 조건 병행 적용
DB에 값이 들어오기 전에 애플리케이션에서 입력 범위를 검증하고, 동시에 PostgreSQL의 CHECK 제약 조건을 활용하여 이중 안전망을 구축해야 합니다. 또한 주기적으로 시퀀스의 현재 값과 최대값을 모니터링하여 PK 고갈 상황을 사전에 감지하는 것이 중요합니다.
-- 시퀀스 현황 모니터링 쿼리 (PK 고갈 감지)
SELECT
schemaname,
sequencename,
last_value,
max_value,
ROUND((last_value::NUMERIC / max_value) * 100, 2) AS used_pct
FROM pg_sequences
WHERE last_value IS NOT NULL
ORDER BY used_pct DESC;
관련 에러
- 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가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.