Computer Engineering/web

[WEB] 실시간 서비스를 구현해보자. HTTP vs WebSocket

soohey 2022. 7. 25. 15:05

실시간으로 소통하는 서비스를 만들려면 어떤 방식을 사용해야 할까?

실시간이라는 말 자체가 연결이 지속되어 있는 상태가 유지되어야 할텐데, 내가 기존에 알고있던 HTTP는 요청 응답후 연결을 닫아버린다.

HTTP의 고질적인 문제를 해결하는 방법(웹 소켓)을 소개하려고 한다.

또한 연결이 지속되지 않아도 실시간처럼 눈속임을 할 수 있는 방법들(코멧)도 살펴보자.


HTTP 통신

  • 클라이언트 요청이 있을 때만 서버가 응답하여 해당 정보를 전송하고 곧바로 연결을 종료한다.
  • 실시간 연결이 아닌, 필요한 경우에만 서버에 접근하는 콘텐츠 위주의 데이터 연결
  • 실시간 서비스를 구현하려면 통신이 종료될때까지 계속해서 http 요청을 서버로 보내야한다. 이러한 연결요청은 과부하 위험이 있다.

소켓 통신

  • 서버와 클라이언트가 특정 포트를 통해 실시간으로 양방향 통신을 하는 방식
  • http 통신과 다르게 포트를 이용한 연결을 유지하고 있어 스트리밍 중계나 채팅 같은 즉각적인 정보를 주고 받을 수 있다.

이렇게 서버와 클라이언트 사이의 연결이 끊기는 HTTP 통신과 연결을 지속하는 소켓 통신으로 나눠볼 수 있다.

과거에는 이러한 HTTP의 문제점을 보완하여 실시간 서비스로 눈속임을 하는 폴링 및 코멧방식을 사용하였다.


폴링

  • 프로그램이 충돌 회피, 동기화 처리를 목적으로 다른 프로그램의 상태를 주기적으로 검사하여 일정 조건이 만족되면 데이터 송수신을 하는 방식
  • 주기적인 시간마다 클라이언트가 서버로 요청을 동기적 호출해서 사용가능한 정보가 있는지 확인한다.
  • 요청은 주기적인 간격으로 이뤄지며 클라이언트는 정보가 있든 없든 응답을 받는다.
  • 정보가 있으면 서버는 정보를 송신하고 없으면 부정적인 응답을 반환하고 클라이언트는 연결을 닫는다.
  • 메시지가 전달되는 간격을 정확히 알고 있을 때 적절한 해결책이다.
  • 양방향 통신을 근접하게 구현하기 위한 방법
  • 폴링의 주기가 짧으면 성능에 부담이 가고, 주기가 길면 실시간 성능이 떨어진다.
  • 서버에서 정보 변경이 없으면 응답 과정에서 리소스를 낭비하거나 오버헤드 트래픽이 발생한다.

롱폴링 기법

  • 폴링의 문제점을 개선함
  • 클라이언트가 요청시 서버가 요청에 대한 연결을 이벤트가 발생할때까지 가지고 있다.
  • 변경이 빈번하다면 이득이 크지 않음
  • 일반적으로 HTTP protocol과 JavaScript에서 await 문법과 함께 구현된다.
async function subscribe() {
  let response = await fetch("/subscribe");

  if (response.status == 502) {
    // Status 502 is a connection timeout error,
    // may happen when the connection was pending for too long,
    // and the remote server or a proxy closed it
    // let's reconnect
    await subscribe();
  } else if (response.status != 200) {
    // An error - let's show it
    showMessage(response.statusText);
    // Reconnect in one second
    await new Promise(resolve => setTimeout(resolve, 1000));
    await subscribe();
  } else {
    // Get and show the message
    let message = await response.text();
    showMessage(message);
    // Call subscribe() again to get the next message
    await subscribe();
  }
}

subscribe();

폴링, 롱폴링 모두 푸쉬 기술 구현을 위한 대안이었지만 클라이언트 요청을 통해야만 서버에게 데이터를 전송받는다는 한계점이 존재한다.

