🔣 메타문자 이해로 시작하는 정규표현식
개발을 하다 보면 특정 텍스트만 골라내거나 구분해야 하는 순간이 찾아온다. 이때 가장 빠르고 가벼우며 어디서나 사용할 수 있는 것이 정규표현식(Regular Expression, regex)이다.
정규표현식을 잘 사용하기 위해서는 패턴의 의미를 바꾸는 메타문자에 대해 이해하는 것이 중요하다. 메타문자는 일반 문자와 달리 특별한 동작을 수행해 텍스트를 더 정확하고 짧게 찾고 검증하고 치환하고 추출할 수 있게 한다.
[메타문자]
정규식은 "토큰 + 규칙"의 조합이다. 문자를 실제로 소비하는 토큰(리터럴, 문자 클래스, 축약 클래스, 그룹 등)과 연산자(수량자, 분기) 그리고 문자를 소비하지 않는 제로폭 주장(앵커, 경계, 탐색)을 조합한다.
메타문자는 이 토큰, 연산자, 주장에 특수 의미를 부여하거나 수정하는 기호의 총칭이다.
1) . - 개행을 제외한 임의의 글자 1개
. (dot)은 줄바꿈 문자를 제외하고 어떤 글자든 1개를 의미한다.
a.b → “acb”, “a b”, “a한b”, “a_b” 등이 포함된다.
2) [ ] - 문자 클래스 (후보 중 1글자)
[…] 안에 허용할 문자들의 집합을 적고 각 글자를 매칭한다.
[abc] → a 또는 b 또는 c
[0-9] → 0~9
[^…] 처럼 맨 앞에 ^를 사용하면 부정을 의미한다.
[^0-9] → 숫자가 아닌 1글자
3) - - 하이픈(범위)
문자 클래스 […] 안에서 범위를 의미한다.
[A-Z] → A부터 Z까지
[0-9] → 숫자 0~9
클래스 안에서 맨 앞/뒤에 두는 경우 리터럴로 취급되기 때문에 주의
4) | - 또는/분기
a|b 는 a 또는 b 를 뜻한다. 우선순위가 가장 낮기 때문에 괄호로 묶지 않으면 좌우 전체를 나눈다.
ab|cd = (ab)|(cd)
a|bc = (a)|(bc)
평가 순서는 왼쪽에서 오른쪽으로 진행된다. 현재 시작 위치에서 왼쪽 대안부터 시도하고 그 대안이 전체 패턴을 만족하면 거기서 확정된다.
입력: caterer
패턴: (cat|cater)er
동작: cat대안을 먼저 사용하여 cat + er = cater로 이미 패턴이 성공하면 더 긴 cater 대안은 시도하지 않고 끝난다.
따라서 겹치는 대안이 있다면 긴 것 혹은 구체적인 것을 앞에 두는 것이 좋다.
5) \ - 이스케이프 & 약어 클래스
\ 는 두 가지 의미로 사용되는데 첫 번째는 메타문자를 문자 자체(리터럴)로 사용해야 할 때 이스케이프 한다.
pyomin\.com → 실제 점(.)
file\(name\)\.txt
두 번째는 약어 클래스(자주 쓰는 집합)를 표현할 때 사용된다.
\d → 숫자 [0-9]
\w → 단어문자 [A-Za-z0-9_]
\s → 공백류(스페이스, 탭 \t, 개행 \n 등)
\b → 단어 경계(제로폭)
\p → 유니코드 속성 이스케이프
대문자는 반대 집합을 의미하며(\D, \W, \S) \B는 경계의 부정을 의미한다.
\s를 제외한 나머지는 ASCII 한정이기 때문에 한글/전각 숫자 등은 포함되지 않는다.
6) {m,n} - 수량자 (반복 개수)
바로 앞 토큰을 m~n회 반복한다.
\d{3} → 숫자 정확히 3개
\d{3,} → 숫자 3개 이상
\d{2,4} → 숫자 2~4개
자주 쓰이는 수량자는 축약형이 존재한다.
{0,1} → ?
{0,} → *
{1,} → +
수량자는 기본적으로 탐욕(greedy)이다. 가능한 많이 소비하려 하지만 전체 정규식이 매칭되도록 필요한 만큼은 다시 내어준다. 하지만 수량자 뒤에 ?를 붙이면 게으름(lazy)이 된다. 가능한 적게 소비하되 매칭을 만들기 위해 필요하면 점점 더 먹는다.
대부분의 엔진은 좌측부터 가장 이른 시작 위치를 먼저 고르고 그 선택 안에서 탐욕/게으름 규칙대로 소비량을 조절한다.
예를 들어 a1234b라는 문자열이 있을 때
패턴1: /a\d{2,4}b/ (탐욕)
a 매칭 → \d{2,4} 가장 많이(4개) 먹어 보기 → 1234 → 남은 b검사 → 존재 → 전체 매칭 성공 → 결과: a1234b
패턴2: /a\d{2,4}?b/ (게으름)
a 매칭 → \d{2,4} 최소(2개) 먹어 보기 → 12 → b 검사 (다음 문자가 3이라 실패) → 백트래킹으로 숫자 하나 더(3) 먹음 → b 검사 (다음 문자가 4라서 실패) → 숫자 하나 더(4) 먹음 → b 검사 → 성공 → 결과: a1234b
정리하면 탐욕은 많이 → 필요하면 줄이기, 게으름은 적게 → 필요하면 늘리기로 백트래킹을 통해 전체 패턴이 맞도록 소비량을 조절한다.
7) ^ $ - 시작/끝 앵커
^는 문자열(또는 줄)의 시작을 의미하고 $는 끝을 의미한다.
^\d+$ → 처음부터 끝까지 숫자만
^Hello → “Hello”로 시작
end$ → “end”로 끝
멀티라인 플래그 m을 사용할 경우 각 줄의 시작/끝도 앵커로 취급된다. 또한 ^는 문자 클래스 밖일 때에만 앵커로 사용된다.
8) ( ) - 그룹/캡처
(…)로 묶으면 하나의 토큰처럼 취급되어 수량자를 붙일 수 있다.
(ab){3} → ababab
(https?):// → http:// 또는 https://
또한 (…)로 감싼 부분이 매치될 때 실제 텍스트를 그룹에 1부터 순서대로 저장한다.
입력: 2025-08-28
패턴: (\d{4})-(\d{2})-(\d{2})
그룹: 1 = “2025”, 2 = “08”, 3 = “28”
이렇게 저장된 문자열을 그대로 다시 재사용이 가능하다.
만약 캡처만 하고 그룹을 재사용할 일이 없다면 (?:..)을 사용한다.
^(?:https?|ftp):// → http/https/ftp 공통 접두부 처리