5. 알람기능 구현

선종우·2023년 8월 26일
0

1. 서비스의 핵심 알람기능

  • 서비스의 핵심 기능은 바로 매치와 메시지다.
  • 두 기능 모두 로그인한 "나"의 행동으로도 이벤트가 발생하지만, 상대방에 의해서도 이벤트가 발생한다.
    ex) 상대방 -> 나 메시지 전송, 상대방 -> 나 좋아요를 통한 매치 성사
  • 나(클라이언트)에 의해 매치나, 메시지가 발생한 경우에는 응답값에 해당 내용을 포함시키면 되기 때문에 어려울 게 없다. 문제는 상대방에 의해 이벤트가 발생한 경우다.
  • 웹은 기본적으로 클라이언트의 요청과 서버의 응답으로 이뤄진다. 그렇기 때문에 단순히 기존에 사용하던 방식만으로는 알람 기능을 구현할 수 없었다.

2. 선택 가능한 방법 4가지

2.1. Polling

  • 단순히 일정 주기로 클라이언트가 서버에게 요청을 보내는 구조다. 사용자가 로그인한 동안 while문으로 서버 요청을 계속 보내면 된다. 그러나 이벤트가 없는 경우일 때도 클라이언트는 불필요한 요청을 계속 보내야 한다. 또한 Polling 주기가 길어질 수록 데이터의 실시간성이 떨어진다는 문제가 있다. 구현이 용이하기 때문에 선택지가 될 수 있었지만, 모바일 환경에서 불필요한 데이터가 발생하게 될 수 있다는 생각에 선택지에서 제외했다.

2.2. Long Polling

  • 클라이언트의 요청에 대해 서버가 바로 응답을 보내지 않고 이벤트가 발생할 때까지 Connection을 유지한다.
  • Polling방식보다는 실시간성이 증가하면서도 불필요한 요청수를 줄일 수 있다. 다만 결국은 응답이 올 수도 있는 상황 때문에 불필요한 요청을 보낸다는 점은 동일하다.
  • 스프링은 DeferredResult를 사용하면 된다. Polling보다는 나은 선택이 될 수 있겠지만 자료도 많이 없고, SSE가 있는데 굳이 사용할 이유가 없어 더 알아보지는 않았다.

2.3. SSE(Sever Sent Event)

  • HTML5 표준기술로서 클라이언트 요청없이도 서버 -> 클라이언트로 데이터를 보낼 수 있다.(최초 연결 시에는 클라이언트 측에서 요청을 보내긴 해야 한다)
  • 앞선 두 기술과 다르게 실시간 이벤트 데이터를 받아볼 수 있는 기술이다.
  • HTTP를 사용하기 때문에 방화벽 개방 등의 절차가 불필요하다.
  • 서버는 클라이언트 요청에 따라 sse 커넥션을 유지해야하기 때문에 HTTP 1.1 이상에서 사용할 수 있다.(keep-alive 사용, keep-alive 없이도 사용할 수 있다지만 구현이 복잡해진다고 한다. by chatGPT)

2.4. Websocket

  • 웹소켓은 양방향 통신을 지원하는 Layer 7 프로토콜이다.
  • 최초 HTTP를 통해 통신이 이뤄진 후 클라이언트와 서버는 기존 HTTP 포트를 이용해 Websocket 통신을 하게 된다.wiki
  • SSE보다는 조금 더 양방향 실시간성을 보장해줄 수는 있으나, 소켓 연결 해제에 따른 자동 연결 시 로직 구현 등, 프론트엔드 개발자가 프로토콜 자체에 대해 조금 더 관리해 주어야 한다.(SSE의 경우 이벤트에 대한 처리로직만 구현해주면 됨)

3. SSE를 선택

  • 현재 내 서비스에서는 굳잉 양방향 실시간성이 필요하지 않았다. 사용자는 알람만 받아보면 되고, 채팅기능의 경우도 sse와 http request로 실시간성을 보장할 수 있기 때문이다.
  • Spring에서는 SSE를 편리하게 구현할 수 있도록 SseEmitter 클래스를 지원하고 있다.
  • 구현차원에서 보자면 sse 시나리오는 아래와 같다.
    1. 클라이언트는 sse 연결 요청을 서버로 보낸다.
    2. 서버는 요청을 받아 SseEmitter 객체를 생성한다.
    3. 생성된 객체는 이후 알람을 전송할 때 사용해야 하므로 자체 저장소에 저장한다.(객체 자체가 클라이언트와의 연결을 유지시켜주기 때문에 DB 같은 것이 아닌 ConcurrentHashmap등을 이용해 객체를 힙메모리에 유지 및 찾을 수 있는 형태로 저장해야 한다.)
    4. 알람 이벤트 발생 시 해당 클라이언트에 해당하는 SseEmitter객체를 저장소에서 찾은 다음 메시지를 전송한다.
  • 내 경우에는 사용자 id + randomId를 붙여 각 SseEmitter의 식별자로 사용하였다.

