Spring에서 Websocket 사용하기(+ Websocket 테스트)

개발하는 구황작물·2024년 4월 8일
0
post-thumbnail

Websocket

HTML5 표준 기술로 HTTP 환경에서 클라이언트 - 서버 간 하나의 TCP 연결을 통해 실시간으로 전이중 통신을 가능하게 하는 프로토콜

전이중 통신은 양방향으로 송수신이 가능한 것을 뜻한다.

websocket 이전에 활용한 방법들

HTTP Polling

주기적으로 클라이언트가 서버로 HTTP 요청을 보내면, 즉시 응답을 받는 방식
클라이언트가 많아지면 서버의 부담이 급증하고, HTTP 오버헤드(Http Header가 커지는 문제)가 발생한다.

원하는 대상을 찾기 위해 헤더에 정보를 추가하면 정보 전송에 신뢰성은 높아지나 TPS도 같이 늘어남

HTTP Long-Polling

HTTP Polling과 비슷하나 즉시 응답이 돌아오는 방식이 아닌 클라이언트가 요청을 보내면 서버는 대기하다가 서버에서 클라이언트에게 전달할 이벤트가 있으면 그 순간 요청을 전달하고 연결을 종료하는 방식이다. 클라이언트는 다시 요청을 전송하여 서버의 다음 이벤트를 기다린다.

일반 Polling 보단 서버의 부담은 줄어드나, 이벤트 간의 간격이 좁으면 Polling과 별 차이가 없어진다.

HTTP Streaming

서버가 요청을 보내고 HTTP 연결을 끊지 않고 서버로부터 데이터를 수신하는 방법이다.

Websocket 진행 과정

최초 연결 요청 시 클라이언트에서 HTTP를 통해 서버에게 연결 요청(Handshake)을 한다.

Request Header

GET ws://localhost:8080/ws/init HTTP/1.1
Host: localhost:8080
Connection: Upgrade
Upgrade: websocket
Origin: https://localhost:9000
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: YTwYIE6qJlfakye/Q62cVQ==
  • Connection: Upgrade : 클라이언트 측에서 프로토콜을 바꾸고 싶다는 의미
  • Upgrade: websocket : 클라이언트 측에서 요청한 프로토콜이 websocket라는 뜻
  • Origin : 클라이언트 오리진을 나타낸다. 서버는 Origin 헤더를 보고 어떤 웹 사이트와 통신을 하는지 결정하기 때문에 매우 종요한 역할을 한다. (CORS 정책으로 만들어진 헤더)
  • Sec-Websocket-Version : 웹 소켓 프로토콜 버전
  • Sec-WebSocket-Key : 보안을 위해 브라우저에서 생성한 키, 서버가 웹 소켓 프로토콜을 지원하는지 확인하는데 사용된다.

서버는 아래와 같이 응답한다.

HTTP/1.1 101
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: 7orViGJ4sANNTn3FujUUrQKRlkA=\
Date: Mon, 08 Apr 2024 05:15:18 GMT
  • HTTP/1.1 101 : http에서 ws로 프로토콜 전환이 승인되었음을 뜻한다.
  • Sec-WebSocket-Accept : 요청 헤더의 Sec-WebSocket-Key에 특정 값을 추가한 후, SHA-1로 해싱한 후 base64로 인코딩한 결과, 이 값으로 클라이언트는 정상적인 핸드쉐이크 과정을 검증한다.

위의 과정이 끝나면 그때서야 본격적인 데이터 통신이 시작된다. 이때 데이터는 프레임(Frame) 단위로 이루어진다.

또한 서버와 클라이언트는 상대방에게 ping 패킷을 보내고, ping을 수신한 측은 상대방에게 빨리 pong 패킷을 전송하여 생존신고를 주기적으로 한다. 이를 Heartbeat라고 한다.

이제 연결을 종료 하려 하면 클라이언트/서버 누구든 연결을 종료할 수 있다. 연결 종료를 원하는 측은 Close Frame을 상대한테 전달한다.

Stomp

Simple Text Oriented Messaging Protocol의 약자로 텍스트 기반 메시지 프로토콜이다.

websocket 자체는 메시지를 주고받는 형식이 정해져 있지 않다.

그래서 서브 프로토콜을 통해 메시지의 형태를 사용하는데 이때 사용되는것이 Stomp이다.

Stomp는 Command, Header, Body로 구성되며 아래와 같은 구조를 가진다.

COMMAND
header1:value1
header2:value2

Body^@
  • COMMAND : COMMAND/SEND/SUBSCRIBE 등의 명령을 통해 메시지의 동작을 정의
  • Header : 메시지의 수신 대상과 메시지에 대한 정보를 설명
  • Body : 데이터(payload) 가 포함된다.

Stomp는 pub/sub 방식으로 작동한다.

  1. url로 topic 지정하여 메시지 전송(Publishing prefix를 /app로, Subscription prefix를 /topic로 사용)
  2. 해당 메시지가 message broker(broker channel)로 도달
  3. message broker는 해당 토픽에 대응하는 response channel로 route
  4. receiver가 메시지 수신

Spring에서의 Websocket 활용법

  1. gradle
implementation 'org.springframework.boot:spring-boot-starter-websocket'
  1. websocket config
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws/init") //web socket connection이 최초로 이루어지는 곳(handshake)
                .setAllowedOriginPatterns("*")
                .withSockJS(); 
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // @Controller 객체의 @MessageMapping 메서드로 라우팅, 클라이언트가 서버로 메시지 보낼 URL 접두사(pub)
        registry.setApplicationDestinationPrefixes("/app");
        // /topic 로 클라이언트로 메시지 전달(sub)
        registry.enableSimpleBroker("/topic");
    }
}
  1. controller
@Slf4j
@RequiredArgsConstructor
@Controller
public class StompController {

    // test connection

    @MessageMapping("/example") // 클라이언트가 /app/example로 메시지 전송
    @SendTo("/topic/messages")  // 서버가 /topic/messages로 메시지 전달
    public String example(String str) {
        return "success";
    }
}

Websocket Test

https://apic.app/online/#/tester 로 웹 소켓 테스트를 할 수 있다.

여러모로 불편한 점이 많았으나 subscribe, send 테스트가 가능한 테스트 툴은 내가 찾은 것 중에서 유일했다.
(postman은 connection test 만 가능)

참고로 WebSocketConfig.java에서 .withSockJS(); 를 제외해야만 테스트가 작동하였다.(...)

profile
어쩌다보니 개발하게 된 구황작물

0개의 댓글