4P by GN⁺ | ★ favorite | 댓글 1개
  • 실시간 웹 앱에서 서버-클라이언트 이벤트 전달은 Long Polling, WebSockets, SSE, WebRTC, WebTransport 중 무엇을 고르느냐에 따라 지연시간, 양방향성, 구현 난이도, 운영 제약이 크게 달라짐
  • WebSockets는 단일 장기 연결로 양방향 통신을 제공하지만, 실제 운영에서는 연결 유실 감지, 재연결, ping-pong 하트비트 때문에 Socket.IO 같은 라이브러리를 함께 쓰는 경우가 많음
  • Server-Sent Events는 HTTP 기반 서버→클라이언트 단방향 스트림이라 구현과 재연결 처리가 단순하지만, 기본 EventSource API는 POST 본문이나 커스텀 헤더 전송에 제약이 있음
  • WebTransport는 HTTP/3 QUIC 기반의 다중 스트림과 신뢰·비신뢰 전송을 지원하지만, 2024년 3월 기준 Working Draft이고 Safari·Node.js 네이티브 지원이 없어 아직 범용 선택지로 보기 어려움
  • 모바일 백그라운드 종료, 도메인당 연결 수 제한, 기업 프록시·방화벽, 재연결 중 이벤트 누락 때문에 실제 앱에는 동기화 복구 로직과 인프라 테스트가 함께 필요함

실시간 서버-클라이언트 통신 기술의 흐름

  • 실시간 웹 애플리케이션에서는 서버가 클라이언트로 이벤트를 보내는 기능이 핵심 요구사항이 됨
  • 초기에는 HTTP 위에서 동작하는 Long Polling이 브라우저에서 가능한 서버-클라이언트 메시징 방식으로 쓰였음
  • 이후 WebSockets가 양방향 통신을 위한 더 견고한 방식으로 등장함
  • Server-Sent Events(SSE) 는 서버에서 클라이언트로만 보내는 단방향 통신을 더 단순하게 제공함
  • WebTransport는 더 효율적이고 유연하며 확장성 있는 방식이 될 가능성이 있지만, 현재는 지원 범위가 제한적임
  • WebRTC는 일부 틈새 서버-클라이언트 이벤트 용도로 고려할 수 있으나, 주요 선택지로 다루기에는 목적이 다름

Long Polling

  • Long Polling은 일반 XHR 요청으로 서버 푸시 통신을 흉내 내는 방식임
  • 클라이언트가 서버에 요청을 열어두면, 서버는 새 데이터가 생길 때까지 응답을 보류함
  • 새 정보를 보낸 뒤 연결은 닫히고, 클라이언트는 즉시 다음 요청을 다시 시작함
  • 전통적인 주기적 폴링보다 업데이트가 빠르고 불필요한 네트워크 트래픽과 서버 부하를 줄일 수 있음
  • 다만 WebSockets 같은 실시간 기술보다 효율이 낮고, 데이터 전송 시점에 따라 지연이 생길 수 있음
  • 클라이언트 구현은 단순하지만, 백엔드에서는 재연결 중인 클라이언트가 이벤트를 놓치지 않도록 보장하기 어려움

WebSockets

  • WebSockets는 클라이언트와 서버 사이에 단일 장기 연결을 만들고 전이중(full-duplex) 통신을 제공함
  • 연결이 수립된 뒤에는 HTTP 요청-응답 사이클의 오버헤드 없이 양쪽이 독립적으로 데이터를 보낼 수 있음
  • 실시간 채팅, 게임, 금융 거래 플랫폼처럼 낮은 지연시간과 잦은 업데이트가 필요한 앱에 적합함
  • 기본 WebSocket API는 사용하기 쉽지만, 프로덕션에서는 연결 유실과 재생성 처리가 복잡해짐
  • 연결이 여전히 사용 가능한지 감지하기 어렵기 때문에 보통 ping-and-pong 하트비트를 추가함
  • 이런 복잡성 때문에 많은 경우 Socket.IO 같은 라이브러리를 사용하며, Socket.IO는 필요하면 Long Polling 폴백도 제공함

