정규 표현식 기초를 넘어서면 Named Group, Lookbehind Assertion, dotAll, Unicode 플래그 같은 강력한 기능이 있습니다. ES2018 이후 추가된 이 기능들을 알면 더 읽기 좋고 강력한 패턴을 작성할 수 있습니다.

Named Capturing Group

숫자 인덱스 대신 이름으로 캡처 그룹에 접근합니다.

JS
// 기존 방식 — 인덱스 기반 (읽기 어려움)
const dateRegex = /(\d{4})-(\d{2})-(\d{2})/;
const match = "2026-03-28".match(dateRegex);
console.log(match[1]); // "2026" — 뭔지 기억해야 함

// Named Group — 이름 기반 (읽기 쉬움)
const namedRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const namedMatch = "2026-03-28".match(namedRegex);
console.log(namedMatch.groups.year);  // "2026"
console.log(namedMatch.groups.month); // "03"
console.log(namedMatch.groups.day);   // "28"

// 구조 분해와 함께
const { groups: { year, month, day } } = "2026-03-28".match(namedRegex);

replace에서 Named Group 참조

JS
// $<name>으로 참조
"2026-03-28".replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  "$<year>년 $<month>월 $<day>일"
);
// "2026년 03월 28일"

// 함수와 함께
"2026-03-28".replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  (...args) => {
    const { year, month, day } = args.at(-1); // 마지막 인자가 groups
    return `${year}/${month}/${day}`;
  }
);

Lookbehind Assertion (ES2018)

JS
// Lookahead (앞을 봄) — 이전부터 있었음
/\d+(?=원)/.exec("가격: 5000원");     // ["5000"] — "원" 앞의 숫자
/\d+(?!원)/.exec("5000달러");          // ["5000"] — "원"이 아닌 것 앞의 숫자

// Lookbehind (뒤를 봄) — ES2018 추가
/(?<=\$)\d+/.exec("가격: $100");       // ["100"] — "$" 뒤의 숫자
/(?<=₩)\d+/.exec("₩5000");            // ["5000"] — "₩" 뒤의 숫자

// Negative Lookbehind
/(?<!\$)\d+/.exec("가격: 100원");      // ["100"] — "$"가 아닌 것 뒤의 숫자

실전 — 비밀번호 마스킹

JS
// 앞 4자리와 뒤 4자리만 남기고 마스킹
function maskMiddle(str) {
  return str.replace(/(?<=.{4}).(?=.{4})/g, "*");
}
console.log(maskMiddle("1234567890")); // "1234**7890"

dotAll 플래그 (s)

JS
// 기본: .은 줄바꿈을 매칭하지 않음
/hello.world/.test("hello\nworld"); // false

// dotAll: .이 줄바꿈도 매칭
/hello.world/s.test("hello\nworld"); // true

// 실전: 여러 줄 HTML 매칭
const html = `<div>
  <p>내용</p>
</div>`;

html.match(/<div>.*<\/div>/s);  // 매칭됨
html.match(/<div>.*<\/div>/);   // 매칭 안 됨

Unicode 플래그 (u)와 유니코드 속성

JS
// u 플래그 — 유니코드 모드
/\u{1F600}/u.test("😀"); // true
/\u{1F600}/.test("😀");  // false (u 없으면 리터럴로 해석)

// Unicode Property Escapes (\p{})
/\p{Emoji}/u.test("😀");       // true — 이모지
/\p{Script=Hangul}/u.test("가"); // true — 한글
/\p{Script=Han}/u.test("漢");   // true — 한자
/\p{Letter}/u.test("a");        // true — 모든 문자

// 실전: 한글만 매칭
const koreanOnly = /^\p{Script=Hangul}+$/u;
koreanOnly.test("안녕하세요"); // true
koreanOnly.test("Hello");     // false
koreanOnly.test("안녕123");   // false

v 플래그 (ES2024) — 유니코드 집합

JS
// v 플래그는 u의 상위 호환
// 집합 연산 지원
/[\p{Script=Greek}&&\p{Letter}]/v; // 그리스 문자 중 글자만
/[\p{Decimal_Number}--[0-9]]/v;    // 10진 숫자 중 ASCII 제외
/[[\p{Letter}]&&[\p{ASCII}]]/v;    // ASCII 문자만

// 문자열 속성
/\p{Basic_Emoji}/v.test("🫠"); // true

matchAll — 모든 매칭 반복

JS
const text = "오늘 날짜: 2026-03-28, 어제: 2026-03-27";
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;

for (const match of text.matchAll(regex)) {
  const { year, month, day } = match.groups;
  console.log(`${year}${month}${day}일 (위치: ${match.index})`);
}
// 2026년 03월 28일 (위치: 6)
// 2026년 03월 27일 (위치: 22)

Named Backreference

JS
// 같은 문자가 반복되는 패턴
/(?<char>.)\k<char>/.test("aa"); // true
/(?<char>.)\k<char>/.test("ab"); // false

// HTML 태그 매칭 (열기/닫기 태그 일치)
/(?<tag>\w+)>.*?<\/\k<tag>/.test("div>content</div"); // true

실전 패턴 모음

JS
// IP 주소 추출
const ipRegex = /(?<octet1>\d{1,3})\.(?<octet2>\d{1,3})\.(?<octet3>\d{1,3})\.(?<octet4>\d{1,3})/;

// URL에서 도메인 추출
const urlRegex = /(?<=https?:\/\/)(?<domain>[^\/]+)/;
const { groups: { domain } } = "https://www.example.com/path".match(urlRegex);
// domain: "www.example.com"

// 가격 추출 (통화 기호 뒤의 숫자)
const priceRegex = /(?<=[$₩€¥£])\s*[\d,]+(?:\.\d+)?/g;
"$100, ₩10,000, €50.99".match(priceRegex);
// ["100", "10,000", "50.99"]

**기억하기 **: Named Group((?<name>...))은 캡처 결과를 이름으로 읽을 수 있어 가독성이 좋습니다. Lookbehind((?<=...))는 특정 패턴 뒤의 텍스트를 매칭합니다. u 플래그는 유니코드 처리에, s 플래그는 여러 줄 매칭에 필수입니다.

댓글 로딩 중...