이메일 주소 검증, 무엇이 문제였나?
문제는 반송률 8.3%였습니다. 이 숫자 하나가 하루에 보낼 수 있는 메일 수를 100통에서 50통으로 깎고 있었습니다. 발송 스크립트에는 반송률이 높으면 하루 상한을 절반으로 줄이는 규칙이 들어 있고, 그 규칙이 제 역할을 한 셈입니다. 규칙 자체는 옳았습니다. 반송이 쌓이면 메일 서비스들이 발송 도메인을 스팸 발송자로 의심하기 시작하고, 그때부터는 살아 있는 주소로 보낸 메일도 받은편지함에 들어가지 못합니다.
영업 메일을 매일 보내는 회사에게 하루 상한이 절반으로 준다는 말은 영업 접점이 절반으로 준다는 말과 같습니다. 같은 문안을 같은 목록에 보내도 도달하는 회사 수가 반으로 줄면, 문안을 고치는 실험도 판정에 두 배의 시간이 듭니다. 그래서 반송률은 단순한 품질 지표가 아니라 영업 속도를 묶는 병목이었습니다.
이 회사가 반송과 싸운 게 처음은 아니었습니다. 2026년 7월 20일에도 반송률이 7.2%(125통 중 9통)까지 올라 흔히 위험선으로 보는 5%를 넘겼습니다. 그때 원인은 대부분 폐업했거나 도메인이 만료돼 DNS 자체가 죽은 주소였습니다. 그래서 발송 전에 도메인의 메일 수신 레코드(MX, 없으면 A 레코드)를 한 번 조회해 확정 부재만 거르는 검사를 넣었습니다.
그 뒤에 한 층을 더 쌓았습니다. 회사 홈페이지를 읽어 그 주소가 홈페이지에 적힌 공식 연락처인지 대조하는 검사입니다. 남의 회사 받은편지함으로 메일이 가는 사고를 막으려는 장치였고, 대조 결과에 '신뢰도 높음' 같은 등급을 붙였습니다. 도메인이 살아 있는지 보는 층과 공식 주소인지 보는 층, 두 겹이면 충분하다고 생각했습니다. 그런데 두 겹을 다 통과한 주소가 계속 반송됐습니다.
이메일 주소 검증을 고민하는 분이라면 비슷한 지점에서 멈춰 본 적이 있을 겁니다. 형식 검사와 도메인 검사는 통과하는데 반송은 줄지 않는 상태입니다. 이 글은 그 상태에서 무엇을 재고, 무엇을 잘못 믿었고, 결국 어디를 고쳤는지 순서대로 적습니다.
처음 잰 숫자: 반송 40건 중 32건이 '검증 통과' 주소였다
가장 먼저 반송된 주소 40건을 하나씩 공식 주소 대조 결과와 맞춰 봤습니다. 방법은 단순했습니다. 메일 발송 서비스가 돌려준 반송 목록과, 발송 전에 붙여 둔 대조 등급을 주소 단위로 이어 붙였습니다. 결과는 예상과 반대였습니다. 40건 중 32건이 공식 주소로 등재돼 있었고, 등급도 '신뢰도 높음'이었습니다.

