03 / 가이드
정규 표현식을 안전하게 테스트하는 워크플로
JavaScript 정규 표현식을 위한 작성-테스트-리팩터링 루프: 현실적인 샘플 데이터, 치명적 백트래킹 예방, 앵커 규율, 플래그 의미론, 그리고 브라우저에서 테스트하는 것이 패턴을 비공개로 지키는 이유.
루프: 작성, 테스트, 리팩터링
믿을 수 있는 정규식은 한 번에 쓰이지 않습니다. 실제 예제 하나에 매치되는 가장 작은 패턴으로 시작해 실행한 다음, 다음 예제에 맞춰 넓히거나 좁히세요. 샘플 텍스트가 명세이고 패턴이 구현입니다.
피드백이 즉각적이면 루프는 빠릅니다. 키 입력마다 다시 평가하는 테스터는 모든 편집을 즉답이 있는 실험으로 바꿉니다. 전용 작업 공간이 스크립트를 손으로 재실행하는 것보다 나은 이유입니다.
- 샘플에서 시작기억이 아니라 구체적인 샘플을 두고 패턴을 쓰세요. 파싱하려는 로그나 문서의 실제 한 줄을 붙여 넣습니다.
- 매 편집마다 테스트매번 바꿀 때마다 테스트하세요. 정규식에는 컴파일러 경고가 없으므로, 유일한 피드백은 무엇에 매치되고 무엇을 놓치는가입니다.
- 독자를 위한 리팩터링통과하면 읽기를 위해 리팩터링하세요. 이름 붙은 캡처 그룹과 비캡처 그룹이 작동하는 패턴을 다음 독자에게도 살아남게 합니다.
치명적 백트래킹을 존중하라
JavaScript의 정규식 엔진은 백트래킹합니다. 매치가 실패하면 각 수량자의 길이를 바꿔 재시도합니다. (a+)+처럼 중첩된 수량자가 아슬아슬하게 빗나가는 문자열에 작용하면 재시도 횟수가 지수적으로 폭발합니다. 좋은 입력에는 즉답하는 패턴이 나쁜 입력에는 몇 년이 걸릴 수 있습니다. 공격자들은 이를 ReDoS라 부릅니다.
JavaScript 정규식 테스터는 폭발 반경을 제한합니다. 패턴은 전용 Web Worker 안에서 650밀리초 예산으로 실행되고, 폭주한 표현식은 탭을 얼리는 대신 명시적인 타임아웃으로 보고됩니다. 현실적인 입력에서 그 상한에 닿는다면 발견으로 취급하세요. 같은 입력이 프로덕션에서는 이벤트 루프를 멈출 것입니다.
- 중첩 수량자고전적인 형태는 수량자가 붙은 그룹 안에 수량자가 붙은 토큰이 있는 것—(a+)+, (\w+)*—으로, 같은 문자열의 많은 분할이 모두 내부 패턴을 만족합니다.
- 모호한 선택지모호한 선택지도 폭발을 일으킵니다. (a|aa)+는 추가되는 모든 문자를 매치하는 두 가지 방법을 엔진에 주어 문자마다 탐색 트리를 두 배로 만듭니다.
- 구조적 수정수정은 구조적입니다. 내부 매치를 명확하게 만들고, 명시적인 길이 제한으로 묶고, 유효한 입력만이 아니라 아슬아슬하게 빗나가는 입력을 반드시 테스트하세요.
매치를 신뢰하기 전에 앵커를
부분 문자열을 찾는 패턴은 값을 검증하는 패턴이 아닙니다. /\d+/는 abc123xyz 안에서 매치되고, /^\d+$/는 숫자만 받습니다. 검증 버그의 대부분은 빠진 앵커이고, 추출 버그의 대부분은 없어야 할 앵커입니다.
- 시작과 끝^와 $는 매치를 문자열의 시작과 끝에 고정합니다. 멀티라인 플래그가 켜지면 각 행의 시작과 끝에 고정되어 “전체 입력”의 의미가 조용히 바뀝니다.
- 단어 경계\b 단어 경계는 짧은 패턴이 더 긴 단어 안에서 매치되는 것을 막습니다. \bcat\b는 동물을 찾지 concatenate 안의 cat을 찾지 않습니다.
- 검색 vs 검증검색인지 검증인지를 처음에 결정하세요. 추출 패턴은 보통 앵커를 원하지 않고, 검증기는 거의 항상 둘 다 원합니다.
현실적인 샘플 데이터로 테스트하기
깔끔한 예제 세 개로 증명된 패턴은 프로덕션의 네 번째 실제 입력에서 실패합니다. 입력 분포를 반영하는 샘플 세트를 만드세요. 가장 긴 합법 값, 빈 문자열, 공백 변형, 유니코드 이름, 그리고 거의 매치될 것 같지만 매치되면 안 되는 아쉬운 케이스.
- 아쉬운 케이스아쉬운 케이스를 포함하세요. 이메일 패턴은 잘 형성된 주소만이 아니라 a@b와 a b@c.d로도 테스트해야 합니다.
- 유니코드데이터에 유니코드가 있으면 유니코드를 포함하세요. \w와 .는 u 플래그 아래에서 의미가 바뀌고, 악센트 있는 이름은 ASCII 시대의 가정을 깹니다.
- 세트 재실행샘플 세트를 패턴 곁에 두고, 패턴이 바뀌면 변경을 부른 케이스만이 아니라 세트 전체를 다시 실행하세요.
플래그는 검색만이 아니라 언어를 바꾼다
플래그는 패턴 의미의 일부입니다. g는 첫 매치를 줄지 전부를 줄지 정하고, i는 대소문자를 접고, m은 ^와 $를 행에 다시 고정하고, s는 점이 줄바꿈을 넘게 하고, u는 엔진을 더 엄격한 문법의 유니코드 인식 모드로 전환합니다.
작업 공간은 선택한 플래그로 패턴을 평가하고, 각 매치의 시작과 끝 오프셋, 각자의 위치를 가진 번호·이름 캡처 그룹, 라이브 치환 미리 보기를 나열합니다. 플래그 변경이 이론이 아니라 결과의 구체적인 차이로 나타납니다.
- g와 lastIndexg가 없으면 test()와 match()는 첫 히트만 봅니다. g가 있으면 정규식 객체는 lastIndex 상태를 지녀 반복된 test() 호출이 결과를 번갈아 반환합니다. 악명 높은 프로덕션 버그입니다.
- s는 점을 넓힘s(dotAll)는 .가 줄바꿈에 매치되게 해, 전역으로 켜면 옛 패턴의 식욕을 조용히 키울 수 있습니다.
- u와 친구들u는 클래스와 이스케이프를 유니코드에 맞게 하고 모호한 레거시 문법을 거부합니다. 테스터는 d(매치 인덱스), y(스티키), v(유니코드 집합)도 지원합니다.
패턴과 샘플을 로컬에 유지하기
테스트 데이터는 종종 실제 데이터입니다. 고객 이메일, 내부 로그 행, 사고 보고서의 식별자. 호스팅된 테스터에 붙여 넣는 것은 그것을 남의 인프라로 보내는 것입니다. 브라우저 기반 테스터는 세션을 탭 안에 유지합니다. 패턴과 샘플 텍스트는 내 기기의 엔진이 평가하며 전송되지 않습니다.
로컬 런타임은 상한을 명시합니다. 패턴 최대 4,096자, 샘플 텍스트 최대 100,000자, 치환 최대 20,000자, 실행당 최대 1,000개의 매치 보고입니다. 이 한도는 개발과 디버깅에 맞춰져 있습니다. 대량 로그 분석은 브라우저 탭이 아니라 로컬 스크립트의 몫입니다.
필요한 것—완성된 패턴과 치환 문자열—만 복사해 꺼내고, 샘플 데이터는 시작한 곳, 즉 내 컴퓨터에 남겨 두세요.
로컬에서 패턴 테스트하기추출된 데이터는 보통 JSON에 도착한다.
패턴이 날텍스트에서 값을 뽑아낸 뒤에는, 결정적인 로컬 루틴으로 결과 페이로드를 검사하고 비교하세요.