알림(Notification) 시스템은 모바일 푸시 알림, SMS 메시지, 이메일의 세 가지 주요 채널로 구성된다. 이러한 시스템은 수많은 사용자에게 안정적으로 이벤트를 전달해야 하므로, 확장성과 안정성을 모두 고려한 아키텍처 설계가 필요하다.
1단계 문제 이해 및 설계 범위 확정
요구사항 정의
- 어떤 종류의 알림
- 실시간 시스템인지
- 어떤 종류의 단말을 지원해야하는지: iOS 단말, 안드로이드 단말, 랩톱/데스크톱 등
- 사용자에게 보낼 알림은 누가 만들 수 있나요?
- 클라이언트 애플리케이션 프로그램이 만들 수도 있고, 서버 측에서 스케줄링도 가능
- 사용자의 알림 설정 가능 유무
- 하루에 몇 건의 알림을 보낼 수 있는지
2단계 개략적 설계안 제시 및 동의 구하기
알림 유형별 지원 방안
iOS, 안드로이드, SMS 메시지, 이메일의 알림 메커니즘이 어떻게 동작하는지 알아보자.
1. iOS 푸시 알림
iOS에서 푸시 알림을 보내기 위해서는 세 가지 컴포넌트가 필요하다.
APNS 동작 방식에 대해 자세히 알고싶다면 해당 사이트를 참고하면 좋다.

- 구성 요소
- 알림 제공자(provider) : 알림 요청을 만들어 애플 푸시 알림 서비스(APNS)로 보내는 주체이고 알림 요청을 만들려면 단말 토큰(device token), 페이 로드(payload) 가 필요하다.
- APNS(Apple Push Notification Service) : 애플이 제공하는 원격 서비스로 푸시 알림을 iOS로 보내는 역할을 담당한다.
- iOS 단말 : 푸시 알림을 수신하는 사용자 단말이다.
- 요소: 단말 토큰(Device Token)과 페이로드(Payload)가 필요.
2. 안드로이드 푸시 알림
안드로이드에서 푸시 알림을 보내기 위해서는 세 가지 컴포넌트가 필요하다.
FCM 동작 방식에 대해 자세히 알고싶다면 해당 사이트를 참고하면 좋다.

알림 제공자(provider) : 알림 요청을 만들어 애플 푸시 알림 서비스(FCM)로 보내는 주체이고 알림 요청을 만들려면 단말 토큰(device token), 페이 로드(payload) 가 필요하다.
FCM(Firebase Cloud Messaging) : Google Firebase에서 제공하는 원격 서비스로 푸시 알림을 iOS로 보내는 역할을 담당한다.
안드로이드 단말 : 푸시 알림을 수신하는 사용자 단말이다.
SMS 메시지
SMS 메시지를 보낼 때는 트윌리오, 넥스모 같은 제3사업자의 서비스를 많이 이용한다.

이메일
많은 회사가 상용 이메일 서비스를 이용한다. 센드그리드, 메일침프와 같은 서비스가 있다.

연락처 정보 수집 절차
그렇다면 각 서비스는 알림을 보낼 단말을 어떻게 찾아서 알림을 보낼까? 알림을 보내려면 기기 단말 토큰, 전화번호, 이메일 등의 정보가 필요하다. 이 정보들은 사용자가 앱을 설치하거나, 처음으로 계정을 등록할 때 API서버에서 사용자의 정보를 수집하여 데이터베이스에 저장한다.
개략적 설계안 (초안)

- 각각의 서비스 : 마이크로서비스, 크론잡, 과금서비스와 같은 다양한 서비스다.
- 알림 시스템 : 알림 시스템은 알림 전송/수신 처리의 핵심으로 현재는 서버 1개만 사용하는 시스템이라고 가정했다.
- 제3자 서비스에 전달할 알림 페이로드를 만들어 낼 수 있어야한다.
- 이 시스템은 서비스 1~N에 알림 전송을 위한 API를 제공해야한다.
- 제3자 서비스(third party services) : 이 서비스들은 사용자에게 알림을 실제로 전달하는 역할을 한다.
그러나 이 설계에는 몇가지 문제가 있다.
- SPOF(Single-Point-Of-Failure) : 알림 서비스 서버가 장애가 생기면 전체 서비스 장애로 이어진다.
- 규모 확장성 : 한대 서비스로 푸시 알림에 관계된 모든 것을 처리하므로, 데이터베이스 / 캐시 등 중요한 컴포넌트의 규모를 개별적으로 늘릴 방법이 없다.
- 성능 병목 : 알림을 처리하고 보내는 것은 자원을 많이 필요로 한다. 따라서 모든 것을 한 서버로 처리하면 사용자 트래픽이 많이 몰리는 시간에는 시스템 과부하 상태에 빠질 수 있다.
개략적 설계안 (개선된 버전)
주요 개선 방향
- 데이터베이스와 캐시를 알림 시스템의 주 서버에서 분리한다.
- 알림 서버를 증설하고 자동으로 수평적 규모 확장이 이루어질 수 있도록 한다.
- 메시지 큐를 이용해 시스템 컴포넌트 사이의 강한 결합을 끊는다.