40건 중 32건이면 반송 대부분이 두 검사를 모두 통과한 주소에서 나왔다는 뜻입니다. 도메인은 살아 있고, 홈페이지에도 그 주소가 적혀 있었습니다. 그런데 메일을 보내면 수신 서버가 '그런 계정 없음'을 돌려줬습니다. 홈페이지에 박제된 주소가 실제로는 오래전에 폐기된 계정이었던 경우입니다.
이 숫자를 보고도 바로 결론을 내리지 않은 이유가 있습니다. 반송 40건만 봐서는 등재 주소가 원래 발송 대상에서 차지하는 비율을 알 수 없습니다. 발송한 주소 대부분이 등재 주소였다면 반송도 등재 주소에서 많이 나오는 게 자연스럽습니다. 그래서 반송된 주소만이 아니라 보낸 주소 전체를 등급별로 나눠 반송률을 다시 재야 했습니다. 이 재측정이 뒤에서 가설을 뒤집는 근거가 됩니다.
처음 잰 숫자를 정리하면 세 개입니다. 반송률 8.3%, 하루 상한 50통(원래 100통), 그리고 반송 40건 중 32건이 '검증 통과'였다는 관측입니다. 마지막 숫자는 기존 두 겹의 검사가 이 종류의 반송을 거의 거르지 못한다는 신호였습니다.
세운 가설: 공식 주소에만 보내면 반송이 줄어든다
그런데 첫 가설은 이 신호와 반대 방향으로 갔습니다. 공식 주소 대조를 지금처럼 등급만 붙이는 참고 정보로 두지 말고, 아예 발송 차단 조건으로 걸자는 안이었습니다. 홈페이지에서 확인한 주소만 보내고, 확인 못 한 주소는 발송 대상에서 빼자는 생각입니다. 직관적으로는 그럴듯했습니다. 공식으로 확인한 주소가 확인 못 한 주소보다 살아 있을 확률이 높을 거라고 봤기 때문입니다.
이 안에는 비용이 분명히 있었습니다. 공식 주소 대조가 판정을 내린 범위(커버리지)는 전체 대상의 46%였습니다. 나머지는 홈페이지에서 주소를 찾지 못했거나 판단을 내리지 못한 주소입니다. 이걸 차단 조건으로 걸면 발송 대상이 1,876곳에서 980곳으로 줄어듭니다. 48%가 한 번에 빠지는 셈입니다.
그래도 처음에는 이 비용을 감수할 만하다고 봤습니다. 대상이 절반이 되더라도 반송률이 내려가 하루 상한이 50통에서 100통으로 돌아오면, 하루 실제 발송량은 오히려 지킬 수 있다는 계산이었습니다. 목록을 줄여서 속도를 사는 거래입니다.
공식 주소로 확인한 주소만 보내면 반송이 줄어든다. 발송 대상이 48% 줄어도 하루 상한이 돌아오면 남는 장사다.
돌이켜 보면 이 가설에는 검증하지 않은 전제가 하나 숨어 있었습니다. '공식 주소인가'와 '그 메일함이 살아 있는가'가 같은 방향으로 움직인다는 전제입니다. 앞 절의 40건 중 32건이 이미 그 전제를 의심하라고 말하고 있었는데, 가설을 세울 때는 그 숫자를 등재 주소가 많아서 생긴 착시 정도로 넘겼습니다.
가설은 어디서 틀렸나?
가설을 바로 적용하지 않고 먼저 재 봤습니다. 보낸 주소 전체를 공식 주소 대조 판정별로 나누고, 판정마다 실제 반송률을 계산했습니다. 반송 목록만 보지 않고 분모를 판정별 발송 수로 잡았다는 점이 앞 절과 다릅니다. 결과는 가설과 정반대였습니다.
분모는 판정별 발송 수. 무작위 배정이 아닌 관측 비교
검증을 통과한 174건은 9.8%가 반송됐습니다. 판단을 못 내린 128건은 11.7%였습니다. 그리고 홈페이지에서 아예 찾지 못한 미등재 162건이 4.9%로 가장 낮았습니다. 검증 통과군이 미등재군보다 2배 더 반송한 셈입니다. 공식 주소만 남기자는 안은 반송률이 가장 낮은 무리를 버리고, 더 높은 무리만 남기는 결과를 냅니다.

