정규식이 강력한 이유
정규식(regex)은 텍스트 패턴을 간결하게 표현하는 언어입니다. 하나의 정규식으로 이메일 주소, 전화번호, 날짜, 코드 식별자 등 수천 가지 변형을 매칭할 수 있습니다. 수십 개의 if-else 조건 없이도 가능합니다. 원리를 이해하면 텍스트 검색, 유효성 검사, 변환에서 가장 효율적인 도구 중 하나입니다.
처음에는 배우기 어렵게 느껴집니다. ^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$ 같은 표현은 처음엔 암호처럼 보입니다. 하지만 각 부분에는 정확한 의미가 있고, 구성 요소를 익히면 금방 익숙해집니다.
필수 구성 요소
**리터럴 문자**는 그 자체를 매칭합니다. hello 패턴은 정확히 "hello" 문자열을 매칭합니다.
**점(.)** 은 개행을 제외한 모든 단일 문자와 매칭됩니다. h.t는 "hat", "hit", "hot", "h&t"를 모두 매칭합니다.
**문자 클래스 []** 는 집합에서 하나의 문자와 매칭됩니다. [aeiou]는 모음을, [0-9]는 숫자를 매칭합니다. [^aeiou]는 모음이 아닌 문자를 매칭합니다(괄호 안의 ^는 NOT을 의미합니다).
**수량자**는 반복 횟수를 제어합니다. - * — 0회 이상 - + — 1회 이상 - ? — 0회 또는 1회 (선택적 요소) - {3} — 정확히 3회 - {2,5} — 2~5회
**앵커**는 매칭 위치를 제한합니다. - ^ — 문자열(또는 줄) 시작 - $ — 문자열(또는 줄) 끝 - \b — 단어 경계
**단축 클래스**는 자주 쓰이는 패턴의 약칭입니다. - \d — 숫자([0-9]와 동일) - \w — 문자, 숫자, 밑줄 - \s — 공백 문자 - \D, \W, \S — 대문자 버전은 반대를 매칭
캡처 그룹과 역참조
괄호 ()는 두 가지 역할을 합니다. 패턴의 일부를 묶고, 매칭된 텍스트를 나중에 사용할 수 있도록 캡처합니다. 캡처된 텍스트는 그룹 1, 그룹 2 등으로 접근할 수 있습니다.
예를 들어 (\d{4})-(\d{2})-(\d{2}) 패턴은 "2024-04-15" 같은 날짜를 매칭하고 세 개의 그룹을 캡처합니다: 2024, 04, 15. 치환 작업에서 이를 $1, $2, $3(또는 도구에 따라 \1, \2, \3)으로 참조할 수 있습니다. 날짜를 15/04/2024 형식으로 바꾸려면 치환 문자열을 $3/$2/$1로 지정합니다.
이름 있는 캡처 그룹은 패턴의 가독성을 높입니다: (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}). 이름으로 참조할 수 있습니다: ${year}, ${month}, ${day}.
비캡처 그룹 (?:...)은 텍스트를 캡처하지 않고 묶기만 합니다. 수량자를 위해 그루핑이 필요하지만 매칭 텍스트가 필요 없을 때 유용합니다: (?:https?|ftp)://
룩어헤드와 룩비하인드
룩어헤드와 룩비하인드(통칭 룩어라운드)는 현재 위치 앞이나 뒤에 있는 내용을 기준으로 매칭하되, 그 맥락은 매칭에 포함하지 않습니다.
**긍정 룩어헤드** (?=...) 는 앞에 해당 패턴이 있을 때 매칭합니다. \w+(?=\s+is) 는 "is"가 바로 뒤에 오는 단어를 매칭합니다 — "She is"에서 "She"를 매칭하지만 "She was"에서는 매칭하지 않습니다.
**부정 룩어헤드** (?!...) 는 앞에 해당 패턴이 없을 때 매칭합니다. free(?!dom)은 "free coffee"의 "free"는 매칭하지만 "freedom"의 "free"는 매칭하지 않습니다.
**긍정 룩비하인드** (?<=...) 는 뒤에 해당 패턴이 있을 때 매칭합니다. (?<=\$)\d+는 달러 기호 뒤의 숫자와 매칭됩니다.
실전 패턴 예시
**이메일 유효성 검사:** ^[\w.+-]+@[\w-]+\.[\w.]{2,}$ 완전한 RFC 5321 명세는 훨씬 복잡하지만 대부분의 애플리케이션에는 이 정도로 충분합니다.
**한국 휴대폰 번호:** ^01[016789]-?\d{3,4}-?\d{4}$ 010, 011, 016, 017, 018, 019 번호 형식을 커버합니다.
**IPv4 주소:** ^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$ 각 옥텟이 0-255 범위인지 검증합니다.
**URL 추출:** https?://[^\s"'<>()\[\]]+ 텍스트에서 URL을 매칭하며, 공백과 일반적인 구분자에서 멈춥니다.
**ISO 날짜:** \d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]) 기본 범위 체크를 포함한 연-월-일 검증입니다.
**헥스 색상 코드:** #([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}) #RRGGBB와 #RGB 형식 모두 매칭합니다.
흔한 실수
**탐욕적 vs 게으른 매칭.** 기본적으로 수량자는 탐욕적으로 최대한 많이 매칭합니다. <b>text</b> 문자열에서 <.+> 패턴은 전체 문자열을 매칭합니다. ?를 추가하면 게으른 매칭이 됩니다: <.+?>는 <b>만 매칭합니다. 대부분의 예상치 못한 매칭 결과는 의도하지 않은 탐욕적 매칭에서 비롯됩니다.
**특수 문자 이스케이프.** . * + ? ^ $ { } [ ] | ( ) \은 정규식에서 특별한 의미를 가집니다. 파일 확장자처럼 리터럴 점을 매칭하려면 이스케이프해야 합니다: .txt가 아니라 \.txt.
**유효성 검사 패턴 과설계.** 정규식으로 이메일을 완벽하게 검증하는 것은 잘 알려진 어려운 문제입니다. 대부분의 애플리케이션에서는 흔한 실수를 잡는 단순한 패턴이 아무도 읽을 수 없는 완벽한 패턴보다 낫습니다.
패턴 작성과 테스트
정규식은 점진적으로 작성하세요. 단순한 경우를 매칭하는 패턴으로 시작해서 단계적으로 복잡도를 추가합니다. 유효한 예시와 유효하지 않은 예시 모두에서 테스트해 패턴이 포함해야 할 것과 제외해야 할 것을 올바르게 처리하는지 확인하세요. 실시간으로 매칭을 강조해주는 도구를 쓰면 개발 과정이 훨씬 빠르고 오류가 줄어듭니다.