Skip to content
isdnetworks
Go back

96만 통의 이전 통지 메일은 보내기 전에 받는 메일함이 있는지부터 확인했다

영업을 넘겨받으면서 고객에게 개인정보 이전 사실을 알리는 메일을 보내야 했다. 대상은 약 96만 명이고, 발송은 Amazon SES 로 새로 구축했다. 첫 회차로 1,000통을 보냈더니 영구 반송이 19% 였다. SES 는 반송률이 높은 계정을 검토하거나 발송을 멈출 수 있어서, 이 비율로 나머지를 보내면 통지 자체가 중간에 막힐 수 있었다.

Table of contents

Open Table of contents

원인 — 19% 는 표본 탓인가

처음 의심한 것은 표본이었다. 발송 순서가 가입이 오래된 회원부터였다면, 19% 는 전체가 아니라 앞쪽의 죽은 주소만 반영한 값일 수 있다. 그래서 첫 회차 1,000명과 대상 중간 구간 1,000명의 가입 연도 분포를 Firebase Auth 의 createdAt 으로 비교했다. 2019년 6.4% 대 6.2%, 2020년 20.0% 대 19.4%, 2021년 33.3% 대 32.7% 처럼 두 표본이 거의 같았다. 꺼내는 순서는 적재 순서이고, 원본이 무작위 문자열인 uid 순이라 가입일과 무관했다. 19% 는 목록 전체를 대표하는 값이었다.

다음으로 활동 회원만 먼저 보내면 되는지 봤다. 마지막 토큰 갱신(lastRefreshAt)이 2025년 이후인 회원은 4.7% 뿐이었고, 그들의 반송률도 10.6% 였다. 주문 이력 기준으로 활동 회원 86,014명을 뽑아 대조해도 반송률은 크게 줄지 않았다. 활동 회원만 보내는 안은 대상만 크게 줄이고 반송률은 낮추지 못한다. 문제는 누구에게 보내느냐가 아니라, 받는 메일함이 실제로 있느냐였다.

판단 — 보내 보고 거르기와 먼저 확인하기

남은 선택지는 다음 두 가지 방법이었다.

  1. 그대로 보내고, SES 가 돌려주는 반송으로 주소를 걸러 낸다
  2. 보내기 전에 각 메일 사업자의 받는 서버에 그 메일함이 있는지 확인하고, 없는 주소를 발송 대상에서 뺀다

1번은 반송이 생긴 뒤에 거르므로, 거르는 동안 쌓인 반송률이 그대로 계정 평판이 된다. 반면 2번은 메일을 보내지 않고 수신 서버의 응답만 보고 판정한다. 2번이 맞다고 본 이유는 반송률을 낮추는 유일한 길이 반송될 메일을 아예 보내지 않는 것이기 때문이다. 대가는 확인 자체에 시간이 들고, 확인할 수 없는 사업자가 남는다는 점이다.

해결 — 판정을 되돌릴 수 있게 표시한다

수신 서버의 응답 코드로 두 가지만 표시했다.

표시는 대기 행의 status 를 suppressed 로, error 를 rcpt:5.1.1 처럼 출처가 드러나는 값으로 바꾸는 것이다. 순서는 매번 같았다. 입력 id 와 DB 이메일의 도메인이 100% 일치하고 전부 대기 상태인지 검증하고, 바꿀 행을 TSV 로 백업해 sha256 을 남기고, 한 트랜잭션으로 UPDATE 하되 갱신 수가 예상과 다르면 롤백하고, 끝나면 되읽었다. 발송 중인 행이 0 이고 발송 Job 이 멈춘 상태에서만 갱신했고, 갱신은 대기 행만 건드렸다.

되돌리는 방법은 처음부터 한 줄로 정해 두었다.

UPDATE mail_targets SET status='pending', error=NULL
 WHERE status='suppressed' AND error LIKE 'rcpt:%';

rcpt: 로 시작하는 것만 풀어서, SES 반송으로 억제한 주소는 건드리지 않는다. 받는 메일함 확인은 사업자별로 길게 돌았으므로 결과를 증분으로 붙였다. 마지막 반영 뒤 누적은 메일함 없음 18,714건, 휴면 50,802건이었다.

증분을 붙이다 함정도 하나 밟았다. 입력을 고정하는 기준 시각을 미래로 잡자 검증·백업·적용 사이에 입력이 계속 늘어 건수가 어긋났다. 지금 시각을 읽고 지난 시각으로 고정한 뒤 같은 값으로 세 단계를 돌려야 한다. 백업 명령이 연결이 끊겨 rc=1 로 끝났을 때는 백업이 잘린 것이므로 적용하지 않고 다시 받았다.

영향 범위 — 판정의 한계

판정에는 한계가 있었고 그대로 적었다. 휴면 판정이 실제로는 받을 수 있는 주소를 빼는 거짓 제외는 한 사업자 대조 137건 기준으로 최대 3.9% 수준이었다. 확인할 수 없는 사업자도 있었다. 한 곳은 표본 6건만 확인됐고, 한 곳은 역방향 DNS 검사(FCrDNS)로 확인을 거부했고, 한 곳은 응답이 없었다. 그 주소들은 판정이 없으므로 표시하지 않았다. MX 레코드가 없는 도메인 4,506건과 다른 거절 코드는 승인 범위 밖이라 표시하지 않고 따로 결정을 받기로 했다.

결과 — 먼저 확인하고 뺀 주소가 지킨 평판

정리


Share this post on:

Previous Post
공유 기억의 토큰 사용량은 쓰기에서 LLM 호출을 없애고 읽기에서 주입 줄 수를 줄여 낮췄다
Next Post
공유 기억이 무엇을 꺼낼지는 사람 프롬프트 155개로 재서 순위식과 거리 상한을 골랐다