Dev
정규식 마스터하기 2026 - 패턴별 실전 예제 총정리
이메일, URL, 전화번호 검증부터 복잡한 텍스트 파싱까지. 실무에서 바로 사용 가능한 정규식 패턴과 작동 원리를 코드와 함께 알아봅니다.
Toolypet Team
Development Team
정규식 마스터하기 2026
"정규식은 어렵다"는 말, 맞습니다. 하지만 한 번 익히면 텍스트 처리 시간을 90% 줄일 수 있습니다.
이 가이드에서는 실무에서 가장 많이 사용되는 정규식 패턴을 예제와 함께 정리합니다.
정규식 기초
기본 문법
| 패턴 | 설명 | 예시 |
|---|---|---|
. | 모든 문자 (1개) | a.c → "abc", "a1c" |
* | 0개 이상 | ab*c → "ac", "abc", "abbc" |
+ | 1개 이상 | ab+c → "abc", "abbc" |
? | 0개 또는 1개 | colou?r → "color", "colour" |
^ | 문자열 시작 | ^Hello |
$ | 문자열 끝 | world$ |
\d | 숫자 [0-9] | \d{3} → "123" |
\w | 단어 문자 [a-zA-Z0-9_] | \w+ |
\s | 공백 문자 | \s+ |
문자 클래스
[abc] - a, b, c 중 하나
[^abc] - a, b, c 제외
[a-z] - a부터 z까지
[A-Z] - A부터 Z까지
[0-9] - 0부터 9까지
[a-zA-Z] - 모든 영문자
수량자
{n} - 정확히 n개
{n,} - n개 이상
{n,m} - n개 이상 m개 이하
* - 0개 이상 ({0,})
+ - 1개 이상 ({1,})
? - 0 또는 1개 ({0,1})
그룹과 캡처
(abc) - 캡처 그룹
(?:abc) - 비캡처 그룹
(?<name>abc) - 명명된 그룹
\1 - 첫 번째 그룹 역참조
실전 패턴: 유효성 검증
이메일 주소
// 기본 패턴
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// 테스트
emailRegex.test('user@example.com'); // true
emailRegex.test('invalid-email'); // false
패턴 분석:
^ - 시작
[a-zA-Z0-9._%+-]+ - 로컬 파트 (1자 이상)
@ - @ 기호
[a-zA-Z0-9.-]+ - 도메인 (1자 이상)
\. - . (이스케이프)
[a-zA-Z]{2,} - TLD (2자 이상)
$ - 끝
URL
const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
// 테스트
urlRegex.test('https://example.com'); // true
urlRegex.test('https://sub.example.com/path'); // true
urlRegex.test('invalid url'); // false
전화번호 (한국)
// 휴대폰
const mobileRegex = /^01[016789]-?\d{3,4}-?\d{4}$/;
// 일반 전화
const phoneRegex = /^0\d{1,2}-?\d{3,4}-?\d{4}$/;
// 테스트
mobileRegex.test('010-1234-5678'); // true
mobileRegex.test('01012345678'); // true
비밀번호 강도
// 최소 8자, 대/소문자, 숫자, 특수문자 포함
const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
// 패턴 분석
// (?=.*[a-z]) - 소문자 최소 1개 (전방탐색)
// (?=.*[A-Z]) - 대문자 최소 1개
// (?=.*\d) - 숫자 최소 1개
// (?=.*[@$!%*?&]) - 특수문자 최소 1개
// {8,} - 8자 이상
신용카드 번호
// Visa
const visaRegex = /^4[0-9]{12}(?:[0-9]{3})?$/;
// MasterCard
const mastercardRegex = /^5[1-5][0-9]{14}$/;
// 모든 카드 (하이픈 허용)
const cardRegex = /^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/;
실전 패턴: 텍스트 추출
HTML 태그 내용 추출
const html = '<div class="title">Hello World</div>';
// 태그 내용 추출
const contentRegex = /<div[^>]*>(.*?)<\/div>/;
const match = html.match(contentRegex);
console.log(match[1]); // "Hello World"
// 모든 태그 제거
const noTags = html.replace(/<[^>]*>/g, '');
console.log(noTags); // "Hello World"
URL에서 도메인 추출
const url = 'https://www.example.com/path/page.html';
const domainRegex = /^(?:https?:\/\/)?(?:www\.)?([^/]+)/;
const domain = url.match(domainRegex)[1];
console.log(domain); // "example.com"
로그 파일 파싱
const logLine = '[2026-02-21 14:30:45] ERROR: Connection timeout at module.js:42';
const logRegex = /\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+) at (.+):(\d+)/;
const [, datetime, level, message, file, line] = logLine.match(logRegex);
console.log({
datetime, // "2026-02-21 14:30:45"
level, // "ERROR"
message, // "Connection timeout"
file, // "module.js"
line, // "42"
});
CSV 파싱 (쉼표 구분)
const csvLine = 'John,"Doe, Jr.",30,"New York, NY"';
// 쉼표 분리 (따옴표 내 쉼표 무시)
const csvRegex = /(?:^|,)("(?:[^"]*(?:""[^"]*)*)"|[^,]*)/g;
const fields = [];
let match;
while ((match = csvRegex.exec(csvLine)) !== null) {
fields.push(match[1].replace(/^"|"$/g, '').replace(/""/g, '"'));
}
console.log(fields); // ["John", "Doe, Jr.", "30", "New York, NY"]
실전 패턴: 검색과 치환
전화번호 포맷팅
const phone = '01012345678';
// 하이픈 추가
const formatted = phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
console.log(formatted); // "010-1234-5678"
금액 포맷팅
const amount = '1234567890';
// 천 단위 콤마
const formatted = amount.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
console.log(formatted); // "1,234,567,890"
마스킹
// 이메일 마스킹
const email = 'username@example.com';
const masked = email.replace(/(.{2})(.*)(@.*)/, '$1***$3');
console.log(masked); // "us***@example.com"
// 전화번호 마스킹
const phone = '010-1234-5678';
const maskedPhone = phone.replace(/(\d{3})-(\d{4})-(\d{4})/, '$1-****-$3');
console.log(maskedPhone); // "010-****-5678"
공백 정리
const text = ' Hello World ';
// 앞뒤 공백 제거 (trim)
const trimmed = text.replace(/^\s+|\s+$/g, '');
// 연속 공백을 하나로
const normalized = text.replace(/\s+/g, ' ').trim();
console.log(normalized); // "Hello World"
고급 기능
Lookahead (전방탐색)
// Positive Lookahead: (?=...)
// "foo" 다음에 "bar"가 오는 경우만 매칭
const regex = /foo(?=bar)/;
'foobar'.match(regex); // ["foo"]
'foobaz'.match(regex); // null
// Negative Lookahead: (?!...)
// "foo" 다음에 "bar"가 오지 않는 경우만 매칭
const regex2 = /foo(?!bar)/;
'foobaz'.match(regex2); // ["foo"]
'foobar'.match(regex2); // null
Lookbehind (후방탐색)
// Positive Lookbehind: (?<=...)
// "$" 앞에 있는 숫자
const priceRegex = /(?<=\$)\d+/g;
'$100 and $200'.match(priceRegex); // ["100", "200"]
// Negative Lookbehind: (?<!...)
// "$" 앞에 없는 숫자
const nonPriceRegex = /(?<!\$)\d+/g;
'$100 and 200'.match(nonPriceRegex); // ["200"]
Named Groups (명명된 그룹)
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = '2026-02-21'.match(dateRegex);
console.log(match.groups.year); // "2026"
console.log(match.groups.month); // "02"
console.log(match.groups.day); // "21"
Non-Greedy (비탐욕적)
const html = '<div>Hello</div><div>World</div>';
// Greedy (기본): 가능한 많이 매칭
/<div>.*<\/div>/.exec(html)[0];
// "<div>Hello</div><div>World</div>"
// Non-Greedy: 가능한 적게 매칭
/<div>.*?<\/div>/.exec(html)[0];
// "<div>Hello</div>"
언어별 구현
JavaScript
// 생성 방법
const regex1 = /pattern/flags;
const regex2 = new RegExp('pattern', 'flags');
// 메서드
regex.test(str); // boolean
str.match(regex); // 배열 또는 null
str.replace(regex, replacement);
str.split(regex);
regex.exec(str); // 상세 매칭 정보
// 플래그
// g: 전역 검색
// i: 대소문자 무시
// m: 멀티라인
// s: dotAll (. 이 줄바꿈도 매칭)
// u: 유니코드
Python
import re
# 컴파일
pattern = re.compile(r'pattern', re.IGNORECASE)
# 메서드
re.match(pattern, string) # 시작부터 매칭
re.search(pattern, string) # 전체에서 검색
re.findall(pattern, string) # 모든 매칭 리스트
re.sub(pattern, repl, string) # 치환
# 예시
emails = re.findall(r'[\w.+-]+@[\w.-]+\.\w+', text)
cleaned = re.sub(r'\s+', ' ', text).strip()
Go
import "regexp"
// 컴파일
re := regexp.MustCompile(`\d+`)
// 메서드
re.MatchString(s) // bool
re.FindString(s) // 첫 매칭
re.FindAllString(s, -1) // 모든 매칭
re.ReplaceAllString(s, r) // 치환
// 예시
numbers := re.FindAllString("a1b2c3", -1)
// ["1", "2", "3"]
성능 최적화
컴파일된 정규식 재사용
// ❌ 느림: 매번 컴파일
function validate(email) {
return /^[\w.+-]+@[\w.-]+\.\w+$/.test(email);
}
// ✅ 빠름: 한 번 컴파일
const emailRegex = /^[\w.+-]+@[\w.-]+\.\w+$/;
function validate(email) {
return emailRegex.test(email);
}
비캡처 그룹 사용
// ❌ 불필요한 캡처
const regex = /(https?):\/\/(www\.)?(.+)/;
// ✅ 필요 없는 그룹은 비캡처
const regex = /(?:https?):\/\/(?:www\.)?(.+)/;
재앙적 역추적 방지
// ❌ 위험: (a+)+ 패턴
const bad = /^(a+)+$/;
'aaaaaaaaaaaaaaaaaaaaaa!'.match(bad); // 매우 느림
// ✅ 안전: 원자 그룹 또는 소유적 수량자
// JavaScript에서는 구조 변경으로 해결
const good = /^a+$/;
디버깅 팁
Regex Tester 활용
- 패턴 입력
- 테스트 문자열 입력
- 매칭 결과 하이라이트 확인
- 캡처 그룹 확인
단계별 검증
// 복잡한 패턴을 단계별로 검증
const parts = [
'^', // 시작
'[a-zA-Z0-9._%+-]+', // 로컬 파트
'@', // @
'[a-zA-Z0-9.-]+', // 도메인
'\\.', // .
'[a-zA-Z]{2,}', // TLD
'$', // 끝
];
const emailRegex = new RegExp(parts.join(''));
FAQ
Q1: . vs \. 차이는?
A: .는 모든 문자(와일드카드), \.는 문자 그대로의 점입니다.
Q2: * vs + 차이는?
A: *는 0개 이상, +는 1개 이상입니다.
ab*c→ "ac", "abc", "abbc"ab+c→ "abc", "abbc" ("ac"는 매칭 안 됨)
Q3: 대소문자 무시는?
A: 플래그 i를 사용하세요.
- JavaScript:
/pattern/i - Python:
re.IGNORECASE
Q4: 줄바꿈도 .으로 매칭하려면?
A: s 플래그 (dotAll) 사용하세요.
- JavaScript:
/pattern/s - Python:
re.DOTALL
Q5: 이스케이프가 필요한 문자는?
A: \ ^ $ . | ? * + ( ) [ ] { } 이 문자들은 \로 이스케이프하세요.
마무리
정규식 핵심:
- 기초 문법:
.,*,+,?,[],() - 메타 문자:
\d,\w,\s,^,$ - 수량자:
{n},{n,},{n,m} - 고급 기능: Lookahead, Lookbehind, Named Groups
연습만이 답입니다. Regex Tester에서 실시간으로 테스트해보세요.
관련 도구
| 도구 | 용도 |
|---|---|
| Regex Tester | 정규식 테스트 및 디버깅 |
| JSON Formatter | JSON 포맷팅 |
정규식Regex패턴매칭개발JavaScriptPython
저자 소개
Toolypet Team
Development Team
The Toolypet Team creates free, privacy-focused web tools for developers and designers. All tools run entirely in your browser with no data sent to servers.
Web DevelopmentCSS ToolsDeveloper ToolsSEOSecurity