Server-Sent Events

  • Server-Sent Events(SSE) 는 HTTP 위에서 서버 업데이트를 클라이언트로 푸시하는 표준 방식임
  • WebSockets와 달리 서버→클라이언트 단방향 통신만을 위해 설계됨
  • 라이브 뉴스 피드, 스포츠 점수, 실시간 업데이트처럼 클라이언트가 서버로 메시지를 보낼 필요가 없는 상황에 적합함
  • SSE는 하나의 HTTP 요청에서 연결을 유지한 채, 이벤트가 생길 때마다 백엔드가 응답을 한 줄씩 흘려보내는 방식으로 볼 수 있음
  • 브라우저 클라이언트에서는 EventSource 인스턴스를 초기화해 이벤트 스트림을 받음
  • EventSource는 WebSockets와 달리 연결이 끊기면 자동으로 재연결함
  • 서버는 Content-Type 헤더를 text/event-stream으로 설정하고, SSE specification에 맞춰 이벤트 타입, 데이터 페이로드, 이벤트 ID, retry timing 같은 필드를 포맷해야 함

WebTransport

  • WebTransport는 웹 클라이언트와 서버 사이의 효율적이고 낮은 지연시간 통신을 위한 API임
  • HTTP/3 QUIC protocol을 활용해 여러 스트림에서 데이터를 보낼 수 있음
  • 신뢰 가능한 전송과 신뢰하지 않는 전송, 순서 없는 데이터 전송을 함께 지원함
  • 실시간 게임, 라이브 스트리밍, 협업 플랫폼처럼 고성능 네트워킹이 필요한 앱에 강력한 도구가 될 수 있음
  • 2024년 3월 기준 WebTransport는 Working Draft 상태이며 널리 지원되지 않음
  • Safari browser에서는 아직 사용할 수 없고, Node.js에도 네이티브 지원이 없음
  • 지원이 넓어지더라도 API가 매우 복잡해, 애플리케이션 코드에서 직접 쓰기보다 WebTransport 위에 라이브러리를 만드는 형태가 될 가능성이 큼

WebRTC

  • WebRTC는 브라우저와 모바일 앱 안에서 플러그인 없이 실시간 통신 기능을 제공하는 오픈소스 프로젝트이자 API 표준임
  • 브라우저 간 오디오, 비디오, 데이터 교환을 위한 peer-to-peer 연결을 지원함
  • NAT와 방화벽을 통과하기 위해 ICE, STUN, TURN 같은 프로토콜을 사용함
  • WebRTC는 클라이언트-클라이언트 상호작용을 위해 만들어졌지만, 서버가 클라이언트처럼 동작하게 만들어 서버-클라이언트 통신에 활용할 수도 있음
  • 이런 방식은 틈새 사용 사례에만 맞기 때문에 주요 선택지 비교에서는 제외됨
  • WebRTC가 동작하려면 어차피 시그널링 서버가 필요하고, 이 서버는 WebSockets, SSE, WebTransport 중 하나 위에서 동작하게 됨
  • 이 때문에 WebRTC를 해당 기술들의 직접 대체재로 쓰는 목적은 약해짐

