Spring/Spring WebFlux

[Spring WebFlux] Server Sent Event

꾸준함. 2024. 8. 14. 10:39

서론

SSE(Server-Sent Events)를 완벽히 이해하기 위해서는 먼저 Polling, Long Polling, 그리고 HTTP Streaming과 같은 기술적 개념들의 이해가 선행되어야 합니다.

언급한 기술들은 서버와 클라이언트 간의 통신 방식에서 중요한 역할을 하며, 각기 다른 상황에서 적절하게 사용될 수 있습니다.

 

Polling

  • 클라이언트가 일정한 시간 간격으로 서버에 요청을 보내어, 새로운 데이터가 있는지 확인하는 방식
  • Polling의 장점은 구현이 비교적 간단하다는 점
  • 하지만, 서버에 새로운 데이터가 없더라도 반복적으로 요청을 보내기 때문에 불필요한 리소스 소모가 발생할 수 있음

 

https://tech.makemytrip.com/polling-and-streaming-9eb8301d6c58

 

Long Polling

  • Polling의 단점을 보완하기 위한 방식
  • 클라이언트가 서버에 요청을 보내면, 서버는 즉시 응답하지 않고 새로운 데이터가 생길 때까지 연결을 유지
  • 새로운 데이터가 준비되면 서버가 응답을 보내고, 클라이언트는 데이터를 처리한 후 다시 서버에 요청을 보냄
  • Long Polling은 서버의 리소스를 더 효율적으로 사용할 수 있지만, 여전히 HTTP 요청이 빈번하게 발생한다는 단점

https://ably.com/blog/websockets-vs-long-polling

 

Http Streaming

  • 서버가 클라이언트와의 연결을 유지하면서 지속적으로 데이터를 전송하는 방식
    • 서버는 연결을 유지하고 데이터가 생길 때마다 전달할 이벤트, 데이터를 Chunk 단위로 전달

 

  • 서버는 한 번의 연결로 여러 번 데이터를 전송할 수 있으며, 해당 방식은 실시간으로 데이터를 전달해야 하는 상황에서 유용
  • 서버와 클라이언트 간의 연결을 장시간 유지해야 하므로 리소스 관리가 중요
  • Spring WebFlux에서 Server Sent Event를 사용해서 HTTP Streaming 구현 가능

 

1. Http Streaming 특징

동적으로 content가 생성되는 경우 정확한 Content-Length를 미리 제공할 수 없기 때문에 다음과 같은 방식으로 HTTP Streaming 구현합니다.

  • Transfer-Encoding 헤더
    • Transfer-Encoding:  chunked를 헤더에 추가
    • 텅 빈 chunk를 전달하기 전까지 값을 읽음
    • HTTP/1.1 이상에서만 사용 가능

 

  • EOF
    • Connection: close를 헤더에 추가
    • 서버가 연결을 종료할 때까지 들어오는 값을 읽음

 

https://www.theengineeringprojects.com/2022/03/server-sent-events-with-esp32-and-dht11.html

 

Server Sent Events

  • 클라이언트에게 서버에서 발생하는 이벤트를 실시간으로 푸시하는 방식
  • HTTP 프로토콜을 사용하여 클라이언트와 서버 간의 일방향 데이터 스트리밍을 지원
  • WebFlux는 Spring Framework의 리액티브 프로그래밍 모듈로 비동기적인 처리를 지원하기 때문에 SSE는 WebFlux와 잘 통합되어 실시간 데이터 전송을 쉽게 구현 가능
    • Spring WebFlux에서는 Handler의 반환 타입으로 Flux<ServerSentEvent>, Observable<ServerSentEvent>를 지원
    • Servlet stack에서는 HttpMessageConverter를 사용하지만 reactive stack에서는 HttpMessageWriter를 사용

 

1. Chunked Transfer-Encoding

  • 내부적으로 Chunked Transfer-Encoding 기반으로 동작하며 다음과 같이 데이터를 전송
    • chunk 단위로 여러 줄로 구성된 문자열 전달
    • new line으로 이벤트 구분
    • 문자열은 일반적으로 <field>:<value> 형태로 구성


 

