WebSocket에 대해서

Imnottired·2023년 5월 15일
0

웹소켓을 사용하게 되어서 탐구하게되었다.

아래 출처에 맞추어 정리하였고,
모르는 부분을 검색으로 추가하는 식으로 정리하였다.




WebSocket

WebSocket 프로토콜은 표준된 방법으로 서버-클라이언트 간에 단일 TCP 커넥션을 이용해서 양방향 통신을 제공한다.

특징
기존의 다른 TCP 기반의 프로토콜과 다르게, WebSocket은 HTTP 요청 기반으로 Handshake 과정을 거쳐 커넥션을 생성한다.

덕분에, 초기 WebSocket Handshake 요청은 추가적인 방화벽 설정없이 80, 443 포트를 사용하여 양방향 통신이 가능하다.

뿐만 아니라, HTTP 규격 그대로 유지할 수 있기 때문에 HTTP 인증, CORS 등을 동일하게 적용할 수 있다는 장점이 있다.

HandShake에 대해 몰라서 찾아보았다.
WebSocket handshake

3가지 단계로 나뉘는데

  1. 클라이언트의 handshake 요청
    클라이언트는 HTTP 요청을 사용하여서 서버에 WebSocket handshake 요청을 보냅니다. 일반적으로 HTTP GET 요청을 사용하며, Upgrade 헤더와 Connection 헤더를 포함해야 합니다.

  2. 서버의 handshake 응답
    서버는 클라이언트의 handshake 요청을 받으면, 요청을 검증하고 WebSocket handshake 응답을 반환합니다. 이 응답은 일반적으로 HTTP 응답을 사용하며, Upgrade 헤더, Connection 헤더, Sec-WebSocket-Accept 헤더 등을 포함합니다.

  3. 연결 설정
    클라이언트는 서버에서 반환한 WebSocket handshake 응답을 받으면, 해당 응답을 검증하고 WebSocket 연결을 설정합니다. 이제 클라이언트와 서버는 양방향으로 데이터를 전송할 수 있습니다.

WebSocket handshake 과정에서는 다양한 헤더가 사용됩니다. 가장 중요한 것은 Upgrade와 Connection 헤더입니다. 이 헤더들은 WebSocket handshake 요청과 응답에서 사용됩니다. 또한, Sec-WebSocket-Key 헤더는 클라이언트에서 무작위로 생성한 키를 서버에게 보내며, 이를 서버는 다시 처리하여 Sec-WebSocket-Accept 헤더를 반환합니다.

구체적인 부분이 궁금해서 더찾아보았다.

Opening Handshake

웹소켓 클라이언트에서 핸드쉐이크 요청(HTTP Upgrade)을 전송하고 이에 대한 응답으로 핸드 셰이크 응답을 받는데, 이때 응답 코드는 101입니다. 101은 '프로토콜 전환'을 서버가 승인했음을 알리는 코드입니다.

이 과정에서 요청과 응답의 헤더를 살펴보겠습니다. ws://localhost:8080/chat 으로 접속하려고 한다고 가정합니다.

GET /chat HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://localhost:9000

Upgrade : 프로토콜을 전환하기 위해 사용하는 헤더. 웹소켓 요청 시에는 반드시 websocket이라는 값을 가지며, 이 값이 없거나 다른 값이면 cross-protocol attack이라고 간주하여 웹소켓 접속을 중지시킵니다.

Connection : 현재의 전송이 완료된 후 네트워크 접속을 유지할 것인가에 대한 정보. 웹소켓 요청 시에는 반드시 Upgrade라는 값을 가지며, Upgrade와 마찬가지로 이 값이 없거나 다른 값이면 웹소켓 접속을 중지시킵니다.

Sec-WebSocket-Key : 유효한 요청인지 확인하기 위해 사용하는 키 값.

Sec-WebSocket-Protocol : 사용하고자 하는 하나 이상의 웹 소켓 프로토콜 지정. 필요한 경우에만 사용.

Sec-WebSocket-Version : 클라이언트가 사용하고자 하는 웹소켓 프로토콜 버전. 현재 최신 버전 13

Origin : 모든 브라우저는 보안을 위해 이 헤더를 보냅니다(Cross-Site WebSocket Hijacking와 같은 공격을 피하기 위해서). 대부분 어플리케이션은 이 헤더가 없는 요청을 거부하며, 이러한 이유로 CORS정책이 만들어진 것입니다.

위에서 말한 것처럼 WebSocket은 커넥션을 맺기 위해 HTTP 요청을 보내는데, 아래와 같이 HTTP 요청 헤더에 Upgrade 헤더와 Connection 포함한다.

Upgrade

  • 이미 생성된 커넥션을 다른 프로토콜로 업그레이드/변경
  • 클라이언트가 Upgrade 헤더 값에 나열한 프로토콜 리스트를 서버가 선택한다.
  • 앞쪽에 배치할수록 우선순위가 높음
  • 서버는 Upgrade 하기로 선택한 프로토콜을 응답 Uprade 헤더에 추가해 전달한다.

GET ws://localhost:3000/sockjs-node HTTP/1.1
Host: localhost:3000
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36
Upgrade: websocket
Origin: http://localhost:3000
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Sec-WebSocket-Key: xwGnajy+I6YJ/AW7pTKioA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

(위 내용과 동일하다)
서버는 아래와 같이 101 Switching Protocols 상태 코드로 응답을 하는데, Handshask 이후에도 TCP 커넥션은 지속적으로 유지된다.

HTTP vs WebSocket

WebSocket이 HTTP 요청을 시작되는 호환성을 가지고 있지만, 분명하게 두 프로토콜은 다른 방식으로 동작한다.

