Skip to content

juneyr.dev

알림 서비스 만들기 by 가상면접사례로 보는 시스템 설계 기초

System Design4 min read

서론

잘 쪼개진 개발팀에서 일할 때도 문제가 있다. 내가 직접 일부 기능을 손수 구현해야할 필요성을 못느껴 오래도록 그쪽에 관심이 없는 일이 생긴다는 점이다. 그런 부분은 이 책이 어느 정도 해소해 줄 수 있을 것 같다. 최근 많은 사람들이 좋다! 라고 말했었던, '가상 면접 사례로 배우는 대규모 시스템 설계 기초' 를 읽고 인상적인 부분만 정리해본다.

오늘은 그 중에 알림 서비스를 정리해보려고 한다.

여러가지 기능을 생각해보자 🧐

개인 블로그는 대표적으로 단순한 서비스다. 이 블로그만 생각해도 유저가 페이지에 들어오면 포스트를 내려준다. 작성자는 포스트를 작성하고 수정하고 삭제할 수 있다. 주력 기능이 조회이고, 가끔씩 생성, 수정, 삭제 요청이 나간다. CRUD 가 메인 기능으로 동작하니 구조를 이해하기도 편하고, 프레임워크에 크게 구애받지도 않을 것 같다.

그런데 만약 내가 앱 서비스를 구현한다고 하자. 그러면 개발자인 내가 유저의 관심을 손쉽게 얻을 수 있는 것이 '알림' 즉 notification / push 서비스다. 앱 서비스가 아니어도 좋다. 가입하면 메일이 날아가는 데, 이것 또한 유저 입장에서 알림이다. 이런 건 어떻게 구현할까? 나는 알림을 구현할 때 사내에 있는 플랫폼 sdk 를 사용해서 해당 플랫폼에 요청을 보내는 방식을 사용했다. 그런 게 없다면 날것의 알림은 어떻게 동작하는 걸까? (p.165)

채팅이 서비스에 필요하다고 하자. 앱 to 앱인가? 웹 to 앱인가? 소켓으로 실시간, 뭐시기... 하지 않을까요? 싶지만 실제로 구현해보지 않으면 모른다. 우리는 카카오톡을 쓰고 가끔 라인을 사용하며 페이스북 쓸 때는 페메를 하고 인스타를 할 때는 DM 을 받는다. 이건 어떻게 구현하면 좋을까? (p.197 )

검색을 생각해보자. 검색은 구글이나 네이버 처럼 큰 기업에서만 가능한 거 아니에요? 그들이 문서의 양도 절대적으로 많고 검색 알고리즘이 굉장히 편리하겠지만, 서비스 안에서도 검색이 필요하다. 그런데 검색어를 자동완성하려면? 문자소를 나눠서 어쩌구..... 하지않을까요?? 싶지만 구체적인 내용은 잘 모르겠다. (p.223)

이렇게 서비스 개발자여도 구체적으로 모르는 기능들이 참 많다. '가상 면접 사례로 배우는 대규모 시스템 설계 기초' 를 통해 알림 서비스 부터 찬찬히 살펴보자.

알림 서비스의 구현

알림 시스템은 모바일 푸시 알림, SMS 메시지, 그리고 이메일의 세가지로 분류할 수 있다. 잠깐 생각해보면, 모바일은 iOS / Android 를 지원해야할 것이다. SMS 는 핸드폰이면 종류 무관하게 받을 수 있어야한다. 이메일은 이메일 주소만 있으면 받을 수 있을 것 같다. 그 과정에서 SMTP 등 이메일을 보내는 프로토콜을 써야할 것 같다.

그럼, 책에서 설명하는 알림 유형별 지원 방안을 살펴보자.

푸시 알림

