Spring Boot + SSE로 알림 구현하기 - (1)

dradra·2024년 10월 6일
post-thumbnail

웹소켓(WebSocket)보다 단순하며, 연결 관리가 상대적으로 수월한 특징이 있다. 클라이언트가 서버와 지속적인 연결을 유지하면서 서버에서 발생하는 이벤트를 실시간으로 받는 방식이다.

1. SSE의 기본 개념

SSE(Server-Sent Events)는 서버가 단방향으로 클라이언트에게 실시간 데이터를 전송하는 방식
HTTP 프로토콜을 사용하며, 클라이언트가 서버와의 지속적인 연결을 유지하면서 서버에서 발생하는 이벤트를 수신할 수 있게 해준다.

2. SSE의 특징

  • 단방향 통신: 서버가 클라이언트로 데이터를 전송하는 것만 가능하며, 클라이언트는 별도의 요청을 하지 않는다.

    채팅 애플리케이션에서 클라이언트는 새 메시지가 올 때마다 서버에서 자동으로 알림을 받는다.
    클라이언트는 메시지 창을 리로드하거나 별도의 요청을 보내지 않아도 된다.
    메시지를 보낼 때는 일반적인 HTTP 요청을 사용한다.


  • HTTP 기반: 클라이언트는 HTTP 요청을 통해 SSE 연결을 열고, 서버는 HTTP 응답의 형식으로 데이터를 지속적으로 보낸다.
    HTTP의 요청-응답 모델과 달리, 서버가 한 번 응답을 보낸 후에도 연결을 유지하며 필요한 데이터를 계속해서 보낼 수 있다.

  • 텍스트 스트림: SSE는 text/event-stream 형식을 사용하여 서버에서 클라이언트로 데이터를 보낸다. 전송된 데이터는 텍스트 형식으로 이루어지며, JSON 등과 같은 포맷으로 전송될 수 있다.

    • 예시 : 서버에서 클라이언트로 보내는 데이터 형식
    • id: 12345
      event: message
      data: {
          "user": "John",
          "message": "안녕하세요"
      }

  • 자동 재연결: 클라이언트와의 연결이 끊어졌을 때 클라이언트는 자동으로 재연결을 시도한다.클라이언트는 특정 시간 동안 서버에서 응답이 없으면 연결을 다시 시도한다.

예를 들어, 클라이언트가 네트워크 문제로 인해 서버와의 연결이 끊어졌다고 하자. 일정 시간이 지나면 클라이언트는 다시 연결을 시도하며, 서버로부터 새로운 데이터를 계속 수신할 수 있다.


  • Event ID와 이벤트 흐름 관리
    • 서버는 각 이벤트에 ID를 부여할 수 있으며, 클라이언트는 이벤트를 수신하지 못한 경우 이 ID를 기반으로 다시 데이터를 요청할 수 있다.
      • 클라이언트는 마지막으로 받은 이벤트의 ID를 기억하고, 서버에 "ID 이후의 이벤트를 다시 보내 달라"고 요청할 수 있다.

        예를 들어 클라이언트가 마지막으로 수신한 이벤트의 ID가 12345라고 하면, 재연결 시 서버에 Last-Event-ID: 12345 헤더를 포함시켜 이전에 받지 못한 이벤트부터 수신을 재개할 수 있다.

3. SSE의 동작 흐름:

  1. 클라이언트가 서버에 연결 요청: 클라이언트는 서버에 SSE 연결을 설정하는 HTTP 요청을 보낸다.
  2. 서버가 응답: 서버는 클라이언트와의 연결을 유지하고, text/event-stream 형식으로 지속적인 응답을 보낸다.
  3. 이벤트 전송: 서버는 필요한 경우 데이터(이벤트)를 계속 전송하며, 클라이언트는 이를 실시간으로 처리한다.
  4. 자동 재연결: 연결이 끊어지면 클라이언트는 자동으로 재연결을 시도하여 계속 데이터를 수신할 수 있다..

SSE 이벤트 형식 예시

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

data: 알림이 도착했습니다!
id: 12345
retry: 5000  // 연결이 끊어지면 5초 후에 다시 연결

data: 또 다른 알림이 도착했습니다!

이처럼 서버가 주기적으로 데이터를 보낼 수 있으며, 클라이언트는 연결이 유지되는 동안 계속해서 데이터를 수신한다.


4. SSE와 HTTP의 차이점

SSE와 HTTP는 모두 HTTP 프로토콜을 사용하지만, 동작 방식에 대해 약간의 차이가 있다.