이 비교에는 한계가 있습니다. 주소를 무작위로 나눠 보낸 실험이 아니라, 이미 보낸 결과를 판정별로 나눈 관측 비교입니다. 미등재 주소가 덜 반송된 데에는 판정과 무관한 다른 이유가 섞였을 수 있습니다. 그래서 '미등재 주소가 더 좋다'는 결론까지는 내리지 않았습니다.
다만 차단 조건의 근거를 따질 때는 이 정도로 충분했습니다. 공식 주소 대조를 차단 조건으로 쓰려면 검증 통과군이 적어도 덜 반송해야 합니다. 방향이 반대로 나온 이상, 그 근거는 성립하지 않습니다. 대상을 1,876곳에서 980곳으로 반토막 내면서 반송은 줄지 않는 안이라 기각했습니다.
여기서 얻은 교훈은 측정 순서입니다. 반송 40건만 보고 차단 조건을 걸었다면, 발송 대상 48%를 잃고도 반송률은 그대로인 상태를 몇 주 뒤에야 알았을 겁니다. 대책을 걸기 전에 대책이 기대는 전제를 먼저 재는 편이 훨씬 쌌습니다.
진짜 원인: 공식 주소와 살아 있는 메일함은 다른 질문이다
숫자를 보고 나서야 두 질문이 다르다는 점이 분명해졌습니다. 공식 주소 대조는 '이 주소가 그 회사의 공식 연락처인가'를 봅니다. 반송을 막으려면 '이 메일함이 지금 메일을 받는가'를 봐야 합니다. 앞의 질문은 남의 받은편지함으로 메일이 가는 사고를 막는 데 쓰는 도구이고, 뒤의 질문을 대신하지 못합니다.
왜 공식 주소일수록 더 반송될 수 있을까요. 홈페이지는 한 번 만들고 오래 그대로 두는 경우가 많습니다. 담당자가 바뀌거나 메일 서비스를 옮겨도 홈페이지 하단의 연락처는 몇 년씩 남습니다. 홈페이지에 또렷하게 적혀 있다는 사실이 그 주소가 오래전에 정해졌다는 신호이기도 해서, 그만큼 계정이 폐기됐을 가능성도 함께 커집니다. 이 부분은 반송 40건 중 32건이라는 관측과 맞아떨어지는 해석이고, 별도 실험으로 증명하지는 않았습니다.
기존 두 겹의 검사가 이 반송을 못 거른 이유도 같은 데 있었습니다. DNS 검사는 도메인이 메일을 받을 수 있는지만 봅니다. 회사 도메인은 멀쩡히 살아 있으니 통과합니다. 공식 주소 대조는 홈페이지에 그 주소가 있는지만 봅니다. 홈페이지에도 적혀 있으니 통과합니다. 두 검사 모두 계정 하나가 지금 살아 있는지는 묻지 않았습니다.
| 검사 | 묻는 질문 | 폐기된 계정을 거르나 |
|---|---|---|
| DNS(MX·A 레코드) 조회 | 도메인이 메일을 받을 수 있나 | 못 거른다(도메인은 살아 있다) |
| 공식 주소 대조 | 홈페이지에 적힌 공식 주소인가 | 못 거른다(홈페이지에 남아 있다) |
| SMTP RCPT 확인 | 수신 서버에 그 계정이 있나 | 거른다(서버가 계정 없음을 답할 때) |
결국 필요한 건 세 번째 질문을 직접 던지는 검사였습니다. 메일을 실제로 보내지 않고 수신 서버에게 이 계정이 있는지만 묻는 방법이 SMTP 프로토콜 안에 이미 있습니다.
조치: 발송 직전에 수신 서버에 계정 유무를 어떻게 묻나?
메일을 보낼 때 발송 서버와 수신 서버는 몇 마디 대화를 주고받습니다. 인사(EHLO), 보내는 사람(MAIL FROM), 받는 사람(RCPT TO), 그리고 본문(DATA) 순서입니다. 수신 서버는 RCPT TO 단계에서 그 계정을 받을 수 있는지 답합니다. 본문을 보내기 전에 대화를 끊으면 메일은 한 통도 나가지 않고, 계정 유무에 해당하는 답만 손에 남습니다.
그래서 발송 스크립트에 세 번째 검사를 넣었습니다. 도메인의 MX 레코드로 수신 서버를 찾아 25번 포트로 접속하고, EHLO와 MAIL FROM, RCPT TO까지만 말한 뒤 끊습니다. 순서는 DNS 검사 뒤입니다. 도메인이 죽은 주소는 이미 앞에서 빠지니, 접속할 가치가 있는 주소만 세 번째 검사로 넘어옵니다.

