WebSocket 프레임 구조: 마스킹, 분할 및 기타
프레임 구조
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
위 다이어그램은 RFC 6455 WebSocket 프로토콜에서 가져온 WebSocket 프레임의 구조를 보여줍니다.
프레임 크기
- 가장 작은 유효한 WebSocket 메시지는 2바이트입니다 (예: 페이로드가 없는 서버의 종료 메시지: 138, 0).
- 가장 긴 가능한 헤더는 14바이트로, 64KB보다 큰 페이로드를 가진 클라이언트에서 서버로 보내는 메시지를 나타냅니다.
페이로드 길이
TCP는 스트림 지향 프로토콜이므로 메시지(또는 프레임)의 개념이 없습니다. 단순히 바이트 시퀀스일 뿐입니다. WebSocket과 같은 TCP 위의 솔루션은 자체적인 프레임을 제공해야 합니다.
WebSocket은 두 번째 바이트의 처음 7비트를 사용하여 가변 길이 길이 접두사를 지원합니다:
- 7비트가 125 이하: 페이로드 길이는 이 값 자체입니다.
- 7비트가 126: 다음 2바이트가 페이로드 길이를 나타냅니다.
- 7비트가 127: 다음 8바이트가 페이로드 길이를 나타냅니다.
마스킹
프레임의 길이 부분 다음에는 4바이트 마스크가 있습니다. 마스킹은 악의적인 클라이언트 측 코드가 전선에 나타나는 정확한 바이트 시퀀스를 제어하는 것을 방지하기 위한 보안 기능입니다.
마스크와 페이로드는 클라이언트에서 서버로 보내기 전에 XOR 연산을 수행하며, 서버는 이를 역으로 수행해야 합니다.
메시지 유형
WebSocket 프레임은 6가지 유형 중 하나일 수 있습니다:
- 텍스트
- 바이너리
- 핑
- 퐁
- 종료
- 연속
또한 모든 프레임은 fin 프레임이거나 그렇지 않습니다. 첫 번째 바이트는 프레임 유형(opcode)과 fin 프레임 여부를 나타내는 데 사용됩니다.
분할
WebSocket은 프레임 분할을 지원합니다. 하나의 메시지를 여러 프레임으로 보낼 수 있습니다. 이는 다음과 같은 세 가지 이유로 존재합니다:
- 스트리밍: 서버가 전체 페이로드를 가지고 있지 않고 최종 길이를 모르지만 여전히 일부 데이터를 보내고 싶을 때
- 대용량 메시지 중간에 핑과 같은 특수 제어 프레임을 삽입할 수 있는 기능
- 프록시가 메시지를 자유롭게 분할할 수 있는 기능
결론
- 가장 짧은 WebSocket 프레임(2바이트)은 페이로드가 없는 서버-클라이언트 메시지입니다.
- 가장 긴 가능한 헤더(14바이트)는 16KB보다 큰 페이로드를 가진 클라이언트-서버 메시지입니다.
- 클라이언트에서 서버로 보내는 메시지는 마스킹으로 인해 더 길고 예측할 수 없습니다.
- 서버에서 클라이언트로 보내는 메시지는 마스크가 없으므로 항상 4바이트 더 짧고 무작위가 아닙니다.
WebSocket 프레이밍 추가 설명
왜 클라이언트 ➔ 서버 프레임은 서버 ➔ 클라이언트와 다를까?
- 클라이언트 ➔ 서버 방향은 반드시 마스킹이 필요합니다 (보안 목적).
- 서버 ➔ 클라이언트 방향은 마스킹을 하지 않습니다.
- 따라서 같은 메시지를 주고받아도 클라이언트에서 서버로 보낼 때는 '랜덤'처럼 보이는 4바이트 마스크키가 포함되어 전체 길이도 길고, 내용도 매번 달라집니다.
페이로드 길이 처리
- 125바이트 이하: 길이 정보를 추가 바이트 없이 표현
- 126: 2바이트 추가 사용 (16비트, 최대 약 64KB)
- 127: 8바이트 추가 사용 (64비트, 매우 큰 데이터 지원)
장점: 작은 메시지에는 오버헤드가 적음
단점: 파싱 로직이 복잡해짐 (조건문 처리 필요)
개인 의견: 4바이트 고정 길이 prefix를 썼으면 더 단순했을 것이라는 주장도 있습니다.
마스킹
- 클라이언트 ➔ 서버 전송 시 4바이트 난수 마스크를 이용하여 payload를 XOR 연산 처리합니다.
- 서버는 마스크를 복원해서 실제 payload를 얻습니다.
- 만약 자체 개발 환경(예: 데스크탑 앱)에서 마스크를 0으로 고정하면 성능 최적화가 가능하지만, 보안 위험이 존재합니다.
메시지 타입 (Opcode)
타입 | 설명 |
---|---|
0x0 | Continuation Frame |
0x1 | Text Frame (UTF-8) |
0x2 | Binary Frame |
0x8 | Connection Close |
0x9 | Ping |
0xA | Pong |
- Text Frame은 UTF-8 유효성 검사가 필요합니다.
- Binary Frame은 유효성 검사 없이 자유롭게 데이터 전송 가능합니다.
- Ping/Pong은 연결 유지(heartbeat) 용도로 쓰입니다.
분할(Fragmentation)
- 하나의 논리적 메시지를 여러 프레임으로 분할 전송할 수 있습니다.
- 첫 프레임: text/binary 타입 + FIN=0
- 중간 프레임: continuation + FIN=0
- 마지막 프레임: continuation + FIN=1
사용 이유:
- 스트리밍처럼 전체 길이를 모를 때
- 큰 메시지 도중에 ping/pong 삽입
- 네트워크 장비(프록시)가 임의로 분할할 때
개발 난이도:
- 분할 관리 로직 필요 (메모리 관리, 조립 과정)
- 중간 control frame (ping/pong/close) 허용 규칙 준수 필요
TCP Socket, WebSocket, STOMP의 차이점과 예시
1. TCP Socket
-
설명:
TCP 소켓은 가장 기본적인 네트워크 통신 방식으로, 신뢰성 있는 바이트 스트림을 제공합니다. 메시지의 경계가 없고, 개발자가 직접 프로토콜을 설계해야 합니다. -
특징:
- 연결 지향적(3-way handshake)
- 신뢰성 보장(순서 보장, 손실 시 재전송)
- 메시지 구분 불가(바이트 스트림)
- 낮은 수준의 네트워크 제어 가능
- 사용자 정의 프로토콜 구현 필요
-
장점:
- 최대한의 제어권: 헤더, 프레이밍, 에러 처리 등 모든 것을 커스터마이징 가능
- 높은 성능: 오버헤드가 적고 직접적인 통신
- 유연성: 어떤 종류의 데이터든 전송 가능
- 플랫폼 독립적: 웹 브라우저 없이도 동작
-
단점:
- 복잡한 구현: 메시지 경계, 에러 처리, 연결 관리를 직접 구현해야 함
- 보안 구현 필요: TLS/SSL 등을 직접 적용해야 함
- 방화벽 이슈: 임의의 포트 사용으로 인한 네트워크 제약
- 브라우저 호환성: 웹 브라우저에서 직접 사용 불가
-
적합한 상황:
- 고성능이 요구되는 시스템: 게임 서버, 실시간 트레이딩 시스템
- 커스텀 프로토콜: 데이터베이스 드라이버, P2P 네트워크
- 시스템 레벨 애플리케이션: 로그 수집 시스템, 모니터링 도구
- IoT 디바이스: 리소스가 제한된 환경에서의 효율적 통신
-
예시:
- 채팅 서버에서 직접 소켓을 열고, 바이트 단위로 메시지를 주고받는 경우
- 예시 코드(파이썬):
import socket s = socket.socket() s.connect(('localhost', 12345)) s.sendall(b'Hello') data = s.recv(1024) print(data)
2. WebSocket
-
설명:
WebSocket은 HTTP 업그레이드를 통해 연결을 맺고, 이후에는 양방향 통신이 가능한 프레임 기반 프로토콜입니다. 메시지의 경계가 명확하며, 브라우저와 서버 간 실시간 통신에 주로 사용됩니다. -
특징:
- HTTP로 핸드셰이크 후, 지속적인 연결 유지
- 양방향, 실시간 통신
- 메시지 단위 전송(프레임 구조)
- 웹 표준으로 브라우저에서 기본 지원
- 기존 HTTP 인프라와 호환
-
장점:
- 웹 호환성: 모든 모던 브라우저에서 기본 지원
- 방화벽 친화적: HTTP 포트(80/443) 사용으로 네트워크 제약 적음
- 간단한 구현: 메시지 경계와 프레이밍이 자동 처리됨
- 보안: TLS 지원으로 안전한 통신 (wss://)
- 실시간성: HTTP 폴링 대비 낮은 지연시간
-
단점:
- 오버헤드: TCP 대비 추가적인 프레임 헤더
- 제한된 제어: 낮은 수준의 네트워크 제어 불가
- 연결 관리: 네트워크 중단 시 재연결 로직 필요
- 확장성 고려사항: 대량의 동시 연결 시 서버 리소스 관리 필요
-
적합한 상황:
- 웹 애플리케이션: 브라우저와 서버 간 실시간 통신이 필요한 경우
- 실시간 업데이트: 채팅, 알림, 라이브 스포츠 스코어, 주식 시세
- 협업 도구: 실시간 문서 편집, 화이트보드, 공유 화면
- 게임: 웹 기반 멀티플레이어 게임
- 대시보드: 실시간 모니터링, 분석 대시보드
-
예시:
- 브라우저에서 실시간 알림, 채팅, 게임 등
- 예시 코드(자바스크립트):
const ws = new WebSocket("ws://localhost:8080/ws"); ws.onopen = () => ws.send("Hello"); ws.onmessage = (event) => console.log(event.data);
TCP Socket vs WebSocket 상세 비교
구분 | TCP Socket | WebSocket |
---|---|---|
계층 | 전송 계층 (Layer 4) | 응용 계층 (Layer 7) |
연결 설정 | 3-way handshake | HTTP 업그레이드 핸드셰이크 |
메시지 경계 | 없음 (바이트 스트림) | 있음 (프레임 기반) |
오버헤드 | 최소 (20바이트 TCP 헤더) | 중간 (2-14바이트 WebSocket 헤더) |
보안 | 직접 구현 필요 | 내장 (wss://) |
방화벽 | 포트별 설정 필요 | HTTP 포트 사용으로 통과 용이 |
브라우저 지원 | 불가능 | 기본 지원 |
프로토콜 설계 | 완전 커스텀 | 표준화된 프레임 |
에러 처리 | 직접 구현 | 일부 자동화 |
개발 복잡도 | 높음 | 중간 |
참고: Socket vs WebSocket 차이점 (opens in a new tab)에서 더 자세한 내용을 확인할 수 있습니다.
WebSocket HTTP 업그레이드 과정
HTTP 업그레이드 과정
WebSocket 연결은 다음과 같은 단계로 이루어집니다:
-
초기 HTTP 요청 (핸드셰이크)
- 클라이언트가 일반 HTTP 요청을 보내되, 특별한 헤더를 포함합니다:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
-
서버 응답
- 서버가 WebSocket 프로토콜로 전환할 준비가 되면 다음과 같이 응답합니다:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-
연결 전환
- 101 상태 코드는 프로토콜 전환이 성공했음을 의미합니다
- 이 시점부터는 HTTP가 아닌 WebSocket 프로토콜을 사용합니다
-
특징
- 이 과정은 단 한 번만 발생합니다
- 연결이 성공하면 TCP 연결은 유지되지만, 프로토콜이 WebSocket으로 변경됩니다
- 이후부터는 양방향 통신이 가능해집니다
-
보안
Sec-WebSocket-Key
와Sec-WebSocket-Accept
는 WebSocket 연결의 보안을 위해 사용됩니다- 이 값들은 중간자 공격을 방지하는 데 도움이 됩니다
이러한 HTTP 업그레이드 과정은 다음과 같은 이점이 있습니다:
- 기존 HTTP 인프라(프록시, 방화벽 등)와의 호환성
- 80/443 포트 사용으로 방화벽 통과 용이
- HTTP 기반 인증 메커니즘 재사용 가능
이 과정이 완료되면, 클라이언트와 서버는 WebSocket 프로토콜을 사용하여 실시간 양방향 통신을 할 수 있게 됩니다.
3. STOMP (Simple Text Oriented Messaging Protocol)
-
설명:
STOMP는 WebSocket 위에서 동작하는 서브 프로토콜로, 메시지 브로커(예: RabbitMQ, ActiveMQ)와의 통신을 위해 설계되었습니다. 목적지(destination) 기반의 pub/sub, 메시지 acknowledge 등 고수준 기능을 제공합니다. -
특징:
- 텍스트 기반 프레임(명확한 명령어와 헤더)
- pub/sub, 큐, 브로커 연동 등 고수준 메시징 지원
- WebSocket, TCP 등 다양한 전송 계층 위에서 동작
- 메시지 라우팅과 큐잉 기능
-
장점:
- 고수준 메시징: pub/sub, point-to-point 패턴 기본 지원
- 메시지 브로커 연동: RabbitMQ, ActiveMQ 등과 쉬운 통합
- 확장성: 여러 서버 인스턴스 간 메시지 공유 가능
- 표준화된 프로토콜: 다양한 클라이언트/서버 구현체 존재
-
단점:
- 추가 복잡성: WebSocket보다 높은 학습 곡선
- 성능 오버헤드: 추가적인 프로토콜 레이어
- 의존성: 메시지 브로커 인프라 필요
-
적합한 상황:
- 복잡한 메시징 시스템: 여러 채널과 구독자가 있는 시스템
- 마이크로서비스: 서비스 간 비동기 메시징
- 확장 가능한 채팅: 여러 서버에서 동작하는 대규모 채팅 시스템
- 이벤트 드리븐 아키텍처: 도메인 이벤트 발행/구독
-
예시:
- 실시간 알림, 채팅방, 주식 시세 등 pub/sub 구조
- 예시 코드(자바스크립트, SockJS + STOMP):
import SockJS from "sockjs-client"; import Stomp from "stompjs"; const sock = new SockJS("http://localhost:8080/ws"); const stomp = Stomp.over(sock); stomp.connect({}, () => { stomp.subscribe("/topic/notice", (msg) => console.log(msg.body)); stomp.send("/app/hello", {}, JSON.stringify({ name: "user" })); });
전체 비교 요약
구분 | TCP Socket | WebSocket | STOMP |
---|---|---|---|
계층 | 전송 계층 (Layer 4) | 응용 계층 (Layer 7) | WebSocket/TCP 위 메시징 |
연결 방식 | 3-way handshake | HTTP 업그레이드 | WebSocket 기반 |
메시지 경계 | 없음 (바이트 스트림) | 있음 (프레임) | 있음 (명령어+헤더) |
브라우저 지원 | 불가능 | 기본 지원 | 라이브러리 필요 |
오버헤드 | 최소 | 중간 | 높음 |
개발 복잡도 | 매우 높음 | 중간 | 낮음 (고수준) |
확장성 | 직접 구현 | 서버별 관리 | 브로커 기반 확장 |
고급 기능 | 직접 구현 | 없음 | pub/sub, 큐, ACK |
적용 사례 | 게임서버, DB연결 | 웹 실시간 통신 | 대규모 메시징 시스템 |
기술 선택 가이드라인
성능 우선순위: TCP Socket > WebSocket > STOMP
개발 편의성: STOMP > WebSocket > TCP Socket
웹 호환성: WebSocket ≥ STOMP > TCP Socket (불가)
확장성: STOMP > WebSocket > TCP Socket
추가 참고자료:
- WebSocket vs TCP 비교 (opens in a new tab)
- 더 자세한 성능과 확장성 분석을 원한다면 각 기술의 공식 문서를 참조하세요.