웹소켓(WebSocket)이란? 기초 개념 정리

채팅창과 실시간 시세는 어떻게 새로고침 없이 바뀔까요? 폴링의 한계에서 출발해 핸드셰이크, 프레임, ws와 wss까지 차례로 따라가 봅니다.

한눈에 보기

웹소켓(WebSocket)은 웹 브라우저와 서버가 하나의 TCP 연결을 열어 둔 채 양쪽이 언제든 메시지를 보낼 수 있게 해 주는 프로토콜입니다. 일반 HTTP는 클라이언트가 물어봐야 서버가 답하는 구조라 서버가 먼저 소식을 전하기 어려웠고, 이를 흉내 내려고 반복해서 서버에 묻는 '폴링'이 쓰였습니다. 이 글에서는 웹소켓이 왜 생겼는지, 연결이 어떻게 시작되고 데이터가 어떤 모양으로 오가는지, 그리고 언제 웹소켓이 맞고 언제 더 단순한 대안이 나은지를 순서대로 살펴봅니다.

왜 만들어졌을까: 배경

웹 초기에는 브라우저가 요청을 보내고 서버가 응답하는 것이 전부였습니다. 채팅이나 시세처럼 서버에서 새 소식이 생기는 서비스는 Comet 같은 우회 기법이나 Flash 같은 플러그인에 기댔는데, 위키백과(WebSocket 항목)는 이런 방식이 구현이 복잡하고 작은 메시지를 보낼 때 TCP 연결과 HTTP 헤더 부담 때문에 비효율적이었다고 설명합니다. 웹소켓은 2008년 무렵 HTML5 초안에서 TCPConnection이라는 이름으로 처음 언급되었고, 2009년 Google Chrome 4가 기본 활성화 상태로 완전 지원한 첫 브라우저가 되었습니다. 2010년 2월 표준화 작업이 W3C·WHATWG에서 IETF로 옮겨졌고, 2011년 12월 I. Fette(Google)와 A. Melnikov(Isode)가 쓴 RFC 6455가 확정되었습니다. RFC 6455 초록은 목적을 "여러 HTTP 연결(XMLHttpRequest, 롱 폴링 등)에 기대지 않고 양방향 통신이 필요한 브라우저 기반 응용을 위한 방법"이라고 밝힙니다.

🔌

웹소켓(WebSocket)이란

한 번 연결을 맺으면 서버와 클라이언트가 계속 열려 있는 하나의 통로로 데이터를 양방향으로 주고받을 수 있게 해주는 프로토콜입니다.

일반 HTTP 통신은 클라이언트가 요청을 보내야만 서버가 응답하는 "요청-응답" 구조라, 서버가 먼저 클라이언트에게 새 정보를 알려줄 수 없습니다. 웹소켓은 이 한계를 넘어 연결이 유지되는 동안 서버와 클라이언트 어느 쪽이든 원할 때 자유롭게 메시지를 보낼 수 있는 실시간 양방향 통신을 제공합니다.

🔁

웹소켓 이전의 방식: 폴링

실시간처럼 보이는 기능을 흉내 내기 위해 클라이언트가 몇 초마다 반복해서 서버에 새 소식이 있는지 물어보는 방식이 폴링입니다.

짧은 폴링(short polling)은 일정 주기로 계속 요청을 보내 서버와 네트워크에 불필요한 부하를 주고, 롱 폴링(long polling)은 서버가 새 소식이 생길 때까지 응답을 미루는 방식으로 이를 다소 개선했지만, 여전히 매 요청마다 HTTP 헤더 오버헤드가 발생하고 완전한 양방향 통신은 아니었습니다. 웹소켓은 이런 반복 요청 없이 연결 하나로 문제를 해결합니다.

🤝

핸드셰이크와 프로토콜 업그레이드

웹소켓 연결은 일반 HTTP 요청으로 시작해, 서버가 이를 수락하면 같은 TCP 연결을 웹소켓 프로토콜로 "업그레이드"하는 방식으로 전환됩니다.

