웹 실시간 통신 파헤치기 (2) - Polling vs Long Polling vs WebSocket vs SSE

Sydney·2024년 11월 8일
post-thumbnail

웹 실시간 통신 파헤치기(1)에 이은 두번째글이다.

Server-sent events(SSE)

Traditionally, a web page has to send a request to the server to receive new data; that is, the page requests data from the server. With server-sent events, it's possible for a server to send new data to a web page at any time, by pushing messages to the web page. These incoming messages can be treated as Events + data inside the web page.
출처: MDN

위의 설명에 따르면, 전통적으로 웹페이지는 새로운 데이터를 받기 위해 서버에 요청을 보낸다(클라이언트 → 서버). 하지만 SSE를 사용하면 서버에서 필요할 때마다 데이터를 웹 페이지로 푸시할 수 있다.

SSE 특징

SSE의 특징을 알기 쉽도록 WebSocket과 비교해보겠다.

SSE

  • 단방향 데이터 흐름(서버 → 클라이언트)
  • WebSocket과 달리 HTTP 프로토콜만으로 사용 가능하기 때문에 가벼움
  • 전송 데이터 형식은 only 텍스트 데이터(UTF-8 인코딩)
  • 접속에 문제가 있으면 자동으로 재연결을 시도
  • HTTP/1.1 프로토콜 사용시 브라우저에서 1개 도메인에 대해 생성할 수 있는 EventStream의 최대 개수는 6개로 제한된다. HTTP/2 프로토콜 사용시에는 브라우저와 서버간의 조율로 최대 100개까지 생성 가능 [출처]
  • 주로 실시간 알림, 뉴스 피드, 주식 시세 업데이트와 같이 서버에서 클라이언트로 지속적으로 정보를 전달해야 하는 서비스에서 사용

WebSocket

  • 양방향 데이터 흐름(서버 ←→ 클라이언트)
  • TCP 프로토콜을 처리하기 위해 전이중 연결과 새로운 웹 소켓 서버가 필요
  • 전송 데이터 형식은 이진 데이터, 텍스트 데이터
  • 재연결시 별도 구현이 필요
  • 브라우저 연결 한도는 없지만 서버 셋업에 따라 동시 접속 한도가 다름
  • 실시간 채팅, 온라인 게임, 실시간 협업 도구에 사용

동작 방법 (클라이언트)

동작 과정

  1. 클라이언트에서는 EventSource 객체 생성과 이벤트 리스너를 등록해 서버에 연결을 요청한다.
var source = new EventSource('updates.cgi');
  1. 연결이 열리면 서버로부터 들어오는 메시지가 이벤트 스트림 형식으로 전달되는데, 다음 세 가지 형태가 있다.

1) 데이터만 있는 메시지

// 두 줄 이상의 연속된 줄은 하나의 데이터 블록으로 간주된다.
// 마지막 행을 제외한 줄은 \n(마지막 행은 \n\n)
data: This is the first message.

data: This is the second message, it
data: has two lines.

data: This is the third message.

2) 이벤트명이 설정된 메시지

예를 들어 클라이언트에서 이벤트 핸들러를 등록하면,

var source = new EventSource('updates.cgi');
source.addEventListener('add', addHandler, false);
source.addEventListener('remove', removeHandler, false);

서버에서는 아래와 같이 응답한다.

event: add
data: {"username": "bobby", "time": "02:33:48"}

event: remove
data: {"username": "bobby", "time": "02:33:48"}

event: add
data: {"username": "sean", "time": "02:34:36"}

여기서 data가 반드시 JSON일 필요는 없으며 첫번째 예시에서 보았듯 텍스트여도 가능하다.

3) id가 포함된 메시지

data: first event
id: 1

data: second event
id

data: third event

첫 번째 블록에는 이름이 각각 dataid인 두 개의 필드가 있다.
여기서 data: first event라는 데이터로 이벤트가 발생한다.
id는 1로 설정되어있는데, id는 마지막으로 수신한 데이터의 id이다.
id를 설정하면 브라우저가 마지막 이벤트를 추적해 서버 연결이 끊어지면 Last-Event-ID 헤더가 요청으로 설정된다.
이 값을 이용하여 서버는 유실된 데이터를 재전송 할 수 있게 된다.

두 번째 블록의 data 필드에는 second event가, id필드에는 값이 없다.
여기서 data: second event라는 라는 데이터로 이벤트가 발생한다.
id 필드가 비어있는데, 이것은 Last-Event-ID를 빈 문자열로 재설정해(Last-Event-ID 헤더가 없음을 의미), 다시 연결을 시도하는 경우 전송된다.

만약 SSE연결이 시간 만료 등의 이유로 끊어졌을 때 알림이 발생하면 어떻게 될까?
그 시간 동안 발생한 알림은 클라이언트에 전달되지 못할 것이다.
이를 방지해 주는 것이 Last-Event-ID헤더이다. 이 헤더 값을 이용하여 서버는 유실된 데이터를 재전송 할 수 있게 된다.

주의할 점

  • 모든 브라우저가 SSE를 지원하지 않기 때문에(IE와 구형 버전의 Edge), event-source-polyfill 을 사용해 브라우저 호환성을 높일 수 있다.

reference

공식문서
https://html.spec.whatwg.org/multipage/server-sent-events.html
왜 Last-Event-ID를 사용하는가
https://stackoverflow.com/a/58374643
velog
https://velog.io/@max9106/Spring-SSE-Server-Sent-Events%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%95%8C%EB%A6%BC
GeekNews, Hacker news
https://news.hada.io/topic?id=5972
https://news.ycombinator.com/item?id=30312897

profile
숲을 보며 나무를 심는 프론트엔드 개발자🌳

0개의 댓글