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

Sydney·2024년 10월 25일
post-thumbnail

새로 들어간 사이드 프로젝트에서 채팅 기능을 구현하게 되어 WebSocket과 SSE(Server-Sent-Event)를 조사하였고, 이전의 실시간 통신 방식도 알아보게 되었다. 이를 함께 소개하며 WebSocket과 SSE에 대한 이해를 돕고자 한다.

⚠️ 이 포스팅은 WebSocket 이전 실시간 통신 방식과 WebSocket에 관한 글로, SSE에 대해 알고싶다면 다음 포스팅에서 확인할 수 있다.

HTTP통신

기본적으로 웹 어플리케이션은 클라이언트-서버간의 HTTP요청과 응답을 하는 것으로부터 통신을 하게 된다.
이러한 통신을 실시간으로 하기 위하여, 다음과 같은 방법들이 등장하게 되었다.

Polling

polling

  • 클라이언트가 주기적으로 서버에게 요청을 송신하는 방법이다.
  • 서버에 클라이언트가 요청한 업데이트가 없을 경우 대부분의 응답은 빈 응답이 될 가능성이 높다.

단점

  • 요청의 주기만큼 딜레이가 발생하여 클라이언트는 서버의 상태 변화를 실시간으로 따라갈 수 없게 된다. (실시간 통신에 적합하지 않다.)
  • 불필요한 네트워크 요청이 잦아 서버 부담이 커지게 된다.
  • HTTP 오버헤드가 발생한다.

    💡HTTP 오버헤드
    보내지는 헤더와 같은 정보로 인해 데이터량이나 처리시간이 오히려 증가하는 것을 의미한다.
    헤더 정보를 통해 트래픽을 거르게 되면 안정성이 확보되지만 결국은 작은 정보라도 TPS(전송시간)가 늘어나게 되는데, 거기서 HTTP 오버헤드가 발생하게 된다.
    출처: https://inpa.tistory.com/608

Long Polling

long polling

기존 Polling과의 차이점

  • Polling에서는 클라이언트가 서버에 요청을 보내고 응답을 받으면 연결을 종료하고, 일정 주기 후에 다시 요청을 보내는 방식
    => 서버에 클라이언트가 요청한 새로운 데이터나 이벤트가 발생하지 않아도 응답을 보낸다.
  • Long Polling에서는 클라이언트가 서버에 요청을 보낸 뒤 서버에 클라이언트가 요청한 새로운 데이터나 이벤트가 발생할 때까지 연결을 유지
  • 서버가 새로운 데이터를 준비하면 그때 응답을 반환하고, 클라이언트는 응답을 받으면 즉시 다시 요청을 보내어 연속적인 연결을 유지하는 방식
  • Long Polling은 Polling 방식의 빈번한 요청을 줄이고, 더 즉각적인 데이터 업데이트를 가능하게 한다.

장점

Polling방식에 비해 HTTP 오버헤드가 적게 발생한다.

단점

클라이언트로 보내는 이벤트들의 시간간격이 좁거나, 다수의 클라이언트에게 동시에 이벤트가 발생할 경우, 모두 서버에 접속을 시도해 서버 부담이 급증하게 되므로 Polling방식과 큰 차이가 없다.

WebSocket🌏

The WebSocket Protocol enables two-way communication between a client running untrusted code in a controlled environment to a remote host that has opted-in to communications from that code. The security model used for this is the origin-based security model commonly used by web browsers. The protocol consists of an opening handshake followed by basic message framing, layered over TCP. The goal of this technology is to provide a mechanism for browser-based applications that need two-way communication with servers that does not rely on opening multiple HTTP connections (e.g., using XMLHttpRequest or iframes and long polling).
출처: RFC 6455

위의 설명을 요약한 WebSocket의 정의는 다음과 같다.

  • WebSocket Protocol은 양방향 통신을 지원한다. 즉, 클라이언트와 서버가 자유롭게 메시지를 주고받을 수 있는 구조를 가진다.
  • origin-based security model을 사용한다.
  • opening handshake가 이루어지고 TCP를 통해 계층화 한다.
  • 이 기술의 목표는, 다수의 HTTP 연결을 여는 방식을 사용하지 않고서도 서버와의 양방향 통신이 필요한 브라우저 기반 애플리케이션에 메커니즘을 제공하는 것이다.