iOS 에서 푸시 알림을 보내기 위해서는 세 가지 컴포넌트가 필요하다.

  • 알림 제공자

    • 알림 요청을 만들어서 APNS(애플 푸시 알림 서비스, Apple Push Notification Service) 로 보내는 주체다.
    • 클라이언트일 수도, 서버일 수도 있을 것이다!
    • 요청에는 다음과 같은 데이터가 필요하다.
      • 단말 토큰(device token): 알림 요청을 보내는데 필요한 고유 식별자. 사용자의 어떤 기기에 보낼 건가요?
      • 페이로드(payload) : 알림의 내용이 담긴 JSON 딕셔너리
  • APNS(애플 푸시 알림 서비스, Apple Push Notification Service)

    • 요청받은 푸시 알림을 iOS 디바이스 (기기)로 보내는 역할을 담당한다)
  • iOS 디바이스(기기 / 단말)

    • 알림을 수신하는 사용자용 단말

한편, Android 푸시 알림도 비슷한 절차로 전송된다. APNS 대신 FCM 을 사용한다는 점만 다르다. (* 첨: 옛날에는 GCM이였던 듯. 전체적으로 마이그레이션하는 과정이 있었던 것으로 기억한다.)

  • 알림 제공자
  • FCM (파이어베이스 클라우드 메시징, Firebase Cloud Messaging)
  • 안드로이드 단말

SMS 메시지

SMS 메시지를 보낼 때는 트윌리오 (Twilio), 넥스모(Nexmo) 와 같은 제 3 사업자의 서비스를 많이 이용한다고 한다. 그러나 저자인 알렉스 쉬는 외국인이니까.. 한국은 뭘 쓸까? 선호도와 상관없이 찾아보았다.

책에서는 구체적으로 sms 를 보내는 로직에 대해서는 설명되지 않았다. 이 부분은 아쉽다.

이메일

대부분의 회사는 고유 이메일 서버를 구축할 역량은 갖추고 있다. 그럼에도 많은 회사가 상용 이메일 서비스를 이용한다. 그중 유명한 서비스로 샌드그리드(Sendgrid), 메일침프(MailChimp)가 있다. 전송 성공률도 높고, 데이터 분석 서비스도 제공한다.

알림서비스 하기 전에, 정보를 알아야한다

그런데 알림 서비스를 하기 위해서 생각해야하는 것이 있다. 유저의 개인정보를 어떻게 수집할 것인가! 유저의 개인정보는 민감한 문제이기도 하다. 수집하기 전에 이 정보들이 결합되어서 한 개인을 식별할 수 있는지도 검토가 필요하겠다.

일단 수집 방법에 대해 논의해보자. 알림을 보내려고하면 위와 같이 모바일 단말 토큰, 전화번호, 이메일 주소 등의 정보가 필요하다. 사용자가 우리 앱을 설치하거나 계정을 등록하면 서버는 해당 사용자의 정보를 수집해 DB에 저장한다. 이때 한 사용자가 여러 device 정보를 담을 수 있으니, 1:N 형태로 설계 하는 것이 유리하다.

알림 보내는 서비스 만들기

알림의 요구사항에 따라서 알림을 만드는 곳이 달라진다. 그 서비스는 별도의 마이크로서비스 일 수도 있고, 크론잡(cronjob) 일수도, 분산 시스템 컴포넌트 일수도 있다.

알림 시스템(혹은 core)은, 알림 전송/수신 처리의 핵심이다. 이 시스템은 서비스에 알림 서비스를 보내는 API 를 제공해야하고, 서드파티 서비스에 전달할 payload 를 만든다. 우리가 만들 서비스에서 알림 관련된 서비스는 이 알림 시스템을 찔러서 보내도록 구성한다.

이 시스템에서 서드파티 서비스는 실제로 사용자에게 알림을 전달하는 역할을 한다. 서드파티 서비스를 추가 및 통합할 때 주의 점은 확장성이다. 쉽게 새로운 서비스를 통합하거나 기존 서비스를 제거할 수 있어야 한다는 뜻이다. 또 하나 고려해야 할 것은, 어떤 서비스는 다른 시장에서 사용할 수 없다는 것이다. 가령 중국에서는 FCM 을 사용하지 못해서 Jpush, PushY 와 같은 서비스를 사용해야만 한다.

이 구조를 간단하게 그려보면 다음과 같다.

notification-system