판정은 세 가지로만 냅니다. 수신 서버가 계정이 없다고 명시한 5xx 응답이면 '거절'로 보고 발송에서 뺍니다. 2xx면 '정상'입니다. 그 밖의 모든 경우, 즉 4xx 임시 거절(그레이리스팅), 접속 실패, 시간 초과는 '판정 불가'로 보고 그대로 보냅니다. 확실하지 않을 때 통과시키는 이 방식을 fail-open이라고 부릅니다.
fail-open을 고른 이유는 손익 계산이었습니다. 일시 장애 때문에 멀쩡한 영업 대상을 영구히 버리는 손해가, 반송 한 건을 더 맞는 손해보다 큽니다. 같은 이유로 '정상' 판정도 계정이 있다는 증명으로 읽지 않습니다. 없는 주소에도 일단 250을 돌려주는 catch-all 서버가 있기 때문입니다. 이 검사는 살아 있는 주소를 골라내는 장치가 아니라, 확실히 죽은 주소만 빼는 장치입니다.
운영 값도 정했습니다. 수신 서버 응답은 15초까지 기다리고, 넘으면 판정 불가로 통과시킵니다. DNS 조회는 4.0초로 끊습니다. 동시에 6개 주소를 확인하는데, 하루 상한이 100통 이하라 이 정도면 1분 안에 끝납니다. 일부 클라우드나 통신사 회선은 25번 포트를 막아서 모든 주소가 판정 불가로 나오므로, 환경 변수 하나로 이 검사를 통째로 건너뛸 수 있게 했습니다.
- DNS 조회: 4.0초 제한, 확정 부재(NXDOMAIN·MX/A 없음)만 제외
- SMTP RCPT 확인: 25번 포트, 응답 대기 15초, 동시 6개
- 거절 기준: 계정 없음을 명시한 5xx이면서 정책 차단 문구가 없을 때만
- 그 밖의 응답(2xx·4xx·접속 실패·시간 초과): 통과(fail-open)
- 25번 포트가 막힌 환경: 환경 변수로 검사 전체 생략
기각한 대안도 둘 더 있었습니다. 상용 이메일 검증 API는 같은 신호를 사서 받는 방법이지만, 매달 고정비가 새로 생깁니다. 자체 RCPT 확인으로 같은 신호를 얻을 수 있으니 쓰지 않았습니다. 메일 제목의 광고 표기를 바꿔 스팸 필터를 피하자는 의견도 나왔는데, 제목 맨 앞의 '(광고)'는 정보통신망법 제50조 제4항이 정한 법정 표기라 임의로 바꿀 수 없어 기각했습니다.
초판 분류기는 왜 정상 주소 10%를 막았나?
세 번째 검사의 첫 판은 단순했습니다. 5xx 응답이면 계정 부재로 보고 뺐습니다. 이 판을 과거에 보낸 주소에 돌려 판별력을 쟀더니, 반송군의 57%를 막고 대조군(반송되지 않은 주소)도 10%를 막았습니다. 반송을 잘 잡는 것처럼 보였지만, 정상 주소 10곳 중 1곳을 버리는 검사였습니다.
원인은 한 주소에서 드러났습니다. bd_korea@bd.com 은 실제로 저희에게 회신까지 보낸 살아 있는 주소였는데, RCPT 확인에서 '550 5.7.1 blocked using Spamhaus'로 거절당했습니다. 이 응답은 그 계정이 없다는 뜻이 아니었습니다. 확인하러 간 쪽, 즉 저희 IP가 차단 목록에 올라 있다는 뜻이었습니다. 검사를 돌린 PC가 가정용 회선이라 Spamhaus에 등재돼 있었고, 실제 발송은 메일 발송 서비스의 IP로 나가니 발송 자체와는 무관한 차단이었습니다.
5xx라고 다 같은 5xx가 아닙니다. 5.1.1이나 'user unknown'처럼 계정이 없다고 말하는 응답이 있고, 5.7.x나 'blocked', 'policy'처럼 보내는 쪽을 막았다고 말하는 응답이 있습니다. 앞의 것은 주소의 문제이고 뒤의 것은 확인하는 쪽의 문제입니다. 이 둘을 뭉뚱그리면 멀쩡한 영업 대상을 영구히 버립니다. 이 응답 코드를 자세히 다룬 글은 SMTP 550 5.7.1 거절을 계정 없음으로 읽으면 생기는 일에 따로 정리했습니다.

그래서 분류기를 응답 문구 기준으로 다시 짰습니다. 계정 부재 문구를 잡는 정규식과 정책 차단 문구를 잡는 정규식을 따로 두고, 앞의 것이 잡히면서 뒤의 것이 잡히지 않을 때만 '거절'로 봅니다. 계정 부재 문구가 있어도 정책 차단 문구가 같이 있으면 계정 유무를 알 수 없으니 통과시킵니다. 실측 문구 중에는 국내 메일 호스팅의 'this account is unknown user (#5.5.1)'과 일반 메일 서버의 '550 5.1.1 User unknown'이 있었습니다.
문구로 정책 차단을 분리하자 판별력은 반송군 48%, 대조군 2%로 바뀌었습니다. 반송군 차단율이 57%에서 48%로 내려간 숫자를 손해로 읽으면 안 됩니다. 초판의 57%와 10%는 양쪽 다 부풀어 있었고, 그중 일부는 우리 IP 차단을 계정 부재로 잘못 센 몫이었습니다. 대조군을 다시 구성하자 오차단은 0%가 됐습니다. 이 판정 규칙이 다시 무너지지 않도록 실측 응답 문구 그대로 9개 케이스를 회귀 테스트로 묶었습니다.
재측정 결과: 반송군 48% 차단, 정상 주소 오차단 0%
재측정은 이미 결과를 아는 과거 주소로 했습니다. 반송된 주소 묶음(반송군)과 반송되지 않은 주소 묶음(대조군)에 새 분류기를 돌려, 각 묶음에서 몇 %를 거절로 판정하는지 셌습니다. 대조군은 수신거부 주소를 뺀 60건입니다. 수신거부 주소는 다시 보내지 않으니 판별력 계산에서 제외했습니다.
| 단계 | 반송군 차단 | 대조군 오차단 | 무엇이 달랐나 |
|---|---|---|---|
| 초판(5xx면 거절) | 57% | 10% | 우리 IP 차단(5.7.1)까지 계정 부재로 셈 |
| 문구로 정책 차단 분리 | 48% | 2% | 계정 부재 문구 + 정책 차단 문구 없음일 때만 거절 |
| 대조군 재구성 후 | 48% | 0% | 수신거부 주소 제외 60건 기준 |
과거 발송 주소에 분류기를 다시 돌린 결과. 배포 뒤 실제 반송률이 아니다

