티스토리 뷰

개발일지

웹소켓 사용하기

땅속 디그다 2022. 11. 22. 14:53

관리자와 직원 사이에 양방향 통신을 할 일이 존재했고 polling 방식과 websocket 방식 사이에 선택을 해야하는 것이 필요했습니다.

 

하지만 다음과 같은 이유로 웹소켓을 통해 양방향 통신을 제공했습니다.

 

1. 관리자 - 직원 간의 통신하는데 있어 실시간 통신이 필요했다.

2. polling 방식을 통해서 구현시 데이터 조회시에 select 절의 사용이 꽤나 큰 비용이였다. (웹소켓은 패킷 데이터에 대한 내용을 db에 insert 하기만 하면 됐고, 이전 문의 내역이나 공지사항 같은 내용은 초기 연결 시에 db에서 select 하게 되므로 그 후의 내용들은 select 가 발생하지 않는다.)

3. polling 은 매번 커넥션을 맺는 비용이 드는 반면 웹소켓의 경우 매번 커넥션을 맺지 않고 유지를 하면 된다.

4. 웹소켓을 도입해도 추가로 port 내용 작성이나, cors 문제를 신경쓸 필요가 없다. 톰캣에서도 웹소켓을 지원하기 때문이다. 즉, 따로 어플리케이션 level의 코드 부분을 추가 할 뿐이지 따로 인프라 구축에 대한 추가 cost 가 발생하지 않는다.

5. 웹소켓이라는 기술에 대해 도전을 하고 싶었다.

 

다음과 같은 3가지 이유로 프로젝트에 웹소켓을 도입하게 되었습니다.

 

다만 결과로써 아쉬웠던 점은 채팅방의 개념이 아닌 관리자 - 직원간의 소통에 대한 실시간 통신이였기 때문에 서브 프로토콜을 사용한 sub - pub 구조가 필요하지 않아 따로 서브 프로토콜을 작성하지 않고 데이터 패킷 형식을 front-end 와 맞추어 진행하였습니다.

 

웹소켓을 먼저 알아보겠습니다.

 

웹소켓이란?

 

기존의 단방향 HTTP 프로토콜과 호환되어 양방향 통신을 제공하기 위해 개발된 프로토콜 입니다. http 프로토콜과 포트 공유가 가능하며 접속까지는 HTTP 프로토콜을 이용하고 이후 통신은 자체적인 WebSocket 프로토콜로 통신하게 됩니다.

 

과정은 어떻게 되는가?

1. tcp/ip 연결 (3 way handshake)

2. 웹소켓 열기 handshaking 과정

3. 웹소켓으로 데이터 송 수신

 

2번을 자세하게 살펴보도록 하겠습니다.

웹소켓을 여는 과정에서 클라이언트가 http 요청을 하게 됩니다. 하지만 반드시 Upgrade, Connection, websocket-key 및 version 에 대한 헤더, 즉 웹소켓과 관련된 헤더들이 포함 되어야 합니다.

 

Upgrade 의 경우 클라이언트 쪽에서 서버에게 어떠한 프로토콜로 전환하고 싶은지를 명시하는 헤더입니다.

Connection 의 경우 http 연결에서 프로토콜을 변경하기 위해서는 반드시 "upgrade"로 명시를 해야합니다.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade

 

Upgrade - HTTP | MDN

The HTTP 1.1 (only) Upgrade header can be used to upgrade an already established client/server connection to a different protocol (over the same transport protocol). For example, it can be used by a client to upgrade a connection from HTTP 1.1 to HTTP 2.0,

developer.mozilla.org

 

전체적으로 간략화 해서 살펴보겠습니다.

 

HandShake Request

GET /chat HTTP/1.1
Host: server.gorany.org
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: 임의의 키값
Origin: http://localhost:8080
Sec-WebSocket-Protocol: 서브 프로토콜 대표적으로 stomp 존재
Sec-WebSocket-Version: 13
 Header Name   div   Description 
GET Require 요청 명령어는 GET을 사용해야 하며, HTTP 버전은 1.1 이상이어야 한다.
Host Require 웹소켓 서버의 주소
Upgrade Require WebSocket이라는 단어를 사용해야 한다. 대소문자는 구분 X
Connection Require Upgrade라는 단어를 사용해야 한다. 대소문자는 구분 X
Sec-WebSocket-Key Require 길이가 16Byte인 임의로 선택된 숫자를 base64 인코딩한 값 이다.
Origin Require 클라이언트로 웹 브라우저를 사용하는 경우 필수항목으로, 클라이언트의 주소
Sec-WebSocket-Version Require 13을 사용한다.
Sec-WebSocket-Protocol Option 클라이언트가 사용하고 싶은 하위 프로토콜 이름을 명시한다.
Sec-WebSocket-Extensions Option 클라이언트가 사용하고 싶은 추가 옵션을 기술한다.

 

HandShake Response

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 클라이언트에서 넘겨준 키값을 계산한 값
 Header Name   div   Description 
