11P by GN⁺ 7일전 | ★ favorite | 댓글 3개
  • WebSocket은 실시간 통신에 유용하지만, 항상 필요한 것은 아니며 HTTP 기반 대안이 더 간단하고 안정적일 수 있음
  • 트랜잭션 처리, 연결 관리, 서버 복잡성 등에서 WebSocket은 과도한 오버헤드를 유발할 수 있음
  • HTTP Streaming과 라이브러리(eventkit)를 활용하면 WebSocket 없이도 실시간 동기화와 이벤트 처리가 가능함

WebSocket이란 무엇인가

  • WebSocket은 클라이언트와 서버 간에 지속적인 양방향 통신 채널을 여는 기술임
  • HTTP를 통해 연결을 시작하지만, 이후에는 별도의 프로토콜로 통신이 이루어짐
  • 실시간 애플리케이션 구현에 자주 사용되며, 쌍방향 통신이 가능하다는 점에서 유용함

WebSocket 메시지는 트랜잭션 방식이 아님

  • WebSocket은 요청과 응답 간의 직접적인 연관을 보장하지 않음
  • 상태 변경 명령과 그 결과 메시지가 동일한 스트림에서 섞여 도착할 수 있음
  • 예를 들어, 하나의 클라이언트가 상태를 변경하고 에러가 발생해도, 어떤 명령에 대한 에러인지 알기 어려움
  • 해결 방법은 requestId를 포함시켜 명령과 응답을 연결하는 방식이지만, 이는 복잡성과 관리 비용을 증가시킴
  • HTTP를 활용한 트랜잭션 방식으로 명령을 보내고, WebSocket은 상태 변경 방송에만 사용하는 방식이 더 단순함
  • 전송 측은 HTTP 요청으로, 수신 측은 WebSocket 또는 다른 스트리밍 방식으로 분리 가능함

WebSocket 연결 생명주기 관리의 어려움

  • WebSocket을 사용할 경우 연결의 시작, 종료, 에러, 재연결 등을 직접 처리해야 함
  • 브라우저에서의 기본 처리 예시에는 연결 오픈, 메시지 수신, 에러 발생, 연결 종료 이벤트 핸들링이 포함됨
  • 재연결 로직, 메시지 버퍼링, 지수적 백오프 등 추가 로직이 필요함
  • 반면 HTTP는 요청 단위로 시작과 종료가 명확하여 구현이 간단함
  • 복잡한 생명주기 관리는 WebSocket을 사용할 이유가 확실할 때만 정당화됨

서버 코드 복잡도 증가

  • WebSocket은 HTTP 업그레이드 요청을 처리해야 하며, 이는 추가적인 핸드셰이크 로직을 요구함
  • Sec-WebSocket-Key 같은 특수 헤더를 검증하고, 응답 헤더를 적절히 반환해야 함
  • WebSocket 연결 후에는 지속적인 메시지 수신 및 송신 상태를 유지해야 하며, 부분 프레임 처리와 같은 문제도 발생 가능함
  • HTTP만을 사용할 경우보다 디버깅 및 오류 처리 난이도가 높아짐
  • 프레임워크가 일부 과정을 추상화하지만, 기본 복잡성은 여전히 존재함

대안: HTTP Streaming

  • HTTP는 원래 스트리밍을 지원하는 프로토콜로, 전체 파일이 아닌 데이터 스트림을 실시간으로 전송 가능함
  • 기존 WebSocket의 수신 측 기능만 HTTP 스트리밍으로 대체 가능함
  • 비동기 제너레이터를 사용해 상태 업데이트를 스트림 형태로 처리 가능함
  • 서버측 흐름
    • 상태 업데이트는 명령 처리 함수에서 수행됨
    • 연결된 클라이언트들은 제너레이터를 통해 새로운 값이 나올 때마다 전달받음
    • 상태 변경 명령은 HTTP POST를 통해 전송되고, 실시간 스트림은 GET 요청으로 구독함
  • 클라이언트 측 흐름
    • Fetch API와 Stream Reader를 통해 실시간 데이터 수신
    • 텍스트 디코딩 후 UI 업데이트 수행
  • 이 구조로 WebSocket 없이도 실시간 상태 동기화 구현 가능함

