본문으로 건너뛰기
김신건의 로그

[Javascript] Regular Expression

· 수정 · 📖 약 1분 · 422자/단어 #javascript #regex #regexp
JS regex, JS RegExp, 정규식 JavaScript

정의

JavaScript 의 정규식. RegExp 객체 또는 리터럴 /.../ 로 표현. ECMAScript 정규식 문법 (Perl 의 부분집합).

생성

const re = /pattern/flags;
const re = new RegExp('pattern', 'flags');
const re = new RegExp(pattern);    // pattern 이 string 또는 RegExp

리터럴이 가독성 좋음. 동적 패턴은 new RegExp.

flags

flag의미
gglobal (모든 매치)
icase-insensitive
mmultiline (^ $ 가 각 줄에)
sdotAll (.\n 포함)
uunicode (Unicode 코드 포인트)
ysticky (lastIndex 부터 매칭)
dindices (group 의 시작/끝 위치)
/python/gi      // global + case-insensitive

자주 쓰는 메서드

메서드의미
regex.test(str)true/false
regex.exec(str)match 객체 또는 null
str.match(regex)g 면 배열, 아니면 exec 같음
str.matchAll(regex)iterator (g 필수)
str.search(regex)첫 위치 (없으면 -1)
str.replace(regex, repl)치환
str.replaceAll(regex, repl)g flag 필수
str.split(regex)분할
/\d+/.test('abc 123')              // true
'abc 123'.match(/\d+/)             // ['123', index: 4, ...]
'abc 123 456'.match(/\d+/g)         // ['123', '456']
'hello'.replace(/l/g, 'L')          // 'heLLo'

자주 쓰는 패턴 cheatsheet

패턴의미
.임의 문자 (개행 제외)
\d, \D숫자, 비숫자
\w, \W영숫자_, 그 외
\s, \S공백, 비공백
^, $문자열 시작, 끝
\b, \B단어 경계, 비경계
*0+
+1+
?0 또는 1
{n}, {n,m}정확/범위
[abc]집합
[^abc]부정 집합
[a-z]범위
|OR
()캡처 그룹
(?:...)비캡처
(?<name>...)named group
(?=...)lookahead
(?!...)negative lookahead
(?<=...)lookbehind
(?<!...)negative lookbehind

캡처 그룹

const m = '2024-01-15'.match(/(\d{4})-(\d{2})-(\d{2})/);
// m[0] = '2024-01-15'  (전체)
// m[1] = '2024'         (1번 그룹)
// m[2] = '01'
// m[3] = '15'

// named
const m2 = '2024-01-15'.match(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/);
m2.groups.y;    // '2024'

치환

'foo bar'.replace(/(\w+) (\w+)/, '$2 $1');          // 'bar foo'
'2024-01-15'.replace(/(\d+)-(\d+)-(\d+)/, '$3/$2/$1');   // '15/01/2024'

// named
'2024-01-15'.replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, '$<d>/$<m>/$<y>');

// 함수 replacer
'abc'.replace(/./g, (ch) => ch.toUpperCase());      // 'ABC'

// 함수 + 그룹
'2024-01-15'.replace(/(\d+)/g, (n) => n.padStart(4, '0'));

lookahead / lookbehind

// 다음에 'USD' 가 오는 숫자만
'100 USD, 200 EUR'.match(/\d+(?= USD)/g);    // ['100']

// 이전이 '$' 인 숫자
'$100 €200'.match(/(?<=\$)\d+/g);             // ['100']

// 다음에 'px' 가 아닌 숫자
'10em 20px 30em'.match(/\d+(?!px)/g);

test vs match vs matchAll

// 존재 검사만
/\d+/.test('abc 123')               // true (빠름)

// 첫 매치
'abc 123 456'.match(/\d+/)
// ['123', index: 4, input: 'abc 123 456']

// 모든 매치 (배열)
'abc 123 456'.match(/\d+/g)
// ['123', '456']

// 모든 매치 + 그룹 (iterator)
for (const m of 'abc 123 456'.matchAll(/(\d+)/g)) {
    console.log(m[0], m[1], m.index);
}

matchAll 가 가장 정보 풍부.

자주 만나는 패턴

email (대략)

/^[\w.+-]+@[\w-]+\.[\w.-]+$/

완벽한 email 정규식은 매우 복잡 (RFC 5322). 보통 위 정도로 충분.

URL

/^https?:\/\/[\w.-]+(:\d+)?(\/.*)?$/

전화번호 (한국)

/^0\d{1,2}-\d{3,4}-\d{4}$/

화이트스페이스 정규화

str.replace(/\s+/g, ' ').trim()

함정

1. lastIndex (g flag)

const re = /\d+/g;
re.exec('abc 123 456');    // ['123', index: 4]
re.exec('abc 123 456');    // ['456', index: 8]
re.exec('abc 123 456');    // null
re.exec('abc 123 456');    // ['123', ...]  (재시작)

g flag 의 lastIndex 가 상태. 같은 regex 객체 공유 시 함정.

2. metachar escape

'a.b.c'.split('.')          // ['a', 'b', 'c']
'a.b.c'.split(/\./)          // ['a', 'b', 'c']
'a.b.c'.split(/./)           // ['', '', '', '', ''] ⚠️ . 은 any

3. 동적 패턴의 escape

function escapeRegex(s) {
    return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
new RegExp(escapeRegex(userInput))

사용자 입력을 정규식으로 만들 때 escape 필수.

4. 욕심쟁이 vs 게으른

'<a><b>'.match(/<.+>/)[0]    // '<a><b>' (욕심)
'<a><b>'.match(/<.+?>/)[0]   // '<a>'    (게으른)

관련 위키

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기