← 목록으로
네트워크
#WebSocket#실시간통신#전이중#RFC6455#Upgrade핸드셰이크
최종 업데이트 · 2026-10-18

웹소켓(WebSocket) 실시간 양방향 통신

1. 개요

웹소켓(WebSocket)은 단일 TCP 연결 위에서 클라이언트와 서버가 동시에 데이터를 주고받을 수 있는 전이중(full-duplex) 양방향 통신 프로토콜로, IETF RFC 6455(2011)로 표준화되었다. 최초 연결만 HTTP 핸드셰이크로 수립한 뒤에는 HTTP의 요청-응답 틀을 벗어나 지속 연결(persistent connection) 위에서 메시지를 자유롭게 교환한다.

웹소켓이 등장한 근본 배경은 HTTP의 단방향·무상태(stateless) 요청-응답 모델이 실시간 상호작용에 구조적으로 맞지 않는다는 데 있다. 전통적 HTTP는 클라이언트가 요청해야만 서버가 응답할 수 있으므로, 서버에서 발생한 이벤트(새 메시지, 시세 변동, 알림)를 클라이언트가 즉시 받아볼 방법이 없었다. 초기 웹은 이 한계를 폴링(polling), 즉 클라이언트가 주기적으로 "새 소식 있나요?"를 반복 질의하는 편법으로 우회했지만, 이는 대부분의 요청이 빈 응답으로 끝나 네트워크·서버 자원을 크게 낭비했다.

웹소켓 이전에도 실시간성을 흉내 내려는 시도는 있었다. 대표적으로 숨겨진 iframe에 무한 스트림을 흘려보내는 '코멧(Comet)' 기법이나 롱 폴링이 쓰였지만, 이들은 HTTP 요청-응답이라는 제약을 우회하는 편법(workaround)에 불과했다. 표준화된 프로토콜이 없다 보니 구현이 제각각이고, 프록시·방화벽과의 호환성 문제도 잦았다. 웹소켓은 이러한 임시방편들을 대체하기 위해 W3C(API)와 IETF(프로토콜)가 공동으로 표준화한 결과물로, 브라우저에 일관된 WebSocket API를 제공하고 서버·중간 장비가 따를 명확한 와이어 포맷(wire format)을 규정했다는 점에서 질적 도약이었다.

2000년대 후반 구글 지도의 드래그, 채팅, 주식 거래 화면처럼 서버가 능동적으로 데이터를 밀어 넣어야(server push) 하는 리치 웹 애플리케이션이 보편화되면서, 매 요청마다 HTTP 헤더(수백 바이트~수 KB)를 반복 전송하고 TCP 연결을 새로 맺는 비효율이 심각한 병목으로 드러났다. 웹소켓은 바로 이 지점을 겨냥해, 한 번 맺은 연결을 계속 유지하며 양쪽 모두 먼저 말을 걸 수 있게 설계되었다. 연결 수립 이후 프레임의 오버헤드가 최소 2바이트에 불과하다는 점은, HTTP 폴링 대비 수십 배의 효율 격차를 만들어내는 핵심이다.

웹소켓의 특성은 네 가지로 요약할 수 있다. 첫째, 전이중 양방향성으로 서버와 클라이언트가 상대의 요청을 기다리지 않고 언제든 먼저 데이터를 보낼 수 있다. 둘째, 지속 연결로 한 번 맺은 TCP 연결을 재사용하므로 연결 수립·해제 비용과 핸드셰이크 지연이 반복되지 않는다. 셋째, 낮은 오버헤드로 HTTP 헤더 없이 작은 프레임 단위로 통신해 대역폭을 절약한다. 넷째, 텍스트·바이너리 모두 지원해 JSON 같은 텍스트 메시지뿐 아니라 이미지·오디오 같은 바이너리 데이터도 직접 주고받을 수 있다. 이 네 특성이 결합되어, 웹소켓은 '실시간'이라는 요구를 가장 자연스럽게 충족하는 웹 표준 프로토콜로 자리 잡았다.

다만 이러한 이점은 비용을 수반한다는 점을 처음부터 인식해야 한다. 지속 연결은 서버가 접속자 수만큼의 연결 상태를 메모리에 상주시켜야 함을 뜻하고, 양방향성은 어느 쪽이든 먼저 메시지를 보낼 수 있다는 유연함과 함께 흐름 제어·인증·보안의 책임을 애플리케이션에 지운다. 즉 웹소켓은 "실시간성을 얻는 대신 무상태 HTTP가 공짜로 주던 단순함을 포기하는" 거래이며, 이 거래가 수지에 맞는지를 설계 초기에 판단하는 것이 핵심이다. 본 문서는 이 관점에서 프로토콜 구조부터 확장·보안까지를 차례로 다룬다.

