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

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

이 글에서 다루는 내용

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

2202E array subscript error 는?

PostgreSQL 에러 코드 2202Earray subscript error로, 배열 인덱스(subscript)를 잘못 사용했을 때 발생하는 오류입니다. 주로 배열의 유효하지 않은 인덱스 범위를 참조하거나, 배열 인덱스에 NULL 또는 비정상적인 값을 사용할 때 트리거됩니다. 이 에러는 데이터 조회, 업데이트, 함수 처리 등 다양한 상황에서 발생할 수 있으며, 특히 동적으로 배열 인덱스를 계산하는 로직에서 자주 나타납니다.


주요 발생 원인

1. 배열 인덱스에 NULL 값 사용

PostgreSQL에서 배열 인덱스로 NULL을 사용하면 해당 에러가 발생합니다. 예를 들어, 변수나 컬럼 값이 NULL인 상태에서 이를 배열 subscript로 활용하면 시스템이 유효한 인덱스를 결정할 수 없어 오류를 던집니다. 이 경우는 특히 PL/pgSQL 함수 내에서 동적으로 인덱스를 계산할 때 흔히 발생합니다.

2. 정수가 아닌 값을 배열 인덱스로 사용

배열 인덱스는 반드시 정수(integer) 타입이어야 합니다. 소수점이 포함된 숫자(float), 문자열(text), 또는 명시적 형변환 없이 다른 타입의 값을 인덱스로 사용하면 이 에러가 발생할 수 있습니다. 특히 사용자 입력값을 그대로 배열 인덱스로 사용하는 경우 타입 불일치 문제가 자주 발생합니다.

3. 배열 슬라이싱(slice) 시 잘못된 범위 지정

PostgreSQL은 array[1:3]과 같은 배열 슬라이싱 문법을 지원하는데, 이때 시작 인덱스가 종료 인덱스보다 크거나 음수, NULL 등의 비정상 값이 포함될 경우 에러가 발생합니다. 슬라이싱 범위를 동적으로 계산하는 쿼리에서 경계값 검증이 누락되면 운영 환경에서 예기치 않은 오류로 이어질 수 있습니다.


해결 방법

원인 1 해결: NULL 인덱스 방지

배열 인덱스로 사용할 값이 NULL인지 사전에 확인하고, COALESCE 또는 조건문으로 안전하게 처리합니다.

-- 문제 발생 예시
DO $$
DECLARE
    arr  integer[] := ARRAY[10, 20, 30, 40, 50];
    idx  integer;  -- NULL 상태
    val  integer;
BEGIN
    val := arr[idx];  -- 2202E 에러 발생!
    RAISE NOTICE 'value: %', val;
END;
$$;

-- 해결 방법: COALESCE로 NULL 방어
DO $$
DECLARE
    arr      integer[] := ARRAY[10, 20, 30, 40, 50];
    idx      integer;  -- NULL 상태
    safe_idx integer;
    val      integer;
BEGIN
    safe_idx := COALESCE(idx, 1);  -- NULL이면 기본값 1 사용
    val := arr[safe_idx];
    RAISE NOTICE 'value: %', val;
END;
$$;

-- 테이블 컬럼에서 NULL 인덱스 방지 예시
SELECT
    data_array[COALESCE(index_col, 1)] AS safe_value
FROM my_table
WHERE index_col IS NOT NULL;  -- NULL 행 자체를 필터링

원인 2 해결: 정수 타입으로 명시적 형변환

인덱스 값이 정수형인지 확인하고, 필요하다면 ::integer 또는 CAST를 사용하여 명시적으로 변환합니다.

-- 문제 발생 예시: text 타입 인덱스 사용
DO $$
DECLARE
    arr       text[]  := ARRAY['apple', 'banana', 'cherry'];
    idx_text  text    := '2';
    val       text;
BEGIN
    -- text를 직접 인덱스로 사용하면 에러 가능
    val := arr[idx_text::integer];  -- 명시적 형변환으로 해결
    RAISE NOTICE 'value: %', val;
END;
$$;

-- 실수(float) 인덱스를 정수로 변환
SELECT
    tags[FLOOR(position_value)::integer] AS tag_name
FROM content_table
WHERE position_value IS NOT NULL
  AND FLOOR(position_value)::integer BETWEEN 1 AND array_length(tags, 1);

-- 사용자 입력값 검증 후 사용
CREATE OR REPLACE FUNCTION get_array_element(
    p_array  text[],
    p_index  text
) RETURNS text AS $$
DECLARE
    v_index integer;
BEGIN
    -- 입력값이 정수인지 검증
    BEGIN
        v_index := p_index::integer;
    EXCEPTION WHEN invalid_text_representation THEN
        RAISE EXCEPTION '유효하지 않은 배열 인덱스: %', p_index;
    END;

    -- 범위 검증
    IF v_index < 1 OR v_index > array_length(p_array, 1) THEN
        RAISE EXCEPTION '인덱스 범위 초과: % (배열 크기: %)',
            v_index, array_length(p_array, 1);
    END IF;

    RETURN p_array[v_index];
