-
웹소켓 애플리케이션을 위한 Spring Boot 구성개발 일지/Back-end 2023. 5. 17. 15:27반응형
웹소켓 애플리케이션의 대표적인 사례는 채팅이라고 할 수 있습니다.
하지만 그 외에도 현대 애플리케이션의 여러 웹 서비스 중에서는 특정 조건에 따라 서버에서 클라이언트(사용자)에게 서비스를 제공하기도 합니다.
(사용자가 서버에 서비스를 요청하지 않더라도요.)
우리 일상에서 보면 모바일 앱에서 푸시 알림을 떠올려볼 수 있겠죠.
웹 기술로 보면 "pub/sub"을 생각하면 될 것 같습니다.
일정 토픽(주제)에 대해 '구독'설정을 해두면, 서버에서 사용자 방향으로 '발행' 서비스를 수행할 수 있습니다.
자, 이제 Spring.io 의 튜토리얼을 참고해서 Spring Boot 프로젝트를 준비해 보겠습니다.
https://spring.io/guides/gs/messaging-stomp-websocket/
우선, 의존성을 추가해줘야 합니다.
저는 Gradle 빌드 툴을 사용할 것이기에 'build.gradle'에 의존성을 선언해두겠습니다.
Front-end는 Next.js 프로젝트로 구성할 예정이기에 Spring Boot 프로젝트('build.gradle')에는
'org.springframework.boot:spring-boot-starter-websocket'
'org.springframework.boot:spring-boot-starter-websocket' 만 추가하면 됩니다.
다음으로 '메시지' 관련 클래스와 컨트롤러를 생성하기 전에 구성 컴포넌트부터 만들어보겠습니다.
(튜토리얼 순서와 반대)
WebSocketCofing - Configure Spring for STOMP messaging
WebSocketConfig를 통해 웹소켓 및 STOMP 메세징을 사용할 수 있도록 설정합니다.
package com.example.messagingstompwebsocket; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/my-websocket").withSockJS(); } }
@Configuration 어노테이션을 통해 '스프링 구성 클래스'라는 것을 명시합니다.
@EnableWebSocketMessageBroker는 메시지 브로커가 지원하는 WebSocket 메시지 처리를 활성화합니다.
configureMessageBroker() 메서드는 WebSocketMessageBrokerConfigurer의 기본 메서드를 구현하여 메시지 브로커를 구성합니다.
간단한 메모리 기반 메시지 브로커가 인사말 메시지를 /topic 접두사가 있는 대상의 클라이언트로 다시 전달할 수 있도록 enableSimpleBroker()를 호출하여 시작합니다.
또한 @MessageMapping으로 어노테이션이 달린 메서드에 바인딩된 메시지의 '/app' prefix를 지정합니다.
registerStompEndpoints() 메서드는 '/my-websocket' end-point를 등록하여 WebSocket을 사용할 수 없는 경우 대체 전송을 사용할 수 있도록 SockJS 폴백 옵션을 활성화합니다.
다시 "채팅" 서비스를 생각해 볼까요.
해당 서비스에서는 2명의 사용자가 있습니다. (물론 그 이상이 될 수도 있죠.)
A라는 사람이 "안녕(hello)"라는 메시지를 보냅니다.
그러면 A의 메시지가 서버에 도달하고 서버는 특정 사용자(또는 채팅 방/채널에 브로드캐스트)에게 전달합니다.
앞서 표현한 것처럼 "구독/발행" 관점에서도 생각해 볼 수 있겠죠.
아래는 '메시지'를 처리하는 컨트롤러 코드입니다.
package com.example.messagingstompwebsocket; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; import org.springframework.web.util.HtmlUtils; @Controller public class GreetingController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(HelloMessage message) throws Exception { Thread.sleep(1000); // simulated delay return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); } }
쉽게 생각해 보시면 금방 이해하실 수 있습니다.
@MessageMapping("/hello") 어노테이션은 앞의 설정으로 인해 '사용자 A'가 보낸 "/app/hello" 요청에 해당됩니다.
이 요청에는 메시지가 담겨 있겠죠.
바로 greeting(HelloMessage message) 메소드의 인자로 들어옵니다.
그리고 Thread.sleep(1000)은 1초간 지연을 발생시켜서 웹소켓 통신에 대해 물리적으로 확인가능하게 만든 부분이죠.
그리고 Greeting 클래스에서 생성자를 통해 "Hello, " 텍스트와 함께 HelloMessage message 에 담겨 있는 name을 이어서 리턴합니다.
리턴한 응답은 @SendTo("/topic/greetings")로 전달됩니다.
그리고 바로 "/topic/greetings"가 사용자들(현재 예시 시나리오에서)이 구독하고 있는 API 입니다.
메시지 관련 객체 클래스
1. HelloMessage
package com.example.messagingstompwebsocket; public class HelloMessage { private String name; public HelloMessage() { } public HelloMessage(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
2. Greeting
package com.example.messagingstompwebsocket; public class Greeting { private String content; public Greeting() { } public Greeting(String content) { this.content = content; } public String getContent() { return content; } }
그럼 다음 포스트에서 클라이언트(사용자: 엔드유저) 웹소켓 구독 관련 부분을 살펴보도록 하겠습니다.
클라이언트의 경우 서버사이드인지 클라이언트사이드(CSR) 방식인지 잘 구분해서 구현해야 합니다.
Next.js의 경우 웹소켓을 지원하지 않는 것 같고 SSR(서버사이드 렌더링) 방식이라 SockJS가 먹히질 않는군요.
그래서 Next.js 프로젝트에서는 Socket.IO(서버사이드 라이브러리)를 활용해 구현해보겠습니다.
반응형'개발 일지 > Back-end' 카테고리의 다른 글
Spring MVC로 이미지/미디어 데이터 변환하기 (0) 2022.01.26 Spring boot 프로젝트 구조 잡기 (2) 2022.01.10 Stomp (The Simple Text Oriented Messaging Protocol) 번역 (0) 2021.12.13 Spring AMQP를 사용한 메시징 (0) 2021.12.02 Pub-Sub vs. Message Queues (0) 2021.12.02