Menu

자바스크립트 정규식: test, match, replace, 캡처 그룹 총정리

자바스크립트에서 정규표현식을 제대로 쓰는 법. 패턴 만들기부터 test·match·replace 같은 핵심 메서드, 플래그, 캡처 그룹까지 실전 예제로 정리했습니다.

정규식이란 문자열의 패턴을 정의하는 것

정규표현식(regex)은 문자열의 "생김새"를 표현하는 도구입니다. 예를 들면 "숫자 4자리", "단어 뒤에 쉼표가 붙은 형태", "이메일처럼 보이는 문자열" 같은 식이죠. 자바스크립트에서 정규식을 만드는 방법은 두 가지가 있습니다:

index.js
Output
Click Run to see the output here.

리터럴 표기법, 즉 슬래시로 패턴을 감싸고 닫는 슬래시 뒤에 플래그를 붙이는 방식이 대부분의 경우에 쓰는 방법이에요. 패턴 자체가 동적으로 바뀌어야 할 때 — 예를 들어 사용자 입력이나 변수로 패턴을 조립해야 할 때 — 는 new RegExp(...)을 씁니다.

끝에 붙은 i플래그 입니다. i는 대소문자를 구분하지 않겠다는 뜻이고요. 플래그에 대한 자세한 얘기는 곧 이어집니다.

test: 매칭되는지 확인하기

정규식에 가장 간단히 물어볼 수 있는 건 "이 문자열에 매칭되는 부분이 있나요?" 입니다. 바로 test가 그 역할을 해요:

index.js
Output
Click Run to see the output here.

\d는 "숫자 하나"를 뜻합니다. test는 오직 true 아니면 false만 돌려주죠. 입력값 검증이나 배열 필터링처럼 '맞냐 아니냐'만 알면 충분한 상황에선 test가 딱입니다.

match: 매칭된 문자열 꺼내오기

매칭된 문자열 자체가 필요하다면, 문자열의 match 메서드를 쓰면 됩니다:

index.js
Output
Click Run to see the output here.

g 플래그가 없으면 match는 첫 번째 매치와 함께 index, input 같은 메타데이터를 담은 배열을 돌려줍니다. 반면 g 플래그를 붙이면 매치된 모든 문자열을 담은 단순 배열이 반환되죠. 매치되는 게 하나도 없을 땐 빈 배열이 아니라 null이 나오니, 이 부분은 꼭 방어 코드로 처리해 주세요:

index.js
Output
Click Run to see the output here.

플래그로 매칭 동작 바꾸기

플래그는 닫는 슬래시 뒤에 붙여서 매칭 방식을 조정하는 옵션입니다. 자바스크립트 정규식에서 실무상 가장 자주 쓰는 건 다음과 같습니다:

  • g — 전역(global) 플래그. 첫 번째 매치에서 멈추지 않고 모든 매치를 찾습니다.
  • i — 대소문자를 구분하지 않습니다.
  • m — 멀티라인 모드. ^$가 문자열 전체가 아니라 각 줄의 시작과 끝에 매칭됩니다.
  • s — dotall 모드. .이 개행 문자까지 포함해서 매칭됩니다.
  • u — 유니코드 인식 모드. 이모지나 ASCII 밖 문자를 다루는 패턴에서 꼭 필요합니다.
index.js
Output
Click Run to see the output here.

m 플래그가 없으면 ^는 문자열 전체의 맨 앞에만 걸립니다. 반면 m을 붙이면 각 줄의 시작 부분에 매칭되기 때문에 장미는 빨갛고제비꽃은 파랗다 둘 다 잡히는 거죠.

문자 클래스와 수량자

정규식 패턴을 구성하는 기본 재료들입니다:

  • \d는 숫자, \w는 단어 문자(문자/숫자/언더스코어), \s는 공백 문자입니다.
  • [abc]는 a, b, c 중 하나. [^abc]는 그 외의 모든 문자. [a-z]는 범위 지정이에요.
  • .은 줄바꿈을 제외한 아무 문자나 매칭합니다.
  • *는 0개 이상, +는 1개 이상, ?는 0개 또는 1개.
  • {3}은 정확히 3개, {2,5}는 2~5개, {2,}는 2개 이상을 의미합니다.
  • ^는 시작, $는 끝을 나타냅니다.

이걸 실제로 조합해 보면 이렇습니다:

index.js
Output
Click Run to see the output here.

