2026년 10월 17일 | DBMS Error 가이드
이 글에서 다루는 내용
2201B 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2201B invalid regular expression 는?
PostgreSQL 에러 코드 2201B는 invalid_regular_expression으로, 사용자가 정규 표현식(Regular Expression)을 사용하는 함수나 연산자에 잘못된 패턴을 전달했을 때 발생합니다. 이 에러는 SIMILAR TO, ~, regexp_match(), regexp_replace(), regexp_split_to_table() 등 정규 표현식을 내부적으로 처리하는 모든 PostgreSQL 함수와 연산자에서 발생할 수 있습니다. PostgreSQL은 내부적으로 Henry Spencer의 ARE(Advanced Regular Expressions) 라이브러리를 사용하며, 해당 라이브러리가 파싱할 수 없는 패턴이 입력되면 즉시 이 에러를 반환합니다.
주요 발생 원인
1. 괄호, 브라켓 등 메타문자의 짝 불일치
정규 표현식에서 (, [, { 등의 여는 기호는 반드시 대응하는 닫는 기호 ), ], }와 쌍을 이루어야 합니다. 실무에서는 복잡한 패턴을 동적으로 생성하거나 사용자 입력값을 그대로 정규 표현식에 삽입할 때 이 문제가 자주 발생합니다. 예를 들어 [0-9처럼 닫는 브라켓이 없거나, (abc처럼 닫는 괄호가 누락된 경우가 대표적입니다.
2. 잘못된 수량자(Quantifier) 사용
{n,m} 형태의 수량자는 n이 m보다 크거나, 숫자가 아닌 값이 입력되거나, {와 } 사이에 형식이 맞지 않으면 에러가 발생합니다. 또한 *, +, ? 같은 수량자가 적용될 수 있는 앞 요소(atom) 없이 단독으로 사용되는 경우에도 이 에러가 발생합니다. {3,1}처럼 최솟값이 최댓값보다 큰 경우는 특히 놓치기 쉬운 실수입니다.
3. 잘못된 이스케이프 시퀀스 또는 POSIX 클래스 오타
PostgreSQL의 정규 표현식에서는 \d, \w 등 일부 이스케이프 시퀀스를 지원하지만, 지원하지 않는 시퀀스를 사용하면 에러가 발생합니다. 또한 [:alpha:], [:digit:] 같은 POSIX 문자 클래스는 반드시 [[:alpha:]]처럼 이중 브라켓 안에 넣어야 하는데, [:alpha:]만 단독으로 사용하거나 오타가 있을 경우 이 에러로 이어집니다.
해결 방법
원인 1 해결: 괄호/브라켓 짝 맞추기
잘못된 패턴 예시와 수정 방법입니다.
-- ❌ 에러 발생: 닫는 브라켓 누락
SELECT * FROM users WHERE phone ~ '[0-9';
-- ERROR: invalid regular expression: brackets ([ ]) not balanced
-- ✅ 수정: 브라켓 닫기
SELECT * FROM users WHERE phone ~ '[0-9]';
-- ❌ 에러 발생: 닫는 괄호 누락
SELECT regexp_match('hello world', '(hello');
-- ERROR: invalid regular expression: parentheses () not balanced
-- ✅ 수정: 괄호 닫기
SELECT regexp_match('hello world', '(hello)');
-- ❌ 에러 발생: 중괄호 형식 불일치
SELECT * FROM logs WHERE message ~ 'error{';
-- ERROR: invalid regular expression
-- ✅ 수정: 올바른 수량자 형식 사용
SELECT * FROM logs WHERE message ~ 'error{1,}';
원인 2 해결: 수량자 올바르게 사용하기
-- ❌ 에러: 최솟값 > 최댓값
SELECT regexp_match('aaa', 'a{5,2}');
-- ERROR: invalid regular expression: invalid repetition count(s)
-- ✅ 수정: 올바른 범위 지정
SELECT regexp_match('aaa', 'a{2,5}');
-- ❌ 에러: 앞에 적용 대상 없이 수량자 단독 사용
SELECT * FROM products WHERE name ~ '*shirt';
-- ERROR: invalid regular expression: quantifier operand invalid
-- ✅ 수정: 수량자 앞에 적용 대상 추가
SELECT * FROM products WHERE name ~ '.*shirt';
-- ❌ 에러: 빈 수량자
SELECT regexp_replace('abc123', '[0-9]{}', '');
-- ERROR: invalid regular expression
-- ✅ 수정: 수량자 값 명시
SELECT regexp_replace('abc123', '[0-9]{1,}', '');
원인 3 해결: 이스케이프 및 POSIX 클래스 올바르게 사용하기
-- ❌ 에러: POSIX 클래스 이중 브라켓 누락
SELECT * FROM users WHERE username ~ '[:alpha:]+';
-- ERROR: invalid regular expression
-- ✅ 수정: 이중 브라켓으로 감싸기
SELECT * FROM users WHERE username ~ '[[:alpha:]]+';
-- ❌ 에러: 지원하지 않는 이스케이프 시퀀스 (주의: \d는 일부 컨텍스트에서 동작하나 명시적 사용 권장)
-- SIMILAR TO에서는 정규식 이스케이프가 다르게 동작
SELECT * FROM users WHERE username SIMILAR TO '\d+';
-- SIMILAR TO는 SQL 표준 패턴을 사용하므로 주의 필요
-- ✅ 수정: ~ 연산자와 올바른 패턴 사용
SELECT * FROM users WHERE username ~ '^[0-9]+$';
-- 실무 예제: 이메일 형식 검증
SELECT email,
CASE
WHEN email ~ '^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$'
THEN '유효한 이메일'
ELSE '잘못된 이메일'
END AS validation_result
FROM users;
동적 쿼리에서 안전하게 정규 표현식 검증하기
-- 사용자 입력을 정규 표현식으로 사용하기 전에 유효성 검사하는 함수
CREATE OR REPLACE FUNCTION is_valid_regex(pattern TEXT)
RETURNS BOOLEAN
LANGUAGE plpgsql
AS $$
BEGIN
-- 빈 문자열에 대해 패턴 테스트를 시도
PERFORM '' ~ pattern;
RETURN TRUE;
EXCEPTION
WHEN invalid_regular_expression THEN
RETURN FALSE;
END;
$$;
-- 사용 예시
SELECT is_valid_regex('[0-9]+'); -- true
SELECT is_valid_regex('[0-9'); -- false
SELECT is_valid_regex('a{5,2}'); -- false
-- 실무 활용: 동적 필터링에서 유효하지 않은 패턴 우아하게 처리
DO $$
DECLARE
user_pattern TEXT := '[invalid'; -- 사용자로부터 입력받은 패턴
result_count INT;
BEGIN
IF is_valid_regex(user_pattern) THEN
EXECUTE format(
'SELECT COUNT(*) FROM users WHERE name ~ %L',
user_pattern
) INTO result_count;
RAISE NOTICE '검색 결과: % 건', result_count;
ELSE
RAISE NOTICE '유효하지 않은 정규 표현식입니다: %', user_pattern;
END IF;
END;
$$;
예방 방법
1. 정규 표현식 입력 전 유효성 검증 함수 도입
앞서 소개한 is_valid_regex() 같은 래퍼 함수를 애플리케이션 레이어 또는 데이터베이스 레이어에 반드시 도입하세요. 특히 사용자 입력값을 그대로 정규 표현식 패턴으로 사용하는 경우(예: 검색 필터, 데이터 검증 규칙 동적 생성)에는 반드시 사전 검증 단계를 거쳐야 합니다. 이 방법은 2201B 에러뿐만 아니라 ReDoS(Regular Expression Denial of Service) 공격도 어느 정도 방어하는 효과가 있습니다.
-- CHECK 제약 조건에도 적용 가능
ALTER TABLE search_patterns
ADD CONSTRAINT chk_valid_pattern
CHECK (is_valid_regex(pattern_value));
2. 정규 표현식 패턴 테이블 중앙 관리 및 테스트 자동화
프로젝트에서 사용하는 모든 정규 표현식 패턴을 별도의 설정 테이블에 중앙 관리하고, CI/CD 파이프라인 또는 정기 점검 쿼리를 통해 패턴의 유효성을 자동으로 검사하는 체계를 갖추세요. 이렇게 하면 데이터베이스 스키마 변경이나 PostgreSQL 버전 업그레이드 시에도 정규 표현식 호환성 문제를 사전에 발견할 수 있습니다.
-- 정규 표현식 패턴 관리 테이블 예시
CREATE TABLE regex_patterns (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
pattern TEXT NOT NULL,
description TEXT,
is_valid BOOLEAN GENERATED ALWAYS AS (is_valid_regex(pattern)) STORED,
created_at TIMESTAMPTZ DEFAULT now()
);
-- 유효하지 않은 패턴 모니터링 쿼리
SELECT name, pattern, description
FROM regex_patterns
WHERE is_valid = FALSE;
관련 에러
22025–invalid_escape_sequence: 정규 표현식 내 이스케이프 문자 처리 시 잘못된 이스케이프 시퀀스가 사용된 경우 발생하며,2201B와 함께 자주 혼동됩니다.2201W–invalid_row_count_in_limit_clause: 직접적인 연관은 없으나, 동적 쿼리 생성 과정에서 함께 발생할 수 있는 에러입니다.42601–syntax_error: 정규 표현식 자체의 문제가 아니라 SQL 문법 오류로 인해 발생하며, 동적 쿼리 디버깅 시2201B와 혼동하지 않도록 주의해야 합니다.22019–invalid_escape_character:LIKE또는SIMILAR TO패턴에서 잘못된 이스케이프 문자를 지정했을 때 발생합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.