2. WebSocket 핸드셰이크와 프로토콜 전환

웹소켓의 가장 독창적인 설계는 HTTP 위에서 시작해 HTTP를 벗어난다는 점이다. 연결은 평범한 HTTP GET 요청으로 출발하되, Upgrade: websocket 헤더를 통해 프로토콜 전환(protocol switching)을 요청한다. 서버가 이를 수락하면 동일한 TCP 연결이 그대로 웹소켓 채널로 '승격'되며, 이후로는 HTTP가 전혀 개입하지 않는다. 아래 순서도는 핸드셰이크 전 과정을 나타낸다.

sequenceDiagram
    participant C as 클라이언트(브라우저)
    participant S as 서버
    Note over C,S: 1) HTTP Upgrade 핸드셰이크
    C->>S: GET /chat HTTP/1.1<br/>Upgrade: websocket<br/>Sec-WebSocket-Key: (랜덤 16B base64)
    S->>C: HTTP/1.1 101 Switching Protocols<br/>Sec-WebSocket-Accept: (해시값)
    Note over C,S: 2) 동일 TCP 연결을 WebSocket으로 승격
    C->>S: 텍스트/바이너리 프레임 (언제든지)
    S->>C: 텍스트/바이너리 프레임 (언제든지)
    Note over C,S: 3) Ping/Pong으로 연결 유지
    C->>S: Close 프레임 (정상 종료)

핸드셰이크의 핵심은 Sec-WebSocket-Key와 Sec-WebSocket-Accept의 챌린지-응답이다. 클라이언트는 16바이트 난수를 base64로 인코딩해 Sec-WebSocket-Key로 보내고, 서버는 여기에 고정 매직 문자열(258EAFA5-E914-47DA-95CA-C5AB0DC85B11)을 이어 붙여 SHA-1 해시 후 base64로 인코딩한 값을 Sec-WebSocket-Accept로 응답한다. 이 값은 암호학적 보안을 위한 것이 아니라, 중간의 캐시 프록시가 웹소켓 핸드셰이크를 일반 HTTP 응답으로 오인해 캐싱하거나 재사용하는 사고를 방지하기 위한 검증 장치다. 응답 코드 101 Switching Protocols가 반환되면 비로소 연결이 승격된다.

HTTP 위에서 출발하는 설계는 방화벽·프록시 통과(traversal)라는 실용적 이점도 낳는다. 웹소켓은 HTTP와 동일한 80(ws)·443(wss) 포트를 사용하고 표준 HTTP 요청으로 핸드셰이크를 시작하므로, 기업 방화벽이나 중간 프록시가 별도 포트 개방 없이도 트래픽을 허용하는 경우가 많다. 특히 TLS로 암호화된 wss://는 중간 장비가 페이로드를 들여다볼 수 없어 간섭·차단을 줄여주므로, 실무에서는 호환성을 위해서라도 wss://를 사실상 기본값으로 삼는다. 전용 포트를 쓰는 다른 실시간 프로토콜 대비 웹 인프라에 그대로 얹힌다는 점이 웹소켓 확산의 숨은 동력이었다.

핸드셰이크 단계에서는 서브프로토콜(subprotocol) 협상도 이루어진다. 클라이언트가 Sec-WebSocket-Protocol 헤더에 자신이 이해하는 애플리케이션 수준 프로토콜 목록(예: chat, wamp, mqtt)을 싣고, 서버가 그중 하나를 선택해 응답하면 양측은 동일한 메시지 규약을 공유하게 된다. 웹소켓 자체는 메시지를 운반하는 통로일 뿐 그 안의 의미는 규정하지 않으므로, 실무에서는 이 위에 JSON·MessagePack·Protocol Buffers 같은 직렬화 포맷과 자체 메시지 스키마를 얹어 애플리케이션 프로토콜을 정의한다. 이 계층 분리는 전송 메커니즘과 메시지 의미를 독립적으로 진화시킬 수 있게 하는 설계적 이점이다.