HTTP Require HTTP 버전은 1.1이며, 클라이언트로부터의 요청이 이상 없는 경우 101을 상태코드로 사용한다.
Upgrade Require WebSocket이라는 단어를 사용해야 한다. 대소문자는 구분 X
Connection Require Upgrade라는 단어를 사용해야 한다. 대소문자는 구분 X
Sec-WebSocket-Accept Require 클라이언트로부터 받은 Sec-WebSocket-Key를 사용하여 계산된 값이다.
Sec-WebSocket-Protocol Option 서버에서 서비스하는 하위 프로토콜을 명시한다. 클라이언트가 요청하지 않는 하위 프로토콜을 명시하면 HandShake는 실패한다.
Sec-WebSocket-Extensions Option 서버가 사용하는 추가 옵션을 기술한다. 클라이언트가 요청하지 않는 추가 옵션을 명시하면 HandShake는 실패한다.

 

웹소켓의 송수신 데이터 프레임 구조는 다음과 같다

https://developer.mozilla.org/ko/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#step_2_%EB%8D%B0%EC%9D%B4%ED%84%B0_%ED%94%84%EB%A0%88%EC%9E%84_%EA%B5%90%ED%99%98

 

웹소켓 서버 작성하기 - Web API | MDN

웹 소켓 서버는 특정한 프로토콜을 따르는 서버의 임의 포트를 리스닝하고 있는 TCP 어플리케이션입니다. 사용자 서버를 만드는 작업은 두려운 일일수도 있습니다. 하지만, 당신이 선택한 플랫

developer.mozilla.org

 

https://docs.spring.io/spring-framework/docs/4.2.3.RELEASE/spring-framework-reference/html/websocket.html

 

25. WebSocket Support

This part of the reference documentation covers Spring Framework’s support for WebSocket-style messaging in web applications including use of STOMP as an application level WebSocket sub-protocol. Section 25.1, “Introduction” establishes a frame of m

docs.spring.io

https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket

 

Web on Servlet Stack

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more com

docs.spring.io

공식 사이트를 통해 추가로 알아보겠습니다.

 

공식 사이트에도 나와 있듯이 proxy server를 사용하여 WebSocket server 가 webServer 뒤에서 동작한다면 redirect 시에 upgrade request 를 포함해주어 야합니다. 실제로도 spring boot 프로젝트 적용시에 문제가 되었던 부분입니다.

 

Note that, if a WebSocket server is running behind a web server (e.g. nginx), you likely need to configure it to pass WebSocket upgrade requests on to the WebSocket server. Likewise, if the application runs in a cloud environment, check the instructions of the cloud provider related to WebSocket support.

 

WebSocket 을 사용할 때 http 를 사용하기 때문에 Spring MVC 에 의존적인 코드를 사용해야 할 것 같지만 Spring 은 WebSocketHttpRequestHandler 를 통해 Spring MVC 에 의존적인 코드를 사용하지 않게 해줍니다.

 

의구심이 드는 것은 톰켓이 어떻게 웹소켓을 지원하는지에 대한 부분이 남았습니다.

톰켓이 단순하게 서블릿 표준만 지원하는 서블릿 컨테이너인 줄 알았는데 따로 웹소켓 컨테이너 기능이 포함되어 있었습니다.

자세한 부분은 알 수 없었으나 다음과 같은 클래스로 대략적인 맥락이 이론과 동일했음을 알 수 있었습니다.

 

 

공식 사이트에서도 잘 찾을 수 없었으나 웹소켓을 직접 만드는 예제를 작성한 블로그가 있었고, 그냥 단순하게 소켓을 유지하는 것으로 보입니다.

https://nowonbun.tistory.com/285

 

[Java] Servlet에서 사용하는 웹 소켓 (WebSocket)

안녕하세요. 명월입니다. 이 글은 Java의 Servlet에서 사용하는 웹 소켓 (WebSocket) 에 대한 글입니다. 보통의 웹 환경은 브라우저(클라이언트)에서 웹 서버에 Html 문서를 요청하면, 웹 서버는 Html를 작

nowonbun.tistory.com

 

결론적으로는 웹소켓에 대한 upgrade, 해석에 대한 내용들은 모두 톰켓에서 도맡는 것을 확인 할 수 있었고 일반 웹서버와 포트를 공유하는 것이 가능합니다. url 의 protocol prefix 로 어느 컨테이너에서 처리할 것인지 선택하는 것으로 보입니다. (이부분은 확실치 않습니다.)

또한 톰켓의 웹소켓 컨테이너의 경우 WsSession 을 통해 연결을 관리하게 되는 것으로 보입니다.

 

spring 에서는 이러한 웹소켓을 어떻게 지원하게 될까요? 예제를 통해 알아보겠습니다.

 

프로젝트에서 또한 간단한 양방향 통신만을 사용하려 했기 때문에 서브프로토콜을 사용하지 않고 간단하게 양방향 통신을 다루어 보겠습니다.

 

spring 에서 웹소켓을 활용할 때에는 단순하게

1. 웹소켓 핸드쉐이킹(upgrade 과정) 및 핸드쉐이킹 전 interceptor

2. 클라이언트에서 데이터프레임이 들어왔을 때의 handling 과정

3. cors

4. WebSocketSeesion 의 관리

이 4가지로 볼 수 있습니다.

WebSocketSession 을 통해서 클라이언트를 식별하고 메시지를 전송하기 때문에 Set으로써 관리하는 편이 좋아보입니다.

 

 

 

웹소켓을 공부하면서 통신에 대한 내용들이 너무 추상화가 잘되어 있어 low level 쪽에서의 부분들에 대해 많은 지식의 부족을 느꼈던 것 같습니다. 소켓 프로그래밍에 대한 공부의 필요성을 느끼며 포스팅을 마치겠습니다.

댓글
05-20 12:39
Total
Today
Yesterday
링크