클라이언트가 `Upgrade: websocket` 헤더가 포함된 HTTP 요청을 보내고, 서버가 이를 받아들이면 HTTP 상태 코드 101(Switching Protocols)로 응답합니다. 이 순간부터 같은 연결이 더 이상 HTTP 요청-응답 방식이 아니라, 양쪽이 언제든 프레임을 주고받을 수 있는 웹소켓 연결로 바뀝니다.

🔒

ws:// 와 wss:// 의 차이

ws://는 암호화되지 않은 웹소켓, wss://는 TLS로 암호화된 웹소켓 연결을 의미하며 HTTP와 HTTPS의 관계와 같습니다.

일반 웹사이트에서 HTTPS를 쓰는 것처럼, 웹소켓도 민감한 데이터를 주고받는다면 반드시 wss://를 사용해 중간에서 데이터가 도청되거나 변조되지 않도록 해야 합니다. 대부분의 브라우저는 HTTPS로 접속한 페이지에서 암호화되지 않은 ws:// 연결 시도를 보안상의 이유로 차단합니다.

💬

실제 활용 사례

실시간 채팅, 주식·코인 시세 표시, 온라인 게임의 실시간 상태 동기화, 협업 문서의 동시 편집 반영 등에 널리 쓰입니다.

메신저 앱에서 상대방이 보낸 메시지가 새로고침 없이 바로 나타나는 것, 주식 차트의 실시간 가격이 계속 움직이는 것, 여러 명이 같은 문서를 동시에 편집할 때 서로의 커서와 수정 내용이 실시간으로 보이는 것 모두 웹소켓(또는 이와 유사한 실시간 기술)이 뒤에서 동작하고 있는 대표적인 예시입니다.

🆚

웹소켓과 대안 기술 비교

서버에서 클라이언트로만 실시간 데이터를 보내면 되는 경우에는 더 단순한 SSE(Server-Sent Events)가 대안이 될 수 있습니다.

SSE는 HTTP 위에서 서버가 클라이언트로 일방향으로 계속 데이터를 흘려보내는 기술로, 웹소켓보다 구현이 간단하고 기존 HTTP 인프라와도 잘 어울립니다. 반면 클라이언트도 서버에 자주 메시지를 보내야 하는 채팅이나 게임 같은 경우에는 양방향 통신이 가능한 웹소켓이 더 적합합니다.

연결은 이렇게 열린다: 핸드셰이크 들여다보기

RFC 6455에 따르면 클라이언트는 Upgrade: websocket, Connection: Upgrade, 그리고 임의의 값인 Sec-WebSocket-Key 헤더가 담긴 HTTP GET 요청을 보냅니다. 서버가 받아들이면 상태 코드 101(Switching Protocols)과 함께 Sec-WebSocket-Accept 헤더를 돌려줍니다. 이 값은 클라이언트가 보낸 키 뒤에 RFC가 정한 고정 문자열(GUID) 258EAFA5-E914-47DA-95CA-C5AB0DC85B11을 이어 붙여 SHA-1 해시를 낸 뒤 base64로 인코딩한 것입니다. 상태 코드가 101이 아니거나 Accept 값이 맞지 않으면 연결은 성립하지 않습니다. 이 값은 '이 서버가 웹소켓 요청임을 알고 응답했다'는 확인용이며 암호화나 로그인 수단이 아닙니다.

직접 계산해 본 예시입니다. 키가 dGhlIHNhbXBsZSBub25jZQ==이면 위 규칙대로 만든 Accept 값은 s3pPLMBiTxaQ9kYGzzhZRbK+xOo=입니다(Node.js로 검산).

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

연결 뒤에는 무엇이 오갈까: 프레임 구조

업그레이드 이후 데이터는 '프레임'으로 오갑니다. RFC 6455의 기본 프레임에는 메시지의 마지막 조각을 알리는 FIN 비트, 확장용 RSV1~3 비트(확장이 정의하지 않으면 0), 4비트 opcode, MASK 비트, 페이로드 길이가 들어 있습니다. opcode 0x1은 텍스트, 0x2는 바이너리이고, 제어용으로 0x8(닫기), 0x9(Ping), 0xA(Pong)가 있습니다. 길이는 7비트, 또는 7+16비트, 또는 7+64비트로 표현되고 가능한 한 짧은 형태를 써야 합니다.

