하루 발송 상한은 왜 100통에서 50통으로 줄었나
SMTP 550 5.7.1 이야기를 하기 전에 출발점부터 적는다. 2026년 8월 4일, 우리 콜드메일 스크립트의 하루 발송 상한은 100통이 아니라 50통이었다. 스크립트에는 반송률이 높아지면 상한을 절반으로 줄이는 장치가 있고, 그날 반송률이 8.3%였다.
반송률은 보낸 메일 가운데 받는 서버가 되돌려 보낸 비율이다. 이 비율이 높으면 발송 도메인의 평판이 떨어지고, 평판이 떨어지면 반송되지 않은 메일까지 스팸함으로 간다. 그래서 반송이 오르면 발송량을 스스로 줄이게 만들어 두었다. 장치는 제 역할을 했지만, 영업 메일 수가 하루 절반으로 줄어든 상태가 이어지고 있었다.
상한을 깎는 장치는 원인을 고치지 않는다. 발송량을 줄여 피해를 늦출 뿐이고, 반송을 만드는 주소는 큐에 그대로 남는다. 그래서 상한을 되돌리려면 반송 주소를 보내기 전에 걸러 내는 수밖에 없었다.
앞서 7월 20일에도 비슷한 일이 있었다. 그때 반송률은 7.2%(125통 중 9통)로, 스크립트 주석에 적어 둔 위험선 5%를 넘었다. 원인 대부분이 폐업이나 도메인 만료로 DNS 자체가 죽은 주소라서, 발송 전에 도메인의 메일 수신 레코드(MX)를 확인하는 검사를 붙였다. 8월 4일의 8.3%는 그 검사를 붙인 뒤에 나온 숫자다.

처음 잰 숫자: 반송 40건은 어디서 왔나
당시 발송 전 검사는 두 겹이었다. 첫째는 도메인에 메일 서버가 살아 있는지 보는 DNS 검사, 둘째는 그 주소가 회사 홈페이지에 적힌 공식 주소와 맞는지 대조하는 검사다. 둘째 검사는 주소마다 검증 통과, 판단 불가, 미등재 셋 중 하나를 매긴다.
측정 방법부터 적는다. 반송 여부는 발송 서비스 Resend의 배송 상태를 정기적으로 조회해 모은 반송 목록에서 가져왔다. 하드 반송, 스팸 신고, 발송 서비스가 막은 주소가 이 목록에 들어간다. 이 목록을 발송 기록과 주소 단위로 맞대어, 보낸 주소마다 반송 여부와 공식 주소 대조 판정을 한 줄로 붙였다. 그다음 판정 묶음별로 반송 주소 수를 전체 주소 수로 나눴다.
반송된 40건을 한 건씩 대조하니 32건이 홈페이지 공식 주소로 등재돼 있었고, 신뢰도도 가장 높은 등급이었다. 반송의 대부분이 두 검사를 모두 통과한 주소였다는 뜻이다.
판정별로 반송률을 다시 쟀다. 검증 통과 174건은 9.8%, 판단 불가 128건은 11.7%, 미등재 162건은 4.9%가 반송됐다. 검증을 통과한 주소가 미등재 주소보다 두 배 높게 반송됐다.

