알림 시스템 설계 - 대규모 시스템 설계 기초 10장

broccolism·2022년 10월 16일
2

가상 면접 사례로 배우는 대규모 시스템 설계 기초

슬슬 재밌는 부분이 나온다. 본격적으로 특정 서비스를 설계하기 시작한다. 점점 다이어그램이 많아질 것 같다.

알림 시스템의 종류와 구조

iOS, 안드로이드 푸시 알림

알림 시스템이라고 하면 가장 먼저 떠오르는 것이다. (사실 나는 알림 시스템이라고 하면 이런 푸시 알림만 생각했다.ㅎ) 아이폰과 갤럭시의 푸시 알림 모양이 다른걸로 보아 알 수 있듯이, 푸시 알림 자체는 각 운영체제에서 보여준다. 예를 들어 카카오톡 푸시 알림을 렌더링하는 주체는 카카오톡 어플이 아니라 iOS, Android인 것이다. 따라서 각 운영체제가 알아들을 수 있는 포맷으로 ‘알림’ 데이터가 전달되어야 한다. 이를 위해 iOS는 APN(Apple Push Notification), Android는 FCM(Firebase Cloud Messaging) 을 사용하면 된다.

… 라고 책에 적혀있지만 사실 FCM 부분은 좀 이상하다. 링크를 눌러서 나오는 페이지를 보면…

FCM은 크로스 플랫폼 메시징 솔루션이라고 나온다. 실제로 FCM을 이용해서 플러터 앱이나 안드로이드, iOS 앱으로 알림을 보낸 경험이 있다. 흠.. 뇌피셜로는 원래 안드로이드용으로 쓰던걸 구글에서 상업용 솔루션으로 푼게 아닌가! 하는 생각이 들기도 한다.

SMS 메시지, 이메일

이 둘로 보내는 내용도 알림이라고 볼 수 있다. 푸시 알림과 전체적인 시스템 구조는 똑같고, 사용자가 받는 알림의 형태만 메시지나 이메일인 것이다. 푸시 알림을 보내기 위해 APN이나 FCM이 필요한 것처럼 메세지, 이메일을 보내기 위해서도 특화된 SMS 서비스, 이메일 서비스가 필요하다.

간단하지만 깨지기 쉬운 구조

애플리케이션을 곧바로 알림 시스템과 연결한 구조다. 이렇게 되면 가장 큰 문제점은 SPOF가 생긴다는 것이다. 바로 알림 시스템이다. 여기가 죽으면 아무런 알림도 보낼 수 없다.

알림 시스템이 죽지 않고 살아있더라도 문제가 생긴다. 트래픽이 몰리면 서버 한대로는 감당할 수 없는 양의 알림 전송 요청을 받을 수 있기 때문이다. 알림 전송이 지연되거나 누락되는건 시간문제다.

좀 더 견고한 구조

앞서 봤던 구조에서 SPOF 문제를 해결한 구조다.

각 애플리케이션에서 알림 서버로 ‘알림 생성 요청’을 보낸다. 알림 서버에서는 그 요청과 DB에 저장되어있던 사용자 정보, 단말 토큰, 알림 설정값 등을 조합하여 알림 데이터를 만든다. 그리고 이 알림을 곧바로 FCM과 같은 써드파티 알림 서비스에 보내는게 아니라, 메세지 큐로 보낸다. 메세지 큐는 보내려는 알림의 형태(안드로이드 푸시 알림, iOS 푸시 알림 등)마다 하나씩 존재한다. 메세지 큐에 쌓여있는 알림은 작업 서버가 꺼내서 써드파티 알림 서비스로 전달한다.

메세지 큐가 있으면 알림 데이터가 유실되는걸 막을 수 있을 뿐만 아니라 각 컴포넌트간의 결합을 느슨하게 할 수 있다. 이전 구조에서는 알림 하나를 처리할 때, 알림 시스템의 작업이 끝나지 않으면 각 써드파티 서비스로 알림이 전달될 수 없었다. 하지만 메세지 큐가 있기 때문에 알림 서버와 써드파티 서비스는 독립적으로 작동할 수 있다. 메세지 큐를 알림의 형태별로 하나씩 둔 것도 독립성을 위해서다. 만약 SMS 서비스에 장애가 있다고 하더라도 나머지 유형의 알림은 정상적으로 보낼 수 있다.

책에서 나온 구조가 100% 정답은 아니다. 또 다른 방향으로 설계할 수 있다. 같이 스터디하는 분께서 당근 마켓의 푸시 알림 서비스 구조 글을 가져오셨는데, 비슷하면서도 살짝 다르다. 전체적인 큰 구조는 같다. 위 그림의 알림 서버는 Express.js Server에, 작업 서버는 Node.js Workers에 대응된다. 하지만 당근마켓의 워커 서버가 좀 더 많은 일을 하고 있다는 점이 다르다. 푸시 토큰 관리를 이쪽에서 하기 때문이다.

안정성: 데이터 손실 & 중복 전송 방지

알림 전송 시스템의 가장 중요한 요구사항 가운데 하나는 어떤 상황에서도 알림이 소실되면 안 된다는 것이다.

알림 소실을 막기 위해서는 retry 메커니즘이 필요하다. ‘좀 더 견고한 구조’ 그림을 잘 살펴보면 작업 서버에서 큐로 retry 하는 부분이 있다. 작업 서버에서 알림 로그를 쌓는 DB를 하나 두고, 써드파티 서비스로부터 알림 전송에 실패했다는 응답을 받으면 해당 로그를 보고 다시 알림을 재전송하면 된다. (물론 이외에도 다른 재시도 정책이 많을 것 같다.)

알림이 중복 전송되는 것도 막아야 한다. 분산 시스템 환경에서 정확히 1번만 데이터를 전송하는건 불가능하다고 하는데, 이에 대해서는 다양한 의견이 있는 것 같다. 하지만 사용자 입장에서 1번만 전송되었다고 생각되게 만드는건 가능하다. 책에서는 중복된 이벤트인지 검사하는 로직을 클라이언트쪽에 넣어두는 방법을 언급했다.

추가 고려사항

  • 알림 템플릿: 전송할 알림의 형식을 일관성 있게 유지하고 오류 가능성을 낮출수 있다. 알림 작성 시간도 줄일 수 있다.
  • 사용자의 알림 설정: 알림 종류마다 수신 허용/거부 할 수 있는 기능.
  • 전송률 제한: 사용자가 너무 많은 알림을 받아서 알림 자체를 끄지 않도록 제한하는 방법이 필요하다.
  • 큐 모니터링: 알림 시스템 모니터링의 중요 지표 중 하나다. 큐에 너무 많은 메세지가 쌓여있다면 데이터가 적절히 분산되지 않았거나 작업 서버가 제대로 처리를 못 하고 있음을 의미한다.
  • 이벤트 추적: 알림 확인율, 클릭율, 실제 앱 사용으로 이어지는 빈도 등 알림을 보낸 뒤에도 사용자 행동을 추적한다. 보통 데이터 분석 서비스에서 이런 사용자 행동 추적 기능도 제공한다.
profile
코드도 적고 그림도 그리고 글도 씁니다. 넓고 얕은 경험을 쌓고 있습니다.

0개의 댓글