실제로 작동하는 이메일 유효성 검사 정규식 (2026)
이메일 유효성 검사는 작성하기 전까지는 간단해 보입니다. 간단한 형식 확인처럼 보이는 것이 실제로는 6,000자짜리 RFC 명세, 국제화된 도메인 이름, 인용 문자열, Stack Overflow에서 찾을 수 있는 거의 모든 정규식을 깨뜨리는 예외 케이스를 포함합니다. 이 가이드는 실제로 작동하는 패턴, 이들 간의 트레이드오프, 정규식으로 해결하려는 시도를 중단해야 할 시점을 다룹니다.
이메일 유효성 검사가 생각보다 어려운 이유
이메일 주소 형식은 RFC 5322와 그 이전 규격에 의해 정의됩니다. 전체 명세는 기술적으로 유효하지만 놀라울 정도로 특이하게 보이는 주소를 허용합니다:
"spaces in quotes"@example.com— 로컬 부분의 인용 문자열user+tag@example.com— 플러스 주소 지정(Gmail 필터링에 널리 사용)user@subdomain.example.co.uk— 여러 하위 도메인과 국가 코드 TLDuser@[192.168.1.1]— 도메인으로서의 IP 주소 리터럴very.unusual."@".unusual.com@example.com— 기술적으로 유효하지만 거의 사용되지 않음user@xn--nxasmq6b.com— 국제화된 도메인 이름(Punycode)
완전히 RFC 5322를 준수하는 정규식은 이 모든 것을 처리하면서 기본 형식 규칙에 실패하는 주소는 거부해야 합니다. 결과는 본질적으로 유지보수가 불가능한 1000자짜리 패턴입니다. 실제로 목표는 완전한 RFC 준수가 아니라 — 실제 주소를 차단하지 않으면서 오타를 잡는 것입니다.
간단한 정규식 (99%의 경우에 충분)
대부분의 애플리케이션에서 이 최소한의 패턴이 올바른 선택입니다. @ 앞에 무언가가 있고, 도메인이 있고, TLD가 있는 필수 구조를 검증합니다 — 합법적인 주소에 대한 거짓 음성 없이:
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
분석:
^— 문자열 시작[^\s@]+— 공백이나@가 아닌 하나 이상의 문자 (로컬 부분)@— 리터럴 @[^\s@]+— 공백이나@가 아닌 하나 이상의 문자 (도메인)\.— 리터럴 점[^\s@]+— 하나 이상의 문자 (TLD)$— 문자열 끝
올바르게 수용/거부하는 항목:
유효 (수용):
무효 (거부):
주요 약점은 user@@example.com과 a@b.c(기술적으로 유효하지만 의심스러운)를 수용한다는 것입니다. 대부분의 가입 양식과 API 입력에서 이는 수용 가능한 트레이드오프입니다.
포괄적인 정규식 (RFC 5322 준수)
더 엄격한 유효성 검사가 필요한 경우 — 예를 들어, 무효한 주소가 반송을 유발하고 발신자 평판을 손상시키는 이메일 발송 시스템에서 — 더 철저한 패턴이 필요합니다. 이것은 널리 인용되는 RFC 5322 파생 패턴입니다:
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
이 패턴은 더 길지만 훨씬 더 많은 케이스를 처리합니다. 주요 부분 분석:
- 로컬 부분 (@ 앞): 선택적 점으로 구분된 세그먼트가 있는 허용 문자 시퀀스
[^<>()...]+(\.[^<>()...]+)*, 또는"john doe"@example.com같은 주소를 위한 인용 문자열".+". - 도메인 (@ 뒤): 대괄호 안의 IP 주소 리터럴
\[[0-9]{1,3}...\], 또는 최소 2자 TLD를 가진 표준 호스트 이름([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}. - TLD 최소 2자: 1자 TLD를 거부하면서
.museum이나.technology같은 긴 TLD는 수용합니다.
이 패턴이 올바르게 처리하는 추가 케이스:
여전히 올바르게 거부됨:
JavaScript에서의 이메일 유효성 검사
두 패턴을 사용한 실용적인, 복사 가능한 JavaScript 구현입니다:
간단한 유효성 검사 함수
/** * 대부분의 사용 사례를 위한 이메일 형식 검증. * 빠르고, 가독성 좋으며, 거짓 음성이 매우 적습니다. */ function isValidEmail(email) { const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return pattern.test(email.trim()); } // 사용법 isValidEmail("user@example.com"); // true isValidEmail("not-an-email"); // false isValidEmail("user+tag@gmail.com"); // true
상세 피드백을 제공하는 엄격한 유효성 검사
function validateEmail(email) { const trimmed = email.trim(); if (!trimmed) { return { valid: false, error: "Email is required" }; } if (trimmed.length > 254) { return { valid: false, error: "Email address too long (max 254 characters)" }; } if (!trimmed.includes("@")) { return { valid: false, error: "Missing @ symbol" }; } const [local, ...domainParts] = trimmed.split("@"); const domain = domainParts.join("@"); if (!local || local.length > 64) { return { valid: false, error: "Invalid local part (before @)" }; } if (!domain || !domain.includes(".")) { return { valid: false, error: "Invalid domain (missing TLD)" }; } const rfcPattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; if (!rfcPattern.test(trimmed)) { return { valid: false, error: "Invalid email format" }; } return { valid: true, email: trimmed.toLowerCase() }; } // 사용법 const result = validateEmail(" User@Example.COM "); // { valid: true, email: "user@example.com" }
HTML5 이메일 유효성 검사
JavaScript에 의존하기 전에 브라우저가 무료로 제공하는 것을 고려하십시오. input type="email" 요소에는 양식 제출 시 실행되는 내장 유효성 검사가 있습니다:
<!-- 기본 내장 유효성 검사 --> <input type="email" name="email" required> <!-- 더 엄격한 유효성 검사를 위한 사용자 정의 패턴 --> <input type="email" name="email" required pattern="[^\s@]+@[^\s@]+\.[^\s@]+" title="유효한 이메일 주소를 입력해 주십시오" >
브라우저의 type="email" 내장 알고리즘은 HTML5 명세(RFC 5322의 단순화된 하위 집합)를 따릅니다. 명백한 비이메일을 거부하고, 네이티브 유효성 검사 툴팁을 표시하며, JavaScript 없이 작동합니다. pattern 속성을 사용하면 추가 제약 조건을 위에 계층화할 수 있습니다.
알아야 할 제한 사항: 내장 유효성 검사는 양식 제출 시에만 작동하며 사용자 입력 시 실시간으로는 작동하지 않습니다. 실시간 피드백을 위해서는 JavaScript가 필요합니다. 또한 :invalid CSS 의사 클래스를 사용하면 무효한 입력에 스타일을 적용할 수 있지만, :not(:placeholder-shown)도 함께 사용하지 않으면 손대지 않은 빈 필드에서도 작동합니다:
/* 사용자 상호작용 후에만 빨간 테두리 표시 */ input[type="email"]:not(:placeholder-shown):invalid { border-color: #ef4444; outline-color: #ef4444; } input[type="email"]:not(:placeholder-shown):valid { border-color: #34d399; }
일반적인 예외 케이스
대부분의 이메일 유효성 검사 구현에서 문제가 되는 케이스들입니다:
플러스 주소 지정(하위 주소 지정)
user+tag@gmail.com은 유효하며 널리 사용됩니다. Gmail, Outlook 및 대부분의 최신 이메일 제공업체가 필터링을 위해 지원합니다. 정규식은 로컬 부분에서 + 문자를 거부해서는 안 됩니다. 과도하게 제한적인 많은 패턴이 이 부분에서 실패합니다.
하위 도메인
first.last@mail.company.co.uk는 로컬 부분과 도메인 모두에 점이 있는 유효한 이메일입니다. 도메인에 세 개의 수준이 있습니다. 좋은 패턴은 도메인에서 여러 점으로 구분된 레이블을 처리할 수 있어야 합니다.
긴 TLD
TLD는 더 이상 2자 또는 3자로 제한되지 않습니다. .museum, .photography, .technology 및 수백 개의 다른 gTLD가 유효합니다. TLD 최대 길이를 4자 또는 6자로 제한하는 정규식은 거짓 음성을 생성합니다. 최소값(2자)만 적용하고 최대값은 적용하지 마십시오.
국제화된 도메인 (IDN)
도메인 이름에는 Punycode로 인코딩된 비ASCII 문자가 포함될 수 있습니다. user@munchen.de는 유효하며 — 실제 도메인은 ASCII 형식으로 xn--mnchen-3ya.de입니다. 대부분의 정규식 패턴은 Punycode를 직접 유효성 검사할 수 없습니다. IDN 지원이 필요한 경우 전용 이메일 유효성 검사 라이브러리를 사용하십시오.
IP 주소 리터럴
user@[192.168.1.1]은 RFC 5321에 따라 기술적으로 유효하지만 실제로는 거의 사용되지 않습니다. SMTP 구현을 구축하는 것이 아니라면 이 케이스를 안전하게 무시할 수 있습니다.
과도한 유효성 검사를 피하십시오
이 글에서 가장 중요한 실용적 조언입니다: 클라이언트 측 이메일 유효성 검사의 목적은 오타를 잡는 것이지 배달 가능성을 확인하는 것이 아닙니다.
이메일 주소가 실제로 존재하는지 또는 그 받은 편지함이 메일을 수신하는지는 어떤 정규식으로도 알 수 없습니다. 받은 편지함에 실제로 배달해 보아야만 확인할 수 있습니다. 과도한 유효성 검사(정규식이 잘못 무효로 판단하는 주소를 거부)는 실제 사용자를 잃게 합니다. 부족한 유효성 검사(잘못된 형식의 주소를 수용)는 반송 비용이 발생합니다.
올바른 접근 방식은 이중 계층 전략입니다:
- 간단한 정규식으로 명백한 형식 오류를 잡습니다(
@누락, TLD 누락, 공백). - 주소가 실제이고 사용자가 그것을 제어하는지 확인하기 위해 확인 또는 검증 이메일을 보냅니다.
확인 이메일이 유일하게 신뢰할 수 있는 관문입니다. 그 외의 모든 것은 휴리스틱입니다. user@localhost나 a@b.io를 거부할지 논의하고 있다면 한 발 물러나십시오 — 이메일을 보내고 SMTP 계층에서 처리하도록 하십시오.
인기 있는 이메일 정규식 패턴 비교
가장 흔히 사용되는 패턴의 실용적 비교로, 트레이드오프를 포함합니다:
| 패턴 | 유형 | 장점 | 단점 | 커버리지 |
|---|---|---|---|---|
| /^[^\s@]+@[^\s@]+\.[^\s@]+$/ | 간단 | 가독성 좋음, 거짓 음성 최소, 빠름 | a@@b.c 수용, 매우 관대함 |
~95% |
| /^[\w.+-]+@[\w-]+\.[\w.]{2,}$/ | 일반 | 짧음, 대부분의 실제 이메일 처리 | 로컬 부분의 유효한 특수 문자 거부 | ~92% |
| /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ | 균형 | 널리 사용됨, 엄격도와 커버리지의 좋은 균형 | 일부 유효한 인용 문자열 로컬 거부 | ~97% |
| /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}...)/ | RFC 5322 | 인용 문자열, IP 리터럴, 긴 TLD 처리 | 길고, 읽기 어렵고, 여전히 100% RFC 준수는 아님 | ~99% |
"균형" 패턴 상세
위 표의 세 번째 패턴은 간단한 패턴보다 더 많은 것을 원하지만 전체 RFC 5322 복잡성은 필요하지 않을 때 프로덕션 사용에 가장 실용적인 선택입니다:
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
분석:
[a-zA-Z0-9._%+-]+— 로컬 부분: 영숫자와.,_,%,+,-@— 필수 @[a-zA-Z0-9.-]+— 도메인: 영숫자, 하이픈, 점(하위 도메인 처리)\.— TLD 앞 점 구분자[a-zA-Z]{2,}— TLD: 최소 2글자, 최대 없음(긴 TLD 처리)
직접 만들지 않고 라이브러리 사용
이메일 유효성이 중요한 애플리케이션(트랜잭션 이메일 시스템, B2B SaaS 가입 흐름)의 경우 직접 정규식을 작성하기보다 전용 유효성 검사 라이브러리를 고려하십시오:
// Node.js — validator.js (가장 인기 있음) import validator from 'validator'; validator.isEmail('user@example.com'); // true // Node.js — email-validator (경량) import { validate } from 'email-validator'; validate('user@example.com'); // true // Python — email-validator (RFC 준수) # pip install email-validator from email_validator import validate_email, EmailNotValidError try: info = validate_email("user@example.com") except EmailNotValidError as e: print(str(e))
이메일 정규식 테스트하기
모든 정규식을 이해하는 가장 좋은 방법은 포괄적인 테스트 입력 세트에 대해 실행하는 것입니다 — 수용할 것으로 예상되는 유효한 주소와 거부해야 할 무효한 주소 모두. Regex 테스터를 사용하면 위의 패턴을 붙여넣고, 테스트 세트를 구축하고, 실시간으로 강조 표시된 일치 결과를 확인할 수 있습니다.
이메일 정규식을 실시간으로 테스트하기
이메일 정규식 패턴을 붙여넣고 직접 입력한 데이터로 테스트하십시오. 플래그, 캡처 그룹, 여러 줄 매칭을 지원합니다. 브라우저에서 완전히 실행됩니다.
Regex 테스터 열기 →관련 개발자 도구
- Regex 테스터 — 실시간 강조 표시로 정규식 테스트 및 디버깅
- ENV 인스펙터 —
.env파일 검증 및 노출된 시크릿 감지 - URL 인코더 / 디코더 — URL 구성 요소 및 쿼리 문자열 인코딩 및 디코딩
- 모든 무료 개발자 도구 보기