
정규식 이메일 패턴 총정리 — 전화번호·URL 검증 regex 치트시트
실무에서 바로 복사해 쓰는 정규식 이메일 패턴과 한국 전화번호·URL 검증 regex를 한자리에 모았다. 완벽한 패턴이 왜 오히려 독이 되는지, 복붙 전에 알아야 할 함정까지 짚었다.
회원가입 폼에 정규식 하나 잘못 넣었다가 실제 고객 이메일을 튕겨낸 적이 있다. 스택오버플로에서 별점 높은 "완벽한 이메일 정규식"을 그대로 붙여 넣었는데, 하필 그 고객 주소에 + 기호가 들어 있었다. 멀쩡한 메일이 "형식이 올바르지 않습니다"에 막혔고, 문의가 들어오고 나서야 원인을 알았다.
정규식만큼 복붙의 유혹이 큰 영역도 없다. 돌아가는 것처럼 보이니까. 문제는 "돌아간다"와 "맞다"가 전혀 다른 말이라는 데 있다. 실무에서 반복해 쓰는 검증 패턴을 모아두고, 복사하기 전에 알아야 할 함정을 같이 적었다.

이메일: 완벽을 포기하면 오히려 안전해진다
이메일 검증에서 가장 흔한 실수가 RFC 5322를 100% 만족시키려는 시도다. 그 정규식은 길이가 400자를 넘고, 사람이 읽지도 고치지도 못한다. 어렵게 통과시켜봤자 그 주소로 메일이 실제 도착하는지는 전혀 보장하지 못한다.
실무에서는 이 정도면 충분하다.
^[^\s@]+@[^\s@]+\.[^\s@]+$
"@ 앞에 공백 아닌 글자, @, 공백 아닌 글자, 점, 공백 아닌 글자"가 전부다. user+tag@gmail.com처럼 +가 든 주소도, 서브도메인이 붙은 me@mail.co.kr도 전부 통과한다. 조금 더 조이고 싶다면 이렇게 쓴다.
^[\w.+-]+@[\w-]+\.[\w.-]+$
핵심은 형식 검증을 "거르는 장치"가 아니라 "오타를 잡는 장치"로 보는 것이다. 진짜 유효성은 인증 메일을 보내 클릭을 확인해야만 증명된다. 정규식은 gmail.con 같은 뻔한 오타를 걸러주는 1차 필터일 뿐이다.
한국 전화번호: 하이픈이 있든 없든
휴대폰 번호는 사용자가 입력하는 방식이 제각각이다. 010-1234-5678로 치는 사람, 01012345678로 붙여 쓰는 사람, 중간에 공백을 넣는 사람까지. 하이픈을 선택적으로 처리하는 게 관건이다.
^01[016789]-?\d{3,4}-?\d{4}$
01[016789]로 010·011·016·017·018·019를 모두 받고, -?로 하이픈이 있어도 없어도 통과시킨다. 가운데 자리를 \d{3,4}로 둬서 3자리 구형 번호와 4자리를 둘 다 허용한다. 요즘처럼 010만 받는다면 더 단순하게 줄일 수 있다.
^010-?\d{4}-?\d{4}$
실용적인 팁 하나 더. 입력 단계에서 정규식으로 하이픈을 강제하지 말고, 저장하기 전에 하이픈을 지워(replace(/-/g, '')) 숫자만 보관하는 편이 낫다. 화면에 보여줄 때 다시 포맷을 입히면 된다. 검증과 정규화를 분리하면 패턴도, 나중에 번호를 비교하는 코드도 훨씬 단순해진다.
URL: 어떤 문자를 받느냐보다 어떤 스킴을 막느냐
URL 검증은 "형식이 맞나"보다 "위험한 스킴을 막았나"가 훨씬 중요하다. javascript:, data:, vbscript: 같은 스킴을 그대로 링크에 넣으면 XSS의 통로가 된다. 그래서 허용 목록(http, https)만 통과시키는 방식이 안전하다.
^https?://[^\s<>"]+$
https?로 http와 https만 받고, 공백·꺾쇠·따옴표를 배제한다. 실제로 이 사이트 백엔드도 같은 발상으로 ^https?://[^\s<>"]{1,500}$처럼 길이 상한까지 붙여 쓴다. 정규식으로 1차로 거른 다음, 코드에서 new URL()로 파싱해 protocol 값을 한 번 더 확인하면 이중 방어가 된다.