기술별 주요 제약

  • 양방향 데이터 전송

    • 같은 연결에서 서버 데이터를 받고 클라이언트 데이터를 보내는 방식은 WebSocketsWebTransport만 지원함
    • Long Polling도 이론적으로는 가능하지만, 기존 long-polling 연결에 새 데이터를 보내려면 추가 HTTP 요청이 필요해 권장되지 않음
    • Long Polling에서는 기존 연결을 방해하지 않고 별도 HTTP 요청으로 클라이언트→서버 데이터를 보내는 편이 나음
    • SSE는 서버로 추가 데이터를 보내는 기능을 지원하지 않음
    • 기본 네이티브 EventSource API는 초기 요청에서도 HTTP body에 POST 같은 데이터를 보내지 못함
    • 데이터를 URL 파라미터에 넣어야 하는데, credentials가 서버 로그, 프록시, 캐시에 유출될 수 있어 보안상 좋지 않음
    • RxDB는 이 문제를 피하기 위해 네이티브 EventSource API 대신 eventsource polyfill을 사용하며, 이 라이브러리는 커스텀 HTTP 헤더 같은 기능을 추가함
    • Microsoft의 fetch-event-source는 body 데이터 전송과 GET 대신 POST 요청 사용을 허용함
  • 도메인당 연결 수 제한

    • 대부분의 최신 브라우저는 도메인당 6개 연결을 허용하며, 이 제한은 안정적인 서버→클라이언트 메시징 방식 전반의 사용성을 제한함
    • 6개 연결 제한은 브라우저 탭 사이에서도 공유되므로, 같은 페이지를 여러 탭에서 열면 탭들이 같은 연결 풀을 나눠 써야 함
    • HTTP/1.1 RFC는 더 낮은 수치인 서버 또는 프록시당 2개 연결을 권고함
    • 이 정책은 방문자를 이용한 DDoS를 막는 데 타당하지만, 합법적인 서버-클라이언트 통신에서 여러 연결이 필요할 때 문제가 될 수 있음
    • 우회하려면 HTTP/2 또는 HTTP/3을 사용해 브라우저가 도메인당 단일 연결만 열고 다중화(multiplexing) 로 데이터를 처리하게 해야 함
    • HTTP/2·HTTP/3에서도 SETTINGS_MAX_CONCURRENT_STREAMS 설정이 실제 동시 스트림 수를 제한하며, 대부분 구성의 기본값은 100 concurrent streams임
    • EventSource 같은 특정 API에 대해 브라우저가 연결 제한을 늘릴 수도 있지만, ChromiumFirefox 관련 이슈는 “won’t fix”로 표시됨
  • 브라우저 앱의 연결 수 줄이기

    • 브라우저 앱에서는 사용자가 앱을 여러 탭에서 동시에 열 수 있다고 가정해야 함
    • 기본적으로 탭마다 하나의 서버 스트림 연결을 열 수 있지만, 대부분은 불필요함
    • 여러 탭이 열려도 단일 연결만 열고 탭 사이에서 공유하는 방식이 가능함
    • RxDBbroadcast-channel npm packageLeaderElection을 사용해 서버와 클라이언트 사이의 replication stream을 하나만 유지함
    • 이 패키지는 RxDB 없이도 다른 애플리케이션에서 단독으로 사용할 수 있음

모바일, 프록시, 방화벽의 운영 제약

  • Android와 iOS 같은 모바일 운영체제에서는 WebSockets를 비롯한 열린 연결을 계속 유지하기 어려움
  • 모바일 OS는 일정 시간 비활성 상태가 지나면 앱을 백그라운드로 보내고 열린 연결을 닫을 수 있음
  • 이 동작은 배터리 절약과 성능 최적화를 위한 리소스 관리 전략의 일부임
  • 개발자들은 서버가 클라이언트로 데이터를 보낼 때 지속 연결 대신 모바일 푸시 알림을 사용하는 경우가 많음
  • 푸시 알림은 지속적인 열린 연결 없이 서버가 새 데이터를 앱에 알리고 앱의 동작이나 업데이트를 유도할 수 있음
  • 기업 환경에서는 프록시와 방화벽이 비HTTP 연결을 차단해 WebSocket 서버를 인프라에 넣기 어려울 수 있음
  • 이런 환경에서는 HTTP 기반인 SSE가 기업 통합에 더 쉬운 방식이 될 수 있음
  • Long Polling도 일반 HTTP 요청만 사용하므로 선택지가 될 수 있음