부연 설명

  • id: 이벤트의 id를 가리키며 클라이언트에서는 id를 저장해서 Last-Event-ID 헤더를 첨부하고 서버는 해당 id 이후의 이벤트만 전송
  • event: 이벤트의 타입
  • data: 이벤트의 데이터, 데이터가 많으면 Multi Line으로 구성
  • retry: reconnection을 위한 대기 시간을 클라이언트에게 전달
  • comment(Empty): 정보를 남기기 위한 역할

 

2. ServerSentEventHttpMessageWriter

  • ServerSentEvent 객체 또는 일반 객체(POJO)를 받아 이를 HTTP 응답 스트림으로 변환
  • ServerSentEventHttpMessageWriter는 데이터를 SSE 이벤트 포맷으로 변환하며 이벤트 스트림은 여러 개의 이벤트(event)로 구성
    • 각 이벤트는 데이터(data), 이벤트 타입(event type), ID 등과 같은 필드를 가질 수 있음

 

  • ServerSentEventHttpMessageWriter는 text/event-stream 미디어 타입을 사용하여 응답을 작성하며 이는 SSE 표준에서 요구하는 미디어 타입으로, 클라이언트가 이 타입의 데이터를 실시간으로 처리할 수 있도록 지원

 

text/event-stream 미디어 타입

 

3. Controller 구현 예시

 

 

3.1 api 예시


 

WebSocket

  • WebSocket은 HTTP를 대체하는 프로토콜이 아니라, HTTP로 연결을 시작한 후에 양방향 통신을 지원하는 독립적인 프로토콜
  • WebSocket을 활용하면 클라이언트와 서버 간의 실시간 상호작용을 가능하게 하며, 특히 채팅 애플리케이션, 실시간 알림, 게임 서버 등에서 자주 사용
  • Spring WebFlux는 WebSocket을 쉽게 구현할 수 있는 다양한 기능을 제공하며 주로 WebSocketHandler와 WebSocketClient를 사용해 서버와 클라이언트 간의 WebSocket 통신을 설정할 수 있음

 

WebSocket vs SSE

 

  WebSocket SSE
통신 방식 클라이언트와 서버 간 양방향 통신 지원

HTTP 연결을 시작한 후 WebSocket 연결로 전환한 후에는 더 이상 HTTP 프로토콜을 사용하지 않음
서버에서 클라이언트로의 단방향 통신 지원

HTTP 기반의 프로토콜로 서버에서 클라이언트로 "text/event-stream" 형식의 HTTP 응답을 지속적으로 전송
연결 성립 및 유지 초기 연결 단계에서 HTTP를 사용하여 서버와 연결을 맺은 후 WebSocket 프로토콜로 전환

연결이 성립된 이후에는 연결 상태를 유지하며 클라이언트와 서버가 독립적으로 메시지를 주고 받을 수 있음

연결이 끊어질 경우 클라이언트가 수동으로 다시 연결을 시도해야함
HTTP 연결을 사용하여 서버와의 연결을 성립

클라이언트는 서버로부터 지속적인 데이터 스트림을 수신

연결이 끊어질 경우 브라우저가 자동으로 연결을 재시도할 수 있음 (안전성 뛰어남)
성능 네트워크 오버헤드가 적고 지속적인 양방향 데이터 전송에 최적화되어 있어 성능 우수 네트워크 오버헤드가 적고 브라우저에서 자동 재연결을 지원하여 안정적인 데이터 전송 가능
사용 사례 실시간 채팅 애플리케이션
온라인 게임
실시간 협업 도구
실시간 뉴스 피드
주식 시세 업데이트
실시간 알림

 

참고

패스트 캠퍼스 - Spring Webflux 완전 정복 : 코루틴부터 리액티브 MSA 프로젝트까지

반응형

'Spring > Spring WebFlux' 카테고리의 다른 글

[Spring WebFlux] R2DBC MySQL  (0) 2024.08.22