나는 이메일 검증 라이브러리를 만들고 npm에 배포했습니다. 여기 내가 배운 모든 것을 정리합니다.

발행: (2025년 12월 26일 오후 02:57 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

이야기

지난 몇 주 동안 이메일 검증 라이브러리를 처음부터 직접 만들고 npm에 배포했습니다. 세상이 또 다른 npm 패키지를 절실히 필요로 해서가 아니라, 이메일 검증이 실제로 어떻게 동작하는지 깊이 이해하고 싶었기 때문입니다. 기존 솔루션들이 만족스럽지 못했기에 직접 만들기로 결심했습니다.

이 글에서 저는 다음을 공유합니다:

  • 제가 만든 이유
  • 배운 점
  • 대안보다 3배 빠른 성능을 구현하게 만든 기술적 결정들
  • 같은 접근 방식을 활용해 여러분도 npm 패키지를 만들 수 있는 방법

그럼 바로 들어가 보겠습니다…

What I’m Going to Cover

  • 왜 이 프로젝트를 시작했는가
  • 대부분의 개발자가 모르는 이메일 검증의 5계층
  • 경쟁 제품보다 3배 빠르게 만든 방법
  • 기술 스택 선택 (그리고 그 이유)
  • 첫 npm 패키지 배포 방법
  • 다음에 다르게 할 점

왜 또 다른 이메일 검증기를 만들었을까?

프로젝트에서 deep-email-validator를 사용하고 있었어요. 동작은 했지만 몇 가지 문제가 눈에 띄었습니다:

  • TypeScript 타입이 오래됐음 (여전히 3.8 사용)
  • 대량 검증 지원이 없음
  • 캐시가 없어 같은 이메일을 두 번 검증하면 모든 작업을 다시 수행함
  • 오류 메시지가 “Invalid email” 같은 일반 문자열임
  • 정규식 검증이 RFC 5322를 준수하지 않음

나는 생각했죠, “뭐, 더 나은 걸 만드는 건 그렇게 어려울까?”
유명한 마지막 말이죠. 실제로 이메일 검증은 대부분이 생각하는 것보다 훨씬 복잡합니다—바로 그 때문에 이 프로젝트를 만들 가치가 있었던 겁니다.

Source:

이메일 검증의 5단계

대부분의 개발자는 이메일 검증을 단순히 정규식 검사라고 생각합니다. 이는 올바른 검증의 20 % 정도에 불과합니다. 실제 서비스에 적합한 검증기는 다음을 확인해야 합니다:

레이어 1 – 정규식 (형식 검증)

이메일이 이메일처럼 보이나요? RFC 5322를 따라야 합니다.
기술적으로 유효한 주소 예시:

  • "john doe"@example.com (인용 문자열)
  • user+tag@example.com (플러스 주소)
  • user@[192.168.1.1] (IP‑주소 도메인)

많은 Stack‑Overflow 정규식은 이 중 절반을 거부합니다.

레이어 2 – 오타 감지

user@gmial.com은 구문적으로는 유효하지만 Gmailgmial.com에 존재하지 않습니다.
이 레이어는 흔한 오타를 잡아내고 “gmail.com을(를) 의미하셨나요?”와 같이 교정을 제안합니다.

레이어 3 – 일회용 이메일 감지

Mailinator, Guerrillamail, 10 Minute Mail…
일회용 이메일 서비스는 40 000개가 넘습니다. 이를 차단하지 않으면 데이터베이스가 가짜 사용자로 가득 찹니다.

레이어 4 – MX 레코드 검증

도메인이 정상처럼 보여도 메일 서버가 있나요?
MX 레코드에 대한 DNS 조회를 통해 해당 도메인이 이메일을 받을 수 있는지 확인합니다. MX가 없으면 → 무효.

레이어 5 – SMTP 검증

최종 보스: 메일 서버에 연결해 “이 메일함이 존재하나요?”를 물어봅니다.
네트워크 호출 때문에 느리고, 때때로 서버가 검증을 차단해 신뢰성이 떨어질 수 있지만, 확실한 결과가 필요할 때는 가장 좋은 방법입니다.

3배 빠르게 만들기

원래 deep-email-validator는 이메일을 순차적으로 검증하고, 캐시가 없으며 DNS와 SMTP 검사를 위해 매번 네트워크에 접근합니다. 저는 5초 이내에 100개의 이메일을 처리하고 싶었습니다.

솔루션 1 – 동시 처리

동시성을 설정하여 여러 이메일을 병렬로 검증합니다:

const result = await validateBulk(emails, {
  concurrency: 10,
  onProgress: (completed, total) => {
    console.log(`Progress: ${completed}/${total}`);
  },
});

10개의 동시 검증 ≈ 10배 빠른 대량 처리.

솔루션 2 – 내장 속도 제한

메일 서버를 과도하게 호출하면 블랙리스트에 오를 수 있습니다. 저는 토큰‑버킷 제한자를 추가했습니다:

await validateBulk(emails, {
  rateLimit: {
    perDomain: { requests: 10, window: 60 }, // 도메인당 1분에 10 요청
    global: { requests: 100, window: 60 },   // 전역적으로 1분에 100 요청
  },
});

성능을 유지하면서 남용을 방지합니다.

솔루션 3 – 조기 종료

정규식이 실패하면 MX 레코드를 확인할 필요가 없습니다. 조기 종료 옵션은 첫 번째 실패 시 검증을 중단하여 잘못된 이메일을 바로 거부합니다:

  • 💻 GitHub:
  • 📚 Docs:

다음은?

I’m planning to add:

  • In‑memory LRU caching (v1.1)
  • Enhanced reputation scoring with configurable weights
  • CLI tool for quick validations
  • Maybe a browser build

If you found this useful, star the repo on GitHub—it helps more than you’d think. And if you build something cool with it, let me know; I’d love to see what you create.

Back to Blog

관련 글

더 보기 »