성능 비교

  • WebSockets, SSE, Long Polling, WebTransport를 비교할 때는 지연시간, 처리량, 서버 부하, 확장성을 함께 봐야 함
  • Go 서버 구현에서 메시지 시간을 테스트한 realtime-web repo는 WebSockets, WebRTC, WebTransport의 성능이 비슷하다는 결과를 보여줌
  • WebTransport는 HTTP/3 기반의 새 기술이므로 2024년 3월 이후 더 많은 성능 최적화가 나올 수 있음
  • WebTransport는 전력 사용을 줄이도록 최적화되어 있지만, 해당 지표는 테스트되지 않음
  • 지연시간

    • WebSockets는 단일 지속 연결 위의 전이중 통신 때문에 가장 낮은 지연시간을 제공함
    • SSE도 서버→클라이언트 통신에서는 낮은 지연시간을 제공하지만, 클라이언트가 서버로 메시지를 보내려면 추가 HTTP 요청이 필요함
    • Long Polling은 각 데이터 전송마다 새 HTTP 연결을 만들기 때문에 지연시간이 더 높음
    • Long Polling에서는 서버가 이벤트를 보내려는 순간 클라이언트가 새 연결을 여는 중이면 지연시간이 크게 늘어날 수 있음
    • WebTransport는 WebSockets와 비슷한 낮은 지연시간을 제공할 것으로 기대되며, HTTP/3의 더 효율적인 다중화와 congestion control을 활용함
  • 처리량

    • WebSockets는 지속 연결 덕분에 높은 처리량을 낼 수 있지만, 클라이언트가 서버 전송 속도만큼 빠르게 처리하지 못하는 backpressure 문제가 처리량에 영향을 줄 수 있음
    • SSE는 WebSockets보다 오버헤드가 적어 단방향 서버→클라이언트 브로드캐스트에서 잠재적으로 더 높은 처리량을 낼 수 있음
    • Long Polling은 연결을 자주 열고 닫는 오버헤드 때문에 일반적으로 처리량이 낮고 서버 리소스를 더 많이 소비함
    • WebTransport는 단일 연결 안에서 단방향·양방향 스트림 모두에 높은 처리량을 지원할 것으로 예상되며, 여러 스트림이 필요한 시나리오에서 WebSockets를 능가할 수 있음
  • 확장성과 서버 부하

    • WebSockets는 많은 연결을 유지할수록 서버 부하가 크게 늘어날 수 있어 사용자 수가 많은 앱의 확장성에 영향을 줄 수 있음
    • SSE는 주로 서버→클라이언트 업데이트가 필요한 시나리오에서 더 확장성이 좋음
    • SSE는 protocol upgrade 같은 WebSocket 절차 없이 일반 HTTP 요청을 사용하므로 연결 오버헤드가 더 낮음
    • Long Polling은 잦은 연결 수립 때문에 서버 부하가 커져 가장 확장성이 낮고, 폴백 메커니즘으로만 적합함
    • WebTransport는 HTTP/3의 연결·스트림 처리 효율을 바탕으로 높은 확장성을 목표로 설계됐으며, WebSockets와 SSE보다 서버 부하를 줄일 가능성이 있음

사용 사례별 권장

  • SSE는 구현이 가장 직관적인 선택지로, 기존 HTTP/S 프로토콜을 사용해 기업 방화벽 제한과 다른 프로토콜에서 생길 수 있는 기술 문제를 피하기 쉬움
  • Node.js와 다른 서버 프레임워크에 쉽게 통합할 수 있음
  • 뉴스 피드, 주식 시세, 라이브 이벤트 스트리밍처럼 서버→클라이언트 업데이트가 자주 필요한 앱에 적합함
  • WebSockets는 지속적인 양방향 통신이 필요한 시나리오에 강함
  • 브라우저 게임, 채팅 애플리케이션, 라이브 스포츠 업데이트처럼 지속적인 상호작용이 필요한 경우 주된 선택지가 됨
  • WebTransport는 잠재력은 있지만 서버 프레임워크 지원이 넓지 않고 Node.jsSafari 호환성이 부족함
  • WebTransport는 HTTP/3에 의존하는데, nginx 같은 많은 웹 서버의 HTTP/3 지원은 아직 experimental 상태임
  • 신뢰·비신뢰 데이터 전송을 모두 지원하는 미래형 기술이지만, 대부분의 현재 사용 사례에는 아직 실행 가능한 선택지가 아님
  • Long Polling은 반복적인 새 HTTP 연결 수립의 비효율과 높은 오버헤드 때문에 대체로 구식 방식임
  • WebSockets나 SSE를 지원하지 않는 환경에서 폴백으로 쓸 수는 있지만, 성능 제한 때문에 일반적인 사용은 권장되지 않음

