WebSocket
contents
웹소켓(WebSocket) 은 단일하고 오래 지속되는 TCP 연결을 통해 전이중(Full-Duplex), 지속적(Persistent) 이며, 양방향 통신 채널을 제공하는 통신 프로토콜입니다. 이는 전통적인 HTTP의 요청-응답 모델의 한계를 해결하기 위해 설계되었으며, 훨씬 낮은 지연 시간과 오버헤드로 실시간 데이터 전송을 가능하게 합니다.
HTTP와의 핵심 차이점 (사용 이유)
웹소켓은 전통적인 HTTP를 사용하여 실시간 애플리케이션을 구축할 때 발생하는 비효율성을 해결하기 위해 만들어졌습니다.
| 특징 | HTTP (전통적) | 웹소켓 |
|---|---|---|
| 통신 방식 | 반이중 (Half-Duplex, 한 번에 한 방향만 통신) | 전이중 (Full-Duplex, 양방향으로 데이터 동시 흐름) |
| 연결 상태 | 무상태, 비지속적 (응답 후 매번 연결 종료) | 상태 유지, 지속적 (명시적으로 닫힐 때까지 연결 유지) |
| 서버 주도 | 없음 (클라이언트가 요청하기 전에는 서버가 데이터를 보낼 수 없음) | 즉각적인 푸시 (서버가 언제든지 클라이언트에게 데이터를 보낼 수 있음) |
| 오버헤드 | 높음 (요청/응답마다 전체 헤더 전송) | 낮음 (초기 핸드셰이크 후 최소한의 프레이밍 전송) |
| 실시간 구현 | 폴링(Polling) (클라이언트가 "새로운 것이 있습니까?"를 끊임없이 물어봄) | 푸시(Push) (새로운 데이터가 있을 때만 서버가 전송) |
아키텍처 및 핸드셰이크 (시작 방법)
웹소켓 연결은 핸드셰이크(Handshake) 라고 불리는 과정을 통해 표준 HTTP 요청으로 시작됩니다. 이 초기 교환은 하위 호환성 및 프로토콜 협상을 위해 필요합니다.
: 웹소켓은 단일 TCP 연결을 통해 전이중 통신 채널을 제공하는 컴퓨터 통신 프로토콜입니다. 클라이언트가 HTTP 업그레이드 요청을 통해 서버와 핸드셰이크를 시작합니다. 연결이 설정되면, 클라이언트와 서버 간의 실시간 양방향 데이터 전송이 가능해져 온라인 게임, 채팅 애플리케이션, 실시간 스포츠 업데이트와 같은 애플리케이션에 이상적입니다. 이미지에는 클라이언트, 서버, 네트워크, HTTP 업그레이드 - 핸드셰이크, 양방향 연결, 채널 닫기 레이블이 포함된 다이어그램이 있습니다.
- 클라이언트 요청: 클라이언트는 특정 헤더를 포함한 표준 HTTP 요청(보통 포트 80 또는 443)을 전송하여 연결을 시작합니다.
Upgrade: websocket: 서버에게 프로토콜을 웹소켓으로 업그레이드하고 싶다고 알립니다.Connection: Upgrade: 프로토콜 업그레이드 요청을 확인합니다.Sec-WebSocket-Key: 보안 검증을 위해 사용되는 고유하고 무작위로 생성된 Base64 인코딩 값입니다.
- 서버 응답: 서버가 프로토콜을 지원하고 요청을 수락하면, 특수 HTTP 상태 코드로 응답합니다.
101 Switching Protocols: 서버가 프로토콜을 변경하고 있음을 확인합니다.Sec-WebSocket-Accept: 서버는 클라이언트의 키를 기반으로 응답 해시를 계산하여 다시 보내, 핸드셰이크의 무결성을 확인합니다.
- 연결 설정: 클라이언트가
101상태를 수신하면 HTTP 계층은 제거됩니다. 기본 TCP 연결은 열린 상태를 유지하며, 클라이언트와 서버 모두 이를 사용하여 경량의 웹소켓 데이터 프레임을 전송할 수 있게 됩니다.
프로토콜: 웹소켓 연결은 보안되지 않은 연결에는 ws:// 스키마를 사용하며, TLS/SSL 암호화로 보호되는 연결에는 wss:// 스키마를 사용하는데, 이는 표준적인 방식입니다.
주요 특징
- 낮은 지연 시간 데이터 프레이밍: 부피가 큰 HTTP 메시지를 보내는 대신, 웹소켓은 작고 효율적인 데이터 프레임을 사용합니다. 이 프레임은 최소한의 오버헤드 정보(프레임 유형 및 페이로드 길이 등)와 원시 데이터 페이로드만 포함합니다. 이는 대역폭 사용량과 전송 지연을 극적으로 줄입니다.
- 전이중 통신: 동일한 연결을 통해 데이터를 동시에 보내고 받을 수 있습니다. 이는 화상 통화와 같이 즉각적인 양방향 데이터 흐름이 필요한 애플리케이션에 필수적입니다.
- 지속성: 연결은 양쪽 중 한쪽이 명시적으로 닫을 때까지 열린 상태를 유지하며 데이터를 전송할 준비가 되어 있습니다. 이는 HTTP가 요구하는 것처럼 TCP 연결을 지속적으로 설정하고 해제하는 오버헤드를 제거합니다.
- 하트비트 (Ping/Pong): 프로토콜에는 연결이 여전히 활성 상태인지 확인하고, 중간 네트워크 요소(프록시 등)가 유휴 연결을 시간 초과로 닫는 것을 방지하기 위해 작은 제어 프레임(핑 및 퐁)을 보내는 메커니즘이 포함되어 있습니다.
사용 사례
웹소켓은 현대 실시간 웹 애플리케이션의 중추입니다.
- 라이브 채팅 및 메시징: 가장 명백한 사용 사례입니다. 수동으로 새로고침할 필요 없이 모든 사용자에게 메시지가 즉시 전달되고 표시되도록 보장합니다.
- 협업 도구: Google Docs 또는 Figma와 같이 여러 사용자가 동일한 문서를 동시에 편집하는 애플리케이션에서 사용됩니다. 한 사용자가 변경한 내용은 다른 사용자에게 즉시 푸시됩니다.
- 금융 및 실시간 데이터 피드: 주식 시세, 스포츠 점수 또는 센서 데이터(IoT)에 대한 지속적인 업데이트를 제공합니다.
- 멀티플레이어 게임: 플레이어와 서버 간의 게임 상태 동기화 시 지연 시간을 최소화하는 데 필수적입니다.
장단점 (트레이드오프)
| 측면 | 장점 (Pros) | 단점 (Cons) |
|---|---|---|
| 성능 | 뛰어난 속도: 지속적인 연결과 최소한의 헤더 덕분에 지연 시간이 극도로 낮습니다. | 캐싱 불가: HTTP와 달리, 웹소켓 데이터는 중간 프록시에 의해 캐시될 수 없어, 서버 측에서 반복되는 데이터 관리가 필요합니다. |
| 효율성 | 오버헤드 감소: 설정된 연결에 대해 반복적인 HTTP 헤더를 제거합니다. | 상태 관리: 서버가 모든 개별 연결에 대한 상태와 리소스를 유지해야 하므로, 대규모에서 비용이 많이 들 수 있습니다. |
| 통신 | 실시간 푸시: 서버가 언제든지 데이터 전송을 시작(푸시)할 수 있습니다. | 프록시/방화벽 문제: 구형이거나 제한적인 프록시 서버는 Upgrade 헤더를 전달하거나 장시간 연결을 유지하는 데 문제가 있을 수 있습니다(현재는 드물어지고 있음). |
WebSocket의 구현은 단일 TCP 소켓 위에서 작동하는 상태 머신으로 구현되며, 최소한의 오버헤드를 가진 바이너리 프레이밍 프로토콜과 보안을 위한 의무적인 페이로드 마스킹을 활용합니다.
이전에 웹소켓이 HTTP의 한계를 해결한다고 설명했지만, 실제 구현은 이전에 사용하던 HTTP 계층을 완전히 벗어나 TCP 계층에서 자체적인 규칙을 정의하는 데 중점을 둡니다.
1. 프로토콜 계층 (The Stack)
웹소켓은 통신 스택에서 다음과 같은 독특한 위치를 차지합니다.
- 전송 계층 (TCP): 웹소켓은 장기간 지속되는 단일 TCP 연결을 기반으로 합니다. 이 연결은 웹소켓 세션이 지속되는 동안 유지됩니다.
- 협상 계층 (HTTP): 연결은
Upgrade헤더를 포함하는 일반 HTTP 요청으로 시작됩니다. 이 초기 핸드셰이크(Handshake)는 기존 네트워크 인프라(프록시, 로드 밸런서 등)와의 호환성을 위해 필요합니다. - 응용 계층 (웹소켓 프레이밍): 핸드셰이크가 성공한 후, 웹소켓은 HTTP 헤더와 오버헤드를 버리고 자체적인 프레이밍 프로토콜을 사용하여 데이터를 바이너리 형식으로 송수신합니다.
2. 연결 관리 (상태 머신)
웹소켓 연결은 명확하게 정의된 상태 머신을 따릅니다. 이는 연결의 생명주기 동안 안정적인 동작을 보장합니다.
- CONNECTING (연결 중): HTTP 핸드셰이크가 시작되었지만 아직 완료되지 않은 초기 상태입니다. 서버가
101 Switching Protocols응답을 기다리고 있습니다. - OPEN (개방): 핸드셰이크가 성공적으로 완료되었으며, 클라이언트와 서버가 웹소켓 데이터 프레임을 자유롭게 주고받을 수 있는 상태입니다.
- CLOSING (닫는 중): 한쪽 끝(클라이언트 또는 서버)에서 연결 종료를 요청하는 Close 프레임을 보냈거나 수신했습니다. 최종적인 TCP 연결 종료 응답을 기다리는 상태입니다.
- CLOSED (닫힘): 웹소켓 연결이 완전히 종료된 상태입니다.
3. 데이터 프레이밍 (바이너리 구조)
웹소켓의 낮은 오버헤드는 HTTP 메시지 대신 사용되는 작고 구조화된 바이너리 프레임 덕분입니다. 각 프레임은 최소 2바이트의 헤더로 시작하며, 이 헤더에 모든 제어 정보가 담겨 있습니다.
| 비트 위치 (1바이트) | 비트 위치 (2바이트) | 내용 | 설명 |
|---|---|---|---|
| 0 (MSB) | FIN 비트 | 1이면 이 프레임이 메시지의 마지막 프레임을 의미합니다. 0이면 메시지가 여러 프레임으로 분할(fragmentation)되었음을 의미합니다. |
|
| 1-3 | RSV1-3 | 예약 비트. 확장 기능을 협상하지 않는 한 0이어야 합니다. |
|
| 4-7 | Opcode (연산 코드) | 프레임의 유형을 정의합니다 (텍스트, 바이너리, 핑, 퐁, 닫기 등). | |
| 8 (MSB) | MASK 비트 | 1이면 페이로드가 마스킹되었음을 의미합니다 (클라이언트에서 서버로 가는 프레임은 필수). |
|
| 9-15 | 페이로드 길이 | 페이로드 데이터의 길이를 나타냅니다. 값이 126이나 127인 경우, 추가 바이트가 필요하여 페이로드 길이가 길어짐을 나타냅니다. |
- 참고:
FIN비트와Opcode를 통해 웹소켓은 일반 데이터(텍스트/바이너리)와 제어 메시지를 구분합니다.
4. 페이로드 마스킹 (보안 요구사항)
클라이언트에서 서버로 전송되는 모든 데이터 프레임의 페이로드는 반드시 마스킹되어야 합니다. 이는 웹소켓 프로토콜에 명시된 엄격한 보안 요구사항입니다.
- 목적: 구형 프록시 서버 및 캐시 시스템에서 발생하는 보안 취약점(예: "캐시 오염")을 완화하기 위함입니다. 프록시 서버가 웹소켓 데이터를 HTTP 요청으로 잘못 해석하여 캐시에 악성 데이터를 저장하는 것을 방지합니다.
- 작동 방식: 클라이언트는 무작위로 생성된 4바이트의 마스킹 키(Masking Key) 를 사용합니다. 페이로드의 모든 바이트는 이 4바이트 키 중 순환적으로 하나씩 XOR 연산되어 암호화됩니다. 클라이언트는 이 키를 헤더에 포함시켜 전송하고, 서버는 동일한 키와 XOR 연산을 역으로 적용하여 페이로드를 복원합니다.
- 서버 마스킹: 서버에서 클라이언트로 보내는 프레임은 마스킹이 필수가 아니며, 보통 수행되지 않습니다.
5. 제어 프레임 (하트비트)
제어 프레임은 연결 자체를 관리하는 데 사용되는 특별한 프레임입니다. 이들은 항상 작고(페이로드 길이가 125바이트 미만), 조각화될 수 없습니다(FIN은 항상 1입니다).
- PING (Opcode 0x9): 어느 한쪽에서 상대방이 여전히 응답하는지 확인하기 위해 전송됩니다.
- PONG (Opcode 0xA): PING에 대한 응답으로 자동으로 전송됩니다. TCP 연결을 활성 상태로 유지하여(하트비트 역할) 중간 네트워크 요소의 타임아웃으로 인한 연결 종료를 방지합니다.
- CLOSE (Opcode 0x8): 연결 종료를 시작합니다. 페이로드에는 연결을 닫는 이유를 설명하는 상태 코드가 포함될 수 있습니다.
references