나이지리아의 SMS가 새벽 2시에 중단되었습니다 - 그래서 우리는 장애를 우회하는 멀티 채널 메시징 아키텍처를 구축했습니다
요약
나이지리아의 불안정한 SMS 환경을 극복하기 위해 구축한 멀티 채널 메시징 아키텍처를 소개합니다. KudiSMS와 Termii를 결합하여 SMS, WhatsApp, 음성 통화를 활용한 단계별 에스컬레이션 및 오류 분류 시스템을 구현했습니다.
핵심 포인트
- KudiSMS와 Termii를 활용한 이중 메시징 구조 구축
- 오류 심각도(재시도 가능 vs 치명적)에 따른 차등 알림 처리
- SMS, WhatsApp, 음성 통화를 활용한 단계별 에스컬레이션 전략
- 서비스 간 독립적인 전화번호 정규화 및 검증 로직 적용
- 알림 피로를 방지하기 위한 정교한 오류 분류 설계
문제점
나이지리아에서는 SMS 전송이 보장되지 않습니다. 통신사가 메시지를 누락시키고, 게이트웨이가 다운되며, 정성스럽게 작성한 트랜잭션 알림 (transactional alert)이 허공 속으로 조용히 사라져 버립니다.
다음과 같은 상황에서 음식 배달 마켓플레이스를 운영한다면:
- 판매자가 몇 초 이내에 새로운 주문을 알아야 함
- 라이더가 제시간에 도착하기 위해 픽업 안내를 받아야 함
- 고객이 가입을 포기하기 전에 OTP (One-Time Password)가 고객에게 도달해야 함
…SMS 신뢰성은 있으면 좋은 기능이 아닙니다. 그것은 기본 요건 (table stakes)입니다.
우리는 하나의 제공업체로 시작했습니다. 그리고 혹독한 경험을 통해 배웠습니다.
아키텍처
우리는 현재 서로 다른 강점을 가진 두 개의 독립적인 메시징 서비스를 운영하고 있습니다:
트랜잭션 SMS (주요 서비스)
→ 오류 분류 (error classification) 기능이 있는 KudiSMS 서비스
→ 치명적 오류 (critical failures) → 개발자 알림
→ 구조화된 검증 (structured validation) + 전화번호 파싱 (phone number parsing)
에스컬레이션 및 멀티 채널 (보조 서비스)
→ Termii 서비스 (SMS + WhatsApp + Voice)
→ 채널별 설정 가능한 토글 (configurable per-channel toggles)
→ 시간에 민감한 판매자/관리자 알림에 사용
작동 방식
KudiSMS는 트랜잭션 트래픽의 대부분을 처리합니다. 모든 주문 확인, OTP, 상태 업데이트는 KudiSMS를 기반으로 하는 전용 알림 채널을 통해 흐릅니다. 이 서비스는 명시적인 오류 분류, 재시도 가능한 실패 (retryable failures) 대 치명적 실패 (critical failures)를 구분하며, 이를 다르게 로그로 기록합니다. 치명적 실패는 즉시 누군가가 알 수 있도록 알림을 트리거합니다.
Termii는 공백을 메웁니다. Termii는 단일 API를 통해 SMS, WhatsApp, 음성 통화 (Voice calls)를 지원합니다. 주문 에스컬레이션을 위해, 작업 (job)은 여러 채널을 통해 알림을 발송합니다:
- 판매자의 전화로 SMS 발송 (첫 번째 시도)
- 판매자의 선호 채널이 설정되어 있다면 WhatsApp 발송
- 긴급한 주문을 위한 최후의 수단으로 음성 통화 (Voice call) 발송
- 관리자 에스컬레이션 - N분 후에도 판매자가 응답하지 않으면 관리자에게도 WhatsApp + 음성 통화 알림이 전송됨
각 채널은 설정을 통해 독립적으로 토글할 수 있습니다. 이는 WhatsApp이나 음성 통화 전송에 영향을 주지 않으면서 유지보수를 위해 SMS를 비활성화할 수 있음을 의미합니다.
중요한 미세한 설계 결정 사항들
전화번호 정규화 (Normalization)는 서비스별로 분리되어 있습니다. 나이지리아 전화번호는 다양한 형식(080…, +23480…, 23480…)으로 들어옵니다. 각 서비스는 독립적으로 정규화를 수행합니다. 따라서 한 서비스에 버그가 발생하더라도 다른 서비스는 여전히 작동합니다.
검증 (Validation)은 조용히 넘어가지 않고 빠르게 실패합니다. KudiSMS는 모든 요청 시 설정을 검증하며, 자격 증명 (Credentials)이 누락된 경우 즉시 오류를 발생시킵니다. 잘못 설정된 서비스는 메시지를 조용히 누락시키는 대신, 명확하게 실패를 알립니다.
오류는 심각도에 따라 분류됩니다. 우리는 재시도 가능한 실패 (Retryable failures, 예: 네트워크 일시 오류)와 치명적인 실패 (Critical failures, 예: 인증 문제, 잘못된 응답)를 구분합니다. 오직 치명적인 오류만이 담당자를 깨웁니다. 이는 일시적인 네트워크 불안정으로 인한 알림 피로 (Alert fatigue)를 방지합니다.
에스컬레이션 (Escalation)은 상태를 인지합니다. 에스컬레이션 작업 (Escalation job)은 알림을 보내기 전에 주문이 이미 수락되었는지 확인합니다. 이미 전화를 받아 주문을 수락한 업체에 스팸성 알림을 보내지 않습니다.
실제 운영 환경에서의 모습
업체가 새로운 주문 알림을 받는 과정:
- KudiSMS가 트랜잭션 SMS를 전송 → 전달 완료, 매우 좋음
- 만약 KudiSMS가 실패할 경우 → 오류가 분류 및 기록되며, 개발자에게 알림이 전송됨
- 그동안 에스컬레이션 타이머가 작동하기 시작함
- 수락 없이 N분이 경과하면 → Termii가 WhatsApp 메시지를 전송함
- 추가로 N분이 경과하면 → Termii가 음성 통화를 시도함
- 추가로 N분이 경과하면 → 관리자에게 WhatsApp과 음성으로 알림이 전송됨
이 시스템은 단순히 장애 조치 (Failover)만 수행하는 것이 아닙니다. 점점 더 침투적인 채널을 통해 단계적으로 에스컬레이션을 진행합니다.
교훈 (Lessons Learned)
- 중복성 (Redundancy)은 독립적인 장애 모드 (failure modes)를 의미합니다. 동일한 통신사나 동일한 API 패턴을 사용하는 두 개의 SMS 제공업체는 함께 장애가 발생할 것입니다. 우리의 제공업체들은 서로 다른 인프라를 가지고 있습니다.
- 멀티 채널 (Multi-channel)이 멀티 제공업체 (multi-provider)보다 더 낫습니다. WhatsApp과 음성 (Voice)은 SMS 통신사와 경쟁 관계가 아닙니다. 채널을 추가하는 것은 진정으로 직교하는 (orthogonal) 전달 경로를 제공합니다.
- 알림 피로 (Alert fatigue)는 사고 대응 (incident response)을 망칩니다. 우리는 알림 시스템 자체를 구축하는 데 들인 시간만큼이나, 언제 알림을 보내지 않을지 설계하는 데에도 많은 시간을 소비했습니다. 오류를 심각도에 따라 분류하고, 소음이 심한 것들은 조용하게 유지하세요.
- 폴백 (Fallback)을 테스트하세요. 보조 제공업체가 실제로 트래픽을 처리하는 것을 본 적이 없다면, 당신에게는 폴백이 없는 것입니다. 우리는 경로를 검증하기 위해 주기적으로 Termii를 통해 테스트 트래픽을 라우팅합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기