승격 이후 통신 단위는 HTTP 메시지가 아니라 프레임(frame)이다. 프레임은 FIN 비트(메시지의 마지막 조각 여부), opcode(프레임 종류), 마스킹 비트, 페이로드 길이, (클라이언트→서버의 경우) 마스킹 키, 페이로드로 구성된다. opcode는 데이터 프레임(0x1 텍스트, 0x2 바이너리)과 제어 프레임(0x8 Close, 0x9 Ping, 0xA Pong)으로 나뉜다. 특히 클라이언트가 보내는 모든 프레임은 32비트 마스킹 키로 XOR 마스킹해야 한다는 의무 규정이 있는데, 이는 악의적 스크립트가 예측 가능한 평문을 반복 전송해 중간 프록시의 캐시를 오염시키는 캐시 오염 공격(cache poisoning)을 막기 위한 설계다. 다음 표는 주요 프레임 구성 요소를 정리한 것이다.

구성 요소 크기 역할
FIN 1 bit 메시지의 마지막 프레임인지 표시(단편화 지원)
Opcode 4 bit 프레임 종류(텍스트·바이너리·Close·Ping·Pong)
Mask 1 bit 페이로드 마스킹 여부(클라이언트→서버는 필수)
Payload len 7/16/64 bit 가변 길이 인코딩으로 짧은 메시지 오버헤드 최소화
Masking-key 0/4 byte 마스킹 시 사용하는 32비트 키
Payload 가변 실제 데이터(UTF-8 텍스트 또는 바이너리)

프레임 설계에서 또 하나 중요한 개념은 단편화(fragmentation)다. 하나의 논리적 메시지를 여러 프레임으로 쪼개 보낼 수 있으며, 첫 프레임에만 opcode를 싣고 이어지는 프레임은 continuation(0x0)으로 표시하되 마지막 프레임에서 FIN 비트를 1로 세운다. 이 구조 덕분에 수신 측은 전체 메시지 크기를 미리 알지 못해도 스트리밍으로 데이터를 받아 처리할 수 있어, 대용량 바이너리(파일·미디어) 전송이나 생성형 AI의 토큰 단위 응답처럼 길이가 가변적이고 점진적으로 생성되는 데이터에 유리하다. 또한 제어 프레임(Ping·Pong·Close)은 데이터 프레임 사이에 끼어들 수 있도록 허용되어, 대용량 전송 중에도 연결 상태 점검이 가능하다.

제어 프레임과 데이터 프레임을 분리한 이유는 연결 관리와 데이터 전송의 관심사를 분리(separation of concerns)하기 위함이다. 애플리케이션이 주고받는 실제 콘텐츠는 텍스트·바이너리 데이터 프레임으로 전달되는 반면, 연결이 살아 있는지 확인하고 정상적으로 닫는 메타 작업은 제어 프레임이 담당한다. 이 분리 덕분에 애플리케이션 로직은 데이터 처리에만 집중하고, 연결 유지·종료는 프로토콜·라이브러리 계층에서 투명하게 처리될 수 있다.

프레임 길이 필드가 가변 길이 인코딩인 점도 효율의 핵심이다. 페이로드가 125바이트 이하면 7비트 길이 필드 하나로 충분하고, 더 크면 16비트 또는 64비트 확장 필드를 쓴다. 즉 채팅 한 줄처럼 짧은 메시지는 2~6바이트의 극소 헤더만 붙고, 대용량 전송 시에만 길이 필드가 늘어난다. 이는 "흔한 경우를 가장 싸게"라는 설계 원칙의 전형으로, 수많은 작은 메시지가 오가는 실시간 서비스에서 누적 대역폭 절감 효과가 크다.

3. 통신 아키텍처와 연결 생애주기

웹소켓은 상태 보존형(stateful) 연결이라는 점에서 무상태 HTTP와 근본적으로 다르며, 이는 서버 아키텍처 설계에 직접적 영향을 준다. 아래 개념도는 다수 클라이언트가 접속한 웹소켓 서버와 메시지 브로커를 포함한 전형적 아키텍처를 보여준다.

graph TB
    subgraph Clients["클라이언트"]
      C1["브라우저 A"]
      C2["브라우저 B"]
      C3["모바일 앱"]
    end
    LB["로드밸런서<br/>(Sticky Session/L7)"]
    subgraph Backend["WebSocket 서버 계층"]
      W1["WS 서버 #1<br/>(연결 상태 보유)"]
      W2["WS 서버 #2<br/>(연결 상태 보유)"]
    end
    MB["메시지 브로커<br/>(Redis Pub/Sub·Kafka)"]
    C1 -->|wss://| LB
    C2 -->|wss://| LB
    C3 -->|wss://| LB
    LB --> W1
    LB --> W2
    W1 <--> MB
    W2 <--> MB