재연결 중 이벤트 누락 문제

  • 모든 실시간 스트리밍 기술 위에 기능을 만들 때는 연결 중단과 재연결 상황을 고려해야 함
  • 클라이언트가 연결 중이거나 재연결 중이거나 오프라인이면 서버에서 발생한 이벤트를 스트림으로 받지 못할 수 있음
  • 서버가 주식 시세처럼 매번 전체 내용을 스트리밍한다면 누락 이벤트는 중요하지 않을 수 있음
  • 백엔드가 부분 결과만 스트리밍한다면 누락 이벤트를 반드시 처리해야 함
  • 백엔드가 클라이언트별로 어떤 이벤트가 성공적으로 전송됐는지 기억하는 방식은 확장성이 좋지 않음
  • 이 문제는 클라이언트 측 로직으로 처리하는 편이 나음
  • RxDB Sync Engine은 두 가지 동작 모드를 사용함
    • checkpoint iteration mode: 일반 HTTP 요청으로 백엔드 데이터를 반복 조회해 클라이언트가 다시 동기화될 때까지 따라잡음
    • event observation mode: 실시간 스트림의 업데이트로 클라이언트를 동기화 상태로 유지함
  • 클라이언트 연결이 끊기거나 오류가 발생하면 replication은 잠시 checkpoint iteration mode로 전환해 다시 서버와 같은 상태가 될 때까지 동기화함
  • 이 방식은 누락 이벤트를 보정하고 클라이언트가 항상 서버와 정확히 같은 상태로 동기화될 수 있게 함

기업 인프라에서의 확인 사항

  • 기업 인프라에서는 스트리밍 기술 전반에 문제가 발생할 수 있음
  • 프록시와 방화벽은 트래픽을 차단하거나 요청·응답을 의도치 않게 깨뜨릴 수 있음
  • 이런 환경에서 실시간 앱을 구현할 때는 선택한 기술 자체가 해당 인프라에서 동작하는지 먼저 테스트해야 함

댓글과 토론