-> 불필요한 헤더와 높은 트래픽을 야기하는 단점이 생긴다.

코멧(comet)

  • 웹 페이지가 다시 로드되지 않고 실시간으로 변동사항을 반영해 변화하려면 어떻게 해야할까? 예를 들면 실시간 모니터링 도구, 스포츠 실황중계처럼 매우 짧은 시간간격으로 변화하는 서비스, 실시간 채팅 서비스등이 있다.
  • 서버가 모니터링하다가 그 상태를 수시로 클라이언트에게 전달해야 한다. 이것을 서버 푸쉬라고 하는데 웹 http 로는 구현할 수 없다.  HTTP는 클라이언트가 요청하면 서버는 응답후 연결을 끊는다. 클라이언트가 요청해야 서버가 움직일 수 있다.
  • 이러한 문제를 해결하기 위한 편법이 코멧이다.
  • 코멧도 HTTP를 이용하기에 완벽한 실시간(real-time)은 아니다. 그저 짧은 시간간격으로 웹페이지를 업데이트하는 것일뿐이다. 하지만 시간간격이 짧으면 짧을수록 서비스에는 큰 문제가 없다. 이런 부분때문에 실시간으로 간주한다.

코멧의 종류 4가지

1. Ajax polling

  • Ajax로 상태를 가져온 후 화면을 업데이트하는 자바스크립트를 작성하고, setInterval()로 일정시간마다 이 함수를 호출하는 방식

2. Hidden iframe

  • css를 이용해 보이지 않게 한 <iframe>태그를 만들어놓고, 이 태그가 임베디드하는 파일이 연결을 끊지 않고 계속 작동하면서 화면을 업데이트하는 스크립트를 실행하는 방식

3. Long Polling by Ajax

  • Ajax로 호출하는 파일이 반복문을 돌며 대기하다가 화면을 업데이트해야할 상황이 되면 다시 동일한 파일을 Ajax로 호출하는 과정을 반복한다.

4. Long Polling by Script Tag

  • <script> 태그가 임베디드하는 파일이 화면을 업데이트하는 스크립트를 실행한 후 다시 자신을 임베디드하는 스크립트 태그를 만드는 방식

Server-sent Events

  • 웹 브라우저 관섭없이 서버에서 단방향으로 이벤트를 전송한다
  • 웹 브라우저가 이벤트를 구독하여 서버의 이벤트를 전송받는 기법이다.
  • 웹 브라우저에서는 서버로 이벤트를 요청할 수 없다.
  • HTML5 표준에 포함되어있으며 HTTP 프로토콜 기반으로 동작한다. 최소 HTTP 2.0을 통해 이용하는 것을 권장한다. 
  • 일반적으로 JavaScript의 EventSource()함수를 통해 이용한다.
  • 웹소켓과 비교하면 Event Subscribe(이벤트 구독)에 특화되어 있다.
  • 단방향 통신만 지원하지만 각 이벤트를 이벤트 ID를 통해 관리하고, 네트워크 장애시 재연결 과정도 웹소켓에 비해 잘 정의되어 있다. 

Streaming

  • 하나의 웹 요청에 대해 웹 접속을 계속 열어두고 데이터를 이벤트 때마다 지속적으로 내려받는 방식
  • 이는 응답마다 다시 요청하는 롱 폴링에 비해 효율적이고 서버의 상태 변경이 잦은 경우 유리하지만 연결을 언제까지 맺을 건이지에 관한 유효성 관리등의 부담이 발생한다.

