정규식 테스트기

JavaScript 정규식 실시간 매칭·하이라이트 + 캡처·치환·분할 + 정규식 패턴 30+ (한국 데이터 10종).

2026년 9월 기준 점검ECMAScript 정규식(브라우저 내장 엔진) 문법 기준 · 주민등록번호 부여체계 개편(2020.10) · 개인정보 보호법 시행령 제19조출처 MDN 정규 표현식 안내서(새 창) ·ECMA-262 (ECMAScript 명세)(새 창) ·OWASP ReDoS(새 창) ·개인정보 보호법 시행령 제19조(새 창) ·행정안전부 주민번호 부여체계 개편(새 창)작성 리만 (Youtil 운영자)

개인정보 검증은 정규식만으로 끝내지 마세요
정규식 매칭은 모두 브라우저에서 실행되며 입력 데이터는 외부로 전송되지 않습니다. 주민등록번호·카드번호 패턴은 형식만 확인하므로 실제 서비스의 검증·저장은 관련 법령과 KISA·OWASP 가이드를 따르세요. 잘못된 정규식이 브라우저를 멈추지 않도록 별도 스레드에서 실행하고 입력 100KB · 매치 1만 개 · 실행 1초로 제한합니다. 분야별 안전 안내는 면책조항 참고.
//g
flags (g)
통계

0개 매치 · 실행 0.00ms

하이라이트 미리보기
Hello World — 안녕하세요 정규식 테스트입니다. Line 2 with numbers 123 and email@example.com.

💡 색상 5종 로테이션 · 매치 위치 강조. zero-width 매치(예: \b)는 보이지 않을 수 있어요.

정규식은 유효하지만 매치가 0개입니다. 패턴이 테스트 문자열과 일치하는지 확인해 보세요.

가이드

알아두면 좋은 내용

리만 작성 · Youtil 운영자 소개
최종 업데이트 2026.09.26 · 읽는 시간 약 15분

어떻게 사용하나요?

  1. 매칭 — 정규식·flags·테스트 문자열을 넣으면 매치를 하이라이트하고, 매치마다 위치(인덱스)·길이·캡처 그룹·이름 그룹을 카드로 보여 줍니다.
  2. 치환·분할 — 같은 정규식으로 치환($1·$&·$<name>) 또는 split 결과를 보고, JavaScript·Python·Java·PHP 코드 스니펫을 복사합니다.
  3. 패턴 라이브러리 — 한국 데이터 10종(휴대폰·주민등록번호·사업자등록번호·우편번호 등)과 이메일·URL·UUID·HTML·마크다운 등 30여 개. 누르면 바로 적용됩니다.
  4. 치트시트 — 메타문자·양화 한정자·문자 클래스·그룹·룩어라운드·flags를 한 표로.

정규식·flags·치환 패턴은 이 브라우저에 자동 저장되고, 테스트 문자열은 개인정보가 들어갈 수 있어 저장하지 않습니다. 입력이 바뀌면 200ms 뒤 다시 계산합니다.

정규식 기본 — 메타문자·양화 한정자

정규식은 무엇을(메타문자·문자 클래스) 몇 번(양화 한정자) 찾을지의 조합입니다. 한국어 텍스트를 다룰 때는 \w·\d가 ASCII 전용이라는 점이 가장 흔한 함정입니다.

구문의미예시한국어 텍스트 주의점
.줄바꿈을 뺀 글자 1개.+ → "abc"u flag 없으면 이모지는 2글자로 취급
\d숫자 [0-9]\d{3} → "123"전각 숫자 123은 불일치
\w단어 문자 [A-Za-z0-9_]\w+@ → "user@"한글은 포함되지 않음 (u flag여도 동일)
\s공백·탭·줄바꿈 등\s+ → " "전각 공백(U+3000)·NBSP도 포함
\b단어 경계\bcat\b\w 기준이라 한글 단어 경계로 못 씀
^ $시작·끝 (m flag면 각 줄)^abc$—
* + ?0회 이상 · 1회 이상 · 0~1회colou?r → "color"—
{n,m}n~m회\d{2,4}—
*? +?가능한 적게 (lazy)<.*?> → 태그 하나씩—

캡처 그룹 — 번호 그룹 · 이름 그룹 · 비캡처 그룹