Hacker News 의견들
  • Server-Sent Events는 늘 정이 갔음. 단순하고 쓰기/구현하기 쉬움

    • IPv6가 있으면 이제 쉽게 완전 확장 가능하고, 제대로 만들면 클라이언트에 SSE 서비스 목록만 주면 거의 무상태처럼 동작해서 훨씬 확장하기 쉬움
      WebSocket은 사용량이 어느 수준을 넘으면 확장이 꽤 복잡해짐
    • 단순한 덕분에 CDN으로 WebSocket보다 훨씬 쉽게 확장할 수 있음: https://www.fastly.com/blog/server-sent-events-fastly
    • 동의함. 다만 브라우저 인스턴스당 오리진별 SSE 스트림이 6개로 제한돼서, 클라이언트 쪽 복잡도를 추가하지 않으면 탭 6개가 한계일 수 있음
      https://crbug.com/275955
    • 단점은 페이로드를 base64로 인코딩하거나 줄바꿈을 제거해야 한다는 것임
      왜 그냥 multipart 스트리밍 응답으로 만들지 않았는지 궁금함. 메타데이터도 지원하고, 아주 흔히 구현된 형식인데 말임
    • 평범한 Apache prefork와 PHP에서도 동작함
  • 추가로 알아둘 단점들이 있음
    WebSocket은 흐름 제어(배압)와 다중화가 없어서 필요하면 직접 만들거나 RSocket 같은 걸 써야 함. SSE도 바이너리 데이터를 직접 보낼 수 없어 base64 같은 인코딩이 필요함
    WebTransport는 이 문제들을 다루고 HOL 블로킹도 해결하지만, Python 2→3이나 IPv6 전환처럼 사람들이 기존 버전을 계속 쓰기 쉽고 업그레이드 이점이 작게 느껴지는 문제가 생길까 걱정됨
    브라우저가 TCP로 계속 동작하는 한 일부 네트워크는 UDP, 따라서 HTTP/3/WebTransport를 아예 막을 수 있음

    • HOL 블로킹은 문제지만, TCP는 흐름 제어를 제공함. 그걸 쓰지 않는다면 HTTP/3 위로 가는 것임
      WebTransport 전환이 늦을 수 있다는 걱정은 예전에 TLS 전송, HTTP/3, XHR에도 똑같이 할 수 있었던 말임. 주요 브라우저 엔진 몇 개가 지배하는 구조라 새 브라우저 기능과 프로토콜 배포는 비교적 쉬움
      TCP가 가능하니 일부 네트워크가 UDP를 막는다는 논리라면, HTTP 1.1 비TLS가 가능하니 HTTP/2와 TLS도 계속 막힐 거라는 얘기와 비슷함. 완전히 틀리진 않지만 HTTP/2와 특히 TLS의 광범위한 채택을 보면 생각보다 큰 문제는 아닌 듯함
    • UDP를 막는 네트워크 얘기는 계속 듣지만 실제로 본 적은 없음. UDP 위에서 돌아가는 것도 많음
      작은 사무실이나 영화에나 나올 법한 디스토피아 기업 환경에서는 닫을 수도 있겠지만, 일부 네트워크가 UDP를 금지할 수 있다는 사실이 왜 큰 관련이 있는지 모르겠음. 어떤 네트워크는 google.com이나 wikipedia.com도 막지만, 그렇다고 그 서비스들이 실패하진 않음
    • WebSocketStream이 곧 Chrome에 들어가며 배압을 추가함: https://chromestatus.com/feature/5189728691290112
    • base64보다 나은 접근으로 binary-sse가 있음: https://github.com/luciopaiva/binary-sse
    • 그런 상황이면 HTTP/2는 여전히 동작할 테니 다중화 걱정은 줄어듦
  • 글의 WebRTC 설명은 정확하지 않음. 클라이언트/서버 WebRTC는 별도 “시그널링 서버” 없이도 가능하고, 서버가 시그널링을 하면 됨
    왕복이 몇 번 더 필요할 뿐 별도 서버가 필요하진 않음. WebRTC 데이터 채널은 WebSocket이나 SSE 대체로 꽤 잘 동작하며, 특히 HOL 블로킹을 피하고 싶을 때 좋음. Pion이나 str0m처럼 거의 모든 작업을 해주는 라이브러리도 많음
    WebTransport API가 복잡하다는 말도 과장으로 보임. 고급 기능이 필요 없으면 무시하면 되고, WebSocket처럼 쓰고 싶으면 양방향 스트림 하나만 열면 거의 끝임. HOL 블로킹을 피하려면 메시지마다 스트림을 열면 됨. 조금 더 복잡하지만 라이브러리가 꼭 필요한 수준은 아니고, GitHub Copilot이 코드도 써줄 가능성이 큼. 다만 WebTransport가 아직 성숙 중이라 서버 라이브러리는 많지 않고 Safari 지원도 기다리는 중임

    • 클라이언트/서버 WebRTC를 “시그널링 서버” 없이 할 수 있다는 건 의아함
      보통 시그널링 서버는 WebSocket으로 구현됨. 기존 클라이언트들의 탈중앙화된 부트스트랩을 제안하는 게 아니라면 WebRTC 자체로 구현할 수는 없음
  • 전통적인 “엔터프라이즈”와 “보안” IT 인프라를 가진 고객을 대상으로 만든다면, 새로고침 버튼을 추가하고 끝내는 편이 나음
    이런 환경에서 내 경험상 꾸준히 실패하고 끝없는 절차 때문에 고칠 수도 없는 게 바로 이런 고객을 대상으로 실시간 기능을 만들려는 시도였음

    • Jetty/CometD는 다른 전송 방식이 안 되면 긴 폴링으로 폴백함
    • 솔직히 이 계열 기술은 전부 각자의 문제가 있고, 새로고침 버튼도 예외는 아님
    • 브라우저에는 새로고침 버튼이 있지만, 아마 그걸 누르면 애플리케이션이 깨질 가능성이 큼
  • WebSocket과 SSE는 규모가 커지면 관리가 큰 골칫거리임. 특히 백엔드에서는 별도 관찰성이 필요하고, 모바일 기기에서 아주 조심히 구현하지 않으면 프런트엔드 디버깅이 악몽이 됨
    기기는 배터리 절약 때문에 네트워크를 끄거나 느리게 만들고, 전용 API로 명시적으로 I/O를 하지 않으면 더 그렇음
    새 연결 생성은 비용이 큰 작업이고, 서버는 상태를 어딘가에 저장해야 함. 이 상태 저장 계층에 문제가 생기면 클라이언트가 계속 재시도하고 시간 초과에 걸리며, 비용 큰 작업에 영원히 붙잡힘. 처리량을 쉽게 제어하면서 데이터베이스에 천천히 부하를 걸 방법도 아님
    신뢰성 측면에서는 경험상 긴 폴링이 가장 나았음. 이벤트 기반 흐름이 정말 중요해도 프런트엔드는 1계층 백엔드에 긴 폴링을 하고, 그 1계층이 2계층 백엔드에 WebSocket으로 구독하는 2계층 구조가 낫고, 신뢰성 제어가 훨씬 좋아짐

    • 완전히 동의함. WebSocket과 SSE로 스스로 발등 찍는 경우를 봤음. 긴 폴링은 비용이 들지만, 가장 설명 가능하고 확장 가능한 접근이라고 봄
    • SSE는 긴 폴링을 지원함. 서버가 원할 때 연결을 닫게 만들 수 있음
      SSE는 자동 재연결을 지원하고, 마지막으로 본 ID도 포함해서 서버가 끊김 없이 이어갈 수 있게 해줌
    • 링크된 글에서 이런 부분 상당수가 다뤄졌고, rxdb에는 여러 우려를 완화하는 장치가 있음
  • 글에는 없지만 짧은 폴링도 관련 있음. 서버에서 클라이언트로 메시지를 보내는 방식은 아니지만, 공유 호스팅처럼 다른 선택지가 없을 때는 여전히 유용함
    내 경험상 폴링 간격이 길어도, 예를 들어 20초라도, 각 응답에 메시지 목록을 같이 넣으면 꽤 잘 동작함. 사용자가 버튼을 누르면 클라이언트가 서버에 요청을 보내고, 서버는 데이터와 최신 메시지 목록을 함께 응답해서 클라이언트가 최신 상태가 됨

    • 빠르게 바뀌는 데이터에도 적용 가능함. 폴링 중 업데이트가 포함되는 비율이 높다면 특히 그렇음
  • 아직도 WebSocket과 SSE가 초기 요청에서 Authorization 같은 헤더 전송을 왜 지원하지 않는지 모르겠음. 실시간 서비스 인증을 전부 구현자에게 맡겨버림
    스펙에 좋은 방식이 있을 수도 있지만, 너무 다양한 접근을 봐서 이제는 사실상 없다고 말해도 될 정도임

    • EventSource API는 아쉬운 점이 많음. 가장 많이 쓰이는 EventSource 폴리필의 유지보수자이지만, 최근에는 EventSource 클라이언트를 현대적으로 다시 만든 프로젝트를 시작함: https://github.com/rexxars/eventsource-client
      커스텀 헤더 처리뿐 아니라 모든 요청 메서드(POST, PATCH 등), 요청 본문 포함, 이름 있는 이벤트 구독, 초기 last event ID 설정도 지원함. 비동기 반복자로도 쓸 수 있음
      Server-Sent Events의 단순함은 좋아하지만, EventSource API는 급하게 구현된 뒤 그냥 남아버린 것처럼 보임
      [1]: https://github.com/eventsource/eventsource
    • 초기 요청은 표준 HTTP 헤더 전체와 쿠키까지 보낼 수 있는 것 아닌가?
    • 쿠키는 보냄
    • TLS 인증서도 있긴 함
    • 몇 년째 써왔는데, 뭔가 놓치고 있는 건가?
  • 순진한 생각일 수 있지만, HTTP/2 이상을 전제로 하면 EventSource와 메시지 전송용 fetch() 조합이 단일 TCP 연결을 쓰는 다른 프로토콜만큼 괜찮아 보임. HTTP/3는 UDP를 쓰니 더 좋고
    탭이 포그라운드에 있을 때만 연결 유지가 필요하다는 전제임. 실제로 이 방식을 시도했을 때 어떤 문제가 있었는지 궁금함

    • 한계 중 하나는 SSE가 텍스트 전용이라 바이너리 데이터를 효율적으로 보낼 수 없다는 것임. base64 같은 인코딩이 필요함
    • 원하는 걸 해주는 라이브러리가 있음
      https://www.npmjs.com/package/@microsoft/fetch-event-source
    • 나도 정확히 같은 생각을 했음. HTTP/2와 SSE면 문제의 99%는 해결되는 것 아닌가 싶음
      완전히 다른 걸 하기보다 지연 시간, 메모리 사용량, CPU 자원을 더 낮추면서 SSE를 더 밀어붙일 수 있을지 궁금했음
    • 주된 사용 사례가 서버→클라이언트라는 전제라면 맞음
  • 이런 글을 보면 좀 재미있음. 90년대 후반에 온라인 경매 시스템을 설계했는데, XHR 요청은 전혀 없었음
    실시간 업데이트는 전부 server-push/HTTP 스트리밍으로 처리했음. 당시에는 열린 연결을 모두 다루기가 쉽지 않았지만, 적절한 아키텍처가 있으면 수용 가능한 규모까지 가능했음

    • HTTP 스트리밍의 중요성을 사람들에게 설명하느라 정말 많은 시간을 썼고, 확실히 쉽지 않은 싸움임
      HTTP/2나 HTTP/3의 장점은 훌륭하지만, 사실상 어디서나 지원되는 HTTP 1.1에서 활용할 수 있는 것도 알아둘 필요가 있음
  • 긴 폴링이 좀 그리움. 최신 기술에 비하면 정말 단순했음. WebRTC가 최고라고 생각하는 입장에서도 그렇게 느껴짐

    • SSE는 긴 폴링보다 딱히 더 복잡하지 않음. 차이는 서버가 응답을 보낸 직후 연결을 닫지 않는다는 것뿐임
      대신 다시 데이터를 기다렸다가 같은 스트림으로 추가 응답을 보냄
    • 그렇게 단순했으면 좋겠지만 아님
      Second Life의 네트워킹은 “이벤트 채널”에 긴 폴링 HTTPS를 쓰고, 서버가 그 채널로 클라이언트에 이벤트 메시지를 보냄. 대부분의 메시지는 UDP로 가지만, 암호화가 필요하거나 큰 메시지는 HTTPS/TCP 이벤트 채널로 감
      클라이언트 쪽 C++ 클라이언트는 libcurl을 쓰는데, 기본 시간 초과 설정이 긴 폴링과 맞지 않음. libcurl이 연결을 끊고 새 요청을 만들며, 그 결과 메시지 손실이나 중복이 생길 수 있음
      서버 쪽에서는 Apache가 실제 시뮬레이션 서버 앞단에 서서 무관한 연결 시도를 걸러내는데, Apache도 자체 시간 초과가 있어 연결을 중단하고 클라이언트 재시도를 유발함
      메시지 일련번호로 손실을 막으려 하지만, Second Life 서버는 클라이언트가 확인용으로 돌려보내는 일련번호를 무시함. Open Simulator의 일부 호환 서버는 순차 번호를 건너뛰기도 함
      결과적으로 신뢰 가능해야 할 메시지를 잃거나 중복할 수 있는 HTTPS 기반 시스템이 됨. 일부 메시지는 잃어버리면 게임 내 사용자 활동이 멈춰버림
      이걸 설계한 사람들은 오래전에 떠났고, 현재 직원들은 이 혼란이 얼마나 심각한지 몰랐음. 외부 사용자들이 문제를 찾아 문서화해야 했고, 회사 직원들은 몇 달째 고치려 하고 있음. 고치기 충분히 어려워서 현재는 작업을 미루는 쪽으로 보임
      그래서 긴 폴링은 “멍청할 정도로 단순”하지 않음. 올바른 방식은 TCP와 HTTPS 계층이 시간 초과되지 않을 만큼 자주 keep-alive 메시지를 보내는 것일 가능성이 큼. 그러면 Apache와 libcurl이 잘 동작하는 경로에 머무름
    • 아직도 자주 씀. 요청 오버헤드를 감수하는 대신 모든 걸 기존 HTTP API 맥락 안에 유지하는 게 합리적인 애플리케이션이 많음
    • 요즘도 HTTP/2로 긴 폴링을 쓸 수 있고, 사라지지 않을 것임