멈재

[Network] 실시간 통신 기술 (폴링, 롱폴링, 웹소켓, SSE) 본문

Network

[Network] 실시간 통신 기술 (폴링, 롱폴링, 웹소켓, SSE)

멈재 2023. 6. 10. 14:15
728x90

토이 프로젝트에 알림 기능이 추가됨에 따라 실시간 통신 기술을 알아볼 필요성이 있었다.
 
알아볼 실시간 통신 기술은 다음과 같다.
 

실시간 트릭 기술

  • Polling
  • Long Polling

 

실시간 기술

양방향(bidirectional)

  • Web Socket

 
단방향(unidirectional)

  • SSE

 
크게 실시간 트릭 기술실시간 기술로 나누었다.
 
 


 
 
실시간 통신 기술 설명하기 전에 HTTP에 대해서 간단하게 설명해보려고 한다.
HTTP는 서버와 클라이언트간에 데이터를 주고받을 때 사용하는 프로토콜로 다음과 같은 특징을 가진다.
 
클라이언트 서버 구조
클라이언트는 서버에 요청을 보내고 서버는 요청에 대한 결과를 만들어 응답을 보내게 된다.
 
무상태(Stateless)
클라이언트의 상태를 보존하지 않는다.
 
비연결성(Connectionless)
요청을 주고받을 때만 연결을 유지하고 응답을 주고 나면 연결을 끊는다.
 
 
 
이제 폴링 방식과 롱 폴링 방식부터 알아보자.
 

폴링(Polling)

폴링 방식은 특정 주기를 가지고 서버에 HTTP Request를 하는 방식으로 언제 통신이 발생할지 예측이 불가능하기 때문에 일정한 주기마다 서버에 요청을 보내 이벤트를 전달받는 방식이다.

이미지 출처: https://warmth424.tistory.com/18

 
즉 클라이언트가 계속해서 요청을 보내게 되기 때문에 다음과 같은 문제가 발생하게 된다.

  • 클라이언트가 많아지면 서버에 대한 부하가 증가하고, 매번 요청을 처리하고 응답해야 하기 때문에 서버 리소스의 낭비가 발생할 수 있다.
  • 일정한 주기마다 요청을 보내서 이벤트를 전달받기 때문에 실시간(real-time)의 빠른 응답을 기대하기는 어렵다.
  • HTTP 오버헤드가 발생한다.
    • 여기서 말하는 오버헤드란 데이터 양에 비해 헤더의 크기가 상대적으로 커서 발생하는 것을 의미한다.
    • 예를 들어, 요청 헤더에 요청의 유형, 쿠키, 인증등의 다양한 부가 정보가 포함되게 되는데 이러한 부가적인 정보보다 데이터의 크기가 상대적으로 큰 경우를 오버헤드가 발생한다고 한다.

여러 포스팅에 폴링/롱 폴링의 단점으로 HTTP 오버헤드가 발생한다는 단점이 있었는데, 내 생각에는 폴링하는 방식의 단점이라기보다 HTTP 프로토콜의 단점같아서 줄 그음 처리를 해놓았다.
 
 

롱 폴링(Long Polling)

롱 폴링 방식은 폴링 방식의 변형으로 클라이언트가 요청을 보내면 서버는 즉시 응답을 보내는 게 아니라 일정 시간 동안 연결을 유지(Connections)하다가 연결이 유지되는 동안 이벤트가 발생한다면 그 즉시 응답 메시지를 전달하는 방식이다.
응답을 받은 클라이언트는 다시 새로운 요청을 서버로 보내 다음 이벤트를 기다리게 된다.
 

이미지 출처: https://warmth424.tistory.com/18

 
통신 방식의 순서를 정리하면 다음과 같다.

  • 클라이언트가 서버에게 요청을 보낸다.
  • 서버는 응답을 즉시 보내않고 연결을 유지한 상태로 대기한다.
  • 서버에 이벤트가 발생하거나 특정 조건(타임 아웃 등)이 되면 응답을 보낸다.
  • 클라이언트는 응답을 받은 후 새로운 요청을 보내 다음 이벤트를 기다린다.

일정 간격으로 요청을 보내는 폴링 방식보다 서버의 부담이 일부 감소하겠지만 클라이언트가 많아지게 되면 결국 폴링 방식처럼 요청과 응답이 자주 일어나기 때문에 롱 폴링 방식 또한 서버의 부담이 증가하게 된다.
 

