WebSocket 동작 방식

mseo39·2025년 12월 23일

TIL

목록 보기
18/19
post-thumbnail

WebSocket을 선택한 이유

실시간 통신을 구현하는 방법은 다양하지만 양방향 통신 및 효율성 측면을 고려하여 WebSocket을 선택하였습니다

기술통신 방향특징
HTTP Polling단방향주기적인 서버 요청으로 데이터 확인 (오버헤드 발생)
SSE (Server-Sent Events)서버 → 클라이언트연결을 유지하며 서버로부터 데이터 수신만 가능 (클라이언트 응답 시 별도 HTTP 요청 필요)
WebSocket양방향한 번의 연결로 자유로운 양방향 데이터 송수신 가능

WebSocket 동작 방식

웹소켓 연결은 Upgrade 메커니즘을 통해 이루어지며 크게 '연결 확립'과 '양방향 통신', '연결 종료' 단계로 나뉩니다

연결 확립

  1. TCP/TLS 연결
  • 클라이언트와 서버 간 TCP 3-Way Handshake를 통해 신뢰할 수 있는 TCP 연결 수립
  • 만약 보안이 적용된 wss라면 TLS 핸드쉐이크를 통해 암호화된 세션 구성
  1. HTTP Upgrade 요청
  • 클라이언트는 HTTP/1.1 GET 요청 헤더에 Upgrade: websocket, Connection: Upgrade 헤더와 함께 랜덤 생성된 Sec-WebSocket-Key를 포함
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
  1. HTTP 101 응답 및 검증
  • 서버는 클라이언트로부터 받은 Sec-WebSocket-KeyMagic String을 더해 SHA-1 해싱
  • 해싱된 값을 Sec-WebSocket-Accept 헤더에 담고 HTTP 101 Switching Protocols 상태 코드로 응답
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

Accept + Magic String 사용하는 이유
중간 프록시 서버가 101 Switching Protocols 응답을 캐싱하여 클라이언트에게 잘못된 연결 정보를 제공하는 상황을 방지합니다. 클라이언트는 이 Sec-WebSocket-Accept 값을 검증함으로써 서버가 웹소켓 표준을 정확히 구현하였음을 확인하고, 안전하게 데이터를 전송할 수 있습니다.

  1. 연결 완료

클라이언트가 서버의 Sec-WebSocket-Accept 키를 성공적으로 검증하면 통신프로토콜은 WebSocket으로 전환됨

양방향 통신

  • 웹소켓은 메시지를 프레임 단위로 나누어 데이터를 주고받습니다
  • 각 프레임은 최소 2바이트의 헤더를 가지며 효율적인 통신을 지원합니다

FIN (1 bit): 이 프레임이 전체 메시지의 마지막 조각인지 여부

  • 1: 마지막 프레임 (단일 프레임 메시지 포함)
  • 0: 뒤에 프레임이 더 있음 (메시지 분할 전송 시)

RSV1, 2, 3 (각 1 bit): 확장을 위해 예약된 비트로 확장이 사용되지 않으면 0

Opcode (4 bits): 전송되는 payload 데이터 유형을 정의

  • 0x0: 이전 프레임과 이어짐
  • 0x1: Text (UTF-8)
  • 0x2: Binary
  • 0x8: 연결 종료
  • 0x9: Ping
  • 0xA: Pong

Mask (1 bit): payload 데이터가 마스킹(암호화) 여부

  • 클라이언트 → 서버: 반드시 1이어야 함 (보안상의 이유)
  • 서버 → 클라이언트: 반드시 0이어야 함

Payload Length (7 bits): payload 데이터의 길이를 나타내며 이 7비트의 값에 따라 실제 길이 표현 방식이 달라짐

  • 0~125: 실제 데이터 길이
  • 126: 뒤에 오는 Extended Payload Length (16 bits)를 사용하여 길이 표현
  • 127: 뒤에 오는 Extended Payload Length (64 bits)를 사용하여 길이 표현

Masking-Key (32 bits): Mask 비트가 1일 때만 존재하며 클라이언트가 데이터를 전송할 때 사용하는 4바이트의 랜덤 키입니다.

Payload Data: 실제 전송하려는 데이터 본문

🤔왜 최소 오버헤드인가?

  • HTTP: 최소 수백 바이트의 텍스트 기반 헤더를 매번 보냄
  • WebSocket: HTTP와 달리 프레임 앞에 최소 2바이트의 헤더만으로 통신이 가능하여 HTTP보다 오버헤드가 줄어듦

🤔 왜 클라이언트 → 서버는 마스킹하고 그 반대는 안할까?

  • 중간 프록시 서버가 웹소켓 데이터를 HTTP 요청으로 오해하여 캐시를 오염시키는 것을 방지하기 위함
  • 반면 서버는 신뢰할 수 있는 주체로 간주되며 수많은 클라이언트에게 데이터를 보낼 때 발생하는 CPU 부하를 줄이기 위해 서버 측 마스킹은 수행하지 않음

연결 종료

  1. WebSocket 종료
  • 클라이언트 또는 서버가 Close Frame을 보내고 상대방도 Close Frame 응답
  1. TCP 종료
  • TCP 4-Way Handshake를 통해 연결 해제
  • 연결 종료 시에는 Close Frame 내 Status Code를 통해 종료 원인(예: 1000 - 정상 종료1001 - 서버 재시작1006 - 비정상 종료)을 전달

참고자료

profile
하루하루 성실하게

0개의 댓글