연결의 생애주기는 수립(Open) → 메시지 교환(Message) → 유지(Ping/Pong) → 종료(Close)의 단계로 흐른다. 브라우저 API 관점에서는 new WebSocket(url) 생성 후 onopen·onmessage·onerror·onclose 이벤트 핸들러로 각 단계를 처리한다. 이벤트 기반 비동기 모델이므로 애플리케이션은 메시지 도착을 폴링할 필요 없이 콜백으로 반응하며, 송신은 send() 한 번으로 즉시 전송된다. 다만 네트워크가 불안정한 모바일 환경에서는 연결이 수시로 끊기므로, onclose를 감지해 지수 백오프(exponential backoff)로 재연결을 시도하고, 끊긴 동안의 상태를 재동기화하는 로직을 클라이언트가 직접 구현해야 견고한 서비스가 된다. 이 재연결·재동기화 부담 때문에 실무에서는 앞서 언급한 Socket.IO 같은 라이브러리가 선호된다.

수립 단계의 또 다른 설계 포인트는 연결과 세션의 매핑이다. 서버는 수립된 각 연결을 사용자·채널·방(room) 등 도메인 개념과 연결 지어 관리해야, 특정 사용자나 그룹에게만 메시지를 선택적으로 전송(targeted push)할 수 있다. 예컨대 채팅방 모델에서는 연결을 방 단위로 묶어 두고, 한 사용자의 발화를 같은 방 구성원의 연결에만 broadcast한다. 이 매핑 테이블은 서버 메모리에 상주하는 상태이므로, 수평 확장 시 앞서 설명한 브로커 백플레인을 통해 서버 간에 공유·조정되어야 한다. 수립 단계에서 서버는 각 연결을 고유 세션 객체로 메모리에 보유하므로, 접속자 수가 곧 서버가 유지해야 하는 동시 연결(상태)의 수가 된다. 수만~수십만 동시 연결을 다루려면 이벤트 기반 비동기 I/O(Node.js, Netty, Go의 고루틴 등)로 적은 스레드로 많은 연결을 처리하는 구조가 필수적이다.

메시지 교환 단계에서는 순서 보장과 배압(backpressure)이 설계 고려사항이 된다. 웹소켓은 단일 TCP 연결 위에서 동작하므로 같은 연결 안의 메시지는 전송 순서가 보장되지만, 수신 측이 처리 속도를 따라가지 못하면 송신 버퍼가 쌓여 메모리 압박과 지연이 누적된다. 예컨대 초당 수천 건의 시세 틱을 내려보내는 거래 서비스에서 클라이언트가 느리면, 서버는 오래된 틱을 합치거나(conflation) 버리는 전략으로 최신 데이터 우선을 보장해야 한다. 이는 HTTP의 요청-응답에서는 없던, 지속 연결 특유의 흐름 제어 문제다.

유지 단계의 Ping/Pong 프레임은 실무에서 특히 중요하다. 중간의 로드밸런서·프록시·NAT 장비는 일정 시간 트래픽이 없는 연결을 유휴(idle)로 간주해 임의로 끊어버리는데(흔히 60초 전후), 이를 방지하려면 주기적으로 Ping을 보내 연결이 살아 있음을 알려야 한다. 또한 TCP 연결이 비정상적으로 끊긴 경우(예: 모바일 단말의 네트워크 전환) 애플리케이션은 이를 즉시 알지 못하므로, Ping에 대한 Pong 응답이 일정 시간 내 오지 않으면 좀비 연결(dead connection)로 판단해 정리하고 재접속을 유도하는 하트비트 로직을 반드시 구현해야 한다. 종료 단계에서는 Close 프레임에 상태 코드(예: 1000 정상 종료, 1006 비정상 종료)를 실어 양측이 합의하에 연결을 닫는다. 정상 종료는 양측이 Close 프레임을 주고받는 종료 핸드셰이크를 거치지만, 네트워크 단절처럼 비정상 상황에서는 이 교환 없이 TCP가 끊기므로 서버는 이를 하트비트 타임아웃으로 감지해 정리해야 한다.