처음 세운 가설 두 개는 무엇이었나
첫 가설은 단순했다. 공식 주소로 확인된 주소에만 보내면 반송이 줄어든다. 둘째 검사를 발송 큐의 필수 조건으로 올리자는 안이다. 홈페이지에 적힌 주소는 회사가 직접 공개한 주소이니 살아 있을 확률이 높다고 봤다.
둘째 가설은 첫 가설을 접은 뒤에 나왔다. 발송 직전에 받는 서버에 접속해 RCPT TO 명령까지만 보내 보면, 서버가 그 계정이 있는지 답해 준다. 이때 5xx 응답은 영구 거절이니 계정 없음으로 보면 된다고 생각했다.
두 가설 모두 그 자리에서는 자연스러웠다. 7월 20일에 DNS 검사로 반송을 줄인 경험이 있어서, 이번에도 검사 하나를 더 엄하게 걸면 된다고 봤다. SMTP 쪽도 4xx는 일시 오류, 5xx는 영구 오류라는 일반 규칙을 그대로 옮겼을 뿐이다. 문제는 둘 다 막히는 쪽의 숫자를 재기 전에 정한 규칙이었다는 점이다.
- 가설 1: 공식 주소 대조를 통과한 주소에만 보내면 반송이 줄어든다.
- 가설 2: SMTP RCPT 확인에서 5xx 응답이 오면 그 계정은 없다.
- 두 가설 모두 측정에서 틀렸다. 첫째는 방향이 반대로, 둘째는 정상 주소를 막는 쪽으로.
공식 주소만 보내자는 가설은 어디서 틀렸나
앞의 판정별 반송률이 바로 반증이다. 검증 통과군 9.8%가 미등재군 4.9%보다 높으니, 검증 통과만 남기면 반송률이 오히려 올라갈 수 있다. 이 비교는 무작위로 나눈 실험이 아니라 쌓인 기록을 나눠 본 관측이라 인과까지 말하기는 어렵다. 그래도 방향이 반대로 나온 이상 필수 조건으로 걸 근거는 없다.
비용도 컸다. 이 조건을 걸면 발송 큐가 1,876곳에서 980곳으로 48% 줄어든다. 공식 주소 대조가 확인한 주소 비율, 곧 커버리지가 46%에 그쳤기 때문이다. 큐를 반으로 줄이면서 반송은 줄지 않는 조치라 기각했다.
공식 주소 대조는 그 주소가 회사의 공식 주소인지를 본다. 그 메일함이 지금 살아 있는지는 보지 않는다. 홈페이지에 몇 년 전 박아 둔 주소는 공식 주소 검사를 통과하지만, 계정은 이미 지운 경우가 많다. 반송 40건 중 32건이 바로 이 경우였다.

