정규식 기초 — 이메일·전화번호 검증
메타 문자·플래그·그룹·자주 쓰는 패턴 한 번에.
정규식(regex) 은 한 번 익히면 평생 쓰는 도구 — 텍스트에서 패턴을 찾고·추출하고·바꿉니다. 11편의 한 입 맛본 후, 20편에서 본격 기초를 정리. 메타 문자·플래그·그룹과 자주 쓰는 패턴들. 중급 파트의 마지막입니다.
정규식 만들기 — 두 가지 방법
// 리터럴 (가장 흔함)
const re1 = /hello/;
// RegExp 생성자 (동적 패턴)
const word = "hello";
const re2 = new RegExp(word);
const re3 = new RegExp(`\\d{${digitCount}}`); // 동적 값 끼울 때
주요 메서드 — 사용 빈도순
const text = "전화: 010-1234-5678";
const re = /\d{3}-\d{4}-\d{4}/;
re.test(text); // true — 매치 여부 boolean
text.match(re); // ["010-1234-5678"] — 첫 매치 (g 없으면)
text.matchAll(/\d+/g); // 이터레이터 — 모든 매치 (그룹 포함)
text.replace(re, "[전화]"); // "전화: [전화]"
text.split(/\s+/); // ["전화:", "010-1234-5678"]
text.search(re); // 0 또는 -1 (위치)
플래그 5종
| 플래그 | 의미 |
|---|---|
| g | global — 모든 매치 (한 번이 아니라) |
| i | ignore case — 대소문자 무시 |
| m | multiline — ^$ 가 줄 단위 |
| s | dotAll — . 이 줄바꿈도 매치 |
| u | unicode — 이모지·코드포인트 안전 |
"Hello hello HELLO".match(/hello/gi);
// ["Hello", "hello", "HELLO"]
메타 문자 — 의미 있는 기호
| 패턴 | 매치 |
|---|---|
| . | 임의의 한 글자 (줄바꿈 제외) |
| \d | 숫자 [0-9] |
| \D | 숫자 아님 |
| \w | 단어 문자 [A-Za-z0-9_] |
| \s | 공백 (스페이스·탭·줄바꿈) |
| \b | 단어 경계 |
| ^ $ | 줄 시작 / 끝 |
| [abc] | a·b·c 중 하나 |
| [^abc] | a·b·c 가 아닌 것 |
| [a-z] | 범위 |
| a|b | a 또는 b |
수량자 — 몇 번 반복하나
| 패턴 | 의미 |
|---|---|
| ? | 0 또는 1번 (옵션) |
| * | 0번 이상 |
| + | 1번 이상 |
| {n} | 정확히 n번 |
| {n,} | n번 이상 |
| {n,m} | n~m번 |
| +? | lazy — 가장 적게 매치 (기본은 greedy) |
그룹과 캡처 — ()
// 위치 기반 그룹
const m = "2026-05-17".match(/^(\d{4})-(\d{2})-(\d{2})$/);
// m[0]="2026-05-17", m[1]="2026", m[2]="05", m[3]="17"
// 이름 있는 그룹 (?<name>...)
const m2 = "2026-05-17".match(/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/);
m2.groups.year; // "2026"
m2.groups.month; // "05"
m2.groups.day; // "17"
// 비캡처 그룹 (?:...)
"hello world".replace(/(?:hello|hi) (\w+)/, "안녕 $1");
// "안녕 world" ← 첫 그룹은 캡처 안 함
// 역참조 \1
"hello hello".match(/(\w+) \1/); // 같은 단어 반복
자주 쓰는 패턴 7가지
// 1. 이메일 (대략) — 실제 RFC 5322 는 매우 복잡, 보통 이 정도면 충분
const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// 2. 한국 전화번호 (010-1234-5678 또는 01012345678)
const phoneRe = /^01[0-9]-?\d{3,4}-?\d{4}$/;
// 3. URL
const urlRe = /^https?:\/\/[^\s]+$/;
// 4. 한국어만
const koreanRe = /^[가-힣]+$/;
// 5. 영숫자 + _ - 만 (slug)
const slugRe = /^[a-z0-9_-]+$/;
// 6. 카드 번호 분리 (1234567812345678 → 1234 5678 1234 5678)
"1234567812345678".replace(/(\d{4})(?=\d)/g, "$1 ");
// 7. 좋은 비밀번호 (대문자·소문자·숫자 각 1개 이상, 8자+)
const pwRe = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
lookahead · lookbehind — 위치 기반 매칭
// (?=...) — 뒤따라야 함 (소비 안 함)
"abc123".match(/[a-z]+(?=\d)/); // "abc" — 숫자 앞의 글자
// (?!...) — 뒤따르면 안 됨
"abc def".match(/abc(?! def)/); // null
// (?<=...) — 앞에 있어야 함 (소비 안 함)
"$100".match(/(?<=\$)\d+/); // "100"
// (?<!...) — 앞에 있으면 안 됨
replace + 함수 — 강력한 변환
// 매치마다 함수 호출
"hello world".replace(/\w+/g, m => m.toUpperCase());
// "HELLO WORLD"
// 그룹과 함께
"2026-05-17".replace(/(\d{4})-(\d{2})-(\d{2})/, (_, y, m, d) => `${y}년 ${+m}월 ${+d}일`);
// "2026년 5월 17일"
// 이름 있는 그룹
"a:1, b:2".replace(/(?<k>\w+):(?<v>\d+)/g, (...args) => {
const { k, v } = args.at(-1); // 마지막 인자가 groups
return `${k}=${v}`;
});
안티패턴 / 주의 5가지
1. 이메일 정규식에 너무 정확히. RFC 완전 정확은 너무 복잡 → /^[^\s@]+@[^\s@]+\.[^\s@]+$/ 정도 + 실제로 인증 메일 보내 검증.
2. HTML 파싱. 정규식으로 HTML 파싱 절대 금지. DOMParser 또는 cheerio.
3. 백트래킹 폭주(ReDoS). (a+)+ 같은 중첩 수량자에 긴 입력 → 영겁의 시간. 사용자 입력 패턴 만들 때 주의.
4. g 플래그 + test 함정. regex.test() 가 g 플래그면 lastIndex 가 유지됨 → 같은 regex 인스턴스 재사용 시 다음 호출이 다른 위치부터. 매번 새로 만들거나 lastIndex 리셋.
5. 가독성. 5줄짜리 정규식은 6개월 뒤 자신도 못 읽음. 주석 모드 없는 JS 에서는 변수로 분해하거나 라이브러리(date-fns·zod) 사용.
21편 — 클로저 깊이 (고급 시작)
함수가 함수를 기억하는 메커니즘. 카운터 패턴부터 메모리 누수까지.
📚 쉽게 배우는 자바스크립트 교재
이전: 19편 모듈 · 현재: 20편 (중급 마지막) · 다음 → 21편 클로저 (고급 시작) · 진행: 20/26
이전: 19편 모듈 · 현재: 20편 (중급 마지막) · 다음 → 21편 클로저 (고급 시작) · 진행: 20/26