MYMONDAY.ai
서비스스튜디오도구데이터·포맷73CSS 생성기19색상·이미지10인코딩·보안10웹·SEO11개발·데브옵스10파일·내보내기18계산기6가이드

7분 읽기 · 마지막 업데이트

정규식 정리와 정규표현식 예제: 자바스크립트(ECMA-262) 치트시트, 전부 Node에서 실행해 확인

이 글의 패턴과 결과는 모두 Node 24에서 직접 실행한 값입니다. 기준은 자바스크립트(ECMA-262) 정규식이고, 마지막 절에 PCRE·파이썬과 다른 점을 따로 모았습니다.

문자 클래스와 이스케이프

문자 클래스는 집합 중 한 글자와 맞습니다. 단축 이스케이프는 가장 자주 쓰게 되는데, 다른 언어에서 넘어오면 세 가지가 낯섭니다. \d와 \w는 u 플래그를 켜도 ASCII만 대상으로 하고, \s는 스페이스보다 훨씬 넓습니다.

\w는 [A-Za-z0-9_]만 잡으므로 /\w+/g를 café naïve 안녕 x_1에 적용하면 caf, na, ve, x_1이 나옵니다. 악센트 글자와 한글은 건너뜁니다. \d도 u 여부와 관계없이 ٣ 같은 아랍 인도 숫자와 맞지 않습니다. 언어에 관계없이 글자를 잡으려면 u 플래그와 \p{L}을 씁니다. 같은 문장에 /\p{L}+/gu를 적용하면 café, naïve, 안녕이 나옵니다.

토큰일치 대상비고
\d \D숫자 [0-9] / 숫자 아님ASCII만
\w \W[A-Za-z0-9_] / 그 외ASCII만, 악센트 글자·한글은 제외
\s \S공백 / 공백 아님탭, 줄바꿈, 줄바꿈 없는 공백 U+00A0, 전각 공백 U+3000 포함
.\n, \r, U+2028, U+2029를 뺀 모든 글자s 플래그를 켜면 예외가 사라짐
[abc] [^abc]a, b, c 중 하나 / 그 외 한 글자[^...]는 줄바꿈도 잡음
[a-z0-9]클래스 안의 범위-는 클래스의 맨 앞이나 맨 뒤에서 문자 그대로의 뜻
\p{L} \p{Script=Hangul}유니코드 속성u 또는 v 플래그 필요, 없으면 \p{L}은 그냥 p{L}이라는 글자와 맞음

수량자: 탐욕적 대 게으른

수량자는 바로 앞 항목이 몇 번 반복되는지를 정합니다. 기본은 탐욕적(greedy)이어서 가능한 한 많이 가져가고, 나머지 패턴이 실패할 때만 되돌려 줍니다. 수량자 뒤에 ?를 붙이면 게으른(lazy) 수량자가 되어 최소한만 가져갑니다.

<a><b>에서 탐욕적인 /<.+>/는 .+가 마지막 >까지 삼키므로 문자열 전체 <a><b>를 돌려주고, 게으른 /<.+?>/g는 <a>와 <b>를 따로 돌려줍니다. 횟수를 지정한 수량자도 같습니다. aaaaaaa에 /a{2,3}/g를 쓰면 aaa, aaa(a 하나가 남음)이고 /a{2,3}?/g는 aa, aa, aa입니다.

탐욕적게으른반복 횟수
**?0회 이상
++?1회 이상
???0회 또는 1회
{n}해당 없음정확히 n회
{n,}{n,}?n회 이상
{n,m}{n,m}?n회 이상 m회 이하

앵커, 단어 경계, m 플래그

^와 $는 글자가 아니라 위치와 맞습니다. 플래그가 없으면 입력 전체의 시작과 끝입니다. \b는 \w 글자와 \w가 아닌 글자 사이의 경계라서, cat concat cat.에 /\bcat\b/g를 쓰면 concat 안의 cat은 건너뛰고 두 개만 찾습니다.

m 플래그를 켜면 ^와 $가 모든 줄의 시작과 끝에서 맞습니다. one, 줄바꿈, two에 /^\w+$/g를 쓰면 텍스트 전체가 한 단어가 아니므로 null이지만 m을 붙이면 one과 two가 나옵니다. 자바스크립트는 \n, \r, U+2028, U+2029를 줄 끝으로 보므로 윈도우의 \r\n 파일도 m으로 처리됩니다.

하나 더 있습니다. m이 없을 때 $는 입력의 맨 끝에서만 맞습니다. /^\d+$/.test('12\n')은 false이고 m을 붙이면 true입니다. 끝에 줄바꿈이 붙어 올 수 있는 입력을 검증할 때 중요합니다.

