WebSocket 핸드셰이크와 프레임
실시간 통신4 / 4
- 1.주기 폴링
- 2.롱 폴링
- 3.Server Sent Event
- 4.WebSocket 핸드셰이크와 프레임
클라이언트에서도 메시지를 자주 보내야 한다면 SSE와 POST를 조합하는 것보다 양쪽에서 필요할 때 메시지를 보낼 수 있는 연결 하나를 유지하는 것이 더 좋은 선택입니다.
그 연결은 바로 WebSocket을 말하는 것입니다. WebSocket을 고르는 이유는 크게 두 가지입니다. 서버와 클라이언트가 같은 연결에서 언제든 메시지를 보낼 수 있고, 인증과 구독 상태 같은 리소스도 연결 단위로 묶어서 관리할 수 있습니다.
이 게시글에서는 WebSocket이 어떻게 연결이 되고, 어떤 데이터가 오가는지만 다룹니다.
WebSocket을 고르는 두 가지 이유
첫번째는 양방향 통신입니다. SSE에서는 서버만 열린 연결을 통해 이벤트를 보낼 수 있습니다. 클라이언트가 서버로 메시지를 보내려면 POST 같은 별도 HTTP 요청이 필요합니다. 반면 WebSocket은 하나의 연결에서 서버와 클라이언트 모두 필요할 때 메시지를 보낼 수 있는 풀 듀플렉스(Full Duplex) 통신을 지원합니다. 채팅, 게임, 공동, 편집처럼 양쪽에서 메시지가 자주 발생하는 서비스에서는 이 차이가 큽니다.
둘째는 리소스 관리입니다. SSE는 연결 당 하나의 스레드 할당하고, 클라이언트 메시지는 별도 HTTP 요청으로 들어옵니다. 서버에서는 POST 요청이 들어올 때마다 인증과 요청 컨텍스트를 구성하고, 이 요청을 기존 SSE 연결의 구독 상태와 매핑해야 합니다. SSE 연결 종료와 POST 요청이 엇갈리는 상황까지 고려하면 정리 로직도 두 경로로 나뉩니다.
WebSocket에서는 인증 정보, 구독 목록, 송수신 큐 같은 상태를 하나의 커넥션 컨텍스트에 묶을 수 있습니다. 연결이 열릴 때 생성하고 close 이벤트에서 한 번에 정리하면 됩니다. 클라이언트가 메시지를 자주 보내더라도 매번 HTTP 요청을 파싱하고 요청 컨텍스트를 새로 만드는 오버헤드가 발생하지 않습니다.
물론 서버 푸시가 대부분이라면 SSE가 구현과 운영 모두 더 단순할 수 있습니다. HTTP/2 환경에서는 POST 요청마다 새 TCP 연결이 생기는 것도 아닙니다. 반대로 WebSocket을 사용하면 하트비트, 재연결, 상태 복구까지 애플리케이션에서 신경써야 합니다. 그래서 서버 푸시 위주라면 SSE, 양쪽 메시지가 많고 같은 세션 상태를 공유해야 한다면 WebSocket이라고 구분하는 편이 정확합니다.
왜 HTTP로 시작하냐
WebSocket은 HTTP와 다른 프로토콜인데 왜 HTTP 요청으로 시작할까
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13그 이유는 바로 기존 웹 인프라가 HTTP를 기준으로 구성되어 있기 때문입니다. 방화벽, 프록시, 로드밸런서는 80번과 443번 포트로 들어오는 HTTP 트래픽을 처리하도록 설정되어 있습니다. 완전히 새로운 프로토콜을 별도 포트에서 시작하면 기존 인프라를 그대로 활용하기 어렵습니다.
그래서 HTTP인 척 먼저 요청을 보내고, 그 다음 다른 프로토콜로 갈아탑니다.