PostgreSQL 2201E 오류 원인과 해결 방법 완벽 가이드

2201E
2026년 06월 06일 | DBMS Error 가이드

이 글에서 다루는 내용

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

2201E invalid argument for logarithm 는?

PostgreSQL 에러 코드 2201E는 로그 함수(LOG, LN, LOG10)에 유효하지 않은 인수가 전달되었을 때 발생하는 수학적 오류입니다. 로그 함수는 수학적으로 0 이하의 값에 대해 정의되지 않으므로, 입력값이 0이거나 음수인 경우 PostgreSQL은 이 에러를 발생시킵니다. 실무에서는 사용자 입력 데이터, 집계 결과, 또는 계산된 중간 값이 예상치 못하게 0 또는 음수가 될 때 자주 마주치게 됩니다.


주요 발생 원인

1. 로그 함수에 0 또는 음수 값 직접 전달

가장 흔한 원인으로, LN(0), LOG(0), LOG(-5)처럼 0이나 음수를 로그 함수에 직접 넘기는 경우입니다. 수학적으로 로그는 양수에 대해서만 정의되기 때문에, 0과 음수는 도메인을 벗어난 값입니다. 특히 테이블의 컬럼 값이 0이 될 수 있는 경우(예: 판매량, 재고 수량 등)에 아무런 필터링 없이 로그를 적용하면 이 에러가 빈번하게 발생합니다.

2. 계산된 결과값이 동적으로 0 또는 음수가 되는 경우

단순한 컬럼 값뿐만 아니라, SUM(), AVG(), 혹은 사칙연산으로 도출된 결과가 런타임에 0이나 음수가 될 수 있습니다. 예를 들어 매출 대비 비용을 계산한 후 그 차이에 로그를 취하는 쿼리에서, 특정 데이터 조합에 의해 음수가 생성될 경우 에러가 발생합니다. 개발 및 테스트 환경에서는 문제가 없더라도, 운영 환경의 실제 데이터에서 이 케이스가 터지는 경우가 많습니다.

3. 데이터 타입 변환 또는 NULL 처리 미흡

NULLIF, COALESCE 등의 처리 없이 외부 입력 데이터를 그대로 로그 함수에 넣을 경우, 예상치 못한 값이 들어와 에러를 유발할 수 있습니다. 또한 CAST 또는 암묵적 형 변환 과정에서 값이 0으로 초기화되거나, 소수점 반올림으로 인해 아주 작은 양수가 0으로 처리되는 경우도 있습니다. 이러한 경우는 재현이 어렵기 때문에 디버깅에 많은 시간이 소요될 수 있습니다.


해결 방법

원인 1 해결: NULLIF 또는 CASE 문으로 0/음수 필터링

로그 함수를 호출하기 전에 반드시 입력값이 양수인지 검사해야 합니다. NULLIF를 활용하면 0을 NULL로 바꿔 에러 대신 NULL을 반환하게 할 수 있습니다.

-- 에러 발생 예시
SELECT LN(0);  -- ERROR: invalid argument for logarithm

-- NULLIF를 이용한 해결 (0을 NULL로 처리)
SELECT LN(NULLIF(column_value, 0))
FROM your_table;

-- CASE 문을 이용한 명시적 처리
SELECT
    product_id,
    CASE
        WHEN sales_amount > 0 THEN LN(sales_amount)
        ELSE NULL
    END AS log_sales
FROM sales_data;

-- 음수와 0 모두 필터링
SELECT LN(NULLIF(GREATEST(column_value, 0), 0))
FROM your_table;

원인 2 해결: 집계 결과에 대한 로그 적용 시 보호 로직 추가

집계된 결과에 로그를 적용할 때는 WHERE 절이나 HAVING 절로 사전 필터링하거나, 인라인 CASE 구문으로 보호합니다.

-- 문제가 되는 쿼리 예시
SELECT
    category,
    LN(SUM(revenue) - SUM(cost)) AS log_profit
FROM financial_data
GROUP BY category;

-- 해결: HAVING 절로 양수인 경우만 처리
SELECT
    category,
    LN(SUM(revenue) - SUM(cost)) AS log_profit
FROM financial_data
GROUP BY category
HAVING (SUM(revenue) - SUM(cost)) > 0;

