- 실시간 웹 앱에서 서버-클라이언트 이벤트 전달은 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를 해당 기술들의 직접 대체재로 쓰는 목적은 약해짐
기술별 주요 제약
-
양방향 데이터 전송
- 같은 연결에서 서버 데이터를 받고 클라이언트 데이터를 보내는 방식은 WebSockets와 WebTransport만 지원함
- 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에 대해 브라우저가 연결 제한을 늘릴 수도 있지만, Chromium과 Firefox 관련 이슈는 “won’t fix”로 표시됨
-
브라우저 앱의 연결 수 줄이기
- 브라우저 앱에서는 사용자가 앱을 여러 탭에서 동시에 열 수 있다고 가정해야 함
- 기본적으로 탭마다 하나의 서버 스트림 연결을 열 수 있지만, 대부분은 불필요함
- 여러 탭이 열려도 단일 연결만 열고 탭 사이에서 공유하는 방식이 가능함
- RxDB는 broadcast-channel npm package의 LeaderElection을 사용해 서버와 클라이언트 사이의 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.js와 Safari 호환성이 부족함
- 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로 전환해 다시 서버와 같은 상태가 될 때까지 동기화함
- 이 방식은 누락 이벤트를 보정하고 클라이언트가 항상 서버와 정확히 같은 상태로 동기화될 수 있게 함
기업 인프라에서의 확인 사항
- 기업 인프라에서는 스트리밍 기술 전반에 문제가 발생할 수 있음
- 프록시와 방화벽은 트래픽을 차단하거나 요청·응답을 의도치 않게 깨뜨릴 수 있음
- 이런 환경에서 실시간 앱을 구현할 때는 선택한 기술 자체가 해당 인프라에서 동작하는지 먼저 테스트해야 함