정규식(regex) 패턴은 텍스트 안에서 특정 문자 시퀀스를 찾아내는 재사용 가능한 템플릿이에요. 쉽게 말하면, 거의 모든 프로그래밍 언어에서 동작하는 강력한 "검색" 도구라고 생각하면 돼요. 이메일 주소 유효성 검사, 폼에서 전화번호 추출, 로그 파일 파싱 등 어떤 작업이든 잘 선택된 정규식 몇 개면 필요한 작업의 90%를 처리할 수 있어요. 이 가이드에서는 실무에서 가장 자주 쓰이는 패턴들을 모아 각각의 동작 원리를 설명하고, 상황에 맞게 응용하는 방법까지 알아볼게요.
목차
정규식 문법 빠른 정리
패턴을 살펴보기 전에, 자주 등장하는 기본 구성 요소를 한눈에 정리해 둘게요. 정규식을 써본 적 있더라도 한 곳에 모아두면 편리해요.
| 토큰 | 의미 | 매칭 예시 |
|---|---|---|
.
|
줄바꿈을 제외한 임의의 문자 |
a.c
은
abc
,
a1c
에 매칭
|
\d
|
숫자 (0-9) |
\d\d
은
42
에 매칭
|
\w
|
단어 문자 (문자, 숫자, 밑줄) |
\w+
은
hello_world
에 매칭
|
\s
|
공백 문자 (스페이스, 탭, 줄바꿈) |
\s+
은 여러 개의 공백에 매칭
|
^
/
$
|
문자열의 시작 / 끝 |
^\d+$
은
123
에만 매칭
|
{n,m}
|
n회 이상 m회 이하 반복 |
\d{2,4}
은
12
부터
1234
까지 매칭
|
[abc]
|
문자 클래스 - a, b, c 중 하나 |
[aeiou]
은 모음에 매칭
|
(?:...)
|
비캡처 그룹 | 역참조 없이 그룹화 |
(?=...)
|
긍정 룩어헤드 | 소비하지 않고 뒤에 오는 내용을 단언 |
JavaScript 정규식 문법에 대한 가장 좋은 단일 레퍼런스는 MDN Web Docs 정규식 가이드 예요. 아래 패턴들 대부분은 플래그 차이만 약간 있을 뿐 Python, PHP, Java, Ruby에서도 그대로 활용할 수 있어요.
이메일 유효성 검사
이메일은 정규식의 대표적인 활용 사례예요. 그런데 많은 개발자들이 너무 엄격하게 작성하려다 실수를 해요.
RFC 5322 명세
에 따르면 기술적으로
"very unusual"@example.com
같은 주소도 허용되는데, 이를 정확히 처리하는 정규식은 거의 없어요. 실무 입력 유효성 검사의 99%는 아래처럼 실용적인 패턴으로 충분해요.
^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$
각 부분의 역할:
-
[a-zA-Z0-9._%+\-]+- 로컬 파트 (@ 앞부분); 점, 플러스, 하이픈, 밑줄 허용 -
@- @ 기호 그 자체 -
[a-zA-Z0-9.\-]+- 서브도메인 포함 도메인명 -
\.[a-zA-Z]{2,}- 최소 2자 이상의 TLD (.io, .com, .museum 등)
URL 및 웹 주소
URL 패턴 매칭은 일반 텍스트에서 링크를 추출하거나, 사용자가 입력한 웹사이트 주소를 검증하는 등 다양한 상황에서 활용돼요.
https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&\/=]*)
-
https?-http와https모두 매칭 -
(?:www\.)?- 선택적 www 접두사 -
[-a-zA-Z0-9@:%._+~#=]{1,256}- 호스트명 문자, 최대 256자 -
\.[a-zA-Z0-9()]{1,6}- TLD -
\b(?:[-a-zA-Z0-9()@:%_+.~#?&\/=]*)- 선택적 경로, 쿼리 문자열, 프래그먼트
추출이 아닌 유효성 검사만 필요하다면
^
와
$
앵커로 감싸주세요.
전화번호
전화번호는 국가별, 사용자 습관별로 형식이 천차만별이라 처리하기 까다로워요. 아래 두 가지 패턴으로 대부분의 상황을 커버할 수 있어요.
미국/캐나다 (NANP) 형식
^(\+1[-.\s]?)?(\(?\d{3}\)?[-.\s]?)?\d{3}[-.\s]?\d{4}$
매칭 예시:
555-867-5309
,
(555) 867 5309
,
+1.555.867.5309
,
5558675309
국제 형식 (E.164)
^\+[1-9]\d{6,14}$
E.164는 Twilio, AWS SNS 등 대부분의 전화 API에서 사용하는 형식이에요.
+
로 시작하는 국가 코드 뒤에 공백이나 구두점 없이 이어져요.
날짜 및 시간
날짜 패턴 매칭은 로그 파서, 폼 유효성 검사, 데이터 파이프라인에서 자주 사용돼요. 어떤 형식을 대상으로 하느냐는 입력 소스에 따라 달라져요.
ISO 8601 (YYYY-MM-DD)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
미국 형식 (MM/DD/YYYY)
^(0[1-9]|1[0-2])\/(0[1-9]|[12]\d|3[01])\/\d{4}$
24시간 형식 (HH:MM 또는 HH:MM:SS)
^([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?$
이 패턴들은 형식만 검사할 뿐 달력 논리는 검증하지 않아요. 예를 들어
2024-02-31
(2월 31일)도 통과시켜 버려요. 엄격한 날짜 유효성 검사가 필요하다면 정규식 검사 이후 언어의 날짜 라이브러리로 파싱하세요.
비밀번호 강도 검사
비밀번호 규칙은 보통 다양한 문자 유형의 조합과 최소 길이를 요구해요. 룩어헤드를 활용하면 여러 번 검사할 필요 없이 깔끔하게 처리할 수 있어요.
최소 8자, 대문자 1개 이상, 소문자 1개 이상, 숫자 1개 이상
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$
강력: 8자 이상, 대문자, 소문자, 숫자, 특수문자 포함
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$
각
(?=.*[...])
는 문자열 전체에서 조건에 맞는 문자가 최소 하나 있는지 확인하는 룩어헤드예요. 마지막
.{8,}
가 최소 길이를 강제해요.
NIST SP 800-63B 가이드라인
에 맞추려면
{8,}
를
{12,}
으로 바꿔 최소 12자를 적용하면 돼요.
IP 주소
IPv4
^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$
각 옥텟을 0-255 범위로 명시적으로 매칭하기 때문에
999.0.0.1
같은 잘못된 값은 올바르게 거부해요.
IPv6 (간소화 버전)
^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$
8그룹 전체 형식을 처리해요. 루프백 주소처럼 축약 표기(예:
::1
)는 패턴이 훨씬 복잡해지기 때문에 이 경우엔 정규식보다 네트워크 라이브러리로 파싱하는 게 더 안정적이에요.
HTML 및 마크업
여기서는 특정 목적에 맞는 패턴 몇 가지가 실제로 유용해요. "정규식으로 HTML을 파싱하지 말라"는 일반적인 조언은 전체 문서를 다룰 때는 여전히 유효해요. 전체 HTML 문서 파싱에는 BeautifulSoup이나 DOMParser 같은 제대로 된 DOM 파서를 사용하세요. 하지만 범위가 명확한 특정 작업에는 정규식으로도 충분해요.
모든 HTML 태그 제거
<[^>]*>
특정 태그의 내용 추출 (예: <title>)
([^<]*)<\/title>
캡처 그룹 1에 title 텍스트가 담겨요.
HTML 16진수 색상 코드 매칭
#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})\b
3자리 축약형(
#fff
)과 6자리 전체 형식(
#ffffff
) 모두 매칭해요.
자주 쓰이는 유틸리티 패턴
다양한 프로젝트에서 반복적으로 등장하는 패턴들이에요.
슬러그 (URL 친화적 문자열)
^[a-z0-9]+(?:-[a-z0-9]+)*$
my-blog-post-2024
같은 문자열에 매칭돼요. 대문자, 앞뒤 하이픈, 연속 하이픈은 허용하지 않아요.
신용카드 번호 (기본 형식, 공백 없음)
^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})$
-
4로 시작 - Visa (13자리 또는 16자리) -
51-55로 시작 - Mastercard (16자리) -
34또는37로 시작 - Amex (15자리) -
6011또는65로 시작 - Discover (16자리)
공백 정규화 (연속 공백 축소)
\s{2,}
매칭된 부분을 공백 하나로 치환하면 지저분한 사용자 입력이나 스크래핑한 텍스트를 깔끔하게 정리할 수 있어요.
숫자만 허용
^\d+$
영숫자만 허용
^[a-zA-Z0-9]+$
특정 단어가 포함된 줄 매칭 (플래그로 대소문자 무시)
^.*\bword\b.*$
\b
단어 경계를 사용하면
password
안에 있는
word
까지 매칭되는 것을 방지해요.
버전 번호 추출 (semver)
\bv?(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.]+))?(?:\+([a-zA-Z0-9.]+))?\b
v2.14.0-beta.1+build.42
같은 문자열에서 major, minor, patch, 프리릴리즈 레이블, 빌드 메타데이터를 캡처해요.
플래그 및 실용 팁
정규식 패턴은 어떤 플래그를 적용하느냐에 따라 동작이 달라져요. 가장 자주 필요한 플래그들을 정리했어요.
| 플래그 | JS | Python | 효과 |
|---|---|---|---|
| 대소문자 무시 |
i
|
re.IGNORECASE
|
대문자와 소문자를 동일하게 처리 |
| 전체 검색 (모두 찾기) |
g
|
re.findall()
|
첫 번째 매칭만이 아닌 모든 매칭 반환 |
| 멀티라인 |
m
|
re.MULTILINE
|
^
와
$
가 문자열 경계가 아닌 줄 경계에 매칭
|
| dotall |
s
|
re.DOTALL
|
.
가 줄바꿈 문자에도 매칭
|
디버깅 시간을 줄여주는 몇 가지 습관을 소개할게요.
- 항상 엣지 케이스로 테스트하세요 - 빈 문자열, 최대 길이, 유니코드 문자, 거의 유효하지만 완전히 유효하지는 않은 문자열을 테스트해 보세요.
-
매칭 내용이 필요 없을 때는 비캡처 그룹
(?:...)을 사용하세요 - 캡처 그룹보다 빠르고 깔끔해요. -
유효성 검사 패턴에는 앵커를 붙이세요
-
^와$로 감싸야 유효하지 않은 문자열 안에 있는 유효해 보이는 부분 문자열이 통과되는 걸 막을 수 있어요. -
카타스트로픽 백트래킹을 주의하세요
-
(a+)+같은 중첩 수량자는 특정 입력에서 정규식 엔진을 멈추게 할 수 있어요. 수량자는 단순하고 구체적으로 유지하세요. - 패턴을 작성할 때는 정규식 테스터를 활용하세요. regex101.com 은 실시간 매칭 분석, 각 토큰 설명, PCRE, JavaScript, Python 등 다양한 엔진 간 전환을 지원해요.
정규식 패턴을 추측 없이 테스트하고 검증해 보세요
올바른 도구가 있으면 입력 유효성 검사를 위한 정규식 패턴을 훨씬 빠르게 만들 수 있어요. 무료 개발자 유틸리티를 활용해 정규식 패턴으로 텍스트를 정리하고, 확인하고, 변환해 보세요.
무료 도구 사용해 보기 →
그리디 수량자(예:
.*
)는 가능한 한 많이 매칭한 뒤 백트래킹해요. 레이지 수량자(예:
.*?
)는 가능한 한 적게 매칭해요. 예를 들어
bold
라는 문자열에 대해
<.*>
패턴은 문자열 전체를 매칭하지만,
<.*?>
는
만 매칭해요. 구분자 사이의 내용을 추출할 때는 레이지 수량자를 사용하세요.
대부분은 동일하지만 차이점이 있어요. Python의
re
모듈은 PCRE 스타일 문법을 사용하고
(?P
로 명명 그룹을 지원해요. JavaScript는 플래그 문법이 약간 다르고, ES2018 이전 엔진에서는 룩비하인드를 지원하지 않아요. 여러 언어에서 공통으로 사용하려면 문자 클래스, 수량자, 앵커, 기본 그룹 같은 공통 부분집합만 사용하는 게 좋아요.
형식 유효성 검사에 정규식을 사용하는 건 프로덕션에서도 괜찮아요. 사실상 모든 웹 프레임워크에서 사용하고 있어요. 위험한 건 카타스트로픽 백트래킹을 통해 ReDoS(정규식 서비스 거부) 공격을 허용하는 잘못 작성된 패턴이에요. 중첩 수량자를 피하고, 패턴을 구체적으로 유지하며, 정규식이 실행되기 전에 항상 합리적인 입력 길이 제한을 설정하세요.
대부분의 일반적인 엔진에서 ASCII 입력에 대해서는 동일해요. 차이는 유니코드에서 나타나요. 일부 엔진(예: 유니코드 모드의 Python 3)에서
\d
는 아라비아-인도 숫자 같은 다른 문자 체계의 숫자도 매칭해요. ASCII 숫자 0-9만 정확히 원한다면
[0-9]
를 사용하는 게 명확해요. 대부분의 웹 폼 유효성 검사에서는 이 차이가 문제가 되지 않아요.
두 가지가 필요해요.
dotall 플래그
(
.
가 줄바꿈에도 매칭되도록)와 경우에 따라
멀티라인 플래그
(
^
와
$
가 전체 문자열이 아닌 각 줄에 앵커되도록)예요. JavaScript에서는
/pattern/ms
를 사용하고, Python에서는
re.DOTALL | re.MULTILINE
를 조합하세요. dotall 없이는
.
가 줄바꿈에서 멈춰 패턴이 여러 줄에 걸쳐 매칭되지 않아요.