-- 해결: CASE 구문으로 안전하게 처리
SELECT
    category,
    CASE
        WHEN (SUM(revenue) - SUM(cost)) > 0
        THEN LN(SUM(revenue) - SUM(cost))
        ELSE NULL
    END AS log_profit
FROM financial_data
GROUP BY category;

원인 3 해결: 함수 래퍼(Wrapper Function) 생성

반복적으로 로그 함수를 사용하는 환경이라면, 안전한 래퍼 함수를 DB 레벨에서 만들어 두는 것이 좋습니다.

-- 안전한 로그 래퍼 함수 생성
CREATE OR REPLACE FUNCTION safe_ln(val NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
    IF val IS NULL OR val <= 0 THEN
        RETURN NULL;
    END IF;
    RETURN LN(val);
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- 사용 예시
SELECT
    product_id,
    safe_ln(price) AS log_price,
    safe_ln(quantity) AS log_quantity
FROM products;

-- LOG(base, value) 형태의 래퍼
CREATE OR REPLACE FUNCTION safe_log(base NUMERIC, val NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
    IF val IS NULL OR val <= 0 OR base IS NULL OR base <= 0 OR base = 1 THEN
        RETURN NULL;
    END IF;
    RETURN LOG(base, val);
END;
$$ LANGUAGE plpgsql IMMUTABLE;

데이터 검증 쿼리: 문제 있는 행 사전 확인

-- 로그 적용 전 문제 행 탐지
SELECT
    id,
    column_value,
    CASE
        WHEN column_value IS NULL THEN 'NULL 값'
        WHEN column_value = 0     THEN '0 값'
        WHEN column_value < 0     THEN '음수 값'
        ELSE '정상'
    END AS status
FROM your_table
WHERE column_value <= 0 OR column_value IS NULL;

예방 방법

1. CHECK 제약 조건으로 DB 레벨에서 음수/0 입력 차단

로그 연산이 필요한 컬럼에는 테이블 정의 시점에 CHECK 제약 조건을 걸어, 처음부터 유효하지 않은 값이 저장되지 않도록 합니다. 이렇게 하면 애플리케이션 레벨의 실수를 데이터베이스 레벨에서 방어할 수 있습니다.

-- 테이블 생성 시 CHECK 제약 조건 적용
CREATE TABLE product_metrics (
    id          SERIAL PRIMARY KEY,
    product_id  INT NOT NULL,
    price       NUMERIC CHECK (price > 0),
    quantity    NUMERIC CHECK (quantity >= 0),
    score       NUMERIC CHECK (score > 0)
);

-- 기존 테이블에 제약 조건 추가
ALTER TABLE product_metrics
    ADD CONSTRAINT chk_price_positive CHECK (price > 0);

2. 로그 연산을 포함한 뷰(View) 또는 함수에 방어 로직 내재화

반복 사용되는 로그 계산은 뷰나 함수로 추상화하고, 내부에 항상 유효성 검사를 포함시킵니다. 이를 통해 개발자가 매번 조건문을 작성하지 않아도 안전한 연산이 보장됩니다.

-- 안전한 뷰 생성 예시
CREATE OR REPLACE VIEW safe_product_log_metrics AS
SELECT
    product_id,
    price,
    CASE WHEN price > 0 THEN LN(price) ELSE NULL END AS ln_price,
    CASE WHEN quantity > 0 THEN LOG(10, quantity) ELSE NULL END AS log10_quantity
FROM product_metrics;

관련 에러

  • 22012 (division_by_zero): 0으로 나누기 시도 시 발생하는 에러로, 수학적 도메인 위반이라는 점에서 2201E와 유사합니다.
  • 2201F (invalid_argument_for_power_function): 거듭제곱 함수에 유효하지 않은 인수가 전달될 때 발생합니다.
  • 2201G (invalid_argument_for_width_bucket_function): WIDTH_BUCKET 함수에 잘못된 인수가 전달될 때 발생하며, 수학 함수 입력값 검증 실패라는 공통점이 있습니다.
  • 22003 (numeric_value_out_of_range): 수치 연산 결과가 허용 범위를 초과할 때 발생하며, 로그 연산 후 결과 저장 시 함께 고려해야 합니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기