퇴사하기전 회사에서 알람 시스템을 만든적 있다. 그냥 뭐.. 이메일 보내주고, 카톡으로 광고성 메시지를 보내는 그런 것.. 생각해보니 그냥 돌아가게만 만드는게 중요했고 대강 만들었ㅇ..었... 쿨럭..
여튼 이번 포스팅에서는 이 알람 시스템을 제대로 만들려면 어떤 것들이 필요한지 좀 알아보도록 하자.
요구사항
이 알람 시스템은 일반적으로 아래와 같은 요구 사항이 있다.
- 푸시, SMS, 이메일등 모든 알람 서비스를 지원해야 한다.
- 아주 많은 양을 감당할 수 있어야 한다. (천만 건의 푸쉬, 백만 건의 SMS, 5백만 건의 이메일 등등)
푸쉬알림 구현
일단 휴대폰에서 푸쉬를 날리기 위해서는 크게 3가지 컴포넌트가 필요하다.
- 알람 제공자 :
말그대로 알람을 만들어서 알림 서비스로 보내는 주체다. 알람을 만들려면 아래와 같은 데이터가 필요하다.
- 단말 토큰 : 알람 요청을 보내는 데 필요한 고유 식별자다.
- 페이로드 : 알람 내용을 담은 JSON 객체다.
- APNS : 애플이 제공하는 원격 서비스다. 푸쉬 알람을 iOS 장치로 보내는 역할을 담당한다.
- iOS 단말 : 푸쉬 알림을 수신하는 사용자 단말이다.
참고로 안드로이드는 FCM이라는 원격 서비스가 따로 있다. 안드로이드 휴대폰으로 푸쉬를 날릴려면 FCM을 사용해 따로 개발해야 한다.
이메일 구현
이메일을 보내는 것은 비교적 쉽다. 구글이나 네이버 등에서 제공하는 SMTP를 사용하여 각자 사용하는 개발 언어에 맞는 라이브러리를 사용하여 구현하면 된다. 참고로 나는 nodemailer 라이브러리를 사용해서 구현했다. 또한 유료로 제공되는 서비스인 센드그리드, 메일침프 같은 서비스도 있으니 참고 바란다.
SMS 서비스 구현
SMS를 보낼 때는 보통 트윌리오, 넥스모 같은 제3 사업자의 서비스를 많이 이용한다. 이런 서비스는 대부분 사용 서비스라서 유료다.
연락처 정보 수집
위와 같은 각 알림 서비스를 준비했다면 이제 유저의 연락처 정보를 수집해야 한다. 이러한 정보는 당연히 민감한 정보들이기 때문에 수집할 때 유저에게 잘 고지하여야 한다.
일반적으로 앱 설치 또는 회원 가입하는 시점에서 유저에게 충분한 고시를 진행하고 수집한다. 이러한 절차를 통해 수집한 유저의 정보는 데이터베이스에 저장한다.
설계
위에서 말한 정보들을 모두 적용시키면 아래와 같이 초기제작을 할 수 있을 것이다. 전 회사에서는 딱 이정도 수준으로 개발했었다..

이 정도로만 해도 구동은 되겠지만 여러가지 문제가 있다.
- SPOF(Single-Point-Failure): 일단 서버가 하나 밖에 없다. 그말은 그 서버에 장애가 생기면 전체 서비스의 장애로 이어진다는 뜻이다.
- 규모 확장성 : 한대의 서비스로 푸쉬 알림에 관계된 모든 것을 처리하게되어 데이터베이스나 캐시 등 중요 컴포넌트의 규모를 개별적으로 늘릴 방법이 없다.
- 성능 병목: 각 알림을 처리하고 보내는 것은 자원을 많이 필요로 하는 작업일 수 있다. 따라서 트래픽ㄱ이 많이 몰리는 시간에는 시스템이 과부화 될 가능성이 크다.
그럼 위에 내용을 반영해서 초기 설계 버전을 수정해 보자.
- 일단 데이터베이스와 캐시를 알림 시스템의 주 서버에서 분리한다.
- 알림 서버를 증설하고 자동으로 수평적 규모 확장이 이루어질 수 있도록 한다.
- 메시지 큐를 사용해서 컴포넌트간 강한 결합을 끊는다.

뭔가 많이 복잡해 졌다.
위 그럼 처럼 수정하게 되면 알림 서버가 여러 서비스에서 들어오는 알림 요청을 받아 각 컴포넌트를 통해 알림을 보내게 된다.
알림 서버
이렇게 알림 서버를 두어 아래와 같은 기능을 제공하도록 한다.
- 스팸 방지를 위해 인증된 클라이언트만 알림 서버를 사용할 수 있도록 한다.
- 이메일 주소, 전화번호 등 기본적인 검증을 할 수 있도록 한다.
- 데이터베이스에 쿼리를 캐시를 통해 줄인다.
- 알림 데이터를 큐에 넣어 각 컨포넌트를 통해 알림이 전송되도록 한다.
메시지 큐/작업 서버
알림 서버가 메시지큐에 알림 정보를 넣으면 그 정보에 따라 각 컨포넌트의 작업 서버가 그 큐내용을 꺼내고 알림 전달 작업을 수행한다. 이렇게 하는 이유는 크게 아래와 같다.
- 시스템 컴포넌트간 의존성을 제거하기 위함이다. 큐를 사용하게 되면 알림 서버와 작업 서버간의 의존성이 제거된다. 알림서버는 큐에 맞는 형식으로 데이터를 넣어야 하고, 작업 서버는 큐에 들어있는 데이터를 뽑기만 하면 되는 구조임으로 두 컴포넌트간의 의존성이 제거 된다.
- 대량 전송에 대한 버퍼 역할을 할 수 있다. 갑자기 폭발적으로 많은 알림 요청이 들어온다면 서버로 바로 들어가 작업을 진행하는게 아닌 큐에 해당 요청들이 저장되면서 순차적으로 서버가 감당할 수 있는 만큼만 큐에서 뽑아 쓰는 방식으로 폭발적인 알림 요청에 대응할 수 있다.
- 큐를 사용하면 중복 및 데이터 손실을 방지 할 수 있다.
큐를 제작할 때 가장신경 써야 하는 부분이다. 중복은 같은 알림 요청을 여러번 유저에게 보내지 않게하기 위해 기존에 들어온 요청이 중복되는지를 확인하고 중복이라면 버리는 로직을 가지고 있어야한다. 또한 데이터 손실 방지를 위해 데이터베이스라던지 휘발되지 않는 저장소에 저장해둬야 한다. 중요한 알림이 전달되지 않는... 그런 상황을 항상 대비해야 한다.
전 회사에서는 aws SQS 서비스를 사용해서 구현했었다. 위에 말한 두가지 사항이 아주 잘 제공되는 서비스임으로 직접 구현하기 싫다면 SQS를 사용해 보는 것도 좋겠다. 참고로 유료인다
템플릿
하루에 수백만 건씩 전달되는 알림의 템플릿은 비교적 다 비슷하다.

위 이미지 처럼 카카오톡으로 알림을 보낸다면 이름, 멤버쉽 요금, 기간, 혜택 링크 등만 바뀌고 나머지 내용은 항상 같을 것이다. 따라서 이런 부분들을 모두 커버할 수 있는 템플릿을 만들어 두어 서버에 저장하고 서버 작업량을 줄일 수 있다.