RegExp 자바스크립트 이메일 유효성 검증

원본: RegExp 정규 표현식 객체를 사용하여 이메일 검증하기

웹 개발자로서 사용자의 다양한 입력을 검증하는 것은 매우 중요한 작업입니다. 이 입력 값은 클라이언트와 서버 사이 통신의시작점이 되기 때문에 모든 것이 올바른 값 이어야 할 필요가 있습니다.

또한 사용자 입력 값이 악의적일 수 있기 때문에 보안 요소도 고려해야합니다. 프론트 엔드에서 부터 입력 값의 유효성을 검사하여 문제를 최대한 피하는 것이 좋습니다.

이 글에서는 정규 표현식(regular expression)을 사용하여 이메일 주소를 확인하는 방법을 알아보겠습니다.

자바스크립트의 정규 표현식

정규 표현식에 익숙치 않거나 기억이 가물가물하다면 다음의 내용을 참고하기 바랍니다.

정규 표현식이란 패턴을 나타내는 메타문자의 나열입니다. 이러한 패턴은 다양한 종류가 될 수 있는데 문자와 숫자, 특수문자,  심지어 다른 언어 문자의 혼합일 수 있습니다.

정규식은 약어로 RegEx 또는 RegExp 라고 표현합니다. 이는 메타 문자, 한정자, 그룹 및 이스케이프 문자를 통해 거의 모든 패턴 을 표현할 수 있습니다.

예를 들어, 아래 표현식은 알파벳(소문자 및 대문자 모두) 또는 숫자 사이의 유효한 문자를 임의의 조합으로 가지는 일련의 문자를 표현합니다.

^([A-Za-z]|[0-9])+$

정규 표현식으로 이메일 형식 검증

먼저, 사용 가능한 모든 유효 이메일 주소에 매치되는 정규식은 존재하지 않습니다. 하지만 99.9% 매치되는 항목은 존재합니다.

이메일 또는 모든 입력의 유효성 검사를 할 때 정규식에 매치 시켜보기 전에 어느 정도 사용자 입력을 제한하여 정규 표현식의 매치율을 높일 수 있습니다.

예를 들면 이메일 주소 입력 시 gmail.com 또는 yahoo.com 을 의무적으로 사용 하도록 하고 지원되지 않는 공급자는 입력하지 못하도록 미리 방어하는 것입니다. (하지만 이런 접근법은 확장성과 최신 상태 유지에 문제를 발생 시킬 수 있습니다.)

그런데 여기서 또 다른 의문점이 제기 됩니다. “이메일 형식이란 무엇인가?” 이는 다음 섹션에서 알아 보겠지만 신기하게도 느슨한 정의로 간단히 규정할 수 있습니다.

소스코드로 들어가기 전 이메일의 타입에 대해 간단히 알아보겠습니다:

  • 일반 포멧 – (something)@(some_domain).(some_toplevel_domain)
  • 특정 호스트나 도메인 – 특정 도메인이나 탑레벨 도메인을 나타냅니다.
  • RFC 5322 – Internet Message Format으로 거의 99.9%의 이메일을 커버합니다.

RegExp 일반 이메일 검증

대부분의 개발자들은 강력한 정규식으로 이메일의 유효성을 검사하려는 수많은 시도를 하다가 결국 예전 부터 써온 일반 형식으로 마음을 바꿉니다. 이것이 좋은 방법인지 아닌지는 논쟁의 여지가 있긴 하지만 말입니다.

이메일 주소에는 뭐가 들어갈까요?

@ 기호가 있어야하고 그 앞에 있는 문자열과 그 뒤에 오는 문자열이 있어야 합니다.

또한 뒤의 문자열에는 점을 포함해야 하며 점 뒤에 2~3개의 문자가 추가로 포함됩니다. 결론적으로 대략 아래와 같은 모양이 됩니다:

(randomString)@(randomString2).(2-3 characters)

이를 좀 더 구체적으로 표현하면 다음과 같습니다:

someone@gmail.com
john.doe.1@yahoo.com
david@github.io

이러한 형식을 매치시킨다 생각하고 RegExp 정규 표현식으로 변환하면 다음과 같습니다:

let regex = new RegExp('[a-z0-9]+@[a-z]+\.[a-z]{2,3}');

let testEmails = ["notanemail.com", "workingexample@email.com", "another_working@somethingelse.org", "notworking@1.com"];

testEmails.forEach((address) => {
    console.log(regex.test(address));
});
regexp 이메일 검증 예제

위와 같이 개발자 도구를 열고 console.log() 메소드를 통해 테스트하면 workingexample@email.com과 another_working@somethingelse.org 만이 이메일 형식 검사를 통과했습니다.

하지만 이 코드로는 비정상인 이메일을 거를 수 없습니다. 이 정규식을 통과하는 잘못된 형식의 이메일이 존재합니다.