\b는 단어 경계를 뜻합니다. 단어 문자와 비단어 문자 사이에 있는, 눈에 보이지 않는 경계선이죠. "단어 단위"로 정확히 매칭하고 싶을 때 유용합니다.

캡처 그룹: 매칭된 일부분을 기억하기

소괄호로 감싸면 그룹 이 만들어지고, 그 안에서 매칭된 값이 캡처됩니다. execmatch는 전체 매칭 결과와 함께 이 캡처된 값들을 돌려줍니다:

index.js
Output
Click Run to see the output here.

인덱스 0은 전체 매치이고, 그다음부터 각 캡처 그룹이 순서대로 들어갑니다. 그룹이 두 개를 넘어가면 위치로 세는 게 헷갈리기 시작하니, 이름 있는 그룹을 쓰는 게 좋습니다:

index.js
Output
Click Run to see the output here.

이름 있는 캡처 그룹을 쓰면 호출부에서 가독성이 좋아지고, 패턴 내부 순서를 바꿔도 코드가 안 깨집니다.

replace: 매칭된 문자열 바꾸기

replace는 패턴과 치환값을 인자로 받습니다. 치환값으로는 문자열은 물론 함수도 넘길 수 있습니다:

index.js
Output
Click Run to see the output here.

g 플래그를 빼먹으면 첫 번째로 매칭된 부분만 바뀐다. 정규식 초보자들이 자주 하는 실수인데, 두 번째 이메일이 왜 그대로인지 한참 헤매게 되는 이유가 바로 이것이다.

치환 문자열 안에서는 역참조(back-reference)를 쓸 수 있다. $1, $2 같은 표기는 캡처 그룹을 가리키고, 이름 있는 그룹은 $<이름> 형태로 참조한다:

index.js
Output
Click Run to see the output here.

단순 치환을 넘어서는 작업이라면 함수를 인자로 넘기면 됩니다. 이 함수는 매치된 문자열과 캡처 그룹들을 인자로 받습니다:

index.js
Output
Click Run to see the output here.

언더스코어(_)는 전체 매치 결과인데, 여기서는 필요 없어서 무시했고 n이 첫 번째 캡처 그룹입니다. 이렇게 정규식과 콜백 함수를 조합하는 패턴은 실전에서 문자열을 가공할 때 거의 만능이라고 할 수 있습니다.

matchAll: 모든 매치와 캡처 그룹을 한 번에

String.prototype.matchAll은 모든 매치를 캡처 그룹까지 포함해서 이터레이터로 돌려줍니다. g 플래그를 붙인 일반 match로는 할 수 없는 일이죠.

index.js
Output
Click Run to see the output here.

matchAll을 쓰려면 반드시 g 플래그가 있어야 합니다. 없으면 TypeError가 발생하죠. 이터레이션 대신 임의 접근이 필요하다면 배열로 펼쳐서 쓰세요 ([...text.matchAll(email)]).

특수 문자 이스케이프 처리

. * + ? ( ) [ ] { } | \ ^ $ 같은 문자들은 정규식에서 특별한 의미를 가집니다. 이 문자들을 문자 그대로 매칭하려면 백슬래시로 이스케이프해야 합니다:

index.js
Output
Click Run to see the output here.

이스케이프하지 않은 버전은 examplexcom에도 매칭됩니다. .이 "아무 문자나"를 의미하기 때문이죠. 이런 버그는 흔하면서도 조용히 숨어 있습니다. 정규식이 너무 많이 매칭된다 싶으면 먼저 이스케이프하지 않은 .부터 의심해 보세요.

사용자 입력으로 패턴을 만들 때는 반드시 이스케이프해야 합니다. 그러지 않으면 사용자가 정규식 문법을 주입할 수 있습니다.

index.js
Output
Click Run to see the output here.

치환 문자열에서 $&는 "매치된 전체 문자열"을 의미하는 단축 표기입니다.

전방탐색과 후방탐색 (Lookahead/Lookbehind)

어떤 텍스트 뒤(또는 앞)에 특정 패턴이 오는 경우에만 매치하고 싶은데, 정작 그 특정 패턴은 매치 결과에 포함하고 싶지 않을 때가 있습니다. 바로 이럴 때 쓰는 게 룩어라운드(lookaround)입니다.

index.js
Output
Click Run to see the output here.
  • (?= ...) 긍정형 전방탐색(lookahead): "뒤에 이게 오는 경우."
  • (?<= ...) 긍정형 후방탐색(lookbehind): "앞에 이게 오는 경우."
  • (?! ...), (?<! ...)는 각각의 부정형 버전입니다.