보너스: eventkit 라이브러리 소개

  • eventkit은 비동기 스트림을 쉽게 구성하고 관찰할 수 있도록 해주는 라이브러리임
  • RxJS와 유사하지만, 부작용 관리가 개선되었고 제너레이터 기반으로 설계됨
  • 상태 업데이트를 스트림에 푸시하면, 클라이언트에서 이를 실시간으로 수신 가능함
  • StreamAsyncObservable을 통해 서버/클라이언트 양쪽에서 간단하게 구현 가능함
  • 서버 측 eventkit 활용
    • 상태 변경을 Stream에 push하고, 클라이언트는 해당 스트림을 구독
  • 클라이언트 측 eventkit 활용
    • 스트림 데이터를 받아 디코딩 후 UI 업데이트 수행
  • 공식 GitHub 저장소와 HTTP Streaming 가이드도 제공되고 있음

GitHub: https://github.com/hntrl/eventkit

Sse, websocket 세션들을 관리 하는 것이 뭔가 프레임웍에서 체계적이지 않은거 같고, 불안정한 기분이든다. 똥싸고 뒷처리 안한거 같은 느낌

특히 인증같은 것들 만들다 만느낌

Hacker News 의견
  • HTTP 스트리밍이 이 패턴을 염두에 두고 설계된 것이 아니라고 생각함. HTTP 스트리밍은 큰 데이터를 조각으로 나누는 용도임. 스트리밍을 pub/sub 메커니즘처럼 사용하면 후회할 수 있음. HTTP 중개자들은 이 트래픽 패턴을 예상하지 않음(NGINX, CloudFlare 등). WiFi 연결이 끊길 때마다 fetch API가 요청 실패로 오류를 발생시킬 것 같음

    • WebSockets가 필요 없는 경우가 많음. 서버 전송 이벤트(SSE)가 더 간단한 해결책임. SSE가 주목받지 못한 것이 아쉬움
  • RequestID를 서버에 보내서 요청/응답 주기를 얻는 것은 이상하거나 지나친 것이 아님. 진지한 앱에서는 send(message).then(res => ...) 같은 API를 갖추는 것이 항상 가치 있음

    • 업그레이드 요청은 혼란스러움. 웹소켓 서버가 HTTP 서버 안에 내장되어 통합되지 않는 것이 짜증남
    • 웹소켓 요청에서 headers['authorization']를 읽는 미들웨어를 재사용하는 대신, 요청 헤더인 척하는 connectionParams 객체를 접근해야 함
    • 웹소켓 브라우저 API가 EventSource보다 다루기 좋음
  • 비디오 스트리밍은 클라이언트가 범위로 청크를 요청하며, 단일 HTTP 연결이 아님

  • 이벤트킷 대신 SSE를 사용하는 것이 좋음

  • POC에서 전통적인 HTTP 폼 제출을 사용할 예정임. 다른 것이 필요하지 않음

    • 아키텍트는 웹소켓이 필요하다고 주장함
    • POC에는 XHR이나 웹소켓이 필요하지 않음. 순차적인 구매 흐름임
    • 결국 불필요한 웹소켓을 제공하게 됨
  • HTTP2의 문제는 서버 푸시가 기존 프로토콜 위에 추가된 것임. HTTP는 리소스 전송 프로토콜로, 불필요한 오버헤드를 추가함. HTTP2의 주요 목적은 서버가 파일/리소스를 클라이언트에 미리 푸시하여 왕복 지연을 줄이는 것임

    • WebSockets는 양방향 통신을 위해 설계된 더 간단한 프로토콜임. 단일 연결로 데이터 흐름을 제어하기 쉬움. 상태 관리와 연결 손실 복구가 용이함. 인증 및 접근 제어가 단순해짐
  • WebSockets는 스트림으로 보내는 것이 아니라 데이터그램(패킷)으로 보내는 것임. JavaScript 라이브러리의 WebSockets API는 백프레셔를 처리할 수 없고, 모든 오류를 처리할 수 없음. TCP 스트림으로 사용하려면 주의가 필요함

  • WebSockets를 프로덕션에 배포한 후 후회함. NGINX가 4/8시간 후 연결을 종료하고, 브라우저가 수면 후 재연결하지 않는 등의 문제가 있었음. 가능하면 WebSockets와 장기 연결을 피해야 함

  • WebSockets에 대한 이상적인 인식이 있음. 스트리밍/실시간 사용 사례에 WebSockets를 사용하려는 경향이 있음. WebSockets는 HTTP 도구의 단순함과 이점을 잃음. 스트리밍 서버 변경의 해결책은 h2/h3와 SSE임. 클라이언트당 최대 0.5req/s로 배치할 수 있는 경우 WebSockets가 필요하지 않음

  • HTTP 스트리밍에 관심 있는 사람들은 Braid-HTTP를 확인해야 함. HTTP에 이벤트 스트리밍을 우아하게 확장하여 강력한 상태 동기화 프로토콜을 제공함