SMTP 550 5.7.1은 무엇을 뜻하나
둘째 가설을 코드로 옮긴 첫 판은 5xx 응답을 전부 계정 없음으로 처리했다. 그런데 실측에서 이상한 거절이 나왔다. 실제로 우리에게 답장을 보낸 적이 있는 해외 기업 주소가 아래 응답으로 거절됐다.
550 5.7.1 Client host [...] blocked using Spamhaus
이 응답은 계정이 없다는 말이 아니다. 접속한 쪽의 IP가 Spamhaus라는 스팸 차단 목록에 올라 있어서 받지 않겠다는 말이다. 확인 작업을 돌린 PC가 가정용 회선이었고, 가정용 IP는 이 목록에 흔히 올라 있다. 실제 발송은 Resend의 IP로 나가니 발송과는 상관이 없고, 확인하러 간 우리만 막힌 셈이다.
SMTP 응답의 둘째 숫자 묶음(확장 상태 코드)을 보면 차이가 드러난다. 5.1.1은 받는 사람 주소가 없다는 뜻이고, 5.7.1은 보안·정책상 거절이라는 뜻이다(일반 지식, RFC 3463의 분류). 첫 자리가 똑같이 5라서 둘 다 영구 거절로 묶이지만, 하나는 상대 메일함의 사정이고 하나는 우리 쪽 사정이다.
5xx 전부를 계정 없음으로 읽으면 이 주소는 반송 목록에 들어가 다시는 메일을 받지 못한다. 반송 한 건을 막으려다 답장한 고객을 잃는 구조였다.
이 오판이 특히 위험한 이유는 반송 목록의 성격에 있다. 우리 스크립트는 한 번 반송 목록에 오른 주소를 영구히 제외한다. 같은 주소에 다시 보내 도메인 평판을 깎지 않으려는 장치다. 그런데 이 목록에는 들어가는 길만 있고, 잘못 들어간 주소를 자동으로 꺼내는 길은 없다. 판정 하나가 틀리면 그 고객에게는 다시 연락할 수 없다.
이 일을 겪고 나서 RCPT 응답을 읽는 순서를 정했다. 코드 숫자는 마지막에 본다. 문구에 무엇이 적혀 있는지가 먼저다.
- 첫째, 문구에 정책 차단 표현(spamhaus, blocked, blacklist, denied, not permitted, policy, reputation, spam, relay, 5.7.)이 있는가. 있으면 계정 유무를 알 수 없으니 판정 불가다.
- 둘째, 문구에 계정 없음 표현(user unknown, no such user, does not exist, mailbox not found, invalid recipient, 5.1.1, 5.5.1)이 있는가. 있고 첫째가 없으면 계정 없음이다.
- 셋째, 둘 다 없으면 코드로 나눈다. 400 미만은 받겠다는 응답, 나머지는 판정 불가다.
- 넷째, 250 응답은 받겠다는 뜻일 뿐 계정이 있다는 증명은 아니다.
진짜 원인은 두 겹이었다
반송률이 오른 원인은 홈페이지에 남은 폐기 메일함이었다. 도메인은 살아 있고 공식 주소 목록에도 올라 있지만, 계정은 지워진 주소다. 기존 두 겹 검사로는 이런 주소를 잡을 수 없었다. 둘 다 메일함 자체를 확인하지 않기 때문이다.
왜 이런 주소가 많을까. 회사 홈페이지는 한 번 만들면 몇 년씩 고치지 않는 경우가 많다. 그 사이 담당자가 바뀌거나 메일 호스팅을 옮기면 예전 주소는 지워지는데, 홈페이지 하단의 연락처는 그대로 남는다. 공개 주소라서 믿을 만해 보이지만, 공개된 시점이 오래됐을수록 이미 비어 있는 메일함일 가능성도 함께 커진다(추정: 반송 40건 중 32건이 공식 주소였다는 관측에서 나온 해석이고, 주소별 공개 시점은 재지 않았다).
고치려고 만든 검사 쪽에도 원인이 하나 더 있었다. 5xx라는 코드 숫자만 보고 판정했다. 서버가 거절한 이유가 계정 문제인지, 접속자 문제인지를 응답 문구에서 읽지 않았다.
| 검사 | 보는 것 | 폐기 메일함을 잡나 |
|---|---|---|
| DNS 검사(MX 레코드) | 도메인이 메일을 받는가 | 못 잡음 |
| 공식 주소 대조 | 홈페이지에 적힌 주소인가 | 못 잡음 |
| SMTP RCPT 확인 | 그 계정이 서버에 있는가 | 잡음(계정 없음 문구가 올 때) |
SMTP RCPT 확인은 어떻게 고쳤나
발송 직전에 셋째 검사로 SMTP RCPT 확인을 넣었다. 받는 도메인의 MX 서버에 25번 포트로 접속해 EHLO, MAIL FROM, RCPT TO까지만 말하고 끊는다. DATA 명령을 보내지 않으니 메일은 실제로 나가지 않는다. DNS 검사를 통과한 주소만 이 검사로 넘어온다.
- 1. 받는 도메인의 MX 레코드를 조회해 우선순위가 가장 높은 서버를 고른다.
- 2. 그 서버의 25번 포트에 접속해 EHLO로 우리 호스트 이름을 밝힌다.
- 3. MAIL FROM으로 확인용 발신 주소를 알린다.
- 4. RCPT TO로 확인할 주소를 묻고, 응답 코드와 문구를 받는다.
- 5. DATA 없이 QUIT으로 끊는다. 메일 본문은 한 줄도 보내지 않는다.
이 다섯 단계는 메일 서버끼리 실제로 메일을 넘길 때 주고받는 대화의 앞부분과 같다. 받는 서버는 RCPT TO 단계에서 이 주소를 받을지 말지를 미리 답한다. 그 답을 듣고 본문을 보내기 전에 끊는 방식이다.
확인할 때는 우리 메일 도메인의 호스트 이름(mail.sgkstudio.co.kr)으로 인사하고, 같은 도메인의 확인용 주소를 발신자로 쓴다. 받는 서버가 누가 묻는지 알 수 있게 하려는 설정이다. 다만 접속은 확인 작업을 돌리는 PC에서 나가므로, 서버가 보는 IP는 여전히 가정용 회선의 IP다. 앞의 Spamhaus 차단이 여기서 생긴다.
판정은 정규식 두 개로 가른다. 하나는 계정 없음을 명시한 문구(user unknown, no such user, 5.1.1, 5.5.1 등)이고, 다른 하나는 정책 차단 문구(spamhaus, blocked, policy, reputation, 5.7. 등)다. 계정 없음 문구가 있고 정책 차단 문구가 없을 때만 거절로 판정한다. 국내 메일 호스팅 하이웍스가 실제로 돌려준 'this account is unknown user (#5.5.1)' 같은 문구가 여기에 걸린다.

