개발자 도구8 분 읽기|MJ민재

이메일 검증 정규식, 매번 스택오버플로우에서 복붙하는 당신에게 — 정규표현식 실전 가이드

복붙만 하다 결국 버그 터진 경험 있으신가요? 메타문자, 수량자, 그룹 개념부터 실전 패턴 10개, 언어별 기능 비교표, 서버 터뜨리는 역추적 함정까지 — 한 번 읽으면 직접 쓸 수 있게 정리했어요.

몇 달 전에 회원가입 폼 이메일 검증 로직에서 버그가 터졌어요. 5년 전에 스택오버플로우에서 복붙해서 쓰던 패턴인데, user+tag@gmail.com 같은 주소가 죄다 튕겨나가고 있었던 거예요. '+' 기호를 허용 안 하는 패턴이었는데 그걸 5년 동안 몰랐던 거죠. 그때 처음으로 정규식 문법을 제대로 뜯어봤어요. 생각보다 어렵지 않더라고요. 핵심 기호 열 개 정도만 알면 웬만한 건 직접 쓸 수 있어요.

이 글은 정규식 문법을 처음부터 조각조각 설명하고, 실무에서 바로 쓸 수 있는 패턴 10개와 언어별 차이점, 그리고 서버를 진짜로 멈추게 만들 수 있는 역추적 함정까지 다뤄요. 복붙 말고 직접 쓰고 싶은 분을 위한 가이드예요.

이 글에서 알 수 있는 것

  • 메타문자, 수량자, 그룹 타입별 예시와 함께 — 외워지는 방식으로 정리했어요
  • 이메일, URL, 비밀번호, 날짜, IP 주소 등 실전 패턴 10개를 바로 쓸 수 있게 제공해요
  • 언어별 정규식 기능 비교표와 서버 다운을 부르는 역추적 함정 피하는 법도 있어요

메타문자 — 이 7개만 알면 절반은 끝나요

메타문자는 정규식에서 특별한 의미를 가진 문자예요. 이것만 외우면 나머지는 조합이에요.

  • . (점) — 줄바꿈 제외 모든 문자 하나에 매칭돼요. c.t는 'cat', 'cut', 'c9t'에 매칭되는데 'coat'는 아니에요. 두 글자니까요.
  • \d — 숫자 0~9에 매칭돼요. [0-9]랑 동일해요. \D는 반대로 숫자가 아닌 것에 매칭돼요.
  • \w — 영문자, 숫자, 밑줄에 매칭돼요. [a-zA-Z0-9_]와 같아요. \W는 그 반대예요.
  • \s — 공백 문자(스페이스, 탭, 줄바꿈)에 매칭돼요. \S는 공백이 아닌 것에 매칭돼요.
  • \b — 단어 경계예요. 문자가 아니라 위치를 나타내는 거예요. \bcat\b는 'cat'에는 매칭되지만 'concatenate'에는 매칭 안 돼요.
  • ^ — 문자열의 시작에 앵커를 잡아요. m 플래그를 쓰면 각 줄의 시작에도 매칭돼요.
  • $ — 문자열의 끝에 앵커를 잡아요. ^[0-9]+$처럼 쓰면 숫자만으로 구성된 전체 문자열을 의미해요.
💡

예약 문자는 백슬래시로 이스케이프하세요

. * + ? ^ $ { } [ ] | ( ) 이 문자들은 모두 예약어예요. 도메인 이름의 점처럼 실제 점을 매칭하려면 \. 라고 써야 해요. 그냥 .만 쓰면 아무 문자에나 매칭돼요.

수량자 — 몇 번 반복할지 정하는 것들

수량자는 앞에 오는 요소를 몇 번 반복할지 지정해요. 정규식의 핵심 기능인 동시에 버그의 주요 원인이기도 해요.

  • * — 0회 이상. a*는 빈 문자열, 'a', 'aaa'에 모두 매칭돼요. 0회도 허용하니까 항상 성공해요.
  • + — 1회 이상. a+는 'a', 'aaa'에 매칭되지만 빈 문자열에는 매칭 안 돼요. 최소 1번은 있어야 해요.
  • ? — 0회 또는 1회. 앞의 요소를 선택적으로 만들어요. colou?r는 'color'랑 'colour' 모두에 매칭돼요.
  • {n} — 정확히 n번. \d{4}는 정확히 4자리 숫자에 매칭돼요. 연도나 PIN 검증에 딱이에요.
  • {n,m} — n번 이상 m번 이하. \d{2,4}는 2~4자리 숫자에 매칭돼요.
  • {n,} — n번 이상. \d{3,}는 세 자리 이상 숫자라면 무조건 매칭돼요.

수량자는 기본적으로 욕심쟁이예요. 가능한 한 많이 매칭하려 해요. ?를 붙이면 게으른 수량자가 되는데, 가능한 한 적게 매칭하려고 해요. HTML이나 중첩된 구조를 파싱할 때 이 차이가 엄청나게 중요해요.