운영 관점에서 웹소켓은 관측성(observability) 확보가 까다로운 대상이다. HTTP는 요청마다 상태 코드·응답 시간이 로그에 남아 모니터링이 쉽지만, 장시간 지속되는 웹소켓 연결은 요청 단위 지표가 없어 동시접속 수, 연결 지속 시간 분포, 메시지 처리율, 재연결 빈도, 좀비 연결 비율 같은 연결 중심 지표를 별도로 계측해야 한다. 따라서 실무에서는 연결 수립·종료 이벤트와 메시지 지연을 메트릭으로 수집하고, 비정상 종료 코드(1006 등)의 급증을 장애 신호로 삼는 대시보드를 구성한다. 이러한 계측 설계가 빠지면 대규모 실시간 서비스에서 장애를 조기에 포착하기 어렵다.

한편 지속 연결은 장애의 전파 양상도 HTTP와 다르다. HTTP는 요청이 실패해도 다음 요청으로 자연 복구되지만, 웹소켓 서버 한 대가 다운되면 그 서버가 쥐고 있던 수만 개 연결이 동시에 끊기고, 클라이언트들이 일제히 재연결을 시도하는 재연결 폭주(thundering herd)가 발생해 남은 서버에 과부하를 전가할 수 있다. 이를 완화하려면 재연결 시점에 무작위 지연(jitter)을 섞어 부하를 분산하고, 서버 측에는 연결 수립 속도 제한(admission control)을 두어 급격한 유입을 흡수하도록 설계해야 한다.

4. 기존 실시간 기법과의 비교

웹소켓의 위상을 정확히 이해하려면 그 이전·대안 기법들과 왜 차이가 나는지를 함께 봐야 한다. 폴링은 구현이 가장 단순하지만, 예컨대 1초 주기로 폴링하는 채팅 클라이언트 1만 명이 접속하면 초당 1만 건의 빈 요청이 발생해 대부분이 무의미한 트래픽이 된다. 롱 폴링(Long Polling)은 서버가 새 데이터가 생길 때까지 응답을 보류해 빈 응답을 줄이지만, 매 응답마다 연결을 재수립하고 HTTP 헤더를 재전송하는 비용은 남는다. SSE(Server-Sent Events)는 서버→클라이언트 단방향 스트리밍에는 효율적이고 자동 재연결·HTTP/2 친화성이 장점이나, 클라이언트→서버 방향은 여전히 별도 HTTP 요청에 의존한다. 아래 표는 네 기법의 특성을 비교한 것이다.

구분 폴링 롱 폴링 SSE WebSocket
통신 방향 단방향(요청 기반) 단방향(요청 기반) 서버→클라이언트 단방향 양방향(전이중)
연결 유지 매번 새 연결 응답마다 재연결 지속(단방향) 지속(양방향)
실시간성 낮음(주기 의존) 중간 높음 매우 높음
오버헤드 매우 큼(헤더 반복) 큼 작음 최소(프레임 2B+)
데이터 형식 제한 없음 제한 없음 텍스트(UTF-8) 텍스트+바이너리
대표 용도 간단한 상태 조회 알림 뉴스피드·주가 티커 채팅·게임·협업·거래

웹소켓과 인접한 다른 양방향 기술과의 경계도 짚어둘 필요가 있다. gRPC 양방향 스트리밍은 HTTP/2 기반으로 강타입 계약(Protocol Buffers)과 코드 생성을 제공해 서버 간(service-to-service) 통신에 강점이 있으나, 브라우저에서 직접 쓰기 어렵고 별도 프록시(gRPC-Web)가 필요하다. MQTT는 발행-구독 모델의 경량 IoT 프로토콜로 저전력·불안정 네트워크의 센서 통신에 최적화되어 있으며, 웹 환경에서는 종종 MQTT over WebSocket 형태로 결합된다. 즉 웹소켓은 브라우저-서버 간 범용 양방향 통로로서의 입지가 가장 뚜렷하고, 서버 간 계약 중심 통신은 gRPC, 대규모 IoT 디바이스 수집은 MQTT가 각자의 적소를 갖는다. 기술사 관점에서는 이처럼 요구 맥락에 따라 겹치는 기술군의 경계를 구분해 선택하는 안목이 중요하다.