반송군 48% 차단은 반송될 주소의 절반 가까이를 발송 전에 뺀다는 뜻이고, 대조군 0% 오차단은 그 과정에서 살아 있는 주소를 버리지 않는다는 뜻입니다. 공식 주소 대조를 차단 조건으로 썼다면 발송 대상이 48% 줄었을 텐데, 이 검사는 대상 규모를 거의 건드리지 않습니다. 공식 주소 대조의 판정 범위 46%와 상관없이 모든 주소에 똑같이 돌기 때문입니다.
이 결과로 기대한 숫자도 적어 둡니다. 반송률은 8.3%에서 4%대로 내려가고, 그러면 하루 상한이 50통에서 100통으로 돌아올 거라는 예상이었습니다. 이 검사는 2026년 8월 4일에 반영해 다음 날 오전 9시 10분 정기 발송부터 돌기 시작했습니다.
다만 이 예상은 예상일 뿐입니다. 이 결정을 남긴 기록에는 배포 뒤 실제로 잰 반송률이 없습니다. 위 표의 48%와 0%는 과거 주소에 분류기를 다시 돌린 판별력이고, 실제 발송에서 반송률이 몇 %로 내려갔는지는 따로 재야 할 숫자입니다. 개선율만 적고 끝내면 이 차이가 사라지니, 빈칸은 빈칸으로 남겨 둡니다.
남는 한계: 이 이메일 주소 검증이 못 거르는 것은 무엇인가?
첫째, 일부 메일 서비스는 구조적으로 판정할 수 없습니다. 검사를 돌리는 회선이 Spamhaus에 등재돼 있어서, Outlook이나 Proofpoint 계열 서버는 계정 유무와 상관없이 5.7.1 정책 거절을 돌려줍니다. 새 분류기는 이 응답을 판정 불가로 통과시키니 오차단은 없지만, 그 도메인들에서는 검사가 아무것도 걸러 내지 못합니다. 검사 범위가 처음부터 제한된 상태입니다.
둘째, catch-all 서버는 없는 주소에도 250을 돌려줍니다. 이런 서버 뒤의 폐기 계정은 RCPT 확인으로 잡을 수 없고, 실제로 보내 봐야 반송으로 드러납니다. 반송군 차단율이 48%에서 멈춘 이유 중 일부가 여기에 있을 수 있습니다.
셋째, 발송 직전에 SMTP 왕복이 하나 더 생깁니다. 하루 상한 100통 기준 동시 6개로 1분 안에 끝나는 규모라 지금은 문제가 없지만, 상한을 크게 올리면 시간과 접속 횟수가 함께 늘어납니다. 같은 수신 서버에 짧은 간격으로 확인을 반복하면 그 서버가 확인하는 쪽을 차단할 가능성도 있습니다.
넷째, 이 글의 재측정은 과거 주소에 대한 판별력이지 배포 뒤 반송률이 아닙니다. 판정별 반송률 비교도 무작위 배정이 아닌 관측 비교였습니다. 둘 다 방향을 정하는 데는 충분했지만, 효과의 크기를 확정하는 숫자는 아닙니다.
비슷한 문제를 겪는 분께 드릴 수 있는 순서는 이렇습니다. 반송된 주소만 보지 말고 보낸 주소 전체를 판정별로 나눠 반송률을 잽니다. 대책이 기대는 전제를 먼저 확인합니다. 5xx 응답은 문구까지 읽어 계정 문제와 정책 차단을 가릅니다. 확실하지 않으면 통과시키고, 판정 규칙은 실측 응답 문구로 테스트를 묶어 둡니다.
메일 발송 자동화나 영업 대상 목록 정리를 직접 하기 어렵다면 상담으로 현재 반송률과 발송 구조를 알려 주세요. 어느 층에서 새고 있는지부터 같이 재 보겠습니다.