END;
$$ LANGUAGE plpgsql;

원인 3 해결: 배열 슬라이싱 범위 검증

슬라이싱 시 시작과 끝 인덱스를 사전에 검증하고, GREATEST / LEAST 함수로 안전한 범위를 보장합니다.

-- 문제 발생 예시: 잘못된 슬라이스 범위
DO $$
DECLARE
    arr    integer[] := ARRAY[1, 2, 3, 4, 5];
    s_idx  integer   := 4;
    e_idx  integer   := 2;  -- 시작보다 작은 종료 인덱스
    result integer[];
BEGIN
    result := arr[s_idx:e_idx];  -- 비정상 범위로 에러 가능
    RAISE NOTICE 'result: %', result;
END;
$$;

-- 해결 방법: 범위 정규화
DO $$
DECLARE
    arr      integer[] := ARRAY[1, 2, 3, 4, 5];
    s_idx    integer   := 4;
    e_idx    integer   := 2;
    safe_s   integer;
    safe_e   integer;
    arr_len  integer;
    result   integer[];
BEGIN
    arr_len := array_length(arr, 1);

    -- 안전한 범위로 정규화
    safe_s := GREATEST(1, LEAST(s_idx, e_idx));
    safe_e := LEAST(arr_len, GREATEST(s_idx, e_idx));

    result := arr[safe_s:safe_e];
    RAISE NOTICE 'result: %', result;
END;
$$;

-- 실무 쿼리에서 슬라이싱 안전 처리
SELECT
    scores[
        GREATEST(1, start_pos) :
        LEAST(array_length(scores, 1), end_pos)
    ] AS score_slice
FROM student_records
WHERE scores IS NOT NULL
  AND array_length(scores, 1) >= 1
  AND start_pos IS NOT NULL
  AND end_pos IS NOT NULL;

예방 방법

1. 배열 접근 전 항상 경계값(Boundary) 검증 함수를 활용하라

배열을 다루는 모든 쿼리와 함수에서 array_length() 함수를 활용해 인덱스가 유효한 범위 내에 있는지 사전에 검증하는 습관을 들이세요. PL/pgSQL 함수라면 반드시 인덱스 유효성 검사를 함수 진입 시점에 수행하고, 유효하지 않은 경우 명확한 에러 메시지와 함께 예외를 발생시켜 상위 레이어에서 적절히 처리할 수 있도록 설계하세요.

-- 안전한 배열 접근 유틸리티 함수
CREATE OR REPLACE FUNCTION safe_array_get(
    p_array anyarray,
    p_index integer,
    p_default anyelement DEFAULT NULL
) RETURNS anyelement AS $$
BEGIN
    IF p_array IS NULL OR p_index IS NULL THEN
        RETURN p_default;
    END IF;
    IF p_index < 1 OR p_index > array_length(p_array, 1) THEN
        RETURN p_default;
    END IF;
    RETURN p_array[p_index];
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- 사용 예시
SELECT safe_array_get(ARRAY['a','b','c'], 5, 'unknown') AS result;
-- 결과: unknown (에러 없이 기본값 반환)

2. 애플리케이션 레벨에서 배열 인덱스를 넘기기 전 타입과 범위를 검증하라

데이터베이스에 도달하기 전, 애플리케이션(Java, Python, Node.js 등) 레벨에서 배열 인덱스 값의 타입과 범위를 먼저 검증하는 방어 코드를 작성하세요. 또한 PostgreSQL 파라미터화된 쿼리(prepared statement)를 사용하면 타입 불일치로 인한 에러를 사전에 차단할 수 있으며, 인덱스 값이 NULL인 경우 기본값을 지정하거나 해당 로직을 스킵하는 처리를 반드시 포함하세요.


관련 에러

  • 2202E (array subscript error) 와 함께 자주 등장하는 관련 PostgreSQL 에러 코드들입니다.
  • 2202D – invalid_row_count_in_result_offset_clause: 배열보다는 OFFSET/FETCH 관련이지만 범위 유효성 체계를 공유합니다.
  • 22P02 – invalid_text_representation: 배열 인덱스 형변환 시 텍스트가 정수로 변환되지 않을 때 발생하며, 원인 2와 밀접하게 연관됩니다.
  • 22003 – numeric_value_out_of_range: 인덱스 값이 integer 범위를 초과할 때 발생할 수 있습니다.
  • 42703 – undefined_column: 동적 SQL에서 배열 컬럼명 오타와 혼동될 수 있으므로 함께 확인하세요.

DBMS 에러 코드 시리즈

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

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

댓글 남기기