복사해서 바로 쓰는 보조 패턴
| 용도 | 패턴 |
|---|---|
| 숫자만 | ^\d+$ |
| 한글만 | ^[가-힣]+$ |
| 영문+숫자 8자 이상 비밀번호 | ^(?=.[A-Za-z])(?=.\d).{8,}$ |
| 날짜(YYYY-MM-DD) | ^\d{4}-\d{2}-\d{2}$ |
| 사업자등록번호 | ^\d{3}-?\d{2}-?\d{5}$ |
| 우편번호(5자리) | ^\d{5}$ |
| IPv4(형태만) | ^(\d{1,3}\.){3}\d{1,3}$ |
비밀번호 패턴의 (?=.*[A-Za-z])는 전방 탐색(lookahead)이라고 부른다. 글자를 소비하지 않고 "뒤쪽 어딘가에 영문자가 있는지"만 몰래 확인하는 장치다. 이런 조건을 여러 개 겹치면 "영문도 있고 숫자도 있어야 한다"를 한 줄로 깔끔하게 표현할 수 있다. 다만 IPv4 패턴은 형태만 볼 뿐 999.999.999.999도 통과시킨다는 점을 기억해두자. 각 자리가 0~255인지까지 보려면 정규식을 억지로 늘리기보다 코드에서 점으로 쪼개 숫자 범위를 확인하는 편이 읽기 쉽다.
여기까지가 패턴이고, 이제 한국 개발 현장에서 자주 터지는 지점을 말하고 싶다. 프론트엔드 정규식은 보안 경계가 아니다. 브라우저에서 돌리는 검증은 사용자 편의를 위한 장치일 뿐, 개발자 도구로 몇 초면 우회된다. 서버에서 똑같은 검증을 반드시 한 번 더 걸어야 하는데, 국내 가입 폼 상당수가 이 이중 검증을 빼먹고 클라이언트 정규식만 믿는다.
덜 알려진 함정이 하나 더 있다. ReDoS, 정규식을 악용한 서비스 거부다. (a+)+$처럼 수량자가 중첩된 패턴은 특정 입력에서 매칭 시간이 지수적으로 폭증한다. 한 줄짜리 검증 정규식이 서버 스레드를 통째로 멈춰 세울 수 있다는 뜻이다. 사용자 입력을 받는 패턴일수록 중첩 반복을 피하고, 매칭에 들어가기 전에 입력 길이 상한부터 걸어두는 습관이 안전하다.
한글과 이모지가 섞인 입력이 흔해진 것도 변수다. 옛날식 [ㄱ-힣] 패턴은 조합형 한글이나 이모지 앞에서 쉽게 깨진다. 자바스크립트라면 u 플래그를 붙이고, 파이썬이라면 유니코드 모드를 전제로 패턴을 짜야 요즘 들어오는 입력을 제대로 받는다.
플래그도 짚고 가자. 대소문자를 무시하려면 i, 여러 줄을 줄 단위로 다루려면 m을 붙인다. 그리고 검증용 정규식에서 가장 많이 빠뜨리는 게 앵커다. ^와 $로 양 끝을 고정하지 않으면 "문자열 어딘가에 패턴이 있기만 해도" 통과해버린다. 그러면 abc@def.com 그리고 잡담 같은 입력이 멀쩡한 이메일로 인정되는 사고가 난다. 검증에서 양 끝 앵커는 선택이 아니라 필수다.
정규식은 통째로 외워 쓰는 도구가 아니라, 왜 이렇게 생겼는지 읽어낼 줄 아는 게 더 중요하다. 완벽한 한 줄을 찾겠다고 반나절을 태우느니, 70%만 거르는 단순한 패턴에 서버 검증과 실제 발송 확인을 더하는 쪽이 거의 항상 낫다. 여러분은 이메일 검증을 어디까지 정규식에 맡기시나요? 백엔드에서 어떻게 이중으로 거르는지도 궁금하다.
참고
댓글 0
댓글을 불러오는 중…