괄호로 묶으면 그 부분을 따로 꺼내거나 치환에서 다시 쓸 수 있습니다. 번호는 여는 괄호 (가 나오는 순서대로 1번부터 매겨집니다.

번호 캡처 그룹 (...)

$1, $2 … 로 참조. 패턴 안에서는 \1로 같은 글자 반복을 찾을 수 있습니다.

패턴: (\w+)@(\w+\.\w+)
매치: "hello@example.com"
$1 = "hello"
$2 = "example.com"
치환 "$2/$1" → "example.com/hello"

이름 캡처 그룹 (?<name>...)

$<name>으로 참조(ES2018+). 그룹을 추가해도 참조가 밀리지 않습니다.

패턴: (?<user>\w+)@(?<domain>\w+\.\w+)
매치: "hello@example.com"
groups.user   = "hello"
groups.domain = "example.com"

비캡처 그룹 (?:...)

묶기만 하고 번호를 매기지 않아, 필요한 그룹만 $1·$2를 차지합니다.

패턴: (?:https?|ftp):\/\/(\w+)
매치: "https://example.com"
$1 = "example" (호스트 첫 단어만 캡처)

치환 문자열의 $ 토큰 — 실행 결과로 보기

치환 탭은 JavaScript String.prototype.replace 규칙을 그대로 따릅니다. 아래 표는 입력 abc-123-xyz에 정규식 /(\d)(?<rest>\d+)/를 적용하고 치환 문자열을 [토큰]으로 바꿔 가며 실제로 실행한 결과입니다.

토큰의미실행 결과
$&매치 전체abc-[123]-xyz
$11번 그룹abc-[1]-xyz
$22번 그룹 (이름 그룹도 번호를 가짐)abc-[23]-xyz
$<rest>이름 그룹abc-[23]-xyz
$`매치 앞부분 전체abc-[abc-]-xyz
$'매치 뒷부분 전체abc-[-xyz]-xyz
$$달러 기호 자체abc-[$]-xyz

같은 치환을 다른 언어로 옮기면 참조 표기가 달라집니다. 패턴 (?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2}), 치환 $<y>년 $<m>월 $<d>일, g flag로 치환 탭이 만들어 주는 코드는 다음과 같고, 2026-09-26, 2027-01-05은 2026년 09월 26일, 2027년 01월 05일이 됩니다.

언어생성 코드
JavaScripttext.replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/g, "$<y>년 $<m>월 $<d>일")
Pythonre.sub(r'(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})', r'\g<y>년 \g<m>월 \g<d>일', text)
Javatext.replaceAll("(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})", "${y}년 ${m}월 ${d}일")
PHPpreg_replace('/(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})/', '${1}년 ${2}월 ${3}일', $text)

Python은 이름 그룹을 (?P<name>…), 참조를 \g<name>으로 쓰고, PHP의 preg_replace는 치환 문자열에서 이름 참조를 지원하지 않아 번호(${1})로 바뀝니다. Java는 g flag 대신 replaceAll/replaceFirst로 구분합니다.

flags 6종 상세

flag이름설명예시
g전역(Global)첫 매치에서 멈추지 않고 모든 매치를 찾음matchAll·전체 치환
i대소문자 무시A와 a를 같은 글자로 취급/hello/i → "HELLO"
m다중행(Multiline)^과 $가 문자열 전체가 아니라 각 줄의 시작·끝에 매치/^#/gm → 마크다운 제목 줄
sdotAll.이 줄바꿈까지 매치/<p>.*<\/p>/s → 여러 줄 단락
uUnicode코드 포인트 단위 처리 + \p{…} 유니코드 속성/\p{Script=Hangul}+/u
yStickylastIndex 위치에서만 매치 시도토크나이저·파서

최신 JavaScript에는 매치 위치를 그룹별로 돌려주는 d(hasIndices, ES2022)와 문자 클래스의 차집합·교집합을 지원하는 v(unicodeSets, ES2024)도 있습니다. 본 도구는 위 6종만 선택할 수 있으므로, 두 flag가 필요한 패턴은 브라우저 개발자 도구 콘솔에서 확인하세요.

한국 데이터 패턴 10종 — 통과해도 틀릴 수 있는 값

패턴 라이브러리의 한국 데이터 패턴은 입력 형식을 거르는 1차 필터입니다. 아래 표의 ‘반례’는 형식은 맞지만 실제로는 유효하지 않은 값으로, 각 패턴에 넣어 빌드 때 직접 실행한 결과입니다. 이런 값까지 막으려면 날짜 검사·검증번호 계산·외부 조회를 코드로 추가해야 합니다.

패턴정규식예시반례정규식이 확인하지 않는 것
한국 휴대폰^010-?\d{3,4}-?\d{4}$010-1234-5678 통과010-12345678 통과010만 허용(01X 옛 번호 거부). 하이픈이 한쪽에만 있는 표기도 통과
한국 일반전화^0\d{1,2}-?\d{3,4}-?\d{4}$02-1234-5678 통과010-1234-5678 통과휴대폰 번호도 통과(0 + 두 자리 규칙). 1588 같은 전국대표번호는 거부
주민등록번호 (앞-뒤)^\d{6}-[1-4]\d{6}$901231-1234567 통과991399-1234567 통과생년월일 유효성 미검사. 1800년대 출생(9·0)은 거부. 2020.10 이후 번호는 체크섬 규칙 없음
외국인등록번호^\d{6}-[5-8]\d{6}$901231-5234567 통과000000-5000000 통과생년월일 유효성 미검사 — 자릿수와 7번째 자리(5~8)만 확인
사업자등록번호^\d{3}-?\d{2}-?\d{5}$123-45-67890 통과000-00-00000 통과마지막 자리 검증번호(앞 9자리로 계산) 미검사
법인등록번호^\d{6}-?\d{7}$110111-1234567 통과000000-0000000 통과마지막 자리 검증번호 미검사 — 13자리 숫자면 통과
한국 우편번호 (5자리)^\d{5}$03187 통과99999 통과5자리 숫자면 모두 통과 — 실제 구역번호인지는 주소 DB로 확인
차량번호 (12가1234)^\d{2,3}[가-힣]\d{4}$12가1234 통과999힣9999 통과번호판에 쓰지 않는 한글도 통과. 지역명 구형 번호판(서울12가1234)·공백 표기는 거부
카드번호 (16자리)^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$4123-4567-8901-2345 통과0000-0000-0000-0001 통과Luhn 체크섬 미검사. 16자리 전용이라 15자리 카드(4-6-5)는 거부
계좌번호 (일반)^\d{3,6}-?\d{2,6}-?\d{2,8}$110-123-456789 통과1234567 통과은행마다 자릿수가 달라 7~20자리 숫자면 대부분 통과 — 은행·예금주 조회가 별도로 필요

주민등록번호는 2020년 10월부터 새로 부여·변경되는 번호의 뒷자리가 성별 1자리 + 임의번호 6자리로 바뀌었습니다. 예전 번호용 검증번호 공식으로 거르면 새 번호를 잘못 거부하게 됩니다.

Catastrophic Backtracking — 정규식이 멈추는 이유

JavaScript 정규식 엔진은 실패하면 되돌아가(backtrack) 다른 분할을 시도합니다. (a+)+$에 a 25개와 느낌표를 주면, a들을 안쪽·바깥쪽 반복에 나눠 담는 방법이 224가지 넘게 있고 엔진은 그 전부를 시도한 뒤에야 실패를 선언합니다. 입력이 한 글자 늘 때마다 시간이 약 2배가 되므로, 서버에서 사용자 입력에 이런 정규식을 쓰면 서비스 거부(ReDoS) 공격에 노출됩니다. 본 도구는 정규식을 별도 작업 스레드(Web Worker)에서 돌려 1초가 넘으면 자동 중단합니다.

위험 패턴문제안전한 대체
(a+)+$반복 안의 반복 — 분할 경우의 수가 지수적a+$
(a|aa)+$두 분기가 같은 글자열을 서로 다르게 나눠 가짐a+$
(\w+\s?)+$공백이 선택적이라 단어 경계를 어디서든 나눌 수 있음\w+(?:\s\w+)*$
"(.*)*"빈 문자열도 매치하는 그룹을 반복"[^"]*"
  • lazy로 바꾸는 것은 해결책이 아닙니다 — (a+?)+?$도 실패하는 입력에서는 같은 경우의 수를 모두 탐색합니다. 탐색 순서만 달라질 뿐입니다.
  • atomic group 흉내 — JS에는 (?>…)가 없지만 (?=(a+))\1처럼 lookahead로 잡은 뒤 역참조로 소비하면 그 부분은 되돌아가지 않습니다.
  • 입력 길이 제한 — 서버 측 정규식은 입력 길이 상한과 실행 시간 제한을 함께 두세요. 지수 시간 패턴도 입력이 짧으면 문제가 드러나지 않아 테스트에서 놓치기 쉽습니다.

자주 묻는 질문

JavaScript 정규식과 PCRE·Python 정규식은 무엇이 다른가요?
본 도구는 브라우저에 내장된 JavaScript(ECMAScript) 정규식 엔진으로 실행합니다. 다른 엔진에서 가져온 패턴이 안 될 때 주로 걸리는 차이는 다음과 같습니다. • lookbehind (?<=…): JS는 가변 길이도 허용하지만 Python re는 고정 길이만 받습니다. • atomic group (?>…)·possessive a*+: PCRE·Java·Python 3.11+ re에는 있지만 JS에는 없습니다. 재귀 (?R)는 PCRE(Perl 계열) 전용입니다. • 이름 그룹 표기: JS·PCRE (?<name>…), Python (?P<name>…). • 유니코드 속성 \p{…}: JS는 u(또는 v) flag가 있어야 동작합니다. • 인라인 flag: 패턴 맨 앞의 (?i) 같은 전역 인라인 flag는 Python·Java·PCRE에서만 되고 JS에서는 쓸 수 없습니다(ES2025부터 (?i:…)처럼 일부 구간에만 i·m·s를 거는 문법은 표준이 됐지만, 구형 브라우저에서는 오류). 그래서 치환 탭의 코드 스니펫은 flag를 언어별 표기로 바꿔 줍니다.
greedy(.*)와 lazy(.*?)의 차이는?
greedy(.*, 기본값)는 가능한 한 길게, lazy(.*?)는 가능한 한 짧게 매치합니다. 입력 <b>hello</b><i>world</i>에서 <.*>는 문자열 전체 1개를, <.*?>는 <b>·</b>·<i>·</i> 4개를 각각 매치합니다. 구분자 사이의 짧은 구간을 잡을 때는 lazy가 맞지만, 중첩된 태그·괄호는 정규식만으로 정확히 파싱할 수 없습니다 — HTML은 DOMParser, JSON은 JSON.parse 같은 파서를 쓰세요.
캡처 그룹과 비캡처 그룹의 차이?
캡처 그룹 (…)은 매치한 부분을 기억해 치환에서 $1·$2로, 패턴 안에서 \1로 다시 참조할 수 있습니다. 비캡처 그룹 (?:…)은 묶기만 하고 기억하지 않습니다. 비캡처 그룹을 쓰면 번호가 꼭 필요한 그룹에만 매겨져 $1이 가리키는 대상이 명확해집니다. 예: (?:https?|ftp):\/\/(\w+) — 프로토콜은 묶기만 하고 호스트 첫 단어만 $1로 캡처합니다.
이름 캡처 그룹(named group)은 어떻게 쓰나요?
ES2018에 추가된 문법으로, 그룹에 이름을 붙여 번호 대신 이름으로 꺼냅니다. (?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})로 2026-05-15를 매치하면 match.groups.year가 "2026"이 되고, 치환 문자열에서는 $<year>년처럼 씁니다. 이름 그룹도 번호가 매겨지므로 $1로도 참조됩니다. 그룹을 추가·삭제해도 번호가 밀리지 않아 긴 패턴일수록 유지보수가 쉬워집니다.
lookahead와 lookbehind는?
위치만 검사하고 글자를 소비하지 않는 폭 0 어서션입니다. (?=…) 뒤가 …일 때, (?!…) 뒤가 …이 아닐 때, (?<=…) 앞이 …일 때, (?<!…) 앞이 …이 아닐 때 매치합니다. 예: \d+(?=원)에 1000원, 2000$를 주면 1000만 매치되고 원은 결과에 포함되지 않습니다. 비밀번호 규칙의 (?=.*\d)(?=.*[A-Z])처럼 조건을 여러 개 겹칠 때도 씁니다.
flags g와 y의 차이는?
둘 다 lastIndex 위치부터 매치를 시도하지만, g는 그 위치에서 실패하면 뒤로 한 칸씩 옮겨 가며 계속 찾고, y(sticky)는 정확히 그 위치에서만 시도합니다. 입력 foo bar에서 lastIndex = 3이면 /bar/g는 4번 위치의 bar를 찾지만 /bar/y는 3번 위치가 공백이라 실패합니다. y는 ‘현재 위치에서 다음 토큰이 무엇인가’를 확인하는 토크나이저·파서 작성에 씁니다.
u flag가 왜 필요한가요?
u flag가 없으면 JS 정규식은 문자열을 UTF-16 코드 단위로 봅니다. 그래서 이모지처럼 기본 다국어 평면(BMP) 밖 문자는 . 하나에 매치되지 않고(😀는 코드 단위 2개), \p{L}은 유니코드 속성이 아니라 글자 p{L}로 해석돼 오류 없이 엉뚱한 결과가 나옵니다. u를 켜면 \p{L}(모든 문자), \p{N}(숫자), \p{Script=Hangul}(한글 문자 체계), \p{Extended_Pictographic}(그림 이모지)를 쓸 수 있습니다. 대신 u 모드는 문법이 엄격해 \- 같은 불필요한 이스케이프를 오류로 처리합니다.
한글·이모지 매칭은 어떻게 하나요?
완성형 한글 음절은 [가-힣](U+AC00~U+D7A3, 11,172자)이 표준입니다. 자음·모음 낱자(ㅋㅋ, ㅠㅠ)는 [ㄱ-ㅎㅏ-ㅣ]로 따로 넣어야 하고, 옛한글까지 넓히려면 \p{Script=Hangul}(u flag)를 씁니다. 주의할 점은 \w와 \b가 한글을 인식하지 못한다는 것입니다 — JS의 \w는 u flag를 켜도 [A-Za-z0-9_]뿐이라 /\b한글\b/는 ‘나는 한글 좋아’에서도 실패합니다. 한글 단어 경계는 (?<![가-힣])한글(?![가-힣])처럼 lookaround로 만드세요. 이모지는 \p{Emoji}가 숫자 0~9·#·*까지 포함하므로 \p{Extended_Pictographic}을 쓰고, 국기·피부톤·ZWJ 조합은 패턴 라이브러리의 이모지 패턴처럼 결합 문자까지 묶어야 한 덩어리로 잡힙니다.
주민등록번호·카드번호를 정규식으로 검증하면 충분한가요?
아니요. 정규식은 자릿수·구분자 같은 형식만 봅니다. 주민등록번호 패턴은 991399처럼 없는 날짜도 통과시키고, 2020년 10월 이후 새로 부여된 번호는 성별 자리 뒤 6자리가 임의번호라 예전 검증번호(체크섬) 공식이 맞지 않으므로 체크섬으로 거르면 정상 번호를 떨어뜨립니다. 카드번호는 Luhn 체크섬과 발급사 식별번호(BIN) 확인이 따로 필요합니다. 법적으로는 「개인정보 보호법 시행령」 제19조가 주민등록번호·여권번호·운전면허번호·외국인등록번호를 고유식별정보로 정하고, 주민등록번호는 법령 근거가 있을 때만 처리할 수 있으며 암호화해 보관해야 합니다(같은 법 제24조의2). 카드번호는 고유식별정보는 아니지만 결제 정보라 업계 보안 기준(PCI DSS)에 따라 저장하지 않거나 토큰화하는 것이 일반적입니다. 실제 서비스에서는 수집 자체가 필요한지부터 검토하세요.
정규식이 너무 느려요 (catastrophic backtracking)
(a+)+$처럼 반복 안에 반복이 있고 같은 글자를 여러 방식으로 나눠 가질 수 있으면, 매치에 실패하는 입력에서 엔진이 가능한 분할을 모두 시도해 시간이 입력 길이에 지수적으로 늘어납니다. 해결책은 ① 중첩 반복 제거 — (a+)+ → a+, ② 분기끼리 겹치지 않게 — (a|aa)+ → a+, ③ 구분자를 빼는 문자 클래스 — ".*" → "[^"]*", ④ JS에 없는 atomic group 대신 (?=(a+))\1처럼 lookahead+역참조로 되돌아가기를 막기입니다. lazy(+?)로 바꾸는 것은 해결책이 아닙니다 — 시도 순서만 바뀔 뿐 실패하는 입력에서는 똑같이 모든 경우를 탐색합니다. 본 도구는 정규식을 별도 작업 스레드(Web Worker)에서 실행해 1초가 넘으면 중단하고, 100ms를 넘기면 경고하며, 입력 100KB·매치 1만 개로 제한합니다.

참고 자료

  1. MDN 정규 표현식 안내서(새 창)developer.mozilla.org
  2. ECMA-262 (ECMAScript 명세)(새 창)tc39.es
  3. OWASP ReDoS(새 창)owasp.org
  4. 개인정보 보호법 시행령 제19조(새 창)law.go.kr
  5. 행정안전부 주민번호 부여체계 개편(새 창)korea.kr
이 계산기를 만든 사람 · 리만Youtil 운영자 · 혼자 만들고 운영하는 1인 서비스입니다
운영자 소개
계산 기준
ECMAScript 정규식(브라우저 내장 엔진) 문법 기준 · 주민등록번호 부여체계 개편(2020.10) · 개인정보 보호법 시행령 제19조
오류 제보
결과가 다르면 contact@youtil.kr로 입력 조건을 알려 주세요. 평일 기준 1~3일 안에 답변합니다.

함께 쓰면 좋은 계산기