다음의 딱 봐도 스팸인 이메일 주소가 이 정규식을 통과합니다:

console.log(regex.test("aaa@aa.aa")); // true

가장 강력하고 복잡한 이메일 유효성 검사 식도 이 이메일 주소는 통과시킵니다.

RegExp 특정 이메일 검증

이메일을 검증할 때 불확실성의 정도를 낮추는 것도 도움이 됩니다. 불확실성이 적을수록 표현식에 추가할 제한이 줄어듭니다.

이렇게 하면 앞서 본 것과 동일한 일반 형식의 이메일 주소 유효성 검사가 더 정확해집니다. 더 이상 예외 사례를 다룰 필요가 없습니다.

도메인 및 최상위 도메인을 참조하는 몇 가지 일반적인 경우를 살펴보겠습니다.

1. 이메일 주소 도메인 확인

예를 들어 shinyks 라는 회사에서 일하고 있다고 가정합시다. 모든 직원에게는 @shinyks.com으로 끝나는 이메일이 있으며 각 사용자 마다 앞 쪽 문자열만 변경됩니다. 대략적인 스케치는 다음과 같습니다:

(randomString)@shinyks.com

이렇게 하면 도메인 이름 및 조직 유형과 같은 일부 변수가 고정되었으므로 작업이 훨씬 쉬워집니다.

도메인 이름은 매우 다양하기 때문에 사용자 명과 도메인 이 두 가지는 문제를 가장 많이 일으키는 변수 입니다.

따라서 특정 도메인과 관련된 이메일 주소의 유효성만을 검사하는 것은 RegExp 객체를 활용하기 쉽게 만듭니다.

let regex = new RegExp('[a-z0-9]+@shinyks.com');

let testEmails = ["notanemail.com", "workingexample@shinyks.com", "not_working@shinyks.org"];

testEmails.forEach((address) => {
    console.log(regex.test(address));
});
regexp 예제

이 접근 방식을 사용하면 필요에 따라 리터럴 문자열을 변경할 수 있습니다.

항상 그렇듯이 정규식의 첫 부분은 대문자와 대소문자를 매치하도록 변경할 수 있으며 +또는 _와 같은 특수 문자를 포함할 수 있습니다.

2. 이메일 주소 최상위 도메인 확인

이 방법은 이메일의 마지막 2~3자를 제한한다는 점을 제외하고는 앞의 예제와 거의 같습니다.

최상위 도메인에 들어갈 수 있는 문자는 .com, .org, .edu, .eu, .us 등이 될 수 있습니다.

다음의 예제에서 example@yale.edu.com 같이 최상위 도메인에 .edu가 들어가는 이메일 정규식을 만들어 보겠습니다:

let regex = new RegExp('[a-z0-9]+@[a-z]+\.edu\.[a-z]{2,3}');

let testEmails = ["notanemail.com", "someone@tistory.com", "example@yale.edu.com"];

testEmails.forEach((address) => {
    console.log(regex.test(address));
});
정규표현식 예제

yale 이메일 주소만 제외하고 나머지 이메일에는 최상위 도메인에 .edu가 포함되어 있지 않기 때문에 유효한 형식의 이메일 이어도 검사를 통과하지 못합니다.

RFC 5322 형식

이 RFC 5322는 Internet Message Format(이메일 메시지의 기본 형식)입니다.

RFC 5322는 허용되어야 하는 항목만 기술되어있고 표현식이 아닙니다. 이 규칙을 구현 하는 여러 종류의 표현식이 있는데 상당히 복잡합니다.

올바르게 구현된 RFC 5322 호환 정규식은 유효한 이메일의 99.99%를 검증할 수 있어야 합니다.

다음은 자바스크립트 RegExp 객체로 표현한 예제 입니다:

let regex = new RegExp("([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\"\(\[\]!#-[^-~ \t]|(\\[\t -~]))+\")@([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\[[\t -Z^-~]*])");

추가 엣지 케이스를 허용하는 확장버전과 그림으로 시각화한 자료는 http://emailregex.com/ 에서 확인할 수 있습니다.

위의 정규식으로 이메일 테스트를 해보면:

let regex = new RegExp("([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\"\(\[\]!#-[^-~ \t]|(\\[\t -~]))+\")@([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\[[\t -Z^-~]*])");

let testEmails = ["notanemail.com", "workingexample@stackabuse.com", "example@yale.edu.com"];

testEmails.forEach((address) => {
    console.log(regex.test(address));
});
RGC 5322 RegExp 예제

정리

정규식을 통해 모든 이메일 주소를 검증하는 완벽한 방법은 존재하지 않습니다.

하지만 잘못된 형식을 매치시키는 경우의 수는 최대한 커버해야 합니다.

그렇기 때문에 현존하는 대부분의 이메일 형식을 검증하려면 RFC 5322 호환 정규식을 사용하면 됩니다.

관련 글