이번 포스팅에서는 실시간 데이터 통신이 필요한 경우와 이를 구현하기 위한 웹소켓(WebSocket)에 대해서 알아보자.
실시간 데이터 통신이 필요한 경우에는 어떤 상황이 있을까?
- 카카오톡같은 메신저 및 채팅 애플리케이션
- 코인,주식 및 금융 거래 플랫폼
- Figma와 같은 협업도구
- 실시간 위치 추적 서비스
- ....
대표적으로 위와같은 서비스에서 실시간 데이터 통신을 이용한다.
위와 같은 서비스들은 데이터의 최신화가 굉장히 중요하므로 실시간으로 데이터를 주고받아야 해당 서비스의 요지가 되는 기능을 사용자에게 제공할 수 있다.
즉, 클라이언트에서 서버로 따로 요청을 보내지 않고도 업데이트 된 데이터를 클라이언트에게 보여줘야 하는 것이다.
그럼 실시간으로 데이터 통신을 하는 방법에는 무엇이 있을까?
우리가 흔히 사용하는 HTTP통신은 클라이언트가 요청을 보낼 때만 서버가 해당 요청에 응답하는 단방향 통신이다. 클라이언트가 서버로부터 응답을 받은 후 해당 통신은 바로 연결을 종료한다.
결국, 클라이언트가 요청을 보낼 때만 서버가 응답을 하는 방식이므로 데이터가 오직 한 방향으로만 흐르게 된다. 즉, 서버가 클라이언트로 요청을 하는건 불가능하다.
따라서, 이러한 HTTP통신은 실시간 연결이 아닌, 필요한 경우에만 서버로 접근하는 콘텐츠 위주의 데이터를 사용할 때 용이하게 된다.
다만 HTTP통신을 통해 실시간 데이터 통신을 구현할 수 있는 방식이 있는데 해당 방식이 바로 Polling방식이다.
Polling방식은 서버로 요청을 주기적으로 보내서 서버가 보내주는 응답을 받는 방식이다.
다만, 해당 방식은 클라이언트가 계속적으로 요청을 하기 때문에 클라이언트가 많아지게 된다면 서버의 부담은 배로 증가하게 된다.
또한, 불필요한 오버헤드가 발생하여 효율적이지 않은 자원소모가 발생한다.
Polling방식과 유사하게 동작하는 방식이다.
다만, Polling방식과는 달리 Long Polling방식은 서버가 새로운 데이터가 생길 때까지 클라이언트의 요청을 대기시킨다. 이에 데이터가 준비되면 서버가 응답을 보내고, 클라이언트는 응답을 받은 후 즉시 새로운 요청을 보내는 방식이다.
다만, 해당 방법은 서버에서 대기 시간이 길어질 경우, 클라이언트의 요청이 Timeout될 수 있다.
또한, Polling방식과 마찬가지로 많은 클라이언트가 요청을 보낸다면, 서버가 대량의 대기 요청을 처리해야 하므로 부하가 증가할 수 있다.
웹소켓(WebSocket)방식은 HTTP통신이 아닌 TCP로 인한 통신이다. 단방향 통신이 아닌 양방향 통신이다. 클라이언트와 서버 간의 연결을 유지하여, 데이터가 필요할 때마다 양방향으로 실시간 데이터를 주고받을 수 있다.
다만, 구현이 상대적으로 복잡하며, 서버와 클라이언트가 모두 웹소켓을 지원해야 한다.
또한, 연결이 지속적으로 유지되므로, 대규모 연결을 관리하는 데 있어서 서버의 자원을 많이 소모할 수 있다.
클라이언트가 서버로부터 실시간 업데이트를 받을 수 있는 하는 방식이다. 클라이언트가 서버에 연결을 유지하면서 서버에서 전송하는 데이터를 실시간으로 수신할 수 있게 해준다.
다만, 해당 방식은 단방향 통신이므로 클라이언트에서 서버로의 데이터 전송이 불가하고 클라이언트가 접속을 종료하면 서버에서 감지하기 어렵다.
또한, Polling방식에 비해 실시간성이 좋지만, 웹소켓(WebSocket)에 비해 약간의 지연이 발생할 수 있다.
웹소켓(WebSocket)은 ws
프로토콜을 기반으로 클라이언트와 서버 사이제 지속적인 양방향 연결연결을 만들어 주는 기술이다.
즉, 클라이언트 혹은 서버에서만 요청을 보내는 단방향 통신이 아닌 양쪽에서 요청을 보낼 수 있는 양방향 통신인 것이다.
그럼 웹소켓은 어떤 방식으로 동작할까?
웹 소켓의 동작 과정은 크게 3가지로 나눌 수 있다.
빨간색 박스에 해당하는 Opening Handshake
노란색 박스에 해당하는 Data Transfer
보라색 박스에 해당하는 Closing Handshake
Opening Handshake
와 Closing Handshake
는 일반적인 HTTP TCP통신의 과정 중 하나이다.
접속 요청은 HTTP로 한 뒤, 웹소켓(ws) 프로토콜로 변경된다.
웹소켓 프로토콜로 변경되기 위한 Request HTTP
헤더는 아래처럼 구성되어 있다.
GET /chat HTTP/1.1
Host: localhost:3000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://localhost:9000
GET /chat HTTP/1.1
웹소켓의 통신 요청에서 HTTP버전은 1.1이상이어야 하고 GET메서드를 사용해야 한다.
Connection
현재의 전송이 완료된 후 네트워크 접속을 유지할 것인가에 대한 정보
웹소켓 요청시에는 반드시 Upgrade라는 값을 가진다.
Upgrade와 마찬가지로 이 값이 없거나 다른 값이면 웹소켓 접속을 중지시킨다.
Upgrade
프로토콜을 전환하기 위해 사용하는 헤더
웹소켓 요청시에는 Connection
에 Websocket
이라는 값을 가지며 이 값이 없거나 다른 값이면 cross-protocol attck 이라고 간주하며 웹 소켓 접속을 중지시킨다.
cross-protocol attack은 다른 프로토콜을 혼합하여 사용하는걸 의미한다. 즉 악의적인 프로토콜을 사용하여 의도적으로 여러 프로토콜을 혼합하여 시스템을 공격하는 방법이다.
Sec-WebSocket-Key
웹소켓 프로토콜의 보안을 위해 사용되는 키이다.
이 키는 클라이언트가 서버로 보내며, 서버는 이를 사용하여 웹소켓 연결의 보안성을 확인한다.
Sec-Websocket-Protocol
클라이언트가 사용하려는 하위 프로토콜을 지정하는 헤더이다.
Sec-WebSocket-Version
클라이언트가 사용하려는 웹소켓 프로토콜의 버전을 정의하는 헤더이다.
Origin: http://localhost:9000
CORS정책으로 만들어진 헤더이다.
웹소켓 연결의 요청이 어떤 원본에서 시작되었는지를 정의하는 헤더이다.
즉, 서버에서 허용한 클라이언트의 요청만 접근하게 하려고 만든 헤더인 것이다.
Cross-Site Websocket Hijacking을 방지하기 위한 헤더이기도 하다.
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
HTTP/1.1 101 Switching Protocols
Sec-WebSocket-Accept
Sec-WebSocket-Accept
에 유니크 아이디를 더해서 SHA-1로 해싱한 후 base64로 인코딩한 결과이다.Opening Handshake
에서 승인이 나면, 웹소켓 프로토콜로 노란색 박스 부분인 Data Transfer이 진행된다.
여기서 데이터는 메세지라는 단위로 전달된다.
메세지는 여러 프레임이 모여서 구성되는 하나의 논리적인 메세지 단위이다.
프레임은 통신에서 가장 작은 단위의 데이터이다.
패킷의 개념과 혼동이 올 수 있지만 정확히는 다른 개념이다.
패킷은 네트워크 통신 과정에서 가장 작은 단위의 데이터를 뜻하고, 프레임은 데이터 링크계층(이더넷)에서 주고 받는 가장 작은 단위를 의미한다.
Closing Handshake
는 보라색 박스 부분에 해당하며 클라이언트와 서버 간의 웹소켓 연결을 정상적으로 종료하기 위한 절차이다. 즉, 웹소켓 연결을 종료할 때 발생하며, 클라이언트와 서버 간의 데이터 통신을 중단한다.
위에 정리한 웹소켓 내용만 보면 동작방식도 굉장히 복잡해보이고 웹소켓을 코드로 구현하기 전에 두려워질 수 있다. 하지만 현재 웹소켓을 단순한 설정만으로 이용할 수 있는 패키지가 다양하게 있다.
다음 편에서는 위 패키지 중에 Socket.io
패키지를 통해 실시간 채팅과, 그 외의 실시간 통신 방법인 Polling
방식으로의 실시간 채팅을 NextJS, Express로 구현해보고 둘의 차이점에 대해서 자세하게 알아보도록 하자!
https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
https://medium.com/@cndgml707/websocket%EA%B3%BC-stomp-a7751a508668