그룹, 이름 있는 그룹, 역참조, 룩어라운드

괄호는 묶고 캡처합니다. (?:...)는 캡처 없이 묶고, (?<name>...)은 이름을 붙여 캡처하며 match.groups.name으로 읽습니다. 역참조는 같은 텍스트에 다시 맞습니다. 번호 그룹은 \1, 이름 그룹은 \k<name>입니다. hello aabb에 /(\w)\1/g를 쓰면 ll, aa, bb가 나오고, say "hi" and 'yo'에 /(?<q>['"]).*?\k<q>/g를 쓰면 여는 따옴표와 같은 종류의 닫는 따옴표를 짝지어 "hi"와 'yo'가 나옵니다. 최신 JavaScript는 (?<x>a)|(?<x>b)처럼 둘 중 하나만 참여할 수 있는 별도 대안에서는 같은 이름을 허용하지만, (?<x>a)(?<x>b)는 여전히 문법 오류입니다.

룩어라운드는 주변을 확인하되 소비하지 않습니다. 룩어헤드는 (?=...)와 (?!...), 룩비하인드는 (?<=...)와 (?<!...)입니다. 10 dollars 20 euros에 /\d+(?= dollars)/g를 쓰면 10이 나오고, cost $30 or €40에 /(?<=\$)\d+/g를 쓰면 30이 나옵니다.

부정 룩어헤드에는 함정이 있습니다. 10 dollars 20 euros에 /\d+(?! dollars)/g를 쓰면 20만이 아니라 1과 20이 나옵니다. 엔진이 10을 1로 줄여서 되돌아가고, 1 뒤에는 dollars가 오지 않기 때문입니다. \d+\b(?! dollars)처럼 경계를 넣어 숫자가 줄어드는 것을 막으세요. 다른 엔진과 달리 자바스크립트는 룩비하인드에 가변 길이를 허용합니다. cost $12.50에 /(?<=\$\d+\.)\d+/를 쓰면 50이 나옵니다.

const m = /(?<y>\d{4})-(?<m>\d{2})/.exec("on 2026-10-01");
m[0]        // "2026-10"
m.groups.y  // "2026"
m.groups.m  // "10"

플래그: g i m s u v y d

플래그는 닫는 슬래시 뒤의 한 글자, 또는 new RegExp의 두 번째 인자이며 패턴 전체의 동작을 바꿉니다. new RegExp('a', 'dgimsuy').flags는 알파벳 순으로 보고합니다. v와 u는 함께 쓸 수 없고, 쓰면 엔진이 Invalid flags supplied to RegExp constructor 'uv'를 던집니다.

g 플래그에는 알아 둘 부작용이 있습니다. g나 y가 있는 정규식은 lastIndex에 위치를 기억하므로, 공유한 정규식에 test를 거듭 부르면 답이 번갈아 나옵니다. const r = /a/g; r.test('aa')는 true(lastIndex 1), 다시 true(lastIndex 2), 그다음 false(lastIndex가 0으로 초기화)입니다. 함수 안에서 정규식을 새로 만들거나, 예·아니오만 필요하면 g를 빼세요.

플래그이름바뀌는 것
gglobal모든 일치를 찾음, replace가 전부 바꿈, test·exec가 상태를 가짐
iignoreCase대소문자 무시
mmultiline^와 $가 모든 줄 경계에서 맞음
sdotAll.이 줄바꿈 문자도 잡음
uunicode코드 포인트 단위로 동작. /^.$/u.test('😀')는 true, u 없이는 이모지가 UTF-16 두 단위라서 false. \p{...}와 \u{1F600}를 켬
vunicodeSetsu의 상위 기능으로 집합 연산 지원. /[\p{L}--[a-z]]/v는 é와 맞고 a와는 맞지 않음. /[\p{L}&&\p{ASCII}]/v는 a와 맞고 é와는 맞지 않음
ystickylastIndex 위치에서만 맞춤. /a/y.test('ba')는 false, lastIndex가 1이면 true
dhasIndicesmatch.indices를 추가. /(?<x>a)/d.exec('ba').indices.groups.x는 [1, 2]

replace와 달러 토큰

치환 문자열에는 몇 가지 특수 표기가 있습니다. g 플래그가 없으면 첫 일치만 바뀌는데, 파이썬 re.sub와 반대입니다. 없는 번호를 가리키는 참조는 글자 그대로 남습니다. 'x'.replace(/(x)/, '$2')는 $2를 돌려주고 $0도 마찬가지입니다.

날짜 순서 바꾸기로 번호와 이름 방식을 나란히 볼 수 있습니다. '2026-10-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1')은 01/10/2026, '2026-10-01'.replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, '$<d>.$<m>.$<y>')는 01.10.2026입니다. 템플릿으로 표현할 수 없는 로직은 함수를 넘기면 되고, 마지막 인자가 groups 객체입니다.

