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

22023
2026년 10월 17일 | DBMS Error 가이드

이 글에서 다루는 내용

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

22023 invalid parameter value 는?

PostgreSQL 에러 코드 22023은 invalid_parameter_value로, 함수나 명령어에 전달된 파라미터 값이 유효하지 않을 때 발생합니다. 즉, 문법적으로는 올바른 SQL이지만 특정 함수나 연산자가 허용하는 값의 범위나 형식을 벗어났을 때 이 에러가 트리거됩니다. 실무에서는 날짜/시간 함수, 문자열 처리 함수, 시스템 설정 함수 등을 사용할 때 자주 마주치며, 특히 외부 입력값을 그대로 SQL에 전달하는 경우에 빈번하게 나타납니다.


주요 발생 원인

  • 날짜/시간 함수에 잘못된 포맷 문자열 전달

to_timestamp(), to_date(), date_trunc() 등의 날짜/시간 함수에 PostgreSQL이 인식할 수 없는 포맷 문자열이나 유효하지 않은 단위(unit)를 전달할 때 발생합니다. 예를 들어 date_trunc()에 'weeks'처럼 복수형을 사용하거나, 아예 존재하지 않는 단위를 전달하면 이 에러가 즉시 발생합니다. 이 경우는 공식 문서의 허용 단위 목록을 반드시 확인해야 하며, 사용자 입력을 그대로 전달하는 로직이 있다면 화이트리스트 검증이 필수입니다.

  • SET 명령이나 pg_catalog 함수에 허용되지 않는 값 설정

SET work_mem = '-1kB' 처럼 음수 메모리 값을 설정하거나, SET client_min_messages = 'invalid_level'과 같이 허용되지 않는 열거형 값을 지정할 때 발생합니다. PostgreSQL의 GUC(Grand Unified Configuration) 파라미터들은 각각 허용 범위와 유효 값 목록이 엄격하게 정의되어 있으며, 범위를 벗어나면 예외 없이 22023이 발생합니다. 애플리케이션에서 동적으로 세션 파라미터를 변경할 때 특히 주의가 필요합니다.

  • regexp_* 계열 함수 또는 substring() 에 잘못된 정규식 플래그 전달

regexp_replace(), regexp_match(), regexp_split_to_table() 등의 함수에서 허용되지 않는 정규식 플래그 조합을 사용할 때 이 에러가 발생합니다. 예를 들어 POSIX 정규식에서 지원하지 않는 플래그 문자를 전달하거나, 서로 충돌하는 플래그를 동시에 지정하는 경우가 해당됩니다. 정규식 패턴 자체의 문법 오류는 2201B(invalid_regular_expression)로 분류되지만, 플래그 관련 오류는 22023으로 분류된다는 점을 혼동하지 않도록 주의해야 합니다.


해결 방법

원인 1 해결: 날짜/시간 함수 파라미터 수정

date_trunc() 함수는 복수형 단위를 허용하지 않습니다. 반드시 단수형을 사용해야 합니다.

-- ❌ 잘못된 예시: 복수형 단위 사용
SELECT date_trunc('weeks', NOW());
-- ERROR:  22023: units "weeks" not recognized

-- ✅ 올바른 예시: 단수형 단위 사용
SELECT date_trunc('week', NOW());

-- ❌ 잘못된 예시: 존재하지 않는 단위
SELECT date_trunc('quarter_hour', NOW());
-- ERROR:  22023: units "quarter_hour" not recognized

-- ✅ 올바른 예시: 허용된 단위 목록 내에서 사용
-- 허용 단위: microseconds, milliseconds, second, minute, hour,
--            day, week, month, quarter, year, decade, century, millennium
SELECT date_trunc('hour', NOW());
SELECT date_trunc('month', NOW());
SELECT date_trunc('quarter', NOW());

-- 동적 입력을 사용할 경우, 화이트리스트 검증 함수 활용
CREATE OR REPLACE FUNCTION safe_date_trunc(p_unit TEXT, p_ts TIMESTAMPTZ)
RETURNS TIMESTAMPTZ AS $$
DECLARE
    allowed_units TEXT[] := ARRAY[
        'microseconds','milliseconds','second','minute','hour',
        'day','week','month','quarter','year','decade','century','millennium'
    ];
