정규표현식 (regex) 은 처음에는 외계어처럼 보이지만, 30 분만 투자하면 텍스 트 검증·추출·치환의 80% 를 처리할 수 있다. 이 가이드는 기초 문법부터 그룹·lookaround 같은 중급 기법, 그리고 무엇보다 절대 쓰면 안 되는 패턴 (catastrophic backtracking) 까지 다룬다. 모든 예제 는 정규식 테스터 에 직접 붙여 동작 확인하면서 읽기를 권장한다.
리터럴 vs 메타문자
대부분의 글자는 자기 자신을 의미한다. cat 패턴은 문자열 "cat" 과 매치. 하지만 다음 12 글자는 특별한 의미가 있다.
. ^ $ * + ? ( ) [ ] { } | \이 문자들을 글자 그대로 매치하려면 \ 로 escape — 예: 점 . 매치는 \..
문자 클래스
기본
| 패턴 | 의미 |
|---|---|
. | 줄바꿈 외 한 글자 |
\d | 숫자 (0-9) |
\w | 영숫자 + 언더스코어 ([A-Za-z0-9_]) |
\s | 공백 (스페이스, 탭, 줄바꿈) |
\D \W \S | 위 세 가지의 부정 (negation) |
대괄호 — 직접 정의
[aeiou] # 모음 하나
[a-z] # 소문자 한 글자
[A-Za-z0-9] # 영숫자
[^abc] # a, b, c 제외 한 글자 (^ 가 부정)대괄호 안에서는 대부분의 메타문자가 리터럴이 된다 (단, ] \ ^ - 는 예외).
수량자 (quantifier)
| 패턴 | 의미 |
|---|---|
? | 0 또는 1 번 |
* | 0 번 이상 |
+ | 1 번 이상 |
{n} | 정확히 n 번 |
{n,} | n 번 이상 |
{n,m} | n 번 이상 m 번 이하 |
예: \d{3,4} 는 숫자 3~4 자리. https?:// 는 "http://" 또는 "https://".
greedy vs lazy
수량자는 기본이 greedy — 가능한 한 길게 매치한다. 뒤에 ? 를 붙이면 lazy — 가능한 한 짧게.
<.+> # greedy: <b>text</b> 전체 매치
<.+?> # lazy: <b> 만 매치HTML 태그처럼 "다음 닫는 글자까지만" 매치하려면 lazy 가 거의 항상 정답.
앵커
^— 줄 시작$— 줄 끝\b— 단어 경계 ([A-Za-z0-9_] 와 그 외 사이)
예: ^[A-Z] 는 대문자로 시작하는 줄. \bcat\b 는 단어 "cat" — "category" 안의 cat 은 매치 안 함.
그룹과 캡처
괄호로 묶으면 (1) 패턴을 그룹화하고 (2) 매치 결과를 캡처한다.
(\d{4})-(\d{2})-(\d{2})"2026-05-16" 에 매치하면 group 1 = "2026", group 2 = "05", group 3 ="16". 대부분의 언어에서 match.groups[1] 또는 result[1] 로 접근.
이름 있는 그룹
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})JavaScript: match.groups.year. Python: m.group('year').
non-capturing 그룹
(?:https?|ftp)://그룹화만 하고 캡처 안 함. 결과 배열 인덱스 깨지지 않게 유지.
대안 (alternation)
cat|dog|bird세 단어 중 하나. 보통 그룹과 같이 쓴다 — (cat|dog|bird).
lookaround
"이 위치 앞/뒤에 X 가 있어야 함, 단 X 자체는 매치에 포함 안 함" 을 표현 한다. 매치 결과는 그대로지만 조건만 검사.
| 패턴 | 의미 |
|---|---|
X(?=Y) | X 뒤에 Y 가 오면 매치 (lookahead) |
X(?!Y) | X 뒤에 Y 가 없으면 매치 (negative lookahead) |
(?<=Y)X | X 앞에 Y 가 있으면 매치 (lookbehind) |
(?<!Y)X | X 앞에 Y 가 없으면 매치 (negative lookbehind) |
예: \d+(?= 원) — "1000 원" 의 "1000" 만 매치 (단위 "원" 은 제외).
플래그
i— 대소문자 무시g— 모든 매치 (JavaScript)m— multi-line:^/$가 각 줄에 매치s— dotAll:.가 줄바꿈도 포함u— Unicode (JavaScript)
실전 예제
이메일 (대충)
^[^\s@]+@[^\s@]+\.[^\s@]+$완벽한 RFC 5322 매칭은 수십 줄이지만, 위 패턴이면 실용적 검증 95% 처리. 진짜 검증은 "이메일 보내봐서 응답 받기" 가 정답.
URL
https?://[^\s/$.?#].[^\s]*한글 추출
[가-힣]+Unicode 플래그를 켜고 \p{Hangul} 가 더 정확.
슬러그 검증
^[a-z0-9]+(-[a-z0-9]+)*$kebab-case slug — Slug 생성기 가 만드는 형식과 일치.
Catastrophic Backtracking — 절대 피해야 할 패턴
regex 엔진은 매치 실패 시 백트래킹을 한다. 그런데 일부 패턴은 입력 길이 에 대해 지수 시간이 걸려, 100 글자 입력이 수 분~수 시간 걸리거나 서버를 DoS 시키는 사고가 난다 — 이를 ReDoS (Regular expression Denial of Service) 라고 부른다.
위험한 패턴 1: 중첩 수량자
(a+)+$"aaaaaaaaaaaaaaaaaaaaaaaa!" 같은 입력에 매치 실패하면 엔진이 모든 분할 조합을 시도 — 글자 수에 지수.
위험한 패턴 2: alternation + greedy
(a|a)+$같은 글자를 두 대안으로 표현 — 매 위치에서 2 가지 선택이 누적되어 지수.
완화 방법
- 중첩 수량자 금지 —
(a+)+같은 패턴 제거.a+으로 충분. - atomic group / possessive 수량자 — 일부 엔진 (PCRE, Java) 은
(?>a+)+또는a++로 백트래킹 금지. JavaScript 는 미지원. - 입력 길이 제한 — 사용자 입력 정규식 매치는 길이 상한 (예: 10 KB) 박기.
- 매치 타임아웃 — Java
Pattern.matches가 타임아웃 옵션 미지원이라 별도 스레드 + interrupt 패턴. Go 의 RE2 는 애초에 지수 패턴이 불가능 (정규 표현식의 제한 버전). - 검증 — production 배포 전에 패턴을 ReDoS checker (예: regex101 의 분석 모드) 또는 yutils 의 정규식 테스터 로 시간 측정해 본다.
도구로 직접 보기
- 정규식 테스터 — 패턴 + 플래그 + 입력 으로 실시간 매치·그룹 캡처 확인.
- 문자열 케이스 변환 — regex 로 자주 하는 case 변환은 따로 도구화돼 있어 더 간단.
- Slug 생성기 — 한글·이모지 포함 입력을 kebab-case 슬러그로.
요약
- 메타문자 12 개를 외워라 — 나머진 거의 리터럴.
- 수량자는 기본 greedy. 닫는 문자까지만 매치하려면 lazy (
?추가). - 그룹은 캡처 + 그룹화 두 역할. 캡처가 필요 없으면
(?:…). - lookaround 는 "조건만 검사, 매치에는 미포함" — 단위 분리·치환 효율적.
- 중첩 수량자
(a+)+같은 패턴은 절대 금지 — ReDoS.