프로젝트 - 알림시스템 설계
알림은 단순히 모바일 푸시 알림 뿐 아니라 SMS, 이메일의 세 가지로 분류할 수 있다.
1단계 문제 이해 및 설계 범위확정
- 어떤 알림을 지원해야 하는가? → 푸시, SMS, 이메일
- 실시간 시스템이어야 하는가? → 연성 실시간(soft real-time). 가능한 빨리 전달되어야 하지만 높은 부하가 걸렸을 때 약간의 지연은 무방하다.
- 어떤 단말인가? → iOS, 안드로이드, 랩톱, 대스크톱
- 알림은 누가 만들 수 있는가? → 클라이언트 앱 혹은 서버 측 스케쥴링
- 사용자가 알림을 받지 않도록 설정할 수 있는가? → 그렇다.
- 하루에 몇 건의 알림을 보낼 수 있어야 하는가? → 천만 건의 푸시, 백만 건의 SMS, 5백만 건의 이메일
2단계 개략적 설계안 제시 및 동의 구하기
알림 유형별 지원 방안
iOS 푸시
세 가지 컴포넌트가 필요하다.
- 알림 제공자(provider): 알림 요청을 만들어 푸시 알림 서비스로 보내는 주체.
- 필요 데이터:
- 단말 토큰: 고유 식별자
- 페이로드: 알림 내용 JSON / 딕셔너리
- 필요 데이터:
- APNS: 애플이 제공하는 원격 서비스. 푸시 알림을 보내는 역할
- iOS 단말: 푸시 알림을 수신하는 사용자 단말
안드로이드 푸시
APNS → FCM(Firebase Cloud Messaging)을 사용하는 거 외에 iOS 푸시에 필요한 컴포넌트와 동일
SMS 메시지
보통 트윌리오(Twilio), 넥스모(Nexmo) 등의 제3 사업자의 서비스를 유료로 이용한다.
이메일
가장 유명한 센드그리드(Sendgrid), 메일침프(MailChimp)를 이용한다.
연락처 정보 수집 절차
앱을 설치하거나 계정을 처음 등록하면 API 서버는 모바일 단말 토큰, 전화번호, 이메일 주소 등의 정보를 수집해 데이터베이스에 저장한다.
이 때, 이메일과 전화번호는 user테이블에, 단말에 대한 정보는 device에 저장한다.
알림 전송 및 수신 절차
개략적 설계안(초안)
- 1~N 까지의 서비스: 각각은 마이크로서비스 혹은 크론잡, 분산 시스템 컴포넌트일 수도 있다. 납기일 알림보내는 과금 서비스, 배송 알림을 보내는 쇼핑몰 등이 그 예이다.
- 알림 시스템: 1~N의 서비스가 알림을 전송하기 위한 API를 제공하고, 제3자 서비스에게 전달할 알림 페이로드를 만든다. 초안에서는 서버를 1대 구성한다.
- 제3자 서비스: 실제로 알림을 사용자에게 전달한다. 이 서비스와 통합할 때에는 확장성을 고려해야 한다. 새로운 서비스를 통합하거나 기존 것을 제거할 수 있어야 한다.
- iOS, 안드로이드, SMS, 이메일 단말은 알림을 수신한다.
문제점
- SPOF(Single-Point-Of-Failure): 알림 서비스 서버가 하나 뿐이면 장애가 발생했을 때 전체 서비스에 장애로 번진다.
- 규모 확장성: 한 대 서비스로 푸시를 처리하기 때문에 디비나 캐시 등의 중요 컴포넌트를 확장하기 어렵다.
- 성능 병목: 알림을 보내는 것은 자원을 많이 필요로 하는데 트래픽이 많이 몰리는 시간에는 과부하에 빠질 수 있다.
개략적 설계안(개선)
- 디비와 캐시를 알림 시스템 주 서버에서 분리
- 알림 서버를 증설하고 수평적 규모 확장할 수 있도록 함
- 메시지 큐를 이용해 컴포넌트들의 결합을 끊는다.
각 컴포넌트
- 알림 서버
- 알림 전송 API: 스팸 방지를 위해 인증된 클라이언트와 서비스만 사용 가능
- 알림 검증: 이메일, 전화번호 검증
- 데이터베이스 또는 캐시 질의: 알림에 포함시킬 데이터 가져오기
- 알림 전송: 알림 데이터를 메시지 큐에 넣고 병렬 처리
- 캐시: 사용자 정보, 단말 정보, 알림 템플릿 등을 캐시
- 디비
- 메시지 큐: 의존성 제거. 제3자 서비스 하나가 장애가 발생해도 다른 종류의 알림은 정상 동작
- 작업 서버: 큐에서 알림을 제3자 서비스로 전달
프로세스
- API 호출하여 알림 서버로 알림 전송
- 알림 서버는 사용자 정보, 단말 정보 등의 메타데이터를 캐시나 디비에서 가져옴
- 알림 서버는 각각에 맞는 이벤트를 만들어 큐에 발행
- 작업 서버는 큐에서 알림 이벤트 컨슘
- 작업 서버는 알림을 제3자 서비스로 전송
- 제3자 서비스는 사용자에게 알림 전송
3단계 상세 설계
안정성 (reliability) 확보
데이터 손실 방지
알림이 지연되거나 순서가 틀려도 괜찮지만 알림이 소실되면 안된다. 따라서 알림 데이터를 디비에 보관하고 재시도 매커니즘을 구현해야 한다. → 알림 로그 데이터베이스 유지
알림 중복 전송 방지
특성 상 중복 전송을 완전히 막을 수는 없지만 빈도를 줄이려면 중복을 탐지하는 매커니즘이 필요하다.
- 보내야 할 알림이 오면 알림 이벤트 ID를 검사해서 이전에 보냈는지 확인한다.
하지만 이도 100프로 방지가 불가능하다.
추가로 필요한 컴포넌트 및 고려사항
알림 템플릿
알림 메시지의 대부분은 형식이 비슷해서 처음부터 다시 만들 필요 없도록 parameter, 스타일, 추적 링크 등만 조정할 수 있도록 해준다.
알림 설정
사용자가 알림 설정을 상세히 조정할 수 있도록 해준다. 따라서 알림을 보내기 전에 해당 알림을 켜두었는지 확인해야 한다.
전송률 제한
한 사용자가 받을 수 있는 알림의 빈도를 제한한다. 너무 많이 보내면 알림 자체를 사용자가 끌 수도 있기 때문이다.
재시도 방법
알림 전송에 실패하면 재시도 전용 큐에 넣고, 계속 실패하면 개발자에게 노티를 보낸다.
푸시 알림과 보안
모바일 앱은 appKey, appSecret을 사용해 보안을 유지한다.
큐 모니터링
큐에 쌓인 알림이 많으면 서버를 증설한다.
이벤트 추적
데이터 분석 서비스는 알림 확인율, 클릭율, 실제 앱 사용으로 이어지는 비율 등의 메트릭을 제공한다.
4단계 마무리
중요 포인트
- 안정성: 메시지 전송 실패율을 낮추기 위해 안정적으로 재시도 한다.
- 보안: 인증된 클라이언트만 알림을 보낼 수 있도록 한다.
- 이벤트 추적 및 모니터링: 알림이 전송되기 까지 과정을 추적하고 모니터링한다.
- 사용자 설정: 사용자가 알림 수신 설정을 조정하고, 서버는 보내기 전 설정을 반드시 확인한다.
- 전송률 제한: 알림을 보내는 빈도를 제한한다.