4. SSE vs Polling, SSE vs WebSocker

  • SSE를 이용해 알람서비스를 무리없이 구현했다. 다만 구현이 다 끝나고 나니 SSE에 대한 정확한 이해없이 사용한 감도 있고, 왜 이 방식을 채택했는 지에 대해 나름의 정리를 필요했다.

4.1 SSE와 Polling에 대한 고찰

  • sse의 연결 요청은 client가 수행한다.

  • sse는 연결된 시간 동안 connection을 유지하기 때문에 서버 자원을 소모한다. 따라서 sse connection을 너무 길게 설정하면 불필요하게 서버 자원을 소모한다.

  • 한편 client는 데이터가 오지 않은 상태에서 설정된 retry 시간이 지나면 서버로 다시 sse 연결 요청을 한다.

  • 즉 sse 시간을 너무 짧게 설정하면 long polling방식과 차이가 없어지고, sse를 너무 길게 설정하면 서버 자원이 소모될 염려가 있다.

  • polling과 sse의 가장 큰 차이는 클라이언트 구현에 있다. (서버 입장에서는 Connection을 유지해놓고 데이터를 전송한다는 점에서는 큰 차이가 없다)

  • Long polling의 경우 이벤트 발생 시 request 재전송 등의 로직을 프론트엔드 개발자가 구현해야 한다. 그러나 sse의 경우 이러한 처리를 브라우저 자체가 지원한다.

    EventSource interface allows the application to focus on the business logic: open new connection, process received event notifications, terminate stream when finished.
    As icing on the cake, the EventSource interface also provides auto-reconnect and tracking of the last seen message: if the connection is dropped, EventSource will automatically reconnect to the server and optionally advertise the ID of the last seen message, such that the stream can be resumed and lost messages can be retransmitted.출처

  • 프론트엔드 개발자는 새로운 스트림의 생성, 이벤트별 분기처리 등 핵심 로직에만 집중할 수 있는 게 polling방식과의 차이라고 볼 수 있다.

    • 예를 들어 sse를 사용하면 reconnect 시 유실된 데이터를 식별할 수 있도록, Last-Event-ID헤더를 자동으로 전송한다.
  • polling방식의 경우 클라이언트 request가 제어권을 가진 반면 sse는 재접속 시도 시간의 제어권도 서버에게 있다는 점이 다르다.

4.2 웹소켓과 SSE에 대한 고찰

  • 두 방식 모두 실시간 알람을 받아볼 수 있다는 점에서 같다.
  • 반면 웹소켓의 경우 클라이언트가 통신을 끊으면 서버에서도 자원을 해제할 수 있는 반면, sse는 기본적으로 지정된 시간동안 자원을 유지한다.(클라이언트가 접속중인지 알 수 없음)
  • 따라서 불필요한 자원을 사용하지 않는 웹소켓이 더 나아보이기도 한다. 그러나 클라이언트와 서버가 실시간으로 연결되어 있다는 의미는 클라이언트 역시 연결을 유지하기 위한 자원을 소모한다는 의미이다.(나의 추측)
    • 실제로 구글링 했을 때 웹소켓 방식이 배터리 자원을 더 소모한다는 글이 있다.
  • 또한 polling과 마찬가지로 연결이 끊어졌을 때의 재시작 로직 등을 프론트엔드 개발자가 별도로 구현해줘야 한다.
  • 어떤 글에서는 web socket이 기존의 보안장비에서 막는 경우가 있다고 한다. sse의 경우 순수 http 프로토콜인 반면 웹 소켓의 경우 최초 연결을 위한 HTTP 통신 이후에는 별도의 프로토콜을 사용하게 된다.(이때도 80, 443포트를 사용하기 때문에 단순히 방화벽에서 막힌다는 의미는 잘못된 설명이다.)
  • 웹소켓은 binary도 전송할 수 있으나, sse는 불가능하다.
  • 여러 가지를 종합했을 때 실시간 완전한 양방향 통신이 필요한 경우에는 웹소켓을 사용하고, 단순 텍스트 형태의 알람 정도의 기능은 sse를 쓰면될 듯 하다

    SSE was specifically designed as a simple, efficient, server-to-client transport for text-based data. If you need to transfer binary payloads, then a WebSocket is the right tool for the job.

0개의 댓글