토큰삽입되는 것
$1 ~ $99해당 번호 캡처 그룹의 텍스트
$<name>해당 이름 그룹의 텍스트
$&일치한 전체. 'John Smith'.replace(/(\w+) (\w+)/, '$2, $1 [$&]')는 Smith, John [John Smith]
$$달러 기호 자체
달러와 백틱, 달러와 작은따옴표일치 앞의 텍스트, 일치 뒤의 텍스트
"2026-10-01".replace(
  /(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/,
  (...args) => {
    const g = args.at(-1);        // 마지막 인자가 groups 객체
    return g.d + "/" + g.m + "/" + g.y;
  }
);                                // "01/10/2026"

실무에서 쓸 만한 패턴

정규식은 모양을 검사하는 데는 좋지만 의미를 검사하는 데는 약합니다. 아래 ISO 날짜 패턴은 2026-10-01을 받고 2026-13-01과 2026-1-1을 거부하지만, 2026-02-31도 받아 줍니다. 달마다 며칠인지는 정규식이 풀 문제가 아니기 때문입니다. 모양 검사 뒤에 Date나 날짜 라이브러리로 파싱하세요.

16진 색상 패턴은 #fff와 #FFAA00을 받고 #ffaa0, #ggg, # 없는 값을 거부합니다. 유의적 버전(semver) 패턴은 semver.org에 게시된 것으로, 1.2.3, 1.0.0-rc.1, 1.2.3-beta.1+build.5를 받고 01.2.3, 1.2, v1.2.3을 거부합니다. 검증이 아니라 글 속에서 버전을 찾으려면 release v2.14.0 shipped에 /\bv?(\d+)\.(\d+)\.(\d+)\b/를 써서 2, 14, 0을 캡처합니다.

공백 제거는 내장 trim()이 낫습니다. 정규식은 줄 단위 작업에서 값을 합니다. 'a\n b\n'.replace(/^[ \t]+|[ \t]+$/gm, '')는 모든 줄의 앞뒤 공백을 지우고 줄바꿈은 남깁니다.

// ISO 8601 날짜, 모양만 검사
/^(?<y>\d{4})-(?<m>0[1-9]|1[0-2])-(?<d>0[1-9]|[12]\d|3[01])$/

// 16진 색상: #rgb 또는 #rrggbb (알파가 있는 4자리·8자리 형태 포함 버전도 아래에)
/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i
/^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i

// 유의적 버전 (semver.org)
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/

// 양끝 공백 제거
/^\s+|\s+$/g

한국 휴대전화 번호와 한글 검사

휴대전화 번호 모양은 /^01[016789]-\d{3,4}-\d{4}$/로 검사합니다. 010-1234-5678, 011-123-4567, 010-123-4567은 통과하고 02-123-4567, 하이픈이 없는 01012345678, 010-12345-678은 거부합니다. 하이픈 유무를 모두 허용하되 한 번호 안에서는 일관되게 하려면 역참조를 씁니다. /^01[016789]([-\s]?)\d{3,4}\1\d{4}$/는 010-1234-5678, 010 1234 5678, 01012345678을 받고 010-12345678을 거부합니다. 번호가 실제로 존재하는지가 아니라 모양만 보는 검사입니다.

붙여 쓴 번호에 하이픈을 넣을 때는 '01012345678'.replace(/^(01[016789])(\d{3,4})(\d{4})$/, '$1-$2-$3')이 010-1234-5678을 돌려줍니다. 0111234567은 탐욕적인 \d{3,4}가 한 자리를 되돌려 주면서 011-123-4567이 됩니다. 가운데 네 자리를 가릴 때는 '010-1234-5678'.replace(/^(\d{3})-(\d{4})-(\d{4})$/, '$1-****-$3')으로 010-****-5678을 얻습니다.

한글만 있는지 검사할 때는 u 플래그와 \p{Script=Hangul}이 가장 정확합니다. /^\p{Script=Hangul}+$/u는 한글을 받고 한글a와 한 글(공백 포함)을 거부합니다. 공백을 허용하려면 /^[\p{Script=Hangul}\s]+$/u입니다. [가-힣]은 완성형 음절 U+AC00부터 U+D7A3까지만이어서 결과가 다릅니다. 자모 ㅎㅏㄴ은 [가-힣]과 맞지 않지만 \p{Script=Hangul}과는 맞습니다. ㅋㅋㅋ도 맞습니다. 반대로 한을 NFD로 분해한 3글자는 \p{Script=Hangul}과는 맞고 [가-힣]과는 맞지 않습니다. 완성형 음절만 허용할 것인지, 자모까지 한글로 볼 것인지를 먼저 정하고 패턴을 고르세요. 글 속에서 한글 덩어리를 뽑을 때는 '안녕하세요 hello'.match(/\p{Script=Hangul}+/gu)가 ['안녕하세요']를 돌려줍니다.

치명적 백트래킹: 실측 예제

백트래킹 엔진은 한 가지 방법으로 맞춰 보다가 나머지가 실패하면 되돌아가 다른 방법을 시도합니다. 같은 텍스트를 나누는 방법이 둘 이상이면 시도 횟수가 곱으로 늘어납니다. 대표적인 경우가 수량자 안의 수량자입니다. /^(a+)+$/를 aaaa...ab에 적용하면, 마지막 b 때문에 모든 시도가 실패하고 엔진은 a를 안쪽 반복과 바깥 반복에 나누는 모든 방법을 탐색합니다.

Node 24를 쓴 한 대의 컴퓨터에서 측정한 결과, test 호출 한 번은 a 22개에서 19ms, 24개에서 82ms, 26개에서 327ms, 28개에서 1,424ms, 30개에서 5,467ms가 걸렸습니다. 두 글자가 늘 때마다 4배가 되니 글자 하나마다 두 배입니다. 고친 /^a+$/는 같은 문자열을 몇 천분의 1ms 만에 거부했습니다. 절대 시간은 기기마다 다르지만 두 배씩 느는 성질은 같습니다.

조심할 모양은 안쪽이 같은 텍스트를 여러 방식으로 맞출 수 있는, 수량자가 붙은 그룹입니다. (a+)+, (\w+\s?)*, 겹치는 선택지 (a|a)+(28글자에서 2.3초)가 그렇습니다. 해법은 텍스트의 각 부분이 한 가지로만 맞게 만드는 것입니다. (a+)+ 대신 a+, (\w+\s?)* 대신 \w+(?:\s\w+)*\s?로 바꾸면, 글자 25개 뒤에 !가 오는 입력에서 0.05ms에 끝났습니다.

  • 중첩된 수량자와 겹치는 선택지를 없애고, 입력 전체를 뜻한다면 앵커를 붙이세요.
  • 매칭 전에 입력 길이를 제한하고, 사용자가 준 정규식을 서버의 요청 스레드에서 실행하지 마세요.
  • 룩어헤드와 역참조를 합친 (?=(a+))\1은 원자 그룹처럼 동작합니다. 룩어헤드가 한번 성공하면 엔진이 그 안으로 되돌아갈 수 없기 때문입니다.
  • 이 사이트의 정규식 테스터는 매칭을 Web Worker에서 돌리고 1.5초 뒤에 멈추므로, 폭주하는 패턴이 화면을 얼리는 대신 오류로 표시됩니다.

PCRE·파이썬과 다른 점

PCRE(PHP, Perl, grep -P)나 파이썬 답변에서 복사한 패턴은 자바스크립트에서 자주 실패합니다. 아래 차이는 자바스크립트 열을 모두 Node 24에서 확인했습니다. PCRE와 파이썬 열은 그 엔진들의 오래된 문서화된 동작입니다.

항목자바스크립트PCRE / 파이썬 re
인라인 플래그(?i)는 문법 오류, 범위 지정 (?i:...)는 최신 엔진에서만 동작둘 다 (?i) 지원
소유 수량자 a++, 원자 그룹 (?>...)문법 오류(룩어헤드와 역참조로 흉내 냄)PCRE는 지원, 파이썬은 3.11부터
이름 그룹 문법(?<n>...), \k<n>, $<n>파이썬은 (?P<n>...), (?P=n), \g<n>, PCRE는 두 방식 모두 허용
입력의 시작과 끝\A, \Z 없음, m 없는 ^와 $ 사용둘 다 \A, \Z 있음
끝 줄바꿈 앞의 $맞지 않음(/^\d+$/.test('12\n')은 false)기본적으로 마지막 줄바꿈 앞에서 맞음
\d, \w항상 ASCII파이썬 3의 str 패턴은 기본이 유니코드
유니코드 속성u 또는 v와 \p{...}PCRE는 지원, 파이썬 re에는 \p 없음
룩비하인드 길이가변 길이 허용파이썬 re는 고정 폭 필요
기본 치환 범위첫 번째만, 전부 바꾸려면 g 필요파이썬 re.sub는 전부 치환, 참조는 \1이나 \g<n>
Verbose, 재귀x 플래그와 (?R) 없음PCRE는 둘 다 지원, 파이썬은 re.VERBOSE

직접 해보기