대량 문자발송 시스템의 구성 요소
접수, 발송 큐, 통신사 모듈, 결과 수신, 관리 화면. 대량 발송 시스템이 최소한 갖춰야 할 구성 요소와, 건수가 늘 때 가장 먼저 병목이 생기는 지점을 정리했습니다.
어려운 것은 발송이 아니라 흐름이다
문자 한 건을 보내는 일은 어렵지 않습니다. 중계사 API를 호출하면 끝납니다. 어려워지는 순간은 한 번에 수만 건을 보낼 때, 그리고 결과가 나중에 도착할 때입니다. 이 두 가지 때문에 대량 발송 시스템은 단순한 API 래퍼가 아니라 큐 기반 시스템이 됩니다.
최소 구성 요소
| 접수부 | 화면이나 API로 들어온 발송 요청을 받아 저장만 하고 즉시 응답합니다. 여기서 실제 발송을 하면 사용자는 몇 분을 기다려야 합니다. |
|---|---|
| 발송 큐 | 보낼 건을 건 단위로 쌓아 둡니다. 예약 발송은 발송 예정 시각을 함께 저장합니다. |
| 발송 처리기 | 큐에서 꺼내 통신사·중계사 모듈로 전달합니다. 처리 속도와 동시 실행 수를 조절할 수 있어야 합니다. |
| 결과 수신부 | 성공·실패 결과를 되받아 해당 건의 상태를 갱신합니다. 즉시 오지 않으므로 별도 경로로 들어옵니다. |
| 관리 화면 | 주소록, 템플릿, 발신번호, 충전·과금, 발송내역, 통계. |
왜 접수와 발송을 나누는가
이유는 세 가지입니다.
- 응답 속도 — 수만 건을 요청해도 접수는 즉시 끝나야 합니다.
- 재시도 — 중계사 장애가 나도 요청은 이미 저장돼 있으므로, 복구 후 이어서 보낼 수 있습니다.
- 속도 조절 — 통신사별 처리 한도에 맞춰 내보내는 속도를 조절할 수 있습니다.
미션비 홈페이지도 같은 방식을 씁니다. 상담·문의가 접수되면 담당자에게 문자를 보내는데, 발송 요청은 큐에 적재만 하고 결과는 별도로 처리합니다. 덕분에 문자 발송이 실패해도 문의 등록 자체는 정상 처리됩니다.
상태를 건 단위로 관리한다
발송 묶음(캠페인) 단위로만 상태를 두면 "5만 건 중 37건 실패"를 표현할 수 없습니다. 건마다 상태를 두고, 묶음 상태는 집계로 계산하는 편이 정확합니다.
- 대기 → 발송 요청됨 → 결과 수신(성공/실패) → (필요 시) 재발송
- 실패는 사유 코드와 함께 저장합니다. 번호 오류인지 일시적 장애인지에 따라 재시도 여부가 다릅니다.
과금에서 조심할 것
선불 충전 방식이라면 언제 차감할지가 문제입니다.
- 접수 시점에 차감하면, 발송 실패분을 되돌려 줘야 합니다.
- 결과 수신 후 차감하면, 잔액보다 많이 발송되는 상황이 생길 수 있습니다.
실무에서는 접수 시 예약(홀드)하고 결과에 따라 확정·반환하는 방식이 무난합니다. 어느 쪽이든 잔액 변동은 반드시 이력으로 남겨야 합니다. 잔액은 현재 값이 아니라 이력의 합계여야 대사가 맞습니다.
주소록과 개인정보
주소록은 곧 개인정보 데이터베이스입니다. 다음이 필요합니다.
- 계정별 접근 제한과 조회 이력
- 수신거부 처리와 재발송 차단
- 보관 기간 정책과 만료 데이터 정리
- 발송내역에 남는 본문·수신번호의 보관 기간
성능에서 먼저 막히는 곳
경험적으로 병목은 발송 모듈이 아니라 데이터베이스 쓰기에서 먼저 생깁니다. 건 단위로 행을 만들고 상태를 갱신하기 때문입니다. 대응 방법은 대체로 다음과 같습니다.
- 접수 시 대량 삽입(batch insert)으로 한 번에 저장
- 상태 갱신을 묶어서 처리
- 발송내역 테이블을 기간별로 분리해 조회 부담을 낮춤
- 통계는 실시간 집계 대신 주기적으로 집계된 값을 조회
정리
대량 발송 시스템의 설계는 "실패해도 되는 곳과 실패하면 안 되는 곳"을 나누는 일입니다. 접수는 실패하면 안 되고(사용자가 다시 입력해야 하므로), 개별 발송은 실패해도 됩니다(재시도하면 되므로). 이 경계를 큐로 그어 두면 나머지 기능은 그 위에 얹기만 하면 됩니다.