HTTP는 여러 URL을 기반으로 서버 애플리케이션과 Request/Response 형식으로 상호 작용한다.

WebSocket은 반대로 오직 초기의 커넥션 수립을 위한 하나의 URL만 있고, 모든 애플리케이션 메시지는 동일한 TCP 커넥션에서 전달된다.

즉, WebSocket은 HTTP 프로토콜과 다른 asynchronous, event-driven, messaging 아키텍쳐 모델이다.

또한, HTTP 경우에는 서버가 URI, Method, Headers 정보로 적절한 핸들러로 라우팅해 처리할 수 있다.

하지만 WebSocket은 HTTP 와 다르게 메시지 내용에 의미를 두지 않기 때문에, 클라이언트-서버 간에 임의로 메시지에 의미를 부여하지 않으면 처리할 방법이 마땅히 없다.

이러한 문제를 STOMP 메시징 프로토콜을 통해서 해결할 수 있는데, 상위 프로토콜이 규정한 협약을 토대로 메시지를 처리할 수 있다.

언제 WebSocket을 사용할까?

Traditional Polling
고전적인 Polling 방식은 새로운 정보가 있는지 확인하기 위해 주기적으로 HTTP 요청을 보낸다. 이러한 방식은 지속적으로 요청을 보내기 때문에, 매번 커넥션을 생성하기 위한 Handshake 비용이 많아지며 서버에 부담을 주게 된다.

Long Polling
Long Polling은 Traditional Polling을 개선한 방식이다.

클라이언트는 서버에 요청을 보내고, 서버는 변경 사항이 있는 경우에만 응답하여 커넥션을 종료한다.

그리고 클라이언트는 바로 다시 서버에 요청을 보내어 변경 사항이 있을 때까지 대기하게 된다.

커넥션은 무한히 대기할 수 없으므로, 브라우저는 약 5분 정도 대기하며 중간 프록시에 따라 더 짧게 커넥션이 종료될 수도 있다.

만약 변경 사항이 불규칙적인 간격으로 일어나는 경우 효율적이나, 변경 사항의 빈도가 잦다면 기존 Traditional Polling과 차이가 없으므로 서버의 부담이 증가하게 된다.

HTTP Streaming
HTTP Streaming은 Long Polling 과 동일하게 HTTP 요청을 보내지만, 변경 사항을 클라이언트에 응답한 이후에도 커넥션을 종료하지 않고 유지한다.

따라서 매번 새로운 커넥션을 맺고 끊는 것이 아니라 하나의 커넥션을 유지하며, 변경 사항을 응답 메시지로 전달한다.

HTTP Streaming은 Long Polling 방식에 비해서 서버의 부담을 줄일 수 있지만, 여러 건의 변경 사항이 일어난 경우 동시 처리가 어려워진다.

왜냐하면, 서버가 현재 업데이트된 데이터를 모두 전달해야만, 클라이언트에서 다음 업데이트된 데이터의 시작 위치를 알 수 있기 때문이다.

뿐만 아니라, HTTP Streaming 방식은 서버가 클라이언트에게 전달하는 메시지에 대한 실시간성을 어느 정도 보장하지만, 클라이언트가 서버에게 보내는 요청은 여전히 새로운 커넥션을 생성해야 한다.

이러한 동시성과 서버 부담이라는 Trade Off 사항에서, HTTP Streaming 보다 Long Polling 방식을 많이 사용한다고 한다.

WebSocket
위와 같은 HTTP Long Polling, Streaming 방식이 가지고 있는 문제를 해결하고, 서버-클라이언트 간에 양방향 통신이 가능하도록 WebSocket 이라는 기술이 만들어지게 됐다.

WebSocket은 서비스를 동적으로 만들어 주지만, AJAX, HTTP Streaming, HTTP Long Polling 기술이 보다 효과적인 경우도 있다. 예를 들어, 변경 사항의 빈도가 자주 일어나지 않고 데이터의 크기가 작은 경우에는 AJAX, HTTP Streaming, HTTP Long Polling 기술이 효과적일 수 있다.

즉, 실시간성을 보장해야 하고 변경 사항의 빈도가 크다면 WebSocket은 좋은 해결책이 될 수 있다.




마무리

웹소켓이 어떠한 원리로 돌아가는지 알게 되었고, 어떻게 성장해왔는지 알게 되었다.

그리고 다른 사람이 작성한 글을 여러개 보는 것이 아니라, 그 글을 정리하고 필요한 부분에 맞추어서 넣어준 후 읽어주는 방식이 이해하기 수월했다.

추후에 사용법까지 완벽하게 익혀서 stomp와 sockJS를 사용해보아야겠다.

출처
https://velog.io/@koseungbin/WebSocket
https://kellis.tistory.com/65

profile
새로운 것을 배우는 것보다 정리하는 것이 중요하다.

5개의 댓글

comment-user-thumbnail
2023년 5월 21일

채팅할 때만 쓰이는 줄 알았는데 생각보다 쓰이는 범위가 넓네요 ! 프로젝트에 적용하시나요?

1개의 답글
comment-user-thumbnail
2023년 5월 21일

공부하고 싶었던 개념인데 감사합니다 !

답글 달기
comment-user-thumbnail
2023년 5월 21일

websocket 채팅 기능 구현할 때만 필요한 거라고 알고있었는데 프로토콜이었군요.. 낙준님 글을 읽고 전체적인 개념을 이해할 수 있었어요!!

답글 달기
comment-user-thumbnail
2023년 5월 21일

오.. 한번도 써보지 않은 방식이라 신기하네요 .. Handshake라는 개념 잘 알고 갑니다

답글 달기