SY 개발일지
article thumbnail
Published 2024. 4. 23. 00:37
[CS] WebSocket이란 ? 프로젝트

프로젝트를 진행하며 채팅 기능이 필요할 시 웹소켓을 통해 연결하는 경우가 많습니다. 따라서 웹소켓의 개념에 대해 한번 정리해보고자 합니다.

웹소켓이란

웹소켓은 두 프로그램 간의 메세지를 교환하기 위한 통신 방법 중 하나입니다. 더 자세히 말하자면 W3C와 IETF에 의해 자리잡은 표준 프로토콜 중 하나입니다. 즉 현재 인터넷 환경(HTML5)에서 많이 사용됩니다.

W3C와 IETF란?
W3C: 월드 와이드 웹을 위한 표준을 개발하고 장려하는 조직
IETF: 인터넷의 운영, 관리, 개발에 대해 협의하고 프로토콜과 구조적인 사안들을 분석하는 인터넷 표준화 작업 기구

 

웹소켓을 지원하는 브라우저(Chrome, Edge 등)의 경우 웹소켓 프로토콜을 지원하고 있습니다.

 

 

웹소켓의 특징

1. 양방향 통신(Full-Duplex)

데이터 송수신을 동시에 처리할 수 있는 통신 방법으로서, 클라이언트와 서버가 서로에게 원할 때 데이터를 주고 받을 수 있습니다. 

통상적인 HTTP는 Client가 요청을 보내는 경우에만 Server가 응답하는 단방향 통신입니다.

 

2. 실시간 네트워킹(Real time-Networking)

웹 환경에서 연속된 데이터를 빠르게 노출시켜 줍니다. 예를 들어 채팅, 주식, 비디오 데이터 등을 다루는 서비스가 이에 속합니다. 즉 여러 단말기에서 빠르게 데이터를 교환하는 서비스에서 사용합니다.

 

3. 오버헤드가 적음

웹소켓은 한번 연결하면 그 연결을 계속 사용하기 때문에 stateless한 HTTP에 비해 오버헤드가 적습니다.

 

웹소켓 이전의 실시간 통신

Polling

서버로 일정 주기로 요청을 송신하는 기술입니다. real-time 통신에서는 언제 통신이 발생할 지 예측이 불가능하기 때문에 일정 주기로 데이터를 계속 보냅니다. 그래서 불필요한 request와 connectiong을 생성하게 됩니다. 

이는 real-time이라고 부르기 애매할 정도의 실시간성을 갖게 됩니다.

Long Polling

Polling의 단점을 개선하여 서버에 요청을 보내고 이벤트가 생겨 응답을 받을 때까지 연결이 종료되지 않습니다. 만약 응답을 받게 되면 연결을 끊고 재요청을 하게 됩니다. 하지만 많은 양의 메세지가 쏟아질 경우에는 Polling과 같아지게 됩니다.

Streaming

서버에 요청을 보내고 끊기지 않은 연결 상태에서 끊임없이 데이터를 수신합니다. 그러면 클라이언트에서 서버로의 데이터 송신이 어려워지게 됩니다.

 

결과적으로 이러한 모든 방법이 HTTP 를 통해 통신하기 때문에, Request, Response 둘다 Header가 불필요하게 커집니다.

 

웹소켓 동작 방법

핸드쉐이킹

요청

연결 과정은 기본적으로 HTTP 프로토콜을 사용합니다. 다음 헤더는 연결 수립을 요청할 때의 헤더이다.

  • HTTP의 버전은 1.1 이상이어야 하며 GET 메서드를 통해 연결을 합니다.
  • Host는 웹 서버의 주소를 말합니다.
  • Upgrade: 현재 클라이언트, 서버, 전송 프로토콜 연결에서 다른 프로토콜로 업그레이드 또는 변경하기 위한 규칙. 즉, 해당 핸드쉐이킹을 마치면 websocket으로 변경하여 사용하겠다는 뜻입니다.
  • Connection: Upgrade 헤더 필드가 명시되었을 경우, 송신자는 반드시 Upgrade 옵션을 지정한 Connection 헤더 필드도 전송하여야 합니다.
  • Sec-WebSocket-Key: 길이가 16바이트인 임의로 선택된 숫자를 base64로 인코딩한 값입니다. 이걸 통해서 클라이언트와 웹소켓 간의 신원을 인증하게 됩니다. 

응답

응답으로 101 Switching Protocols가 오게 되면 웹소켓이 연결되었다는 뜻입니다.

  • Sec-WebSocket-Accept: 클라이언트로부터 받은 Sec-WebSocket-Key를 사용하여 계산한 값입니다. 이 헤더도 서로 신원을 인증하는 과정에서 필요합니다. 해당 값이 클라이언트에서 계산한 값과 일치하지 않는다면, 연결이 수립되지 않습니다.

 