알림서비스와 알림 시스템까지는 우리가 만들 서비스의 일부가 된다. 외부 서드파티가 이 전체 시스템을 구성하고 있지만 말이다. 그러나 이런 구조는 바로 한계가 보인다. 바로 하나의 포인트가 잘못되면 모든 시스템이 망가진다 는 점이다. 이런 구조에서는 알림 시스템 (core) 가 느려지는 순간 병목이 생기며, 만약 과부하가 걸린다면 시스템이 정지할 수 있다. 이 점을 개선하려면 무얼 도입할 수 있을까 ?

이 책에서는 다음과 같은 점을 제안한다.

  • 데이터베이스와 캐시를 알림 시스템의 주 서버에서 분리한다. (즉 하나의 서버에 데이터베이스 / 캐시 / 시스템 비즈니스 로직 서버를 두지 않는다.)
  • 알림 서버를 증설하고 자동적으로 수평적 규모 확장이 이루어질 수 있도록 한다.
  • 메시지 큐를 이용해 시스템 컴포넌트 사이의 강한 결합을 끊는다.

이를 기반으로 만들어진 내용은 다음과 같다.

noti-system, but better

알림 서버는 다음의 기능만 수행한다.

  • 알림 전송 API
  • 알림 검증
  • 데이터베이스 와 캐시 질의
  • 알림 전송
    • 알림 데이터를 메시지 큐에 넣는 부분까지만 담당한다.
    • 큐를 여러개 사용하므로, 병렬적으로 처리가 가능하다.

캐시에서는 자주 사용되며 겹치는 정보들이 저장된다. 사용자 정보, 단말 정보, 알림 템플릿 등이다. DB는 사용자, 알림 설정 등 다양한 정보를 저장한다. 메시지 큐는 시스템 컴포넌트 간 의존성을 제거하기 위해 사용하고, 버퍼 역할도 한다. 이 설계에서는 서드파티마다 다른 메시지큐를 사용하였으므로, 만약 iOS의 서드파티가 장애가 있다고 해도 다른 알림은 정상적으로 전송된다.

이렇게 하면 기본 골격의 알림시스템은 완성이다. 그러나 알림 시스템을 고도화할 때는, 더 많은 고려사항이 필요하다고 책에서는 이야기 하고 있다.

  • 알림 템플릿
    • 알림 메시지의 대부분은 형식이 비슷하다. 템플릿을 사용하면 유사성을 고려하여 모든 부분을 만들지 않아도 괜찮게 해준다.
  • 알림 설정
    • 사용자는 알림에 쉽게 피곤함을 느낀다. 많은 서비스는 알림 설정을 상세히 조정할 수 있도록 하고 있다. 이 설정을 도입한 뒤에는 특정 알림을 보내기 전에 사용자의 설정을 조회하는 작업이 필요하다.
  • 전송률 제한
    • 한 사용자가 피로를 느끼게 하지 않기 위해, 알림의 빈도를 제한하는 방법도 있다.
  • 재시도 방법
    • 서드파티가 전송에 실패하면, 해당 알림을 재시도 큐에 넣는 작업
  • 큐 모니터링
    • 알림 시스템 모니터링 중 중요한 부분이다. 만약 큐에 쌓인 알림 갯수가 너무 크다면, 작업 서버 증설을 고려한다.
  • 이벤트 추적
    • 알림 확인과 클릭율 등의 메트릭을 추적하면, 사용자를 이해할 수 있다.
  • 보안
    • 인증된 클라이언트만이 알림을 보내야한다. iOS / android 앱 의 경우, 알림 전송 API 는 appKey와 appSecret 을 사용해서 보안을 유지한다.

기본적인 알림 시스템 구성뿐 아니라 위와 같은 점을 고려하면, 사용자 설정과 안정성, 보안과 추적을 만족하는 시스템을 쌓아올릴 수 있다. 물론 개별 사항을 적용할 때마다 적용할 기술에 대한 검토, 그리고 그 사항이 기존 시스템에 대해 어떤 영향을 끼칠 지 검토해야하는 것은 물론이다.

참고

'가상 면접 사례로 배우는 대규모 시스템 설계 기초', 알렉스 쉬, 인사이트