정의
웹 소켓은 단순한 API로 구성되어 있어, 서버와 클라이언트 간에 Socket Connection을 유지해서 언제든 양방향 대화형 통신 세션을 열 수 있게 해주는 기술이다. 웹 소켓을 사용하면 서버에 메시지를 보내고 서버에 응답을 Polling할 필요 없이 이벤트 기반의 응답을 받을 수 있다.
일단 양방향 대화형 세션이 맺어지면 사용자와 서버 중 어느 한 쪽이 연결을 끊지 않는 한 지속된다.
웹소켓 통신 시 url 은 ws://example.com:port 가 된다.
웹 소켓이 나오게 된 배경은 HTTP의 단점을 보완한다는 목적이 있다.
기존 HTTP는 단방향 통신으로서 클라이언트에서 서버로 Request를 보내면 서버는 클라이언트로 Response를 보내는 방식으로 통신을 종료한다. (HTTP는 기본적으로 Stateless이고 상태를 저장하지 않음)
이는 사용자와 서버 간 통신을 할 경우 접속을 유지하지 않고, 한 번에 한 방향으로만 통신이 가능하다는 것을 의미한다.
이를 half-duplex 라고 한다.
하지만 웹 소켓은 양방향 통신으로 연결이 이루어지면 클라이언트가 요청하지 않아도 데이터가 서버로부터 수신될 수 있다.
HTTP 처럼 별도의 요청을 보내고 응답을 하지 않아도 사용자와 클라이언트가 하나의 TCP 통신 접속에 전이중 통신 채널을 제공해 상호 간에 데이터를 수신할 수 있다는 말이다.
이를 duplex라고 한다.
HTTP의 경우 헤더 데이터가 크기 때문에 네트워크에 과부하가 오고 애플리케이션의 반응성이 낮아진다.
채팅, 문의, 알림, 주식 트레이딩 과 같이 실시간성과 사용자와 서버 간 통신 유지가 긴밀히 필요한 애플리케이션의 경우 HTTP 통신 만으로 구현하기란 근본적인 문제가 있다.
이러한 문제점들을 웹 소켓으로 극복한다.
웹 소켓이 등장하기 이전에는?
웹 소켓이 존재하기 전에 HTTP 는 Polling 이나 Long Polling, Streaming 등의 방식으로 실시간 성을 제공하려 했다. (AJAX로도 해결하려고 했으나 AJAX로 어떻게 해결하려고 했는지는 나중에 정리 해보도록 함)
polling
클라이언트가 평범한 HTTP Request를 서버로 계속 요청해 이벤트 내용을 전달받는 방식. 가장 쉬운 방법이지만 클라이언트가 지속적으로 Request를 요청하기 때문에 클라이언트의 수가 많아지면 서버의 부담이 급증한다. HTTP Request Connection을 맺고 끊는 것 자체가 부담이 많은 방식이고, 클라이언트에서 실시간 정도의 빠른 응답을 기대하기 어렵다.
Long polling
클라이언트에서 서버로 일단 HTTP Request를 요청한다. 이 상태로 계속 기다리다가 서버에서 해당 클라이언트로 전달할 이벤트가 있다면 그 순간 Response 메세지를 전달하며 연결이 종료된다. 곧이어 클라이언트가 다시 HTTP Request를 요청해 서버의 다음 이벤트를 기다리는 방식. polling보다 서버의 부당믄 적으나, 클라이언트로 보내는 이벤트들의 시간 간격이 좁다면 polling과 별 차이 없게 되며, 다수의 클라이언트에게 동시에 이벤트가 발생될 경우 서버의 부담이 급증한다.
Streaming
Long Polling과 마찬가지로 클라이언트 -> 서버로 HTTP Request를 요청한다. 서버 -> 클라이언트로 이벤트를 전달할 때 해당 요청을 해제하지 않고 필요한 메시지만 보내기(Flush)를 반복하는 방식이다. Long Polling과 비교하여 서버에 메세지를 보내지 않고도 다시 HTTP Request 연결을 하지 않아도 되어 부담이 경감 된다고 한다.
웹 소켓 동작 원리
- 브라우저에서 소켓 통신을 이용하기 위해서 소켓 통신이 가능한지 확인하는 핸드셰이크 과정이 필요하다.
- 핸드 셰이크 과정은 이러하다. 브라우저에서 HTTP 통신을 이용해 서버에 소켓 통신이 가능한지 요청을 보낸다. 이 때 헤더에 소켓을 사용하기 위한 Upgrade, Connection, WebSocket에 관한 정보를 함께 전송한다.
- 서버에서 웹 소켓 통신이 가능하다면 서버에서 웹 소켓 통신이 가능하다는 101 상태의 응답을 보낸다. 이 때 서버에서는 클라이언트에서 받은 'Sec-WebSocket-Key' 키 값에 문자를 더한 뒤 암호화하여 'Sec-WebSocket-Accept'로 클라이언트로 응답한다.
- 이후에는 HTTP Upgrade 헤더를 이용해 웹소켓 프로토콜로 변경하고 ('ws' 또는 'wss') 양방향 통신을 진행한다.
- 양방향 통신 연결이 맺어지면 어느 한쪽에서 연결을 끊지 않는 이상 영구적인 동일한 채널이 맺어진다.
왜 사용하는가?
- 실시간 양방향 데이터 통신이 필요한 경우
- 많은 수의 동시 접속자를 수용해야 하는 경우
- 브라우저에서 TCP 기반의 통신으로 확장해야 하는 경우
- 개발자에게 사용하기 쉬운 API가 필요할 경우
- 클라우드 환경이나 웹을 넘어 SOA(Service Oriented Architecture) 로 확장해야 하는 경우
웹소켓 서버에서의 보안 이슈
아래는 https://yozm.wishket.com/magazine/detail/1911/ 에서 발췌한 사례입니다.
- CORS 에러
- 메신저 기능을 위해서 socket.io 라이브러리를 활용함.
- 백엔드 파트에 API 요청을 처리하는 메인 서버와 메시지를 처리하는 웹 소켓 서버를 따로 두고 별도의 포트 할당
- 클라이언트가 접속하는 웹 서버와 요청을 처리하는 백엔드 서버에 도메인과 인증서를 추가하고 HTTPS 통신
- CORS 에러 발생과 일부 요청이 도달하지 못하는 문제 발생
- 양쪽에서 도메인을 붙인 후 서로의 도메인을 허용하도록 했는데 오류가 발생함
- 문제는 클라이언트 - 메인 서버 가 아니라 메인 서버 - 웹 소켓 서버에서 발생함
- 왜? 별도의 포트로 구성했기 때문에 별도의 오리진이라고 인식했기 때문에
- 해결은 프록시 서버 또는 추가적인 API를 구성하면 됨
- SSL/TLS 에러
- 클라이언트 웹 서버와 웹 소켓 서버가 통신할 때 클라이언트 웹서버가 HTTPS로 보호받고 있기 때문에 웹 소켓 통신도 WSS로 보호 받아야했음
- socket.io의 경우 엔드 포인트의 HTTP URL을 받아서 폴링을 통해 연결을 수립한 후, 웹 소켓 프로토콜이 지원되는 지 확인 해 WS 프로토콜로 전환함
- SSL/TLS 암호화 처리가 되어있는 사이트끼리는 HTTPS 프로토콜에 의한 통신이 가능하고, 이들 사이의 웹소켓 통신 역시 암호화 됨
- 하여, 웹 서버와 웹 소켓 서버가 통신을 하기 위해서는 먼저 HTTPS 통신으로 연결을 수립하고 이후 웹 소켓 업그레이드 핸드셰이크를 통해 WSS 프로토콜로 업그레이드 됨.
- 만약 한 쪽이 SSL/TLS 인증서를 가지고 있지 않으면 인증서 오류로 인해 연결이 수립될 수 없다.
- 웹 소켓 서버와 메인 서버의 포트를 분리하고자 한다면 두 서버는 별도의 인증서를 발급 받아야 하고, 두 서버는 서로 직접 통신할 수 없기 때문에 프록시 서버나 API를 거쳐야 한다.
문제 해결
글 작성자 분이 이슈를 파악했고, 단기적인 해결과 장기적인 해결로 분리해 진행하기로 했다고 함.
- 장기적인 해결
- 클라이언트 웹 서버와 메인 서버, 웹 소켓 서버에 대한 요청을 모두 NGINX 에서 받아 각각의 서버로 라우팅 시켜주는 리버스 프록시 방식
- 일단, 백엔드 레포지토리에 섞여 있는 메인 서버와 웹소켓 서버의 소스코드를 별도릐 파일로 분리 해 공유되는 전역 컨텍스트를 없앤다. 이로써 메인 서버, 웹소켓 서버는 모두 독립적으로 실행된다.
- 도메인과 인증서는 NGINX에 연결한다.
- NGINX가 받은 80 요청을 HTTPS용 443 포트로 리다이렉트하고, 요청받은 url을 파싱하여 목적지를 확인한다. 이후 클라이언트 웹서버, 메인 서버, 웹 소켓 서버 각각으로 분리해서 라우팅하도록 한다.
- 단기적인 해결
- EC2 인스턴스 두 개에 각각 메인 서버와 웹소켓 서버를 설치하고, 로드 밸런서를 연결했다.
- ACM 인증서를 발급 받아 로드 밸런서에 연결하고, 로드밸런서에서 HTTPS로 보내준 요청을 EC2 인스턴스로 포워딩
결과적으로 서버 사이를 중개해 줄 프록시 서버를 마련하고 메인 서버와 소켓 서버 간 통신도 우선 차단하고 클라이언트가 API 요청에 대한 응답을 받은 후, 다시 소켓 서버에 직접 요청을 보내는 방식으로 변경됐다.
장단점
- 장점 :
- 실시간 전이중 양방향 데이터 전송
- 데이터 전송 오버헤드가 작다.
HTTP는 헤더의 데이터가 많아서 네트워크 과부하, 서버 과부하 발생 가능성 높아지는데 웹소켓은 아님 - 교차 플랫폼 호환성
- 데이터를 폴링할 필요 없어 신속하게 대응해야 할 때 좋음
- 단점 :
- 오디오 및 비디오 데이터 스트리밍에 최적화되어 있지 않음
- 연결이 종료될 때 자동으로 복구되지 않음
- 프록시 서버가 있는 기업 네트워크 등 특정 환경에서 웹 소켓 연결을 차단할 수 있음
- Stateful 방식으로서 여러 웹 소켓 서버로 구성된 대규모 시스템에서 사용하기 어렵다.
내 생각
웹 소켓 게임에서 많이 사용한다고 들었지 정확히 어떤 통신을 하는 지 몰랐는데 이번 기회에 알게 되어서 좋았다.
실제로 어떻게 사용이 되는지와 오류 해결 방법까지 찾게 되어서 정리 해보니 도움이 많이 된 듯 하다.
[참조] :
https://www.wallarm.com/what/a-simple-explanation-of-what-a-websocket-is
https://code-lab1.tistory.com/300
https://yozm.wishket.com/magazine/detail/1911/
https://yuricoding.tistory.com/134
'Network' 카테고리의 다른 글
Domain 에러 확인 및 대응 (0) | 2024.05.31 |
---|---|
Nginx에서 Upstream의 의미 (0) | 2024.05.22 |
localhost 와 127.0.0.1 차이 (0) | 2023.08.01 |
NAT와 NAPT란? (0) | 2023.07.06 |
HTTP 상태 코드란? (0) | 2023.01.02 |