눈여겨볼 규칙이 둘 있습니다. 첫째, 클라이언트가 서버로 보내는 모든 프레임은 마스킹해야 하고 서버가 보내는 프레임은 마스킹하지 않습니다. 마스킹되지 않은 클라이언트 프레임을 받은 서버는 연결을 닫아야 합니다. 둘째, 제어 프레임(닫기·Ping·Pong)의 페이로드는 125바이트 이하이며 쪼갤 수 없습니다. Ping을 받은 쪽은 Pong으로 답해야 하므로, 연결이 살아 있는지 확인하는 용도로 쓰입니다.

HTTP 폴링과 무엇이 다른가

방식누가 먼저 보내나요청마다 헤더적합한 경우
짧은 폴링클라이언트만 (주기적으로 질문)매번 전체 HTTP 헤더갱신이 드물고 단순한 화면
롱 폴링클라이언트가 요청, 서버가 이벤트 때까지 응답 지연응답마다 새 요청과 헤더웹소켓을 쓸 수 없는 환경의 우회
SSE서버 → 클라이언트 한 방향연결 시 한 번알림·시세처럼 일방향 스트림
웹소켓양쪽 모두핸드셰이크 뒤에는 짧은 프레임 헤더채팅·게임·공동 편집 같은 양방향 상호작용

SSE(Server-Sent Events)는 text/event-stream 형식으로 서버가 계속 데이터를 흘려보내는 기술로, 브라우저에서는 EventSource API로 받습니다. Ian Hickson이 2004년 WHATWG 문서에서 처음 명세했고 W3C는 2015년 2월 3일 권고안으로 발표했습니다(위키백과 기준). 한 방향이면 충분한 서비스에서는 웹소켓보다 구성이 단순합니다.

사례로 보기

사례 1. 표준이 만들어지기까지: 프록시와 부딪힌 초안

웹소켓은 처음부터 지금의 형태가 아니었습니다. 위키백과에 따르면 2010년 중반의 hixie-76 초안은 헤더 뒤에 8바이트를 Content-Length로 알리지 않고 덧붙이는 방식이어서 일부 역방향 프록시가 이를 처리하지 못했습니다. 이후 초안은 그 키 정보를 Sec-WebSocket-Key 헤더로 옮겨 문제를 피했습니다. 지금도 일부 프록시는 웹소켓 트래픽을 막거나 망가뜨릴 수 있고, 암호화된 wss 트래픽이 평문 ws보다 더 안정적으로 통과하는 경우가 많다고 같은 문서는 적습니다. 프로토콜이 인터넷 중간 장비와 함께 동작해야 한다는 점이 설계에 반영된 사례이며, 서비스에서 가능하면 wss를 쓰는 이유를 보여 줍니다.

사례 2. 출처 확인을 빠뜨리면: Cable Haunt (2020)

웹소켓 요청은 브라우저의 동일 출처 정책 제한을 받지 않습니다. 그래서 서버가 요청의 Origin 헤더를 확인하지 않으면, 다른 사이트의 스크립트가 사용자의 브라우저를 통해 웹소켓 서버에 접속하는 '교차 사이트 웹소켓 하이재킹'이 가능해집니다. 위키백과는 실제 사례로 2020년 공개된 Cable Haunt를 들며, 서버가 Origin을 검증하고 민감한 데이터에는 토큰을 쓰라고 권합니다. 배울 점은 단순합니다. 웹소켓 서버를 만든다면 연결을 허용할 출처를 명시적으로 제한하고, 연결이 열렸다는 사실만으로 로그인 상태를 믿지 말아야 합니다.

사례 3. 가상 예시: 폴링이 만드는 트래픽

