
정규식 탐욕적(greedy) vs 게으른(lazy) 매칭: 패턴이 너무 많이 잡아채는 이유
게시일 2026년 7월 24일
굵은 글씨 태그 안의 내용을 잡으려고 <b>.*</b>를 쓰고 <b>Hello</b>로 테스트하면 완벽하게 동작합니다. 그런데 같은 줄에 굵은 글씨 태그가 두 개 있는 실제 HTML — <b>Hello</b> and <b>World</b> — 에 돌려 보면, 매치 두 개 대신 두 태그를 모두 아우르는 거대한 매치 하나가 나옵니다. 정규식 엔진의 버그가 아닙니다. *, +, {n,m}의 기본 동작이고, 무슨 일이 벌어지는지만 알면 한 글자로 고칠 수 있습니다.
.*가 예상보다 많이 잡아채는 이유
기본적으로 정규식의 수량자는 **탐욕적(greedy)**입니다. .*는 “문자 몇 개를 매치하라”가 아니라 “가능한 한 많은 문자를 매치하고, 패턴의 나머지가 절대적으로 요구할 때만 물러나라”는 뜻입니다.
<b>.*</b>를 <b>Hello</b> and <b>World</b>에 대해 단계별로 따라가 봅시다.
<b>가 첫 번째<b>에 매치됩니다..*가 우선 문자열의 나머지 전체를 삼킵니다. 두 번째<b>World</b>를 포함해 끝까지 전부요.- 엔진은 매치를 끝내기 위해
</b>를 찾아야 하므로,.*의 끝에서부터 한 글자씩 물러나기 시작합니다. - (끝에서부터 거꾸로 훑을 때)
</b>가 들어맞는 첫 번째 자리는 문자열의 맨 마지막</b>입니다. “논리적으로” 멈춰야 할 것 같은 첫 번째</b>가 아니라요.
그래서 매치는 <b>Hello</b> and <b>World</b> 전체가 되어 버립니다. 탐욕적 매칭은 항상 가능한 가장 긴 문자열을 먼저 시도하고, 패턴의 나머지를 성공시키는 데 필요한 최소한만큼만 줄이기 때문입니다.
해결책: ?로 수량자를 게으르게 만들기
수량자 바로 뒤에 ?를 붙이면 탐욕적에서 게으른(lazy) 방식(“non-greedy” 또는 “reluctant”라고도 합니다)으로 바뀝니다. *?, +?, ??, {n,m}?처럼요.
게으른 수량자는 정반대로 동작합니다. 가능한 한 적은 문자로 시작해서, 패턴의 나머지가 아직 성공할 수 없을 때만 확장합니다.
같은 문자열에 <b>.*?</b>를 적용하면:
<b>가 첫 번째<b>에 매치됩니다..*?가 우선 0개의 문자를 매치합니다.- 엔진이 확인합니다. 바로 여기서
</b>가 매치되나? 아니요(“Hello…” 지점이지 아직</b>가 아니니까요). 그래서.*?를 정확히 한 글자만 확장하고 다시 확인합니다. .*?가 정확히Hello를 삼킬 때까지 한 글자씩 반복되고, 그 시점에</b>가 곧바로 매치됩니다.
결과: <b>Hello</b>와 <b>World</b>가 두 개의 별도 매치로 돌아옵니다. 태그류나 구분자류 구조를 파싱할 때 거의 언제나 실제로 원했던 결과죠.
탐욕적 매칭이 정말 필요한 경우 (그냥 “잘못된 기본값”이 아닙니다)
탐욕적 동작은 언어 설계의 실수가 아닙니다. 똑같이 흔한 다른 경우 — 안쪽의 가장 작은 단위가 아니라 무언가의 가장 바깥 경계를 매치하는 경우 — 에 올바른 동작입니다. JSON 비슷한 텍스트 덩어리에서 “첫 번째 {와 마지막 } 사이의 모든 것”을 추출한다면(중첩과 무관하게 객체 전체를 잡으려는 경우), 탐욕적 방식이 정확히 맞습니다. 게으른 방식은 첫 번째 안쪽 }에서 멈춰서 잘려 나간 무효한 조각을 돌려줄 겁니다.
경험칙은 이렇습니다. 반복되는 작은 구분 단위(태그, 따옴표 문자열, 목록 항목)에는 게으른 방식, **“바깥 범위 전체 잡기”**에는 탐욕적 방식.
빠른 참조표
| 패턴 | 동작 | 쓰는 상황 |
|---|---|---|
.*, .+, {n,m} |
탐욕적 — 가능한 가장 길게 매치 | 가장 바깥 범위를 원하거나 문자열에 매치가 하나뿐일 때 |
.*?, .+?, {n,m}? |
게으른 — 가능한 가장 짧게 매치 | 비슷한 구분 단위가 여러 개 있고 각각 따로 원할 때 |
테스트 문자열이 아니라 실제 입력에서 패턴이 어느 쪽으로 동작하는지 확신이 없다면, 실시간 매치 하이라이트를 지원하는 정규식 테스터에 실제 다중 매치 텍스트를 넣고 돌려 보세요. 차이가 즉시 눈에 보입니다. 탐욕적이면 거대한 하이라이트 블록 하나가, 게으르면 여러 개의 분리된 블록이 보이는데, 한 글자씩 머리로 따라가는 것보다 대개 이쪽이 빠릅니다.