웹소켓

  • 위와 같은 방법들은 HTTP가 가지는 한계인 단방향 메시지 교환 규칙을 변경하지 않고 구현한 방식이다. 때문에 코드가 복잡해진다.
  • 브라우저와 서버사이의 동적인 양방향 메시지 송수신을 위해서 HTML5 표준안에 WebSocket AP가 등장했다. 웹서버, 클라이언트 모두 websocket API가 지원되어야 한다.
  • socket.io는 node.js모듈로서 자바스크립트를 이용해 브라우저 종류에 상관없이 실시간 웹을 구현할 수 있도록 하는 기술이다.
  • 서버와 클라이언트 사이 웹소켓이 열리면 서버 또는 사용자 둘 중 하나가 세션을 닫을 때까지 언제든지 메시지를 보낼 수 있다.
  • http 는 클라이언트만 필요한 경우에 요청을 보낼 수 있지만 소켓프로그래밍은 서버역시 클라이언트에 요청을 보낼 수 있으며 계속 연결을 유지하는 연결 지향형 방식이기에 실시간 통신이 필요한 경우 자주 사용한다.
  • 단점으로 웹소켓과 http 세션이 서로 다르다. 주기적으로 웹소켓 세션과 서버에 저장하는 http 세션을 동기화하는 모듈을 구현해야 한다.
  • 대용량 트래픽에서는 서버가 클라이언트 수만큼 연결을 유지해야 한다는 부담이 존재한다.
  • 접속 확인시 HTTP를 사용하고 이 후의 통신에서는 WS 프로토콜을 사용한다. 헤더가 매우 작아 오버헤드가 적다.
  • 장시간 접속이 전제되기 때문에 접속한 상태라면 클라이언트나 서버로부터 데이터 송신이 가능하다. 데이터 송신과 수신에 각각 커넥션을 맺을 필요가 없어 하나의 커넥션으로 데이터를 송수신할 수 있다.

웹 소켓이 필요한 경우

  • 실시간 양방향 데이터 통신이 필요
  • 많은 수의 동시 접속자를 수용
  • 브라우저에서 TCP 기반 통신으로 확장
  • 개발자에게 사용하기 쉬운 API 형식
  • SOA(Service Oriented Architecture)로 확장해야 하는 경우

실시간 통신하는 경우 웹소켓을 사용하는 것이 바람직해 보인다.

하지만 이전에 사용하던 기술들이 어떠한 불편함이 있었는지 어떻게 동작하는지 생각해보는 것이 웹소켓의 필요성을 이해하는데 도움이 될 것 같아 이렇게 정리를 하게 되었다.

웹 소켓은 실시간 서비스에 적합하지만 그 만큼 많은 비용과 코드가 복잡해질 수 있는 어려움이 있기 때문에, 앞서 말했던 기존 방법말고 정말 사용해야 할 것인지에 대한 고민이 필요해보인다.

 

참고

 

[대규모 시스템 설계] 채팅 시스템 설계

채팅 앱은 우리가 가장 많이 사용하고 있는 서비스 중 하나다. 채팅 앱에는 1:1 채팅이나 그룹 채팅같이 업무 혹은 개인 메시지에 특화된 채팅 시스템이나 게임 등에 특화된 음성 채팅 등이 존재

jjingho.tistory.com

 

Web Polling, Long Polling, Server-sent Events, WebSocket

Web Browser와 Server 사이에서 여전하 가장 많잉 이용되는 HTTP/1.1 Procotol은 Web Browser (Client)가 먼저 Server에세 요청을 전달하면 Server가 요청에 대한 응답을 전송하는 단방향 Protocol이다. 이러한 제한된

ssup2.github.io

 

Socket 통신 vs http 통신 ( feat. polling, long polling, web socket )

Client - Server 통신 일반적으로 소프트웨어에서 사용하는 데이터들은 보통 서버에서 관리된다. 그럼 클라이언트에서 이 서버에 접속하여 데이터를 주고받게 되는데, 여기서 통신방식이 크게 Http

forward-movement.tistory.com

 

[JavaScript] - Long Polling

네트워크 요청과 관련한 Polling, Long Polling, Streaming에 관한 글입니다.

velog.io

 

[WEB] 🌐 웹 소켓 (Socket) 정리 (역사부터 차근차근)

​ 웹 개발을 처음 배우기 시작했다면 서버와 클라이언트의 통신은 모두 HTTP 프로토콜만 이용해서 이루어진다고 생각할 수 있습니다. 하지만 웹 개발을 하면서 채팅, 게임, 주식 차트 등의 실시

inpa.tistory.com