동작 과정

websocket 동작과정

1. opening handshake🤝🏻

최초 한번만 HTTP Protocol로 handshaking을 하게 되는데, 클라이언트는 GET 요청으로 서버에 연결을 요청한다. 아래는 WebSocket 연결을 위한 주요 헤더 정보이다.

GET /chat HTTP/1.1  // 반드시 GET 요청, HTTP는 1.1이상
Host: server.example.com // WebSocket 서버의 주소
Upgrade: websocket // 현재 클라이언트, 서버, 전송 Protocol 연결에서 다른 Protocol로 업그레이드 또는 변경 요청
Connection: Upgrade // Upgrade 헤더 필드가 있으면 Upgrade 옵션 필수
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // 연결을 위한 base64 인코딩 키 값, 신원 인증을 위한 키
Origin: http://example.com // 클라이언트 주소
Sec-WebSocket-Protocol: chat, superchat // 서브 Protocol
Sec-WebSocket-Version: 13

그러면 서버로부터 다음과 같은 응답헤더를 받아 데이터를 전송할 준비를 완료한다.

HTTP/1.1 101 Switching Protocols // 101 Switching Protocols가 Response로 오면 WebSocket이 연결됐다는 의미
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

요청-응답 후에는 WebSocket Protocol로 변경된다. (ws://www~)
데이터 보안을 위해서 wss Protocol도 사용,권장된다.

2. Data transfer📬

클라이언트와 서버가 모두 handshake를 보내고 handshake가 성공하면 데이터 전송 부분이 시작된다.
클라이언트와 서버는 이 사양에서 message라고 하는 개념 단위로 데이터를 양방향으로 전송한다.
여기서 message는 여러 frame이 모여서 구성하는 하나의 논리적 메세지 단위이다.

그렇다면 어떻게 데이터를 전송할까?

  1. 엔드포인트(클라이언트 혹은 서버)는 WebSocket 연결이 OPEN상태인지 확인한다.
  2. 엔드포인트는 WebSocket frame의 data를 캡슐화한다. 전송할 데이터가 크거나 엔드포인트가 데이터 전송을 시작하려는 시점에 데이터 전체를 사용할 수 없는 경우 엔드포인트는 fragmentation을 통해 일련의 데이터를 교대로 캡슐화할 수 있다.
  3. data를 포함하는 첫 번째 프레임의 opcode는 data에 대해 적절한 값으로 설정한다.(수신자에 의해 텍스트 또는 이진데이터로 해석)
  4. 데이터를 포함하는 마지막 프레임의 FIN 비트는 1로 설정한다.
  5. 데이터가 클라이언트에 의해 전송되는 경우 MASK 비트를 1로 설정한다.
    RSV1, RSV2, RSV3(확장을 위한 비트)에 대한 설정이 이루어졌다면 해당 확장의 정의에 따라 추가 고려 사항이 적용될 수 있다.
  6. 이렇게 형성된 프레임은 기본 네트워크 연결을 통해 전송된다.

참고: frame
frame은 아래와 같이 데이터 전송 과정에서 가장 작은 단위의 데이터로 작은 헤더와 payload로 구성된다.
frame 구조

봐도 무슨 말인지 모르겠지만🤷🏻‍♀️ 간단히 용어를 짚고 넘어가보려 한다.
(너무 어려우면 넘어가도 좋겠다.)

  • FIN: 메시지의 마지막 fragment(조각)이란 것을 알려주는 플래그이다.(1이면 마지막 프레임, 0이면 메시지가 더 있음)

  • RSV1, RSV2, RSV3: 기본은 0값이고 정의를 했을 경우에만 다른 값을 넣을 수 있다.
    만약 정의하지 않은 값이면 Fail the WebSocket Connection이 실행되어야 한다.

  • opcode: payload 데이터의 해석을 정의한다. 알 수 없는 opcode가 수신되면 Fail the WebSocket Connection이 실행되어야 한다.

    	- 0x0: 계속 프레임(Continuation frame). 전체 데이터의 일부 의미
    	- 0x1: 텍스트 프레임(Text frame). 데이터가 UTF-8텍스트이다.
    	- 0x2: 바이너리 프레임(Binary frame) 데이터가 이진 데이터이다.
    	- 0x8: 연결 닫기(Close connection)
  • MASK: payload 데이터를 마스킹할지 여부를 정의한다. 1로 설정하면 마스킹 키가 존재하며 이 키는 payload 데이터의 마스킹을 해제하는데 사용된다. 클라이언트에서 서버로 전송되는 모든 프레임은 이 비트가 1로 설정되어 있다.

  • Payload Len: 이 프레임에 포함된 데이터의 총 길이를 나타낸다.

  • Extended Payload Length (16/64비트): Payload length가 126 이상일 경우 이 필드를 사용하여 실제 데이터 길이를 나타낸다.

  • Masking Key : MASK 비트가 1로 설정된 경우 사용되며, 클라이언트가 서버로 보내는 데이터는 항상 마스킹되어야 합니다.

  • Payload Data: 전송할 실제 데이터입니다. 텍스트 또는 바이너리 데이터로, 필요한 경우 마스킹 키로 마스킹 처리된다.

WebSocket의 특징

  • 다수의 HTTP 연결을 여는 방식을 사용하지 않고서도 서버와의 양방향 통신이 가능하다는 점에서, Polling 방식의 오버헤드를 줄일 수 있다.
  • 처음에 한 번만 핸드셰이킹을 수행하며 http header를 사용한다 → polling방식에 비해 낮은 부하
  • 기존 포트 (http-80, https-443)을 사용해 기존 인프라(예: 방화벽, 프록시 서버)와 호환됨
  • 교환 가능한 데이터 형태는 이진 데이터, 텍스트

WebSocket의 한계

WebSocket은 HTML5 이후로 등장한 기술이므로, HTML5 이전의 기술로 구현된 서비스에서 사용할 수 없다.
=> Socket.IO 라이브러리로 하위 호환이 가능하다.
HTML5 이전의 기술로 구현된 서비스에서 웹 소켓처럼 사용 할 수 있도록 도와주고 그 외 편리한 기능을 제공한다.(직접 구현하면서 알아봐야겠다.)

WebSocket은 대략적으로 문자열들을 주고받게 해줄 뿐 그 이상의 일은 해줄 수 없다.
또한 주고 받는 문자열의 해독은 온전히 어플리케이션에 맡기기 때문에 해석이 어려울 수 있다. 때문에 WebSocket 방식은 sub-protocol을 사용해서 약속을 하는 경우가 많다.
=> STOMP(Simple Text Oriented Message Protocol) 같이 해석이 편한 프로토콜이 있다.

주의할 점

  • WebSocket이 Upgrade 및 Connection을 포함한 hop-by-hop 헤더이기 때문에 클라이언트에서 프록시된 서버로 전달되지 않는다. nginx의 경우 Upgrade 및 Connection header를 명시해주어 수신하는 웹서버가 이 요청이 WebSocket요청임을 알 수 있게 해야한다.
  • 한 번 연결이 될 때 인증, 인가를 적용해줄 수 있지만 그 이후에는 따로 수행되지 않는다. 그렇기 때문에 WebSocket 메시지에 대해서 인증을 하기 위해서는 따로 연결이 되는 동안 메시지 헤더를 통해 인증 인가를 수행할 수 있다.
  • Client와 Server에서 정상적으로 connection이 종료되지 않는 경우 이를 위한 error처리에 신경을 써야 한다.

SSE까지 파헤쳐보려 했지만 글이 너무 길어져 다음 시리즈에서 작성해보는 것으로 하겠다..🪄

reference

polling
https://medium.com/techieahead/http-short-vs-long-polling-vs-websockets-vs-sse-8d9e962b2ba8
websocket개요
https://youtu.be/MPQHvwPxDUw?si=7tWbiVvC8Kbbfr4o
data frame
https://www.rfc-editor.org/rfc/rfc6455#section-5.2
sending data
https://www.rfc-editor.org/rfc/rfc6455#section-6.1
websocket특징
https://gdsc-university-of-seoul.github.io/websocket/

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

0개의 댓글