정규식
JavaScript 정규식을 브라우저에서 실시간으로 테스트. 매치 강조, 캡처 그룹 확인, 모든 플래그 지원.
강조 결과
매치 상세
자주 쓰는 패턴
📖 자주 걸리는 지점
JavaScript 의 정규식 엔진으로 패턴을 실시간으로 평가하고 매치 위치의 하이라이트와 캡처 그룹을 표시합니다. 처리는 브라우저 안에서 끝납니다. 쓰고 있는 것은 JavaScript 의 방언입니다 — 여기서 통한 패턴이 PHP · Python · Go 에서 그대로 동작한다고는 할 수 없습니다. 문자 클래스의 의미, lookbehind 의 유무, 유니코드의 취급이 언어마다 다르기 때문입니다.
| 사례 | 무슨 일이 일어나는가 | 어떻게 하면 되는가 |
|---|---|---|
| g 플래그를 붙이면 두 번째 결과가 달라진다 | g 플래그가 붙은 정규식 객체는 lastIndex 라는 상태를 갖습니다. 같은 객체로 test() 를 반복하면 지난번 매치 위치부터 찾기 시작하므로 같은 문자열에 대해 true 와 false 가 번갈아 돌아옵니다. 이것이 치명적이 되는 것은 정규식을 모듈의 최상위에서 정의해 돌려 쓸 때입니다 — 상태가 요청을 넘어 남아 가끔 검증을 통과해 버린다는 재현되지 않는 버그가 됩니다. 테스트에서는 한 번밖에 부르지 않으므로 우선 알아챌 수 없습니다. |
test() 나 exec() 로 쓰는 정규식에 g 를 붙이지 마세요 — 전부에 매치시키고 싶다는 의도로 붙이기 쉽지만 test() 는 하나라도 있는가를 보는 함수이므로 g 는 불필요합니다. 전건을 얻고 싶다면 str.matchAll(re) 을 쓰세요(이쪽은 g 가 필수입니다). 돌려 쓰는 객체에서 g 가 필요하다면 호출 직전에 re.lastIndex = 0 을 넣으세요. 애초에 정규식 리터럴을 함수 안에서 매번 쓰는 비용은 무시할 수 있습니다 — JavaScript 엔진은 리터럴을 캐시하므로 공유를 피하는 편이 안전하고 속도도 거의 변하지 않습니다. |
| 일본어나 이모지가 생각대로 매치되지 않는다 | \w 는 ASCII 의 영숫자와 밑줄만을 의미합니다 — 일본어에는 일절 매치되지 않습니다. \b(단어 경계)도 같은 이유로 일본어 문장 안에서는 기대대로 작동하지 않습니다. 또 하나 심각한 것이 서로게이트 페어로 u 플래그를 붙이지 않으면 이모지나 일부 한자(𠮷 · 𩸽 등)가 두 글자로 다뤄집니다 — . 가 절반만 매치되고 [𠮷] 라는 문자 클래스는 두 서로게이트 중 하나라는 뜻 모를 집합이 됩니다. 글자 수 카운트나 잘라내기에서 글자가 깨지는 원인의 대부분이 이것입니다. |
u 플래그를 반드시 붙이세요 — 이것만으로 서로게이트 페어가 한 글자로 다뤄지고 나아가 \p{...} 라는 유니코드 프로퍼티를 쓸 수 있게 됩니다. 일본어라면 \p{Script=Hiragana} · \p{Script=Katakana} · \p{Script=Han} 입니다 — [ぁ-んァ-ヶ一-龠] 같은 범위 지정보다 정확하고 의도도 읽힙니다. 이모지를 다룬다면 \p{Extended_Pictographic} 이지만 피부색이나 가족 이모지는 여러 코드 포인트의 조합이므로 한 글자로 다루려면 Intl.Segmenter 가 필요합니다 — 정규식만으로는 한계가 있습니다. 글자 수를 세는 목적이라면 정규식이 아니라 Intl.Segmenter 를 쓰세요. |
| 탐욕적 매치로 생각보다 넓게 잡힌다 | .* 는 가능한 한 길게 매치하려고 합니다. 따라서 <div>A</div><div>B</div> 에 대해 <.*> 를 걸면 첫 < 부터 마지막 > 까지 전체가 하나의 매치가 됩니다. . 는 기본적으로 줄바꿈에 매치되지 않으므로 여러 줄이면 멈추지만 s 플래그를 붙인 순간 파일 전체를 삼킵니다. 이 동작은 올바르지만 기대와 어긋나면 테스트에서는 통과하고 프로덕션의 긴 입력에서 깨지는 형태로 나옵니다. |
비탐욕의 .*? 로 하거나 더 좋게는 [^>]* 처럼 거기에 나타나지 않는 문자로 쓰는 것입니다 — 후자는 백트래킹이 일어나지 않으므로 빠르고 의도도 명확해집니다. 다만 더 근본적인 조언으로 HTML 을 정규식으로 파싱하지 마세요 — 속성값 안의 >, 주석, CDATA, 중첩된 같은 이름의 태그에서 반드시 무너집니다. 브라우저라면 DOMParser, 서버라면 각 언어의 HTML 파서를 쓰세요. 정규식이 맞는 것은 행 단위이고 구조를 갖지 않으며 서식이 정해진 텍스트입니다 — 로그의 행, 설정 파일의 한 줄, 식별자의 서식 검증. 중첩이 있는 구조는 정규식의 수비 범위 밖입니다. |
중첩된 수량자는 ReDoS 의 온상입니다. (a+)+$ 나 (\w+\s?)*$ 처럼 반복 안에 반복이 있는 식은 매치되지 않는 입력에 대해 지수 시간이 걸립니다 — 30 자 정도의 입력으로 서버의 CPU 가 몇 분간 100% 가 됩니다. 얄궂게도 이것이 가장 많이 발견되는 것은 엄밀한 메일 주소 검증의 정규식이며 실제로 대규모 서비스의 장애 원인이 된 사례가 여럿 있습니다. 대책은 세 가지 — 중첩된 수량자를 쓰지 않는다, 입력의 길이에 상한을 둔다, 그리고 백트래킹하지 않는 엔진(Go 의 표준 regexp, Rust 의 regex, C++ 의 RE2)을 쓴다는 것입니다. 방언의 차이도 짚어 두세요 — JavaScript 에는 \A 와 \z 가 없습니다(m 플래그 없는 ^ $ 로 대체합니다). lookbehind 는 ES2018 에서 들어왔지만 Safari 의 대응이 16.4 로 늦었으므로 오래된 환경을 대상으로 한다면 피하세요.
📖 사용법
-
1
패턴 입력정규식을 입력하고 플래그를 토글합니다.
-
2
테스트 텍스트 붙여넣기대상 문자열을 붙여넣으면 매치된 부분이 실시간으로 강조됩니다.
-
3
캡처 그룹 확인오른쪽 패널에 각 매치의 위치와 캡처 그룹 값이 표시됩니다.
❓ 자주 묻는 질문
어떤 정규식 엔진을 사용하나요?
일본어 문자 클래스를 사용할 수 있나요?
성능은 어느 정도인가요?
다른 언어의 코드로 변환할 수 있나요?
🐛 이 도구에서 문제가 발생했나요?
무료 · 가입 불필요. 재현 절차만이라도 도움이 됩니다. 보고는 운영자에게 직접 전달되어 개선에 사용됩니다.
보고 감사합니다!
운영자에게 전달되었습니다. 개선에 사용됩니다.