차이가 생기는 본질은 "연결을 유지하느냐"와 "누가 먼저 말할 수 있느냐"의 두 축이다. 폴링·롱 폴링은 HTTP의 요청-응답 틀 안에 머물러 클라이언트가 주도권을 쥐므로 서버 push가 근본적으로 불가능하지만, 웹소켓은 연결을 승격시켜 이 틀 자체를 벗어난다. 실무적 함의로, 1:1 알림이나 단방향 스트리밍이면 SSE가 더 단순하고 HTTP 인프라와 잘 맞는 합리적 선택이고, 채팅·멀티플레이어 게임·실시간 협업 편집·주식 거래처럼 저지연 양방향이 필수인 경우에만 웹소켓이 정답이다. 예컨대 Slack·카카오톡 웹 같은 메신저, 업비트·바이낸스의 실시간 호가창, 구글 독스의 공동 편집은 모두 웹소켓 계열 기술로 구현된다. 특히 공동 편집은 여러 사용자의 동시 수정이 충돌하지 않도록 OT(Operational Transformation)나 CRDT 같은 동시성 제어 알고리즘을 웹소켓 위에 얹어, 각자의 편집 연산을 실시간으로 서로에게 전파하고 수렴시킨다. 이는 웹소켓이 단순 메시지 전달을 넘어 복잡한 실시간 협업의 전송 기반(transport foundation) 역할을 한다는 점을 보여주는 대표 사례다. 무조건 웹소켓을 쓰는 것은 상태 유지 비용과 운영 복잡도만 키우는 과설계일 수 있다.

구체적 수치로 효율 차이를 보면 그 함의가 분명해진다. 평균 300바이트의 HTTP 헤더를 동반하는 폴링을 1,000명이 1초 주기로 수행하면 메시지 본문이 없어도 초당 약 300KB의 순수 헤더 트래픽이 발생하고, 서버는 초당 1,000건의 연결 수립·해제를 처리해야 한다. 반면 동일 사용자를 웹소켓으로 전환하면 연결은 1,000개로 고정되고 실제 이벤트가 있을 때만 수 바이트 프레임이 오가므로, 네트워크 트래픽과 서버 연산 부하가 한 자릿수 백분율 수준으로 급감한다. 바로 이 격차가 대규모 실시간 서비스가 웹소켓을 택하는 실질적 이유이며, 반대로 이벤트 빈도가 낮고 실시간성이 느슨한 서비스라면 이 이점이 체감되지 않아 단순한 폴링이 더 경제적일 수 있다.

또 하나 주목할 실무적 함의는 지연(latency)의 질적 차이다. 폴링은 주기가 1초라면 이벤트 발생 후 평균 0.5초, 최악 1초의 지연이 구조적으로 존재한다. 주기를 줄이면 지연은 줄지만 빈 요청이 급증하는 상충이 생긴다. 반면 웹소켓은 이벤트가 생기는 즉시 서버가 밀어 넣으므로 지연이 네트워크 왕복 시간 수준(수~수십 ms)으로 수렴한다. 멀티플레이어 게임이나 실시간 경매처럼 수십 ms의 지연이 사용자 경험·공정성을 좌우하는 영역에서는 이 질적 차이가 기술 선택을 사실상 결정짓는다.

5. 심화: 확장성·보안과 최신 동향

웹소켓을 실제 대규모 서비스에 적용할 때 가장 어려운 과제는 상태 보존 연결의 수평 확장이다. HTTP는 무상태이므로 어느 서버가 요청을 받아도 무방하지만, 웹소켓은 특정 서버가 특정 연결 상태를 쥐고 있다. 따라서 서버 A에 접속한 사용자에게 서버 B가 메시지를 보내야 하는 상황이 생기며, 이를 해결하려면 메시지 브로커 백플레인(backplane)이 필요하다. 실무에서는 Redis Pub/Sub나 Kafka를 중앙 버스로 두고, 각 웹소켓 서버가 자신이 보유한 연결로 메시지를 라우팅한다. 예를 들어 동시접속 100만 명 규모의 서비스라면, 서버당 5만~10만 연결을 수용하도록 수십 대를 두고 Redis Pub/Sub로 broadcast를 중계하는 구성이 전형적이다. 로드밸런서는 L7에서 Upgrade 헤더를 이해하고 sticky session으로 동일 연결을 같은 서버에 고정해야 한다.

