본문 바로가기

메모

알림 기능 고찰 🤔

728x90

최근 알림 기능에 대한 고민을 했다. 그러면서 가진 몇 가지 고민을 정리해 보자.

외부 도메인 의존성

알림은 그 자체가 이벤트를 가지기보단, 외부 도메인에서 발생한 이벤트에 관심사가 있다. 그리고 외부 도메인에 필요한 알림 문구, 정보 등은 도메인 별 알림 성격마다 제각각이다. 최근 팀원과 고민했던 부분은 알림 도메인에 의존성이 과하게 늘어나는 것이다.

  문구 데이터
댓글 알림 우주먼지님이 "나 완전 짱세다" 게시글에 댓글을 남겼습니다. 이벤트 대상 유저, 이벤트 발행 주체 유저, 게시글 제목, 이벤트 타입
포인트 알림 우주먼지님이 100 포인트 정립했습니다. 이벤트 대상, 포인트, 이벤트 타입

위 예시를 보면 알 수 있듯 알림 유형에 따라 데이터 모델링이 반정규화된다. 댓글 알림에는 포인트 정보가 필요 없고, 포인트 알림에는 게시글 제목 정보가 필요 없다. 그렇다고 알림 유형별로 테이블을 정규화할 수도 없다. 알림은 서비스 전체적으로 비정기적으로 발생하는 일을 사용자가 쉽게 알 수 있도록 하는 장치이기 때문에 알림은 모아볼 수 있어야 한다.

 

그리고 필요한 데이터는 직접 저장하기보다는 필요할 때 각 도메인에 요청을 보내서 데이터를 조회해야 한다. 예를 들어 댓글 알림의 게시글 제목이, "나 완전 짱 세다"에서 "나 정말 약하다"로 변경되면 어떻게 될까? 만약 제목 자체를 알림 메시지로 저장하고 있다면 글 제목을 수정하기 전 알림과, 수정 후 알림은 동일한 게시글에 대한 알림이지만, 사용자는 별개의 알림으로 인지하게 된다.

 

위처럼 도메인이 늘어나면서 알림에 대한 요구사항이 점점 늘어나면 유저, 게시글, 포인트 등등 점차 외부 도메인에 대한 의존성이 높아지게 된다.

알림 발행

알림은 어떤 이벤트가 발생한 것을 사용자가 쉽게 인지할 수 있도록 하는 것이 목적이다. 경우에 따라선 이벤트 발행 즉시 빠르게 사용자가 확인할 수 있어야 하며, 분산 환경에서도 사용자에게 정확히 1번 전달과 같은 구현 방식에 따라서 난이도가 달리질 수 있다. 기본적으로 알림 자체는 외부 도메인에 의해 발행된 이벤트를 수신하여 적.절.히(마법의 단어) 데이터베이스가 저장한다.

Polling

일반적인 환경에서 준 실시간성을 보장하는 현실적인 구현 방식이다. 클라이언트는 일정 주기마다 서버에 새로운 알림이 있는지 확인한다. 이때 너무 짧은 주기로 조회할 경우 불필요한 서버 리소스가 낭비되는 문제점이 있기 때문에 서비스 형태(실시간성이 낮은 서비스의 경우 길게, 스트리밍 서비스 같이 실시간은 서비스는 짧게)에 따라서 주기를 조절하거나, 다른 방식으로 문제를 해결해야 할 수 있다.

Websocket

앞서 살펴본 polling 방식은 사용자가 많은 경우 잦은 API 요청으로 서버 자원이 소비하게 된다. Websocket은 양방향 통신 가능한, 매 통신하다 HTTP 헤더, 연결 수립 과정에 필요한 hand-shake 과정이 생략되기 때문에 효율적인 통신 방법 중 하나로 웹 서비스에서 실시간성을 필요로 하는 부분(실시간 채팅, 멀티플레이 게임)에 많이 이용된다.

Websocket 연결 과정

Websocket 연결 수립 및 통신 과정

Websocket의 경우 WS/WSS 두 가지 형태의 protocol이 존재한다. 위 사진은 트위치에서 websocket을 wss protocol과 연결하는 과정이다. 처음 TLS handshake 과정을 거친 후 HTTP 프로토콜을 이용해 연결 수립을 요청한다. 이 과정을 거친 후 클라이언트와 서버는 websocket protocol로 통신을 시작한다. 이렇게 연결된 websocket은 서버와 양방향 통신을 하며 동작하게 된다.

SSE (Server-sent-events)

SSE는 서버에서 클라이언트로 단방향 통신을 지원하는 방식으로, Websocket과 비슷하지만, HTTP protocol을 이용하는 점과 단방향 통신이란 차이가 있다.

SSE 통신 과정

통신 과정을 보면 더욱 명확해지는데, 먼저  통신 과정은 HTTP를 사용하며, 서버 -> 클라이언트 방향으로 데이터를 전송한다. 클라이언트는 잘 받았는지 여부(ACK)만 전송하게 된다. 추가적으로 SSE는 HTTP 2를 사용하지 않으면 최대 6개만 연결할 수 있기 때문에 HTTP 2는 필수사항이다.

지속 연결의 장단점

우리가 알아본 Websocket, SSE는 앞서 알아본 polling 방식과 다르게 이벤트 발행 순간에 클라이언트에게 정보를 전달하고, 매번 불필요한 API를 호출하지 않아도 된다는 장점이 있다. 하지만, 지속적으로 클라이언트와 연결되어있기 때문에 서버 자원을 점유하는 상황이 발생한다.

선택하기

앞서 알아본 내용을 바탕으로 현재 서비스에서 어떤 방식으로 구현이 필요한지는 서버 아키텍처에 따라 달라질 수 있다. 규모가 작고 모놀리스 구조의 서버로 동작한다면 Polling 방식을 선택한다.

하지만 많은 사용자가 요청하고, 실시간성이 중요한 경우는 별도의 서버를 구축해야 할 필요가 있다. 왜냐하면 Websocket, SSE 모두 서버와 클라이언트가 연결을 유지하기 때문에 많은 사용자가 몰릴 경우 API 요청에 대한 connection이 부족해 요청을 제대로 처리하지 못할 수 있기 때문이다. 따라서 이런 경우는 서비스 API 서버와 별도로 분리해서 운영할 필요가 있다.