판정이 애매하면 통과시킨다. 4xx 응답(잠시 뒤 다시 오라는 그레이리스팅), 접속 실패, DNS 오류, 15초 타임아웃은 전부 판정 불가로 보고 메일을 보낸다. 일시 장애로 정상 주소를 영구히 버리는 손해가 반송 한 건보다 크다고 봤다. 반대로 250 응답도 계정이 있다는 증명은 아니다. 없는 주소에도 250을 돌려주는 서버(catch-all)가 있기 때문이다.
이 판정 불가 통과 원칙은 첫째 검사인 DNS 검사와 같은 원칙이다. DNS 검사도 타임아웃이나 조회 장애가 나면 통과시키고, 도메인이 없다는 확정 응답이나 MX와 A 레코드가 모두 없을 때만 막는다. MX 레코드가 없어도 A 레코드가 있으면 메일을 받을 수 있으니 통과시킨다(RFC 5321 5.1절의 암묵적 MX). 세 겹 검사가 모두 확실할 때만 막고, 모를 때는 보내는 쪽으로 기울도록 맞췄다.
- 동시 확인 6건: 하루 상한 100통 기준 1분 안에 끝난다.
- 응답 대기 15초, DNS 조회 4초를 넘으면 판정 불가로 통과.
- 25번 포트가 막힌 환경에서는 환경변수 하나로 검사를 통째로 건너뛴다. 전 건이 판정 불가로 나와 시간만 쓰기 때문이다.
- 회귀 테스트 9건: 전부 실제로 받은 응답 문구를 그대로 넣었다. 네트워크 없이 분류 함수만 검사한다.
기각한 대안도 남긴다. 제목의 '(광고)' 표기를 바꿔 스팸 필터를 피하자는 안은 정보통신망법 제50조 제4항이 정한 법정 표기라 손댈 수 없어 기각했다. 상용 이메일 검증 API는 월 고정비가 새로 생기는데, 자체 RCPT 확인으로 같은 신호를 얻을 수 있어 기각했다.
재측정 결과: 정상 주소 오차단은 얼마로 내려갔나
이미 반송된 주소 묶음(반송군)과 반송되지 않은 주소 묶음(대조군)에 분류기를 소급해서 돌렸다. 좋은 분류기는 반송군을 많이 막고 대조군은 막지 않아야 한다. 대조군에서는 수신거부 주소를 빼고 60건을 썼다.
판별력은 두 숫자로 쟀다. 반송군 차단율은 반송군 주소 가운데 분류기가 거절로 판정한 비율이고, 대조군 오차단율은 대조군 주소 가운데 거절로 판정한 비율이다. 앞 숫자는 높을수록, 뒤 숫자는 낮을수록 좋다. 앞 숫자만 보면 분류기를 엄하게 만들수록 좋아 보이니, 두 숫자를 늘 같이 봐야 한다.
첫 판은 반송군 57%, 대조군 10%를 막았다. 숫자만 보면 반송군을 더 많이 잡았지만, 양쪽 다 부풀려진 숫자였다. Spamhaus 정책 차단을 계정 없음으로 세어서 정상 주소도 함께 막았기 때문이다.
문구로 정책 차단을 분리하자 반송군 48%, 대조군 2%가 됐다. 대조군을 60건으로 다시 구성해 잰 최종값은 반송군 48%, 대조군 0%다. 반송 주소 절반 가까이를 걸러 내면서 정상 주소는 한 건도 막지 않았다.