Q> 그래도 롱 풀링 방식이 폴링 방식보다는 무조건 좋지 않을까?
A> 아니다.

 
롱 폴링 방식으로 단체 채팅방에 1000명의 사용자(클라이언트)가 있다고 가정해보자.
만약, 한 사람이 하나의 메시지만 보내는 순간 1000명의 사용자는 서버로부터 응답을 받게 된다. 그와 동시에 채팅방에 존재하는 1000명 사용자가 재연결을 맺는 요청을 서버로 보내기 때문에 순간적인 부하가 발생할 수 있다.
 
따라서 폴링 방식과 롱 폴링 방식 중에 하나를 골라야 한다면 적절한 상황과 비즈니스에 맞추어 선택하는 것이 바람직하다.

폴링 방식과 롱 폴링 방식의 선택 기준은 다음과 같다.

폴링 방식
응답을 실시간으로 받지 않아도 되는 경우

롱 폴링 방식
응답을 실시간으로 받아야 하는 경우
메신저 같이 1 on 1, 혹은 적은 수의 사용자가 동시에 사용하는 경우
스프링의 경우에는 기본적으로 블럭킹 동기 방식이기 때문에 직접 구현을 해야 한다면 커넥션을 위한 스레드 관리와 복잡도가 있을 것이다.
그래서 Spring 3.2부터는 비동기 처리를 수행하는 DeferredResult 클래스를 제공한다고 한다.
참고: https://www.baeldung.com/spring-mvc-long-polling

 
 


 
 
앞서 설명한 폴링 방식과 롱 폴링 방식은 실시간 기술이 아닌 '실시간처럼 보이게 하는' 실시간 트릭 기술이라고 볼 수 있다.

이제는 실시간 기술이라고 부를 수 있는 양방향 통신이 가능한 웹소켓과 단방향 통신인 SSE를 알아보겠다.
 

웹소켓(Web Socket)

웹 소켓은 클라이언트와 서버를 연결해서 실시간 통신이 가능하게 하는 통신 프로토콜로 단방향 통신만 가능한 HTTP와는 다르게 양방향 통신을 지원하고 있다.
 
웹 소켓은 초기 연결을 설정한 이후에 지속적으로 연결을 유지하고 실시간으로 데이터를 주고받을 수 있게 된다.
따라서, 채팅과 같이 연속적인 통신이 이루어져도 초기에 연결을 맺고 나면 추가적인 요청과 응답의 과정이 필요하지 않다.
 
TCP 기반의 HTTP 프로토콜은 TCP 계층에서 Handshake 과정을 거쳐 연결이 이루어진 반면, 웹 소켓은 HTTP 요청을 기반으로 Handshake 과정을 거쳐 연결이 이루어진다.
 
웹 소켓의 통신 과정은 다음과 같다.
 

이미지 출처: https://warmth424.tistory.com/18

 
 
웹소켓 연결을 맺기 위해 Upgrade 헤더와 Connection 헤더를 포함한 HTTP 요청을 보낸다.

이미지 출처: https://tecoble.techcourse.co.kr/post/2021-08-14-web-socket/

 
일반적인 성공에 대한 상태 코드가 아닌 101번의 상태 코드를 응답받아 웹소켓으로 통신 가능한 상태가 되게 된다.

이미지 출처: https://tecoble.techcourse.co.kr/post/2021-08-14-web-socket/

 

1XX : Information responses
서버가 요청을 받았으며, 서버에 연결된 클라이언트는 작업을 계속 진행해도 된다는 상태코드

100 Continue
현재까지의 진행상태에 문제가 없으며, 클라이언트가 계속해서 요청을 하거나 이미 요청을 완료한 경우에는 무시해도 되는 것을 알려준다.

101 Switching Protocol
클라이언트가 보낸 Upgrade 요청 헤더에 대한 응답이 들어가며 서버에서 프로토콜을 변경할 것임을 알려준다. 해당 코드는 Websocket 프로토콜 전환 시에 사용된다.
출처: MDN Web Docs
참고로 HTTP/1.0은 1xx 상태 코드를 정의하지 않았기 때문에 HTTP/1.0 클라이언트에 1xx 응답을 보내서는 안 된다고 한다. - RFC 2616

 
웹소켓의 통신 방식을 정리하면 다음과 같다.

  • 웹소켓 프로토콜은 연결을 잇는 최초의 요청에는 HTTP를 사용한다.
  • Handshake 과정이 정상적으로 이루어지면 http -> websocket으로 프로토콜을 변경하는 프로토콜 스위칭이 발생한다.
  • 그 후의 통신에서는 웹소켓 프로토콜을 통해 통신이 이루어진다.

