이메일 검증을 위한 정규표현식: 실제로 작동하는 패턴
이메일 검증은 가장 일반적인 정규표현식 사용 사례 중 하나이며 — 가장 오해받는 것 중 하나이기도 합니다. 이메일 주소에 대한 RFC 5321 사양은 놀라울 정도로 복잡하며, 이를 완전히 검증할 수 있는 단일 정규표현식은 없습니다. 하지만 실제 이메일 주소의 99.9%를 커버하는 실용적인 패턴은 달성 가능하고 유용합니다.
간단한 패턴 (충분히 좋음)
대부분의 애플리케이션에서 이 패턴이 잘 작동합니다:
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
매칭 대상: 하나 이상의 허용 문자, @, 최소 하나의 점과 2자 이상의 TLD가 있는 도메인.
JavaScript:
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
emailRegex.test('user@example.com'); // true
emailRegex.test('user@.com'); // false
정규표현식 테스터에서 이 패턴을 테스트하세요.
완벽한 정규표현식 검증이 불가능한 이유
이메일 사양 (RFC 5321/5322)은 유효하지만 실질적으로 사용되지 않는 구조를 허용합니다:
"john..doe"@example.com // 연속 점이 있는 인용 로컬 부분
user@[192.168.1.1] // 도메인으로서의 IP 주소
"very.(),:;<>[]\".VERY."very@\ "very".unusual"@strange.example.com
모든 RFC 유효 주소를 처리하는 정규표현식은 수천 문자 길이이며 그래도 모든 엣지 케이스를 커버하지 못할 수 있습니다. 실용적 접근: 기본 포맷 검증에 간단한 정규표현식을 사용하고, 확인 이메일을 보내 실제 존재를 검증하세요.
패턴 분석
로컬 부분 (@ 앞)
[a-zA-Z0-9._%+-]+
- 문자 (대소문자)
- 숫자
- 점, 밑줄, 퍼센트, 플러스, 하이픈
- 하나 이상의 문자
일반적인 실수:
- 시작이나 끝에 점 허용 (기술적으로 무효)
- 연속 점 허용 (인용 없이는 기술적으로 무효)
- 너무 제한적 (Gmail 별칭에 사용되는
+같은 유효 문자 차단)
도메인 부분 (@ 뒤)
[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
- 영숫자 문자, 점, 하이픈
- 최소 하나의 점
- 2자 이상의 알파벳 TLD
일반적인 실수:
- TLD 길이를 3-4자로 제한 (
.museum,.photography존재) - 도메인 이름에서 하이픈 불허
- 서브도메인 미처리 (
user@mail.example.co.uk)
더 나은 패턴
HTML5 표준
HTML5 사양은 <input type="email">에 대해 이 패턴을 정의합니다:
^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$
로컬 부분에서 더 관대하고 도메인 라벨 길이에 더 엄격합니다.
실용 권장 패턴
^[^\s@]+@[^\s@]+\.[^\s@]{2,}$
이 최소한의 패턴은 단순히: 공백 없음, @ 하나, @ 뒤에 최소 하나의 점, 2자 이상의 TLD를 보장합니다. 의도적으로 관대합니다 — 비정상적이지만 유효한 주소를 거부하지 않으면서 명백히 잘못된 입력을 잡습니다.
언어별 구현
JavaScript
function isValidEmail(email) {
// 기본 정규표현식 확인
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
if (!regex.test(email)) return false;
// 추가 확인
if (email.length > 254) return false; // RFC 5321 제한
const [local] = email.split('@');
if (local.length > 64) return false; // RFC 5321 제한
return true;
}
Python
import re
def is_valid_email(email: str) -> bool:
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(pattern, email):
return False
if len(email) > 254:
return False
local, domain = email.rsplit('@', 1)
if len(local) > 64:
return False
return True
정규표현식을 넘어: 올바른 이메일 검증
정규표현식은 첫 번째 방어선일 뿐입니다. 완전한 검증 전략:
- 포맷 확인 (정규표현식): 명백히 무효한 포맷 거부
- 길이 확인: 로컬 부분 ≤ 64자, 전체 ≤ 254자
- DNS 확인: 도메인에 MX 레코드가 있는지 검증
- 일회용 이메일 감지: 필요시 임시 이메일 서비스 차단
- 확인 이메일: 이메일을 진정으로 검증하는 유일한 방법 — 메시지를 보내고 사용자가 수신하는지 확인
// DNS MX 레코드 확인 (Node.js)
const dns = require('dns');
const [, domain] = email.split('@');
dns.resolveMx(domain, (err, addresses) => {
if (err || !addresses.length) {
console.log('도메인에 이메일 서버가 없습니다');
}
});
피해야 할 일반적인 실수
- 과도하게 제한적인 패턴:
user+tag@gmail.com(플러스 주소) 또는user@subdomain.example.com차단 - 대소문자 구분: RFC에 따르면 이메일 로컬 부분은 대소문자를 구분할 수 있지만, 실무에서는 대소문자를 무시하세요
- 공백 제거: 검증 전에 항상 공백을 제거하세요 —
" user@example.com "은 통과해야 합니다 - 국제화 도메인:
.münchen과用户@例え.jp은 국제화 이메일에서 유효합니다
더 많은 정규표현식 패턴은 정규표현식 치트시트를 참조하세요.
FAQ
이메일 검증을 클라이언트에서 할까요, 서버에서 할까요?
둘 다 하세요. 클라이언트 측 검증은 즉각적인 피드백을 제공합니다 (더 나은 UX). 서버 측 검증은 보안을 위해 필수입니다 (클라이언트 확인은 우회 가능). 클라이언트 측 검증만 신뢰하지 마세요.
왜 일부 웹사이트가 유효한 이메일 주소를 거부하나요?
과도하게 제한적인 정규표현식 패턴이 일반적인 원인입니다. + (Gmail 별칭), 긴 TLD (.technology), 또는 서브도메인이 있는 주소는 잘못된 검증에 의해 종종 거부됩니다. 웹사이트를 관리한다면 관대한 정규표현식을 사용하고 확인 이메일로 검증하세요.
관련 리소스
- 정규표현식 테스터 — 이메일 검증 패턴을 실시간으로 테스트
- 정규표현식 치트시트 — 완전한 정규표현식 패턴 참조
- JSON Schema 검증 — format: email로 JSON의 이메일 필드 검증