| 단계 | 반송군 차단 | 대조군 오차단 |
|---|---|---|
| 첫 판(5xx 전부 계정 없음) | 57% | 10% |
| 정책 차단 문구 분리 | 48% | 2% |
| 대조군 60건 재구성 후 | 48% | 0% |
이 검사가 공식 주소 대조의 커버리지 46%와 상관없이 모든 주소에 돈다는 점도 중요했다. 큐를 1,876곳에서 980곳으로 줄이지 않고 반송만 걸러 낸다. 기대한 효과는 반송률 8.3%에서 4%대로, 그에 따라 하루 상한을 50통에서 100통으로 되돌리는 것이었다. 검사는 다음 날 아침 9시 10분 발송부터 적용했다.
같은 기록을 반송률 쪽에서 정리한 글은 이메일 반송률과 SMTP RCPT 확인에 있다. 이 글은 그중 응답 코드 해석만 따로 떼어 깊게 봤다.
남는 한계는 무엇인가
커버리지가 구조적으로 막혀 있다. 확인을 돌리는 IP가 Spamhaus에 올라 있어서, Outlook이나 Proofpoint 계열 메일 서버는 늘 5.7.1 정책 거절을 돌려준다. 이 도메인들은 언제나 판정 불가로 통과하니 검사가 아예 걸리지 않는다. 반송군 차단이 48%에서 멈춘 이유 중 하나다.
적용 후 실제 반송률은 이 기록에 없다. 반송률 4%대는 소급 측정에서 나온 예상치이고, 적용 뒤 며칠 동안의 실제 반송률을 다시 잰 숫자는 이 결정 기록 시점에 없었다. 분류기 판별력은 세 번 쟀지만, 최종 목표인 반송률은 아직 한 번도 재지 않았다는 뜻이다.
비교 방식의 한계도 있다. 공식 주소 대조 판정별 반송률은 관측 비교라 무작위 실험이 아니다. 방향이 반대로 나왔다는 사실은 필수 조건 안을 기각하기에는 충분하지만, 미등재 주소가 더 좋은 주소라고 결론 내릴 근거는 아니다.
- Outlook·Proofpoint 계열은 확인 불가로 늘 통과한다.
- 적용 후 실제 반송률 재측정 값이 이 기록에 없다(4%대는 예상치).
- catch-all 서버의 250 응답은 계정 존재 증명이 아니다.
- 발송 직전 SMTP 왕복이 한 단계 늘었다.
같은 종류의 실수가 다른 검사에도 있었다. 무료 메일 도메인(naver.com, gmail.com 등)으로 끝나는 주소를 막는 검사가 있었는데, 그 근거는 반송 실측이 아니라 개인 계정은 도달률이 낮다는 가설이었다. 2026년 8월 9일 이 차단을 풀자 발송 가능한 주소가 14,107곳 중 206곳에서 1,883곳으로 9.1배 늘었다. 차단은 지우지 않고 발송 기록에 회사 도메인과 무료 도메인을 표시해, 나중에 두 묶음의 반송률을 비교할 수 있게 남겼다.
두 사례에 공통된 점은 막는 규칙이 측정 없이 들어갔다는 것이다. 공식 주소 대조도, 5xx 일괄 판정도, 무료 도메인 차단도 그럴듯한 이유가 있었다. 그런데 막힌 쪽의 숫자를 재 보기 전까지는 그 규칙이 반송을 줄이는지, 정상 주소를 버리는지 알 수 없었다.
콜드메일 반송이나 메일 도달률 문제를 겪고 있다면, 응답 문구부터 원본 그대로 모아 보길 권한다. 코드 숫자만 세면 우리가 처음 그랬듯 원인을 반대로 읽는다. 비슷한 발송 자동화를 점검하고 싶다면 상담으로 상황을 알려 주셔도 좋다.