용량 산정(capacity planning)도 HTTP와 접근이 다르다. HTTP 서비스는 초당 요청 수(RPS)와 평균 응답 시간으로 용량을 가늠하지만, 웹소켓은 동시 연결 수가 곧 상주 자원이므로 연결당 메모리·파일 디스크립터 소비가 1차 제약이 된다. 예컨대 연결당 수십 KB의 버퍼를 잡는다면 10만 연결만으로도 수 GB 메모리가 상주한다. 따라서 도입 전에 대량 연결을 모사하는 부하 테스트(예: 가상 클라이언트로 연결을 서서히 늘려 한계점 측정)를 수행해, 서버당 안전 수용 연결 수와 수평 확장 단위를 사전에 확정해야 한다. 이 산정이 부실하면 평시에는 멀쩡하다가 트래픽 급증 시 연결 거부·메모리 고갈로 연쇄 장애가 발생한다.

인증 설계에는 웹소켓 특유의 제약이 있다. 브라우저의 표준 WebSocket 생성자는 커스텀 HTTP 헤더를 설정할 수 없어, REST API처럼 Authorization: Bearer <token> 헤더로 인증 토큰을 전달하는 방식을 그대로 쓸 수 없다. 이 때문에 실무에서는 핸드셰이크 URL의 쿼리 파라미터에 단기 토큰을 싣거나, 연결 수립 직후 첫 메시지로 인증 정보를 교환하는 패턴을 쓴다. 쿼리 파라미터 방식은 토큰이 로그·히스토리에 남을 위험이 있으므로 짧은 만료 시간의 1회용 토큰을 사용하고, 연결 후 메시지 인증 방식은 인증 완료 전까지 데이터 전송을 차단하는 상태 관리가 필요하다. 이처럼 상태 보존 연결의 인증은 무상태 HTTP와 다른 설계 패턴을 요구한다.

보안 측면에서 가장 주의할 위협은 CSWSH(Cross-Site WebSocket Hijacking)다. 웹소켓 핸드셰이크는 브라우저의 동일 출처 정책(SOP)의 완전한 보호를 받지 못하고 쿠키가 자동 첨부되므로, 공격자 사이트가 피해자의 인증 쿠키를 이용해 몰래 웹소켓을 열 수 있다. 따라서 서버는 핸드셰이크 시 Origin 헤더를 반드시 검증하고, 쿠키에만 의존하지 않는 토큰 기반 인증(예: 연결 시 1회성 토큰 전달)을 병행해야 한다. 또한 전송 구간은 평문 ws:// 대신 TLS로 암호화하는 wss://(기본 포트 443)를 원칙으로 하여 도청·중간자 공격을 차단한다. 메시지 폭주(flooding)에 대비한 속도 제한(rate limiting), 메시지 크기 상한, 입력 검증도 애플리케이션 계층에서 함께 적용해야 한다.

백플레인 설계에는 트레이드오프가 따른다. Redis Pub/Sub는 지연이 낮고 구현이 단순하지만 메시지를 저장하지 않아 구독 시점 이후의 메시지만 받는 fire-and-forget 특성이 있어, 전달 보장이 필요하면 Kafka처럼 로그를 영속화하는 브로커를 써야 한다. 반대로 Kafka는 내구성과 재처리에 강하지만 Redis보다 지연과 운영 복잡도가 크다. 따라서 "채팅 메시지 유실이 허용되지 않는다", "게임 상태는 최신값만 중요하다"와 같은 업무 요구에 따라 브로커와 전달 보장 수준을 달리 설계하는 것이 핵심이다. 또한 수십만 동시 연결에서는 파일 디스크립터 한계(ulimit), 커널 TCP 버퍼, 로드밸런서의 연결 한도 같은 OS·인프라 수준 튜닝도 병행되어야 실제 용량이 확보된다.