전방/후방탐색은 문자를 소비하지 않기 때문에, "들여다본" 부분은 그대로 남아서 패턴의 다음 부분에서 다시 쓸 수 있습니다.

이메일 검증에 대한 한마디

"이메일을 검증하는 정규식 좀 알려주세요"라는 질문은 정말 자주 나옵니다. 솔직히 말하자면, 하지 마세요. 실제 이메일 문법은 생각보다 훨씬 복잡해서, 읽을 만한 길이의 정규식은 어느 한쪽으로든 틀릴 수밖에 없습니다. 폼 입력 검증 정도라면 실용적인 수준의 패턴이면 충분합니다:

index.js
Output
Click Run to see the output here.

다음과 같이 읽으면 됩니다. "공백과 @가 아닌 문자 몇 개, @, 또 같은 종류의 문자들, 점, 그리고 또 같은 문자들." RFC 5322를 엄격히 강제하는 건 아니지만, 눈에 띄는 오타 정도는 걸러낼 수 있습니다. 진짜로 유효한 주소인지 확인하려면 인증 메일을 보내세요.

자주 하는 실수들

몸에 새겨둘 만한 함정 몇 가지를 짚어보겠습니다.

  • replacematchAll에서 g 플래그를 빼먹는 경우. 첫 번째 매치만 바뀌거나, 아예 TypeError가 납니다.
  • 전역 정규식의 lastIndex 상태가 남는 문제. gy 플래그가 붙은 정규식은 test/exec 호출 사이에 어디까지 훑었는지 기억합니다. 서로 관련 없는 문자열에 같은 정규식 객체를 재사용하지 마세요. 매번 새로 만들거나 matchAll을 쓰는 편이 안전합니다.
  • 동적 패턴에서 점과 슬래시를 이스케이프하지 않는 경우. 사용자 입력을 new RegExp(...)에 그대로 넣기 전에는 반드시 이스케이프하세요.
  • 파국적 백트래킹(catastrophic backtracking). (a+)+처럼 중첩된 수량자에 악성 입력이 들어오면 탭 자체가 얼어버릴 수 있습니다. 정규식이 느리다 싶으면 패턴부터 단순하게 다듬으세요.

다음: 날짜와 시간

정규식이 텍스트의 "모양"을 다룬다면, 실제 데이터에는 파싱하고 포맷팅하고 계산해야 하는 타임스탬프도 함께 따라옵니다. 다음 페이지에서는 Date, Intl.DateTimeFormat, 그리고 타임존 버그를 피해가는 사고방식을 정리해봅니다.

자주 묻는 질문

자바스크립트에서 정규식은 어떻게 만드나요?

두 가지 방법이 있습니다. 슬래시로 감싸는 리터럴 방식(/hello/i)과 문자열을 인자로 받는 생성자 방식(new RegExp('hello', 'i'))이죠. 패턴이 고정돼 있으면 리터럴, 런타임에 변수로 패턴을 조합해야 하면 생성자를 쓰면 됩니다.

test, match, exec는 어떻게 다른가요?

regex.test(str)은 매치 여부만 불리언으로 돌려줘서 제일 빠릅니다. str.match(regex)는 매치된 결과 배열(없으면 null)을 반환하고요. regex.exec(str)은 한 번에 한 매치씩 캡처 그룹과 함께 반환하는데, g 플래그를 주면 lastIndex로 위치를 기억하면서 반복 호출할 수 있습니다.

정규식으로 모든 일치 항목을 한 번에 바꾸려면요?

g 플래그를 붙이면 됩니다. str.replace(/foo/g, 'bar')처럼요. g가 없으면 첫 번째 매치만 바뀝니다. str.replaceAll(/foo/g, 'bar')도 쓸 수 있지만, replaceAll에 정규식을 넘길 때는 반드시 g 플래그가 있어야 합니다. 없으면 에러가 납니다.

캡처 그룹이 뭔가요?

패턴 안의 괄호가 캡처 그룹입니다. 괄호 안에서 매치된 부분을 따로 꺼내 쓸 수 있어요. 예를 들어 /(\d{4})-(\d{2})/.exec('2024-11')을 실행하면 결과 배열의 인덱스 1에 '2024', 2에 '11'이 들어갑니다. (?<year>\d{4})처럼 이름을 붙이면 match.groups.year로도 접근할 수 있습니다.

Coddy로 코딩 배우기

시작하기