그룹, 전방탐색, 교대

  • (abc) — 캡처 그룹. 패턴을 감싸서 매칭된 텍스트를 저장해요. 교체 문자열에서 $1이나 \1로 참조할 수 있어요.
  • (?:abc) — 비캡처 그룹. 캡처 없이 그룹화만 해요. 저장이 필요 없을 때 쓰면 더 빠르고 메모리도 덜 써요.
  • (?<name>abc) — 명명된 캡처 그룹. 이름으로 접근할 수 있어요. JavaScript에서는 match.groups.name으로 쓸 수 있어요.
  • a|b — 교대. 논리적인 OR이에요. 'a' 또는 'b'에 매칭돼요. (cat|dog)처럼 그룹으로 감싸면 명확해져요.
  • (?=abc) — 긍정 전방탐색. 뒤에 'abc'가 오는지 확인하는데, 실제로 그 부분은 소비하지 않아요. 비밀번호 검증에 많이 써요.
  • (?<=abc) — 긍정 후방탐색. 앞에 'abc'가 있는지 확인해요. 일부 환경에서 지원 안 되는 경우가 있어요.

지금 이 도구를 사용해 보세요:

정규식 테스터에서 직접 테스트해보기

바로 쓸 수 있는 실전 패턴 10개

지금 당장 프로덕션에 넣을 수 있는 패턴들이에요. 패턴뿐 아니라 왜 이렇게 짰는지도 같이 설명할게요.

  • 이메일: ^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$ — 실제 주소의 99% 이상을 커버해요. + 기호도 허용하기 때문에 Gmail 별칭도 통과시켜요.
  • URL: https?:\/\/(www\.)?[a-zA-Z0-9@:%._+~#=\-]{1,256}\.[a-zA-Z0-9()]{1,6}\b([a-zA-Z0-9()@:%_+.~#?&\/=]*) — 경로와 쿼리 스트링 포함 대부분의 웹 URL에 매칭돼요.
  • 한국 휴대폰 번호: 010[\-\s]?\d{4}[\-\s]?\d{4} — 010-XXXX-XXXX, 010 XXXX XXXX, 01012345678 모두 통과해요.
  • 국제 전화번호: \+?[1-9]\d{0,2}[\s\-.]?\(?\d{2,4}\)?[\s\-.]?\d{3,4}[\s\-.]?\d{3,4} — 국가 코드 포함 다양한 형식을 허용해요.
  • IPv4 주소: ^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$ — 각 옥텟을 0~255로 제한해요. \d{1,3}만 쓰면 999.999.999.999도 통과시키거든요.
  • 날짜(YYYY-MM-DD): ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ — 월(01~12)과 일(01~31) 범위를 검증해요.
  • 강력한 비밀번호: ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$ — 소문자, 대문자, 숫자, 특수문자 모두 필수, 최소 8자예요.
  • HEX 색상 코드: ^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$ — #RRGGBB와 #RGB 약식 모두 매칭돼요. 여기서는 #가 필수예요.
  • URL 슬러그: ^[a-z0-9]+(?:-[a-z0-9]+)*$ — 'my-blog-post' 같은 소문자-하이픈 슬러그에 매칭돼요. 앞뒤 하이픈은 거부해요.
  • 한글만: ^[가-힣\s]+$ — 한글과 공백만 포함된 문자열에 매칭돼요. 이름 검증에 쓸 수 있어요.

언어별 정규식 기능 비교

기능JavaScriptPythonJavaGo
명명된 그룹지원 (ES2018+)지원지원지원
후방탐색지원 (V8 6.3+)지원지원미지원
소유적 수량자미지원미지원지원미지원
인라인 플래그 (?i)미지원 (플래그로 사용)지원지원지원
유니코드 속성 \p{L}지원 (ES2018+, /u 플래그)지원지원지원
재귀 패턴미지원미지원미지원미지원
비역추적(원자)미지원미지원미지원지원 (RE2)
⚠️

역추적 폭발이 서버를 멈출 수 있어요

(a+)+나 (\w+\s*)+ 같은 패턴에 매칭되지 않는 긴 문자열을 넣으면, 정규식 엔진이 조합을 지수적으로 재시도하다가 멈춰버릴 수 있어요. 이게 ReDoS예요. 2016년에 Cloudflare가 이 문제로 서비스 장애를 겪었어요. 해결책: 겹치는 패턴에 중첩 수량자 쓰지 말 것, .* 대신 [^,]* 같이 구체적인 문자 클래스 쓸 것, 'aaaaaaaaaaaab'처럼 악의적인 입력으로 테스트해볼 것.

성능 팁 — 실무에서 진짜 차이 나는 것들

  • 한 번만 컴파일하고 재사용하기: Python에서는 re.compile(), Java에서는 Pattern.compile()을 루프 바깥에서 한 번만 실행하세요. 루프 안에서 매번 컴파일하면 성능이 크게 떨어져요.
  • 앵커를 쓸 수 있으면 쓰기: ^pattern$은 전체 문자열이 안 맞으면 일찍 포기해서 빠르게 실패해요. 전체 문자열을 검증할 때는 항상 앵커를 붙이세요.
  • .* 대신 구체적인 문자 클래스 쓰기: 다음 부분까지 건너뛸 때 .*를 쓰지 말고 [^,]* 나 [^\n]*처럼 구체적으로 쓰세요. 역추적이 훨씬 줄어들어요.
  • 비캡처 그룹 사용하기: 캡처된 값이 필요 없으면 (abc) 대신 (?:abc)를 쓰세요. 메모리 할당이 줄고 약간 더 빨라요.
  • 실제 데이터로 테스트하기: 정규식 성능은 입력에 따라 크게 달라져요. 매칭되는 입력뿐 아니라 매칭되지 않는 긴 문자열로도 반드시 테스트해보세요.

자주 묻는 질문

*와 + 중에 뭘 써야 할지 모르겠어요

0번도 허용해야 하면 *, 무조건 1번 이상 있어야 하면 +를 쓰면 돼요. 예를 들어 ab*c는 'ac'에도 매칭되지만 ab+c는 'ac'에 매칭 안 돼요. 확신이 없으면 이 자리에 아무것도 없어도 유효한가를 기준으로 판단하면 돼요.

대소문자 구분 없이 매칭하려면 어떻게 해요?

i 플래그를 붙이면 돼요. JavaScript: /pattern/i 또는 new RegExp('pattern', 'i'). Python: re.compile('pattern', re.IGNORECASE). Go: 패턴 앞에 (?i) 추가. 유니코드 문자 대소문자 처리는 엔진마다 다를 수 있으니 주의하세요.

regex101에서 되는데 코드에서 안 돼요. 왜 그럴까요?

세 가지를 먼저 확인해보세요. 첫째, 언어마다 정규식 엔진이 달라서 지원하는 기능이 다를 수 있어요. Go의 RE2는 후방탐색을 지원 안 해요. 둘째, 문자열 안에서 백슬래시를 이스케이프했는지 확인하세요. 셋째, regex101의 플래그 설정을 확인하세요. 전역이나 멀티라인 플래그가 켜져 있으면 결과가 달라져요.

점(.)을 실제 점 문자로 매칭하려면 어떻게 해요?

\. 로 이스케이프하면 돼요. 그냥 .은 줄바꿈을 제외한 모든 문자에 매칭되거든요. 괄호, 별표 같은 다른 예약 문자도 백슬래시로 이스케이프해야 해요.

역추적 폭발을 어떻게 피할 수 있나요?

겹치는 패턴에 수량자를 중첩하면 역추적 폭발이 생겨요. (a+)+나 (\w+\s*)+ 같은 패턴이 대표적이에요. 세 가지로 피할 수 있어요: 겹치는 패턴에 수량자 중첩하지 않기, 지원되는 환경에서는 원자 그룹이나 소유적 수량자 사용하기, 넓은 와일드카드 대신 구체적인 문자 클래스 쓰기.

정규식으로 HTML이나 JSON을 파싱해도 되나요?

짧게 말하면 안 돼요. HTML과 JSON은 재귀적이고 중첩된 구조인데 정규식은 재귀를 못 다뤄요. 특정 속성 값이나 특정 태그 내용 정도는 뽑을 수 있지만, 완전한 HTML 파싱은 반드시 엣지 케이스에서 터져요. 브라우저에서는 DOMParser, Python에서는 BeautifulSoup, JSON은 언어 내장 파서를 쓰세요.

언어마다 정규식 성능이 다른가요?

많이 달라요. Go의 RE2 엔진은 역참조 같은 일부 기능을 포기하는 대신 선형 시간을 보장해서 역추적 폭발이 없어요. PHP나 Perl이 쓰는 PCRE와 JavaScript의 V8은 기능이 더 많지만 지수적으로 역추적할 수 있어요. 로그 대량 처리처럼 고처리량 환경이면 엔진 선택과 패턴 설계 둘 다 중요해요.

정규식 테스터

패턴을 직접 입력하고 내 텍스트에 바로 테스트해보세요. 매칭 하이라이트와 그룹 추출을 지원해요.

정규식 테스터 열기

이 글에서 다룬 도구 바로 사용하기

MJ

민재

개발자 겸 테크 라이터. 개발 도구와 파일 변환 기술을 깊이 있게 다룹니다.

이 글이 도움이 되셨나요? 새 가이드 알림 받기

스팸 없이, 새 소식만 보내드립니다. 언제든 취소 가능. · 구독 시 개인정보처리방침에 동의합니다.

이런 글도 좋아하실 수 있어요

84+

제공 도구

100+

블로그 글

English & 한국어

지원 언어

이 페이지를 즐겨찾기하세요! 매주 새로운 무료 도구가 추가됩니다.