아래는 실제 서비스가 아니라 가상 예시입니다. 접속자 1,000명이 5초마다 서버에 새 메시지가 있는지 묻고, 요청과 응답의 HTTP 헤더가 합쳐 800바이트라고 가정하겠습니다(가정치). 그러면 초당 1,000 ÷ 5 = 200번의 질문이 들어오고, 본문이 비어 있어도 헤더만으로 초당 160,000바이트, 한 시간이면 약 576MB가 오갑니다(Node.js로 검산). 웹소켓에서는 연결을 맺은 뒤에는 새 메시지가 있을 때만 프레임이 오가고, 서버→클라이언트 프레임의 기본 헤더는 페이로드가 125바이트 이하일 때 2바이트, 클라이언트→서버는 마스킹 키 4바이트를 더해 6바이트입니다. 실제 차이는 메시지 빈도, 암호화, 프록시 등에 따라 달라지므로 이 계산은 규모감을 보기 위한 것일 뿐입니다.

직접 해 볼 점검 목록

흔한 오해와 정정

한계와 더 알아볼 점

연결을 유지하는 만큼 서버는 동시 연결 수만큼 자원을 쓰므로 대규모 서비스에서는 연결 분산과 재연결 폭주 대비가 설계 과제가 됩니다. 또 웹소켓 자체는 메시지의 의미를 정해 주지 않아, 메시지 형식과 재전송·순서 보장 같은 규칙은 응용이 정해야 합니다. 핸드셰이크의 Sec-WebSocket-Protocol 헤더로 클라이언트가 선호하는 하위 프로토콜을 제안하고 서버가 그중 하나를 고르는 방식이 있습니다. DEFLATE 기반 압축 확장은 RFC 7692에 정의되어 있고, RFC 6455는 이후 RFC 7936·8307·8441로 갱신되었습니다(RFC 에디터 페이지 표시 기준). 세부 내용은 각 RFC 원문을 확인하세요.

HTTP 상태 코드 101이 궁금하다면

웹소켓 핸드셰이크에 쓰이는 상태 코드 101(Switching Protocols)을 포함해 다른 HTTP 상태 코드도 궁금하다면HTTP 상태 코드 완전정복을 확인해보세요.

HTTP 프로토콜 자체의 발전 과정도 함께 보면 좋아요

웹소켓이 등장한 배경이 되는 HTTP/1.1의 한계와 이후 발전 과정이 궁금하다면HTTP/1.1·HTTP/2·HTTP/3 차이 완전정복도 참고해보세요.

자주 묻는 질문

웹소켓 연결도 방화벽에 막힐 수 있나요?

네. 일부 사내망이나 공용 와이파이의 보수적인 방화벽 설정은 표준 HTTP·HTTPS 포트(80·443) 외의 연결이나 프로토콜 업그레이드 요청을 차단하기도 합니다. 다만 대부분의 웹소켓 서비스는 443번 포트를 그대로 활용해 이런 문제를 최소화합니다.

웹소켓은 모바일 환경에서도 안정적으로 동작하나요?

모바일 네트워크 전환(와이파이↔셀룰러)이나 화면이 꺼져 백그라운드로 전환될 때 연결이 끊길 수 있어, 실무에서는 연결이 끊기면 자동으로 재연결을 시도하는 로직을 함께 구현하는 경우가 많습니다.

웹소켓은 항상 폴링보다 좋은가요?

아닙니다. 갱신이 드물거나 서버에서 클라이언트로 한 방향이면 폴링이나 SSE가 더 단순하고 운영하기 쉽습니다. 양방향이고 지연이 중요한 상호작용에서 웹소켓의 이점이 커집니다.

웹소켓 메시지는 자동으로 암호화되나요?

ws:// 연결은 암호화되지 않습니다. TLS로 보호되는 wss:// 를 써야 하며, 그래도 로그인 확인과 Origin 검증은 서버가 따로 해야 합니다.

왜 클라이언트 프레임만 마스킹하나요?

RFC 6455는 클라이언트가 서버로 보내는 모든 프레임은 마스킹하고, 서버가 보내는 프레임은 마스킹하지 않도록 규정합니다. 마스킹 키는 프레임에 함께 실립니다. 이 규칙은 암호화가 아니므로 기밀성은 wss에 맡겨야 합니다.

더 읽어 보기

TLS 핸드셰이크 과정에서 wss의 암호화 협상을, 방화벽 기초에서 포트와 연결 차단 원리를 이어서 볼 수 있습니다.