특징SSE일반 HTTP 요청/응답
데이터 흐름서버 → 클라이언트 (단방향 스트리밍)클라이언트 ↔ 서버 (요청/응답, 쌍방)
지속적인 연결연결이 유지되며 서버는 데이터를 지속적으로 보냄요청에 대한 응답 후 연결 종료
자동 재연결클라이언트가 자동으로 재연결 시도재연결 기능 없음
연결 유지클라이언트가 계속 연결을 유지하여 실시간 데이터를 수신요청마다 연결이 새로 만들어지고 종료됨
데이터 형식text/event-stream으로 이벤트를 스트리밍JSON, XML, HTML 등 다양한 데이터 형식 지원
사용 사례실시간 알림, 주식 가격, 채팅 메시지, 실시간 데이터 업데이트단일 요청/응답으로 처리할 수 있는 데이터 전송

SSE와 HTTP의 차이 설명

  • SSE는 실시간 스트리밍을 위한 HTTP:

    • HTTP는 기본적으로 요청/응답 기반의 프로토콜
    • 클라이언트가 서버에 요청을 보내면 서버는 해당 요청에 대해 응답을 반환하고, 그 이후에는 연결이 종료
    • 반면, SSE한 번의 요청으로 지속적인 연결을 유지하면서 서버가 필요할 때마다 데이터를 푸시한다.
  • SSE는 서버에서 데이터 푸시:

    • 일반적인 HTTP에서는 클라이언트가 요청을 보내야 서버가 응답을 보낸다. 즉, 클라이언트가 주도권을 가진다.
    • SSE에서는 서버가 클라이언트에게 필요할 때마다 데이터를 전송할 수 있다. 이를 통해 실시간 알림이나 데이터 스트리밍 같은 용례에 적합하다.
  • 자동 재연결:

    • HTTP는 기본적으로 요청이 끝나면 연결이 종료되며, 다시 데이터를 요청하려면 새로운 연결을 만들어야 한다.
    • SSE는 연결이 끊어지면 클라이언트가 자동으로 다시 연결하려고 시도한다. 이 때문에 서버에서 푸시하는 데이터가 중단 없이 수신되도록 도와준다.

5. SSE와 비슷한 기술들

SSE와 유사한 기술들을 비교해보면, 웹소켓(WebSocket), 롱 폴링(Long Polling), HTTP/2 Server Push 등이 있다.

5-1 웹소켓 (WebSocket)

  • 웹소켓은 서버와 클라이언트 간의 양방향 통신이 가능하다. 즉, 클라이언트도 서버로 데이터를 보낼 수 있으며, 서버도 클라이언트로 데이터를 보낼 수 있다.
  • SSE처럼 지속적인 연결을 유지하며, 클라이언트와 서버는 양방향으로 데이터를 교환할 수 있다.
  • 실시간 채팅, 게임 등의 복잡한 양방향 통신이 필요한 애플리케이션에서 자주 사용된다.

5-2. 롱 폴링 (Long Polling)

  • 클라이언트가 서버에 요청을 보내고, 서버는 즉시 응답하지 않고 일정 시간 동안 대기했다가 데이터가 준비되면 응답을 반환한다. 데이터가 준비되지 않으면 연결이 끊어지며, 클라이언트는 다시 요청을 보낸다.
  • HTTP 프로토콜을 사용하므로 간단하게 구현할 수 있지만, 비효율적일 수 있다. 서버에서 응답을 대기하는 시간이 클라이언트마다 유지되므로 리소스가 낭비될 수 있다.

5-3. HTTP/2 Server Push

  • HTTP/2의 기능을 활용해 서버가 클라이언트가 요청하지 않은 데이터를 미리 보내주는 방식이다.
  • HTTP/2 Server Push는 주로 리소스 로딩을 최적화하기 위한 용도로 사용되며, 알림처럼 실시간 이벤트를 스트리밍하는 용도로는 자주 사용되지 않는다.

6. 알림 기능을 SSE로 구현하려는 이유

알림 기능의 핵심은 서버에서 발생한 이벤트를 실시간으로 클라이언트에게 전달하는 것이다. 클라이언트는 데이터를 서버로 보낼 필요가 없고, 오직 서버가 클라이언트에게 푸시하는 방식으로 이루어진다. 알림처럼 단순히 서버에서 이벤트를 전송하고, 클라이언트가 이를 받아보는 기능SSE가 가장 효율적이라 생각했다.

웹소켓(WebSocket)은 양방향 통신을 지원하긴 하지만, 알림과 같이 단순히 서버에서 이벤트를 푸시하는 기능에는 오버 엔지니어링이 될 것이라 생각했다.

또한, SSE는 표준 HTTP 프로토콜을 기반으로 작동하기 때문에 대부분의 최신 웹 브라우저와 서버에서 별도의 설정 없이 쉽게 사용할 수 있다는 장점이 있다. 브라우저 측에서는 JavaScriptEventSource API만으로 간단하게 서버와의 SSE 연결을 설정할 수 있다는 것을 확인했다. 내가 진행하고 있는 프로젝트에서 알림 기능을 풀스택으로 도맡아서 하고 있는 상황인지라, 구현의 간단함은 큰 메리트라고 느껴졌다.

0개의 댓글