2026년 06월 09일 | DBMS Error 가이드
이 글에서 다루는 내용
2201B 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2201B invalid regular expression 는?
PostgreSQL 에러 코드 2201B (invalid_regular_expression) 는 정규 표현식(Regular Expression)을 사용하는 함수나 연산자에 잘못된 패턴 문자열이 전달되었을 때 발생합니다. 주로 REGEXP_MATCH, REGEXP_REPLACE, REGEXP_SPLIT_TO_TABLE, SIMILAR TO, ~ 연산자 등에서 문법적으로 올바르지 않은 정규식 패턴이 입력될 경우 이 에러가 트리거됩니다. 잘못된 괄호 쌍, 지원되지 않는 이스케이프 시퀀스, 혹은 유효하지 않은 수량자(quantifier) 조합 등이 대표적인 원인이며, 운영 환경에서 동적으로 생성된 정규식 패턴을 그대로 사용할 때 특히 자주 마주치게 됩니다.
주요 발생 원인
1. 괄호(Bracket) 및 그룹 구문 오류
정규 표현식에서 여는 괄호 ( 와 닫는 괄호 ) 의 짝이 맞지 않거나, 대괄호 클래스 [ 를 열고 닫지 않은 경우 PostgreSQL의 정규식 엔진이 패턴을 파싱하지 못해 에러가 발생합니다. 예를 들어 ([0-9]+ 처럼 닫는 괄호가 누락되거나, [a-z 처럼 문자 클래스가 닫히지 않은 경우가 실무에서 매우 흔하게 발생합니다. 특히 사용자 입력값이나 외부 시스템에서 넘어온 패턴을 그대로 사용할 때 이 문제가 빈번하게 나타납니다.
2. 지원되지 않는 POSIX 또는 Perl 확장 문법 사용
PostgreSQL은 POSIX ERE(Extended Regular Expression) 표준을 기반으로 하며, Perl 호환 정규식(PCRE)의 일부 고급 기능은 지원하지 않습니다. (?P 같은 이름 있는 캡처 그룹, (?<=...) 같은 룩비하인드(lookbehind) 어서션, \d, \w 와 같은 Perl 스타일 단축 이스케이프 등을 별도 플래그 없이 사용하면 에러가 발생할 수 있습니다. 다른 언어(Python, Java, JavaScript 등)에서 작성한 정규식을 PostgreSQL에 그대로 이식할 때 이 문제가 자주 나타납니다.
3. 잘못된 수량자(Quantifier) 또는 이스케이프 시퀀스 사용
{m,n} 형태의 수량자에서 m이 n보다 크거나({5,2}), 중괄호를 올바르게 닫지 않은 경우({3,) 에러가 발생합니다. 또한 백슬래시(\) 뒤에 의미 없는 문자가 오거나, SQL 문자열 내에서 백슬래시 이스케이프가 이중으로 처리되어 의도치 않은 패턴이 만들어지는 경우도 흔한 원인입니다. 특히 E'' 이스케이프 문자열 리터럴과 일반 문자열 리터럴을 혼용할 때 이스케이프 처리 방식 차이로 인해 예기치 않은 에러가 발생하기도 합니다.
해결 방법
원인 1 해결: 괄호 구문 오류 수정
잘못된 예시와 수정된 예시를 비교해 보겠습니다.
-- 에러 발생: 닫는 괄호 누락
SELECT regexp_match('abc123', '([0-9]+');
-- ERROR: invalid regular expression: parentheses () not balanced
-- 수정: 괄호를 올바르게 매칭
SELECT regexp_match('abc123', '([0-9]+)');
-- 결과: {123}
-- 에러 발생: 문자 클래스 대괄호 미완성
SELECT 'hello' ~ '[a-z';
-- ERROR: invalid regular expression: brackets [] not balanced
-- 수정: 대괄호 문자 클래스 완성
SELECT 'hello' ~ '[a-z]+';
-- 결과: true
-- 실무 예시: 전화번호 패턴 매칭
SELECT phone_number,
regexp_match(phone_number, '^(\d{2,3})-(\d{3,4})-(\d{4})$') AS parsed
FROM customers
WHERE phone_number IS NOT NULL;
원인 2 해결: POSIX ERE 호환 문법으로 변환
-- 에러 발생: Perl 스타일 단축 이스케이프 (\d, \w) 직접 사용 시 문제가 될 수 있음
-- PostgreSQL은 ARE(Advanced Regular Expression) 모드에서 \d를 지원하지만
-- 아래처럼 명시적 POSIX 클래스를 사용하는 것이 더 안전합니다.
-- Perl 스타일 (일부 환경에서 동작하지 않을 수 있음)
SELECT regexp_replace('Hello World 123', '\d+', 'NUM', 'g');
-- POSIX ERE 방식으로 변환 (권장)
SELECT regexp_replace('Hello World 123', '[0-9]+', 'NUM', 'g');
-- 결과: Hello World NUM
-- 룩비하인드 어서션 대체 방법
-- PostgreSQL은 룩비하인드를 지원하지 않으므로 다른 방법으로 우회
-- 에러: SELECT regexp_replace('price: 100', '(?<=price: )\d+', '200');
-- 우회 방법: 전체 매칭 후 재구성
SELECT regexp_replace('price: 100', '(price: )[0-9]+', '\1200');
-- 결과: price: 200
-- 이름 있는 캡처 그룹 대체 (번호 기반 그룹 사용)
SELECT (regexp_match('2024-01-15', '([0-9]{4})-([0-9]{2})-([0-9]{2})'))[1] AS year,
(regexp_match('2024-01-15', '([0-9]{4})-([0-9]{2})-([0-9]{2})'))[2] AS month,
(regexp_match('2024-01-15', '([0-9]{4})-([0-9]{2})-([0-9]{2})'))[3] AS day;
원인 3 해결: 수량자 및 이스케이프 오류 수정
-- 에러 발생: 잘못된 수량자 범위 (최소값 > 최대값)
SELECT 'abcde' ~ 'a{5,2}';
-- ERROR: invalid regular expression: invalid repetition count(s)
-- 수정: 올바른 범위 지정
SELECT 'abcde' ~ 'a{2,5}';
-- 결과: false (a가 연속으로 2~5개인 패턴을 찾으므로)
-- 백슬래시 이스케이프 이중 처리 문제 해결
-- standard_conforming_strings = on (기본값) 환경에서
-- 잘못된 방법: 이중 백슬래시 불필요
SELECT regexp_match('file.txt', '\\.txt$');
-- 올바른 방법: 단일 백슬래시 사용 (standard_conforming_strings=on)
SELECT regexp_match('file.txt', '\.txt$');
-- 결과: {.txt}
-- E'' 리터럴을 사용하는 경우 이중 백슬래시 필요
SELECT regexp_match('file.txt', E'\\.txt$');
-- 결과: {.txt}
-- 동적 패턴 입력 시 유효성 검사 함수 활용
CREATE OR REPLACE FUNCTION safe_regexp_match(
p_text TEXT,
p_pattern TEXT
) RETURNS TEXT[] AS $$
BEGIN
RETURN regexp_match(p_text, p_pattern);
EXCEPTION
WHEN invalid_regular_expression THEN
RAISE WARNING '유효하지 않은 정규식 패턴: %', p_pattern;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- 사용 예시
SELECT safe_regexp_match('hello123', '[0-9]+'); -- {123}
SELECT safe_regexp_match('hello123', '[0-9+'); -- NULL (경고 출력 후)
예방 방법
1. 동적 정규식 패턴에 대한 유효성 검사 래퍼 함수 구현
운영 환경에서 사용자 입력이나 외부 소스로부터 정규식 패턴을 받아 처리하는 경우, 반드시 예외 처리 래퍼 함수를 통해 패턴의 유효성을 사전 검증해야 합니다. 아래와 같이 EXCEPTION 블록을 활용한 PL/pgSQL 함수를 만들어 애플리케이션 레이어에서 에러가 전파되기 전에 차단하는 구조를 갖추는 것이 Best Practice입니다.
-- 정규식 유효성 검사 전용 함수
CREATE OR REPLACE FUNCTION is_valid_regexp(p_pattern TEXT)
RETURNS BOOLEAN AS $$
BEGIN
-- 빈 문자열 테스트로 패턴 유효성만 검사
PERFORM regexp_match('', p_pattern);
RETURN TRUE;
EXCEPTION
WHEN invalid_regular_expression THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- 활용 예시: 배치 처리 전 패턴 필터링
SELECT pattern, is_valid_regexp(pattern) AS is_valid
FROM user_defined_patterns
WHERE is_valid_regexp(pattern) = TRUE;
2. 정규식 패턴 테스트 전용 개발 환경 구축 및 문서화
팀 내에서 사용하는 PostgreSQL 정규식 패턴을 버전 관리 시스템(Git 등)에 등록하고, 주요 패턴에 대한 테스트 케이스를 SQL 스크립트 형태로 관리하는 습관을 들이는 것이 중요합니다. 또한 PostgreSQL의 정규식은 POSIX ERE 기반임을 팀 전체가 인지하고, Perl/Python 등 다른 언어의 정규식을 이식할 때는 반드시 호환성 검토를 거치도록 코드 리뷰 체크리스트에 항목을 추가하는 것을 권장합니다.
-- 패턴 테스트 스크립트 예시 (CI/CD 파이프라인에 통합 가능)
DO $$
DECLARE
v_patterns TEXT[] := ARRAY[
'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', -- 이메일
'^[0-9]{2,3}-[0-9]{3,4}-[0-9]{4}$', -- 전화번호
'^[0-9]{6}(-[0-9]{7})?$' -- 주민등록번호 형식
];
v_pattern TEXT;
BEGIN
FOREACH v_pattern IN ARRAY v_patterns LOOP
IF is_valid_regexp(v_pattern) THEN
RAISE NOTICE '패턴 유효: %', v_pattern;
ELSE
RAISE EXCEPTION '유효하지 않은 패턴 발견: %', v_pattern;
END IF;
END LOOP;
END;
$$;
관련 에러
- 2201C (invalid_argument_for_logarithm): 수학 함수의 잘못된 인자와 유사한 카테고리의 에러로, 함수 입력값 유효성 관련 에러군에 속합니다.
- 2200H (sequence_generator_limit_exceeded): 시퀀스 관련 에러이지만, 동일한
22클래스(데이터 예외)에 속하는 에러입니다. - 22P02 (invalid_text_representation): 문자열을 특정 타입으로 변환할 때 형식이 맞지 않는 경우 발생하며, 정규식 에러와 유사하게 입력 데이터 형식 문제로 발생합니다.
- 42601 (syntax_error): SQL 문법 오류로, 정규식 패턴 자체가 아닌 SQL 쿼리 구문 오류 시 발생합니다. 정규식 에러와 혼동하기 쉬우므로 에러 메시지를 정확히 확인해야 합니다.
- 42883 (undefined_function): 잘못된 인자 타입으로 정규식 함수를 호출할 때 발생할 수 있으며, 2201B 에러와 함께 정규식 관련 디버깅 시 자주 마주치게 됩니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.