HTTP/Web Socket 비교

 
초기 한 번의 연결을 맺으면 추가적인 요청과 응답이 이루어지지 않는다는 장점이 존재하지만 웹소켓 특성으로 인해 발생하는 문제점과 단점도 있다.

  • 웹소켓은 연결(Stateful)을 맺는 프로토콜이기 때문에 연결이 끊어졌을 때에 대해서 대응할 수 있어야 한다.
  • 웹소켓을 연결을 지원하지 않을 수도 있다.
    • 예를 들어, HTTP/1.1 미만이거나 IE(Internet Explorer) 10 미만인 경우
  • 클라이언트와 서버 간에 소켓 연결을 유지해야 한다.
    • 클라이언트가 많은 대용량 트래픽인 경우에 부담이 될 수 있다.

 
 


 
 
마지막으로 실시간 단방향 통신인 SSE를 알아보자
 

SSE(Server Sent Events)

SSE는 서버의 이벤트 데이터를 구독하여 실시간으로 수신하는 기술로 클라이언트가 서버로 요청을 보내지 않고도(rather than pulled or requested) 데이터를 받을 수 있다.
웹소켓과 달리 클라이언트가 서버로부터 데이터를 받을 수만 있는 단방향 통신이다. (Server -> Client)
 
서버와 통신하여 데이터를 가져오기 위해 여러 가지 방법이 있었다.

  • 지속적인 요청을 보내 응답(Polling)을 받거나,
  • 페이지를 새로고침하거나,
  • 웹소켓 통신으로 데이터를 받아오거나,

SSE는 서버로 별도의 요청 없이 데이터를 받을 수 있게 된다.
 
SSE는 다음의 통신 과정을 거치게 된다.

이미지 출처: https://ably.com/blog/websockets-vs-sse

 
클라이언트가 서버의 이벤트를 구독한다는 요청을 보낸다.

GET /connect HTTP/1.1
Accept: text/event-stream
Cache-Control: no-cache
  • W3C SSE specification에 따르면 Accept 헤더에 text/event-stream으로 지정하도록 정해져 있다. (HTML Standard Spec)
  • 이벤트를 캐싱을 하지 않고, 지속적인 연결을 하도록 해야 한다. 만약, 캐싱을 허용하면 클라이언트가 이벤트를 즉시 수신하지 못하고 캐시된 이전 데이터를 받을 수 있기 때문이다.

 
서버는 구독 요청에 대한 응답을 보내게 된다. (서버의 이벤트 데이터를 구독한 상태)

HTTP/1.1 200
Content-Type: text/event-stream;charset=UTF-8
Transfer-Encoding: chunked

 
서버에 이벤트가 발생하면 구독하는 클라이언트에게 비동기적으로 데이터를 전송한다.

 
정리해서 설명하면 다음과 같다.

  • 서버로 별도의 요청 없이 데이터를 받을 수 있다.
  • 서버에서 클라이언트로 단방향의 Push Notification을 하는 기술이다.
  • HTTP/1.1 프로토콜 사용 시 최대 동시 접속자수가 6개까지 가능하고, HTTP/2.0 프로토콜 사용 시에는 최대 100개까지 유지가 가능하다.
  • 이벤트 데이터는 UTF-8 인코딩 된 문자열만 지원합니다. 서버에서 이벤트 데이터를 담은 객체를 JSON으로 마샬링하여 전송하는 것이 일반적이다.
  • 웹소켓과 달리 재접속 처리(recovery)를 지원한다.
  • 스프링의 경우 Spring 4.2부터 SSE 통신을 지원하는 SseEmitter클래스를 기본적으로 제공하기 때문에 별도의 라이브러리나 프로토콜 학습 없이 SSE 통신 구현이 가능하다.
    • 웹소켓은 웹소켓 프로토콜(ws)을 사용했던 것과 달리 SSE는 흔히 사용하는 HTTP 프로토콜을 사용함
    • 웹소켓은 프로토콜 처리를 위한 별도의 소켓 서버가 필요한 반면 SSE는 서버 구현이 필요하지 않음

 
웹 소켓 방식과 SSE 방식의 선택 기준은 다음과 같다.

웹소켓 방식
실시간 양방향 통신인 경우
ex) 채팅

SSE 방식
실시간 단방향 통신인 경우
ex) 푸시 알림, 뉴스 레터, SNS 친구 요청

 
 
참고