- 각각의 서비스 : 알림 시스템 서버의 API를 통해 알림을 보낼 서비스이다.
구성 요소
-
알림 서버
- 알림 전송 API : 스팸 방지를 위해 보통 사내 서비스 또는 인증된 클라이언트만 이용가능하다.
- 알림 검증 : 이메일 주소, 전화번호 등에 대한 기본적 검증을 수행한다.
- 데이터베이스 또는 캐시 질의 : 알림에 포함시킬 데이터를 가져오는 기능이다.
- 알림 전송 : 알림 데이터를 메시지 큐에 넣는다. 하나 이상의 메시지 큐를 사용하므로 알림을 병렬적으로 처리할 수 있다.
-
캐시 : 사용자 정보, 단말 정보, 알림 템플릿 등을 캐시한다.
-
데이터 베이스 : 사용자, 알림, 설정 등 다양한 정보를 저장한다.
-
메시지 큐 : 시스템 컴포넌트 간 의존성을 제거하기 위해 사용한다. 다량의 알림이 전송되어야 하는 경우를 대비한 버퍼 역할도 한다.
-
작업 서버 : 메시지 큐에서 전송할 알림을 꺼내서 제3자 서비스로 전달하는 역할을 담당하는 서버이다.
3단계 상세 설계
지금까지 개략적 설계를 진행해봤다. 몇가지 사항을 더 고려하여 최종 설계안을 도출해보자.
- 안정성 : 데이터 손실 방지, 알림 중복 전송 방지
- 추가로 필요한 컴포넌트 및 고려사항 : 알림 템플릿, 알림 설정, 전송률 제한, 재시도 메커니즘, 보안
안정성
데이터 손실 방지
알림 전송 시스템의 가장 중요한 요구사항 중 하나는 어떤 상황에서도 알림이 소실되면 안 된다는 것이다. (알림이 지연되거나 순서가 보장되지는 않아도 된다.)
알림 중복 전송 방지
같은 알림이 여러 번 반복되는 것을 완전히 막는 것은 가능하지 않다. 대부분 알림은 딱 한번 전송되지만 분산 시스템 특성상 가끔은 같은 알림이 중복되어 전송되기도 한다.
- 보내야할 알림이 도착하면 그 이벤트 ID를 검사하여 이전에 본 적 있는 이벤트인지 살피고 중복이벤트라면 버린다. 그렇지 않으면 알림을 발송한다.
- 즉, 이벤트 ID 기반 중복 검증 후 이전 전송 기록을 단기 캐시에 저장하여 중복 수신 방지.
추가로 필요한 컴포넌트 및 고려사항
알림 템플릿
대형 알림 시스템은 하루에도 수백만 건 이상의 알림을 처리하고 대부분 알림 메시지 형식이 비슷하다. 변수 치환 기반 템플릿 사용으로 효율적 메시지 생성하낟.
본문:
인기 상품 [item_name]이 재입고 되었습니다. 현재 잔여 수량은 [item_cnt] 입니다.
알림 설정
많은 웹사이트와 앱에서는 사용자가 알림 설정을 상세히 조정할 수 있도록 하고있다. 알림 설정 테이블에 보관되며 다음과 같은 필드들이 필요하다.
user
channel(알림이 전송될 채널, 푸시 알림, 이메일 등)
opt_in(해당 채널로 알림을 받을 것인지 여부)
전송률 제한
- 사용자별/서비스별 전송 빈도를 제어한다.
- Redis 등 캐시 시스템을 활용한 제한 구현 가능하다.
재시도 방법
제3자 서비스가 알림 전송에 실패하면 해당 알림을 재시도 전용 큐에 넣는다. (만약 같은 문제가 계속 발생하면 개발자에게 통지해야함)
푸시 알림과 보안
iOS와 안드로이드 앱의 경우, 알림 전송 API는 appKey, appSecret을 사용하여 보안을 유지한다. 인증되었거나 승인된 클라이언트만 해당 API를 사용하여 알림을 보낼 수 있도록 한다.
수정된 설계안
지금까지 설명한 내용을 모두 반영하여 수정한 설계안이다.

4단계 마무리
- 안정성(Reliability) : 메시지 전송 실패율을 낮추기 위해 안정적인 재시도 메커니즘을 도입했다.
- 보안(Security) : 인증된 클라이언트만 알림을 보낼 수 있도록 한다.
- 이벤트 추적 및 모니터링 : 알림이 만들어진 후 성공적으로 전송되기까지의 과정을 추적, 시스템 상태를 모니터링하기 위해 알림 전송의 각 단계마다 이벤트를 추적한다.
- 사용자 설정 : 사용자가 알림 수신 설정을 조정할 수 있도록 하였다. 알림을 보내기 전 반드시 해당 설정을 확인하고 알림을 보낼 수 있도록 해야한다.
참고