정규표현식(Regex) 핵심 문법과 파국적 되돌아감(ReDoS) 방어
NFA 정규식 엔진의 동작 메커니즘, 전방/후방 탐색(Lookaround), 서버 지연을 유발하는 파국적 되돌아감(Catastrophic Backtracking) 원인과 실무 필수 패턴을 정리합니다.
정규 표현식 테스터
서버 전송 없이 100% 브라우저 로컬에서 안전하게 직접 테스트하고 검증해보세요.
정규식 엔진의 내부 평가 방식 (NFA vs DFA)
JavaScript, Python, Java, Go 등 대다수 범용 언어는 비결정론적 유한 오토마톤(NFA) 정규식 엔진을 탑재하고 있습니다. 문자열 길이에 비례해 선형 시간 O(n)으로 실행되는 DFA 엔진과 달리, NFA 엔진은 캡처 그룹, 역참조, 전후방 탐색과 같은 풍부한 표현력을 제공합니다.
하지만 NFA 엔진은 매칭을 추측하고 실패할 때 이전 분기점으로 되돌아가는 백트래킹(Backtracking) 방식으로 작동합니다. 정규식 패턴이 비효율적으로 작성되면 이 백트래킹 횟수가 급증할 수 있습니다.
전방 탐색(Lookahead)과 후방 탐색(Lookbehind) 완벽 이해
Lookaround는 문자열의 문자를 소비(매칭 결과에 포함)하지 않고, 특정 조건의 위치만을 검증하는 너비 0의 단언(Zero-width assertion)입니다:
- 긍정 전방 탐색
(?=...): 뒤따라오는 패턴이 일치하는 위치를 찾습니다.- 예:
\d+(?=px)는 뒤에px가 붙은 숫자만 매칭합니다 ("120px"에서"120"만 선택).
- 예:
- 부정 전방 탐색
(?!...): 뒤따라오는 패턴이 일치하지 않는 경우만 매칭합니다.- 예:
passwd(?!\.bak)는.bak확장자가 아닌passwd파일명만 매칭.
- 예:
- 긍정 후방 탐색
(?<=...): 선행하는 패턴이 일치하는 위치를 찾습니다.- 예:
(?<=\$)\d+는 달러 기호 바로 뒤의 숫자만 매칭 ("$99"에서"99").
- 예:
- 부정 후방 탐색
(?<!...): 선행하는 패턴이 일치하지 않는 경우만 매칭합니다.
파국적 되돌아감(ReDoS)의 원리와 방어
정규식 서비스 거부 공격(ReDoS)은 중첩되거나 모호한 수량자(Quantifier)가 포함된 정규식이 매칭에 아슬아슬하게 실패하는 긴 문자열을 만났을 때 발생합니다. NFA 엔진이 수많은 조합을 일일이 백트래킹하며 탐색하느라 노드 이벤트 루프를 멈추거나 과도한 CPU 점유를 유발합니다.
// ❌ 취약한 정규식 패턴: 중첩된 수량자 (a+)+
const vulnerableRegex = /^(a+)+$/;
// 일치하지 않는 긴 문자열을 검증할 때 긴 지연이 발생할 수 있음:
vulnerableRegex.test("aaaaaaaaaaaaaaaaaaaaaaaaab");
// ✅ 단순화된 안전한 패턴 리팩토링:
const safeRegex = /^a+$/;
safeRegex.test("aaaaaaaaaaaaaaaaaaaaaaaaab"); // 선형 시간에 즉시 실패실무 웹 개발에서 자주 쓰이는 검증된 정규식 패턴 모음
- UUID v4:
^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$ - IPv4 주소:
^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ - DevUtils의 실시간 Regex Tester를 통해 복잡한 패턴을 브라우저에서 안전하게 테스트해 보세요.
방어적 정규식 아키텍처: 실행 타임아웃과 시맨틱 버저닝(SemVer) 실전 패턴
사용자 입력값을 정규식으로 처리할 때 서버의 알고리즘적 복잡도 폭주를 방어하기 위한 실무 기법입니다:
- 실행 타임아웃(Timeout) 강제: V8 정규식 엔진은 자바스크립트 메인 스레드에서 동기적으로 실행됩니다. 악의적인 문자열이 인입되면 Node.js 이벤트 루프 전체가 멈춥니다. 신뢰할 수 없는 사용자 정규식은 별도의 Worker Thread에서 실행하거나 실행 시간 제한을 설정해야 합니다.
- 원자적 그룹(Atomic Groups)과 소유 수량자: PCRE 계열 엔진(Java, PHP, Python
regex)에서는 소유 수량자(a++)나 원자적 그룹((?>a+))을 사용하여 한 번 매칭된 부분에 대해 엔진이 되돌아가지 않도록 백트래킹을 원천 차단할 수 있습니다. - 실무 복합 패턴 - 시맨틱 버저닝 (SemVer 2.0): 공식 SemVer 정규식은 비캡처링 그룹
(?:...)과 선택적 사전 출시/빌드 메타데이터를 백트래킹 위험 없이 안전하게 파싱하는 모범 예시입니다.
// 공식 RFC 규격을 준수하는 SemVer 2.0.0 정규표현식
const semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
console.log(semverRegex.test("2.1.0-alpha.1+build.20261003")); // true
console.log(semverRegex.test("v2.1.0")); // false (엄격한 SemVer에서는 접두사 'v' 불허)정규 표현식 테스터
실시간 정규식 매칭 및 캡처 그룹 확인 도구. g, i, m, s, u 플래그 지원.