최신 동향으로는 몇 가지 흐름이 주목된다. 첫째, HTTP/2·HTTP/3 환경에서의 웹소켓이다. RFC 8441은 HTTP/2 스트림 하나에서 웹소켓을 부트스트랩(:protocol = websocket)하는 방식을 정의했고, 더 나아가 WebTransport는 HTTP/3(QUIC) 위에서 신뢰성·비신뢰성 양방향 스트림을 모두 제공해 저지연·다중스트림이 필요한 게임·미디어 분야에서 웹소켓의 후속 기술로 부상하고 있다. 웹소켓이 단일 순서 보장 스트림이라 한 메시지의 지연이 뒤 메시지를 막는 HOL Blocking 한계가 있는 반면, WebTransport는 다중 스트림과 데이터그램을 지원해 이 제약을 완화한다는 점에서 상호 보완·대체 관계로 발전할 전망이다. 둘째, permessage-deflate(RFC 7692) 압축 확장으로 텍스트 메시지 대역폭을 절감하는 기법이 널리 쓰인다. 다만 압축은 CPU 부하와 메모리(압축 컨텍스트)를 추가로 소모하므로, 메시지가 작고 빈번하면 오히려 손해일 수 있어 트래픽 특성에 맞춰 선별 적용해야 한다. 셋째, 연결이 끊겨도 자동 재연결·fallback(폴링 대체)을 제공하는 Socket.IO·SignalR 같은 상위 라이브러리가 사실상 표준처럼 활용되어, 다양한 네트워크 환경에서의 견고성을 보완한다.

끝으로 생성형 AI의 확산은 웹소켓 수요를 다시 끌어올리고 있다. LLM이 답변을 토큰 단위로 점진 생성해 사용자에게 실시간 스트리밍하는 UX가 표준이 되면서, 서버가 생성되는 토큰을 즉시 밀어 넣는 통로로 웹소켓(또는 SSE)이 널리 쓰인다. 여기에 사용자의 중간 개입(생성 중단·추가 질의)까지 처리하려면 양방향성이 유용하므로, 대화형 AI·에이전트 인터페이스에서 웹소켓의 적합성이 재조명되는 추세다.

6. 고려사항 및 시사점

  • 적용 전략(기술 선택의 적합성): 웹소켓은 만능이 아니다. 단방향 서버 push면 SSE, 단순 상태 조회면 폴링으로 충분하며, 저지연 양방향이 핵심 요구사항일 때만 웹소켓을 선택해야 한다. 기능 요구(실시간성·방향성)와 비기능 요구(동시접속 규모·운영 역량)를 함께 저울질해 과설계를 피하는 판단이 기술사의 역할이다.
  • 트레이드오프(효율 대 복잡도): 웹소켓은 프레임 오버헤드를 최소화해 전송 효율을 극대화하지만, 상태 보존 연결·sticky session·브로커 백플레인·하트비트 등 운영 복잡도와 메모리 비용이 HTTP 대비 크게 증가한다. 효율 이득이 이 비용을 상회하는 워크로드인지 정량 분석(동시접속 수·메시지 빈도·지연 요구)으로 검증해야 한다.
  • 보안·거버넌스 관점: 상시 연결과 쿠키 자동 첨부라는 특성상 CSWSH·인증 우회 위험이 상존하므로, Origin 검증·토큰 인증·wss:// 암호화·속도 제한을 설계 단계에서 표준 통제로 내재화해야 한다. 상시 암호화는 네트워크 기반 위협 탐지를 어렵게 하므로, 통제 무게중심을 엔드포인트·애플리케이션 계층(제로 트러스트 정합)으로 이동하는 아키텍처가 병행되어야 한다.
  • 가용성·장애 대응: 상태 보존 연결은 서버 재시작·배포 시 일제히 끊어지는 특성이 있으므로, 무중단 배포 시 연결을 점진적으로 이전(drain)하고 클라이언트 측 자동 재연결·백오프(backoff)를 필수로 구현해야 한다. 좀비 연결 정리, 재연결 폭주(thundering herd) 방지(지터 적용)도 운영 설계의 핵심이다.
  • 전망·연계 기술: 웹소켓은 HTTP/2 부트스트랩(RFC 8441)과 HTTP/3 기반 WebTransport로 진화 경로가 열려 있으며, 실시간 협업·메타버스·IoT 텔레메트리·생성형 AI의 스트리밍 응답(토큰 단위 전송) 등 저지연 양방향 수요가 커질수록 그 중요성이 확대될 것으로 전망된다. 메시지 브로커(Kafka)·서비스 메시·관측성(Observability) 설계와의 연계를 함께 고려해야 한다.

참고자료


한 줄 요약: 웹소켓은 HTTP Upgrade 핸드셰이크로 단일 TCP 연결을 전이중 채널로 승격해, 서버와 클라이언트가 최소 오버헤드로 양방향 실시간 통신을 주고받게 하는 프로토콜이며, 저지연 양방향이 필수인 채팅·게임·협업·거래 분야에 적합하되 상태 보존 연결의 확장성과 CSWSH 보안을 함께 설계해야 한다.