BEGIN
    IF p_unit = ANY(allowed_units) THEN
        RETURN date_trunc(p_unit, p_ts);
    ELSE
        RAISE EXCEPTION 'Invalid date_trunc unit: %', p_unit
            USING ERRCODE = '22023';
    END IF;
END;
$$ LANGUAGE plpgsql;

-- 안전한 호출
SELECT safe_date_trunc('month', NOW());

원인 2 해결: 세션 파라미터 유효 값 확인 후 설정

-- ❌ 잘못된 예시: 음수 메모리 값
SET work_mem = '-1kB';
-- ERROR:  22023: invalid value for parameter "work_mem": "-1kB"

-- ✅ 올바른 예시: 유효한 메모리 값
SET work_mem = '64MB';
SET work_mem = '256kB';

-- ❌ 잘못된 예시: 잘못된 메시지 레벨
SET client_min_messages = 'verbose_debug';
-- ERROR:  22023: invalid value for parameter "client_min_messages"

-- ✅ 올바른 예시: 유효한 메시지 레벨
-- 허용 값: DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL, PANIC
SET client_min_messages = 'WARNING';
SET client_min_messages = 'NOTICE';

-- 현재 허용되는 파라미터 값 범위 조회 방법
SELECT name, setting, unit, min_val, max_val, enumvals
FROM pg_settings
WHERE name IN ('work_mem', 'client_min_messages', 'log_min_messages');

-- 애플리케이션에서 동적으로 세션 파라미터를 설정할 때 안전하게 처리하는 예
DO $$
DECLARE
    v_work_mem TEXT := '128MB';  -- 애플리케이션에서 전달받은 값
BEGIN
    -- pg_settings로 사전 검증
    IF EXISTS (
        SELECT 1 FROM pg_settings
        WHERE name = 'work_mem'
          AND (min_val IS NULL OR v_work_mem::numeric >= min_val::numeric)
    ) THEN
        EXECUTE format('SET work_mem = %L', v_work_mem);
        RAISE NOTICE 'work_mem set to %', v_work_mem;
    ELSE
        RAISE WARNING 'Invalid work_mem value: %', v_work_mem;
    END IF;
END;
$$;

원인 3 해결: 정규식 함수 플래그 수정

-- ❌ 잘못된 예시: 존재하지 않는 플래그 사용
SELECT regexp_replace('Hello World', 'world', 'PostgreSQL', 'z');
-- ERROR:  22023: invalid regular expression option: "z"

-- ✅ 올바른 예시: 유효한 플래그만 사용
-- 허용 플래그: g(global), i(case-insensitive), m(multiline), s(single line/. matches newline), x(extended), w(weird)
SELECT regexp_replace('Hello World', 'world', 'PostgreSQL', 'i');   -- 대소문자 무시
SELECT regexp_replace('Hello World', 'o', '0', 'g');                -- 전체 치환
SELECT regexp_replace('Hello World', 'world', 'PostgreSQL', 'gi');  -- 대소문자 무시 + 전체 치환

-- 플래그 조합 시 충돌 주의
-- ❌ 'g'와 단일 치환은 의미상 충돌 (에러는 아니지만 의도치 않은 결과)
SELECT regexp_match('foo\nbar', '^bar$', 'mg');  -- m 플래그로 멀티라인 처리

-- 안전한 정규식 플래그 검증 함수
CREATE OR REPLACE FUNCTION safe_regexp_replace(
    p_source TEXT,
    p_pattern TEXT,
    p_replacement TEXT,
    p_flags TEXT DEFAULT ''
)
RETURNS TEXT AS $$
DECLARE
    allowed_flags CONSTANT TEXT := 'gimsxw';
    flag CHAR;
BEGIN
    -- 플래그 문자열 유효성 검사
    FOR i IN 1..length(p_flags) LOOP
        flag := substring(p_flags, i, 1);
        IF position(flag IN allowed_flags) = 0 THEN
            RAISE EXCEPTION 'Invalid regexp flag: "%". Allowed: [%]', flag, allowed_flags
                USING ERRCODE = '22023';
        END IF;
    END LOOP;

    RETURN regexp_replace(p_source, p_pattern, p_replacement, p_flags);
