자바스크립트를 활성화 해주세요

정규식 패턴 그룹화(Grouping) 하기

 ·  ☕ 4 min read

() 메타문자

어딜가든 ()를 그룹핑 문자로 쓰는 것 같다. 우리는 보통 ()문자를 연산의 순서를 명확하게 하거나 문맥을 좀 더 명확하게 구분하고 싶을 때 사용한다.

1
var result = 5 + 2*(1 + 1)

()를 정규식에서 쓰는 이유도 범위를 구분짓기 위해 쓴다. {}반복의 양을 나타낼 때 쓰인다..

예제1

1
2
var regexp = /[a-d][1-5]{5}/g
var regexp2 = /([a-d][1-5]){5}/g
  • regexp는 a~d 사이의 문자 하나와 1~5 사이의 문자가 5번 반복되는 패턴을 표현한다(6개 문자). 예를들어, a12345와 같은 문자열이 매칭된다.
  • regexp2는 a~d 사이의 문자 하나와 1~5사이의 문자 하나, 즉 두개의 문자가 5번 반복하는 패턴을 표현한다(10개 문자). 예를들어, a1a2a3a4d5와 같은 문자열이 매칭된다.

예제2

1
2
var txt = "Today is monday and tomorrow is tuesdays and then we have wednesday";
var regexp = /\bmonday|tuesday|wednesday\b/g;

txt에서는 tuesdays고 regexp에서는 tuesday인 것에 주의하자. 이 예제에서 text를 regexp로 검사하면 tuesdays가 매칭된다. 그런데 이는 원하는 결과가 아니다. \b경계 메타문자를 쓴 의도는 정확하게 에누리 없이 tuesday를 찾으려는 것이었을 것이다. 따라서 tuesdays를 검색 결과에서 제거하기 위해 다음과 같이 ()를 쓸 수 있다.

1
var regexp = /\b(monday|tuesday|wednesday)\b/g;

이처럼 ()를 써서 정규식을 그룹핑 시켜주면, 간단히 원치 않는 결과를 피할 수 있다.

그룹의 값 사용(\1, \2, …)

?: 메타문자는 정규식의 그룹 문맥에서 쓰기위해 도입된 것이다. 그룹을 포착한다는 의미의 Capturing Groups란 이름이 붙어있다. 이 메타문자를 이해하려면 정규식에서 그루핑한 특정 패턴의 값을 쉽게 가져다 쓸 수 있다는 사실을 알아야 한다.

1
var regexp = /^(\d{4})[-./](\d{1,2})[-./](\d{1,2})$/

위의 예제는 날짜 정규식의 간단한 예다. 2020-01-01과 같은 문자열과 매칭된다. 이 정규식 에서는 [-./]의 패턴이 2번 나오는데, 이를 그룹핑화 해서 표현하면 다음과 같이 할 수 있다.

1
2
var txt = "2020-01-01";
var regexp = /^(\d{4})([-./])(\d{1,2})\2(\d{1,2})$/

설명하자면, 첫번째로 나오는 [-./]()로 그룹핑 했고, 두번째로 나오는 [-./]\2를 써줬다. 여기서 \2의 의미는, 두번째로 나오는 그룹의 값을 쓰겠다는 말이다(패턴이아닌 인 것에 주의). 위의 예제에서 첫번째 그룹은 (\d{4})이고 두번째 그룹이 (-./)이므로 두번째 그룹의 (-./)자리에 오는 \2자리에 들어가게 된다.

?: None Capturing Groups

정규식의 그룹 문맥에서만 사용되는 메타 문자가 있는데, ?:과 다음에 살펴볼 ?=이 그것이다. ?는 단독으로 쓰일 때, 문자 0개 혹은 1개와 매칭되는데 ?:?=는 그것과 의미가 완전히 다르다.

1
var regexp = /^(?:\d{4})([-./])(\d{1,2})\2(\d{1,2})$/

이 예제는 위의 예에서 맨 앞의 그룹에 ?:만 붙인 것이다. 이 때, \2가 가리키는 값은 ([-./])이 아닌 (\d{1,2})이 매칭시킨 값이다. 따라서 우리는 ?:를 쓰면, 그룹의 순서에서 제외된다는 것을 확인할 수 있다. 위의 예제에서는 \2대신 \1을 써야 원하는 결과를 얻을 수 있다.

?= Lookahead Groups

정규식에서 ?= 메타문자로 input 문자를 필터링 할 수 있는데, 이 말은 그룹의 패턴을 차례대로 일치하는지 살펴보겠다는 말이다. 예를 들어 보겠다.

1
var regexp = /^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).*$/

위의 예시에서는 4개의 그룹핑 패턴이 존재하고, 그 그룹 패턴에는 모두 ?=기호가 들어가 있습니다. 맨 마지막에 .*이 패턴 검사를 위해 사용할 input text가 되는 것이다.

그러니까 위의 정규식을 말로 설명하자면, 모든 문자에 대해 검사하는데(.*), 그 문자는 8자 이상이어야 하고((?=.{8,})), 대문자((?=.*[A-Z])) 혹은 소문자((?=.*[a-z])) 혹은 숫자문자((?=.*[0-9]))가 0개 이상인 패턴을 찾아내는 정규식이 되겠다.

?! Negative Lookahead Groups

?= 메타문자가 매칭 되어야 하는값을 필터링 하는 반면, ?! 메타문자는 매칭 되어선 안되는 값을 필터링 해준다. 예를들어, 위의 예제에서 2번째 그룹인 (?=.*[A-Z])(?!.*[A-Z])로 바꿔보자.

1
var regexp = /^(?=.{8,})(?!.*[A-Z])(?=.*[a-z])(?=.*[0-9]).*$/

이제 이 정규식은 문자가 8자 이상, 소문자와 숫자가 있어도 되는데, 대문자는((?!.*[A-Z])) 하나라도 있으면 안된다는 뜻이다. 그러니까 ?=not을 붙인게 ?!인 것이다.


최성환
글쓴이
최성환
📚Learner🤓Nerd🌐Web Developer

목차