핸드쉐이킹이 완료가 되면, 프로토콜이 기존의 http 에서 ws로 변경되게 됩니다.

물론 여기에서 ssl 보안을 적용한 wss도 사용 가능합니다.

 

데이터 전송

데이터는 Messge를 통해 주고 받습니다. Message란 여러 frame이 모여서 구성하는 하나의 논리적 메세지 단위입니다.

frame은 communication에서 가장 작은 단위의 데이터로서 작은 헤더와 payload로 구성되어 있습니다.

특히 웹소켓 통신에 사용되는 데이터는 UTF8 인코딩이라는 특징을 갖고 있습니다.

 

프레임이란?

프레임은 다음과 같은 구조를 가지고 있습니다. 그 중 헤더 부분에 대해 설명드리겠습니다.

  • 0 END: 이 프레임이 전체 메세지의 끝인지 나타내는 플래그
  • 4 5 6 7 Opcode: 이 프레임이 어떻게 사용될지를 나타냅니다.
    • Continue (0x0): 전체 메세지의 일부임을 의미
    • Text (0x1): 포함된 데이터가 UTF-8 텍스트라는 의미
    • Binary (0x2): 포함된 데이터가 이진 데이터라는 의미
    • Close (0x8): Close 핸드 쉐이크를 시작한다는 의미
  • 9 ~ 15 Length: 이 프레임에 포함된 데이터의 총 길이를 나타내는 단위입니다.
  • RSV1, RSV2, RSV3는 프로토콜별로 사용할 수도, 사용하지 않을 수도 있다고 합니다.

 

연결 종료

연결이 종료될 때에는 Close frame을 주고 받으며 연결이 종료됩니다.

 

 

웹소켓 프로토콜의 특징

  1. 최초 접속에서만 http 프로토콜 위에서 handshaking을 하기 때문에 http header를 사용합니다.
  2. 웹소켓을 위한 별도의 포트는 없으며, 기존 포트(http-80, https-443)를 사용합니다.
  3. 프레임으로 구성된 메세지라는 논리적 단위로 송수신합니다.
  4. 메세지에 포함될 수 있는 교환 가능한 메세지는 텍스트바이너리가 존재합니다.

 

웹소켓 한계

앞서 말했듯이 웹소켓을 HTML5 이후 버전에서만 사용 가능합니다. 그렇다면 HTML5 이전의 기술로 구현된 서비스에서는 어떻게 통신할까요?

Socket.io, SockJS

Socket.io와 SockJS는 HTML5 이전의 기술로 구현된 서비스에서 웹 소켓처럼 사용할 수 있도록 도와주는 기술입니다.

Javascript를 이용하여 브라우저 종류에 상관 없이 실시간 웹을 구현하였으며, WebSocket, FlashSocket, AJAX Long Polling, AJAX Multi part Streaming, IFrame, JSONP Polling을 하나의 API로 추상화하였습니다.

즉, 브라우저와 웹 서버의 종류와 버전을 파악하여 가장 적합한 기술을 선택하여 사용하는 방식입니다.

 

STOMP

웹소켓은 문자열들을 주고 받을 수 있게 해줄 뿐 그 이상의 일을 해주지 않습니다. 주고 받은 문자열의 해독은 온전히 어플리케이션에게 맡깁니다. 즉 HTTP는 형식을 정해두었기 때문에 모두가 약속을 따르기만 하면 해석할 수 있지만, 웹소켓은 형식이 정해져 있지 않기 때문에 어플리케이션에서 쉽게 해석하기 힘들다는 단점을 가지고 있습니다.

따라서 웹소켓 방식은 sub-protocols을 사용해서 주고받는 메세지의 형태를 약속하는 경우가 많습니다.

 

STOMP(Simple Text Oriented Message Protocol)

STOMP는 채팅 통신을 하기 위한 형식을 정의합니다. HTTP와 유사하게 간단히 정의되어 해석하기 편한 프로토콜이라고 할 수 있습니다. 일반적으로 웹소켓 위에서 사용된다는 특징도 갖고 있습니다.

 

프레임 구조

STOMP는 프레임 기반 프로토콜로서 명령, 헤더, 바디로 구성되어 있습니다. 

  • 자주 사용되는 명령은 CONNECT, SEND, SUBSCRIBE, DISCONNECT 등이 있습니다.
  • 헤더와 바디는 빈 라인으로 구분하며, 바디의 끝은 NULL 문자로 설정합니다.

 

 

참고

profile

SY 개발일지

@SY 키키

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!