1) 기본 메타문자(요약)
. : 줄바꿈 문자를 제외한 임의의 한 문자
^ $ : 문자열의 시작/끝 (문맥에 따라 줄 시작/끝)
* + ? : 0회 이상 / 1회 이상 / 0 or 1
{n} {n,} {n,m} : 정확히 n, n회 이상, n~m회 반복
[] : 문자 클래스 (예: [a-zA-Z0-9])
[^...] : 부정 문자 클래스
| : OR (alternation)
() : 그룹(캡쳐)
(?: ) : 비캡처 그룹
\ : 이스케이프 (특수문자를 문자로 취급)
\w \W : 단어 문자 / 비단어 문자
\d \D : 숫자 / 비숫자
\s \S : 공백 / 비공백
\b \B : 단어 경계 / 비단어 경계
2) 앵커와 경계
^ : 문자열(또는 multiline에서 줄)의 시작
$ : 문자열(또는 multiline에서 줄)의 끝
\A : 입력의 시작(항상)
\z : 입력의 끝(항상)
\Z : 입력 끝 또는 끝 앞의 최종 줄바꿈
\b : 단어 경계 — 단어 문자와 비단어 문자 사이
\B : 단어 경계가 아닌 곳
3) 그룹과 캡처
(pattern) : 캡처 그룹 — 이후 \1, \2 등으로 참조 가능
(?:pattern) : 비캡처 그룹 — 그룹화는 하지만 캡처 안 함(성능·명확성에 유리)
이름 있는 그룹:
PCRE / JS(ES2018+) / Java: (?<name>...)
Python: (?P<name>...)
역참조: \k<name> 또는 Python의 (?P=name)
예:
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
4) 수량자(Quantifiers) — 탐욕성 vs 최소매칭
탐욕적(Greedy): *, +, ?, {m,n} — 가능한 한 많이 매칭
최소(Non-greedy / Lazy): *?, +?, ??, {m,n}? — 가능한 한 적게 매칭
소유적(Possessive, Java/PCRE 일부): *+, ++, ?+, {m,n}+ — 백트래킹을 허용하지 않음(성능 보장)
예 — 태그 매칭:
<.+> // 탐욕적: "<a>text</a>" 전체를 매칭
<.+?> // 최소: "<a>" 와 "</a>"를 각각 정확히 매칭
5) 어서션(Assertions, Lookaround) — 뒤/앞 조건
전방 긍정: (?=...) — 뒤에 특정 패턴이 오는 경우
전방 부정: (?!...) — 뒤에 특정 패턴이 오지 않는 경우
후방 긍정: (?<=...) — 앞에 특정 패턴이 있는 경우
후방 부정: (?<!...) — 앞에 특정 패턴이 없는 경우
예:
\d+(?=USD) // "100USD"에서 "100"을 캡처(뒤에 USD가 있어야)
(?<=USD)\d+ // "USD100"에서 "100"을 캡처(앞에 USD 있어야)
주의: 일부 엔진(JavaScript 오래된 버전 등)은 lookbehind를 지원하지 않습니다. 또한 일부 엔진은 lookbehind에 고정 길이만 허용합니다.
6) 플래그(Modifiers)
i : 대소문자 무시 (case-insensitive)
m : multiline — ^/$가 줄별 경계로 동작
s : dotall — .가 줄바꿈도 포함하게 함
x : verbose / free-spacing — 공백·주석 허용(가독성 높임, Python re.X)
u : Unicode 모드 (언어별 차이 있음)
g : global (검색/치환에서 모든 매칭; JS에서 사용)
인라인 선언:
(?i)pattern // 패턴 전체에 case-insensitive 적용
(?imx)pattern // 여러 플래그를 동시에
7) 유니코드/문자속성
많은 엔진(PCRE, Java, JS with /u 등)은 \p{...}로 문자 속성 사용 가능:
\p{L} : 모든 문자(letters)
\p{N} : 숫자
\p{Han} 등 언어별 스크립트
일부 엔진은 표준 \p를 지원하지 않으니(예: 표준 Python re는 미지원) 사용 전 엔진 문서 확인 필요.
8) 자주 쓰이는 실전 패턴들 (간단 & 실용)
이메일(간단 검증. 완전한 RFC 검증은 복잡):
^[^\s@]+@[^\s@]+\.[^\s@]+$
IPv4 주소:
^(?:(?:25[0-5]|2[0-4]\d|1?\d{1,2})\.){3}(?:25[0-5]|2[0-4]\d|1?\d{1,2})$
24시간 시:분 (HH:MM):
^(?:[01]\d|2[0-3]):[0-5]\d$
날짜(YYYY-MM-DD, 기본 검증 — 윤년/월별 일수는 추가처리 필요):
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
16진 색상 (#RRGGBB 또는 #RGB):
^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$
UUID (v1-v5 일반형):
^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89ABab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$
비밀번호 복잡도 예 (최소 8자, 대문자·소문자·숫자·특수문자 포함):
^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^\w\s]).{8,}$
숫자(정수/소수, 음수 허용):
^-?\d+(?:\.\d+)?$
URL(간단 검증):
^(https?:\/\/)?([\w\-]+\.)+[\w\-]+(\/[\w\-./?%&=]*)?$
(실전에서는 URL 파서/라이브러리 사용 권장)
HTML 태그 제거(매칭용, 파싱은 라이브러리 권장):
<[^>]+>
(주의: HTML 구조를 완벽히 다루진 못함)
9) 캡처 그룹/백레퍼런스 예시
(\w+)\s+\1
같은 단어가 연속으로 오는 경우(예: “hello hello”)를 찾음. \1은 1번째 캡처 그룹을 재참조.
이름 있는 그룹 예:
(?<word>\w+)\s+(?P=word) // PCRE/JS + Python 방식 혼용 예시 — 엔진별 문법 확인
10) 성능·안전: ReDoS(정규표현식 DoS)와 회피법
문제 원인: 과도한 백트래킹을 일으키는 패턴(특히 중첩된 반복 (.+)+, (a|aa)+b 등)이 긴 입력에 대해 매우 느려짐.
회피법:
가능한 한 구체적인 문자 클래스를 사용(예: \d 대신 [0-9]는 같지만 명확성 유지).
탐욕적 패턴을 최소화하고, 필요하면 소유적( possessive ) 또는 atomic 그룹 (?>...) 사용(PCRE/Java).
비캡처 그룹 (?:...) 사용으로 캡처 비용 최소화.
복잡한 구조(HTML, CSV 등)는 정규표현식이 아닌 전용 파서 사용.
사용자 입력 길이 제한 및 타임아웃(엔진/라이브러리 제공 시).
예 — atomic group (PCRE/Java):
(?>a+)+b // 백트래킹 차단
11) 엔진별 실무 팁
JavaScript: 문자열 리터럴에선 /pattern/flags. ES2018 이후 named groups · lookbehind 일부 지원(환경 확인). 글로벌(전역) 검색 시 /g 사용.
Python (re): r'pattern'(raw string) 권장. re.X로 가독성 향상, re.UNICODE는 기본(파이썬3). 이름 그룹은 (?P<name>...) 사용. 표준 re는 \p{} 미지원(복잡한 유니코드 속성은 regex 모듈 사용).
PHP / PCRE: 구분자(/.../)와 플래그(/i, /m, /s 등)을 사용. PCRE는 풍부한 확장(atomic, possessive) 지원.
Java (java.util.regex): 문자열 내에서 \\ 이스케이프 주의(예: "\\d+"). Java는 possessive quantifier 지원(++, *+, ?+)하고 성능 측면에 강함.
12) 실전 권장 패턴·관행
명확하게 앵커 사용: 전체 일치를 원하면 ^...$로 감싸기.
불필요한 캡처 금지: 캡처가 필요 없으면 (?:...) 사용.
가독성 위해 verbose 모드 활용: 복잡한 패턴은 주석과 공백을 허용하는 x 모드로 작성.
테스트: 다양한 입력(경계 조건·악의적 입력)을 가지고 충분히 시험. regex101.com 같은 도구로 즉석 확인(엔진 선택 가능).
파싱 작업은 파서 사용: HTML, XML, CSV같이 문법이 있는 데이터는 전용 파서 권장.