EXCEPTION
    WHEN SQLSTATE '22023' THEN
        RAISE EXCEPTION 'Regexp error with pattern "%" and flags "%": %',
            p_pattern, p_flags, SQLERRM;
END;
$$ LANGUAGE plpgsql;

SELECT safe_regexp_replace('Hello World', 'world', 'PostgreSQL', 'gi');

예방 방법

  • 입력값 화이트리스트 검증 및 래퍼 함수 활용

외부 사용자 입력이나 애플리케이션에서 동적으로 전달되는 값을 PostgreSQL 함수에 직접 넘기지 말고, 반드시 허용된 값 목록과 비교하는 검증 단계를 거치도록 설계하십시오. 위에서 작성한 safe_date_trunc(), safe_regexp_replace() 같은 래퍼 함수를 공용 스키마에 정의해두고 팀 전체가 공유하여 사용하는 것이 좋습니다. 또한 애플리케이션 레이어에서도 PostgreSQL에 쿼리를 보내기 전에 enum 또는 상수 집합으로 1차 검증을 수행하면 데이터베이스 왕복 비용 없이 오류를 사전에 차단할 수 있습니다.

  • 예외 처리 블록으로 22023 명시적 핸들링 및 로깅

PL/pgSQL 프로시저나 함수 내에서 파라미터 관련 작업을 수행할 때는 반드시 EXCEPTION WHEN SQLSTATE '22023' 블록을 추가하여 에러를 구체적인 메시지와 함께 로깅하고 상위 레이어로 전파하십시오. 단순히 에러를 삼키는 것이 아니라, 어떤 파라미터에 어떤 값이 들어왔는지를 pg_log 또는 별도의 에러 로그 테이블에 기록하면 나중에 원인 추적이 훨씬 용이해집니다.

-- 예외 처리 Best Practice 예시
CREATE OR REPLACE PROCEDURE process_time_series(
    p_trunc_unit TEXT,
    p_start_date TIMESTAMPTZ,
    p_end_date TIMESTAMPTZ
)
LANGUAGE plpgsql AS $$
BEGIN
    -- 실제 처리 로직
    INSERT INTO processed_log(trunc_unit, trunc_start, trunc_end, processed_at)
    SELECT
        p_trunc_unit,
        date_trunc(p_trunc_unit, p_start_date),
        date_trunc(p_trunc_unit, p_end_date),
        NOW();

EXCEPTION
    WHEN SQLSTATE '22023' THEN
        -- 에러 상세 로깅
        INSERT INTO error_log(error_code, error_msg, context, occurred_at)
        VALUES ('22023', SQLERRM,
                format('trunc_unit=%s, start=%s, end=%s',
                       p_trunc_unit, p_start_date, p_end_date),
                NOW());
        RAISE WARNING '[22023] Invalid parameter in process_time_series: %', SQLERRM;
        -- 필요 시 상위로 재전파
        -- RAISE;
END;
$$;

관련 에러

  • 22007 (invalid_datetime_format): 날짜/시간 형식 자체가 잘못된 경우로, 22023과 함께 날짜 처리 시 자주 쌍으로 발생합니다.
  • 22P02 (invalid_text_representation): 텍스트를 특정 타입으로 캐스팅할 때 형식이 맞지 않아 발생하며, 파라미터 유효성 문제와 혼동되기 쉽습니다.
  • 2201B (invalid_regular_expression): 정규식 패턴 자체의 문법 오류로, 22023의 플래그 오류와 구분해야 합니다.
  • 42704 (undefined_object): 존재하지 않는 설정 파라미터 이름을 SET 명령에 사용할 때 발생하며, 22023과 유사한 상황에서 나타납니다.
  • 22003 (numeric_value_out_of_range): 숫자형 파라미터의 값이 허용 범위를 초과할 때 발생하며, 22023의 숫자 관련 케이스와 겹치는 영역이 있습니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기