implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.springframework.security:spring-security-messaging'
Spring Boot에서 Socket통신을 구현할 때 위 두 종속성을 자주 사용하는거 같아서 각 종속성의 역할을 알아보고자 한다.
Messaging Stomp Websocket을 참고하면 이해가 더 잘 될 것이다.
이 예약어는 implementation 'org.springframework.boot:spring-boot-starter-websocket' 종속성을 사용한 Socket 연결을 설정할 때 사용하는 주석이다.
@EnableWebSocketMessageBroker는 WebSocket 메시징 아키텍처에서 중요한 역할을 하는 설정이다. 이 설정은 메시지 브로커(Message Broker) 를 통해 실시간으로 메시지를 주고받을 수 있는 환경을 구축하는 데 사용된다. 만약 STOMP를 사용하지 않고 싶다면 @EnableWebSocket를 사용하면 된다.
예시로
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/ws"); // WebSocket 핸들러 등록
}
@Bean
public WebSocketHandler myHandler() {
return new MyWebSocketHandler(); // 메시지 처리 로직을 가진 핸들러 구현
}
}
주로 STOMP (Simple Text Oriented Messaging Protocol) 를 사용하는 메시지 통신을 처리하고, WebSocket 메시지가 어떤 경로로 전송되고, 어떤 방식으로 처리될지 결정한다. 즉, WebSocket 연결이 이루어진 후 실제 메시지들이 서버와 클라이언트 사이에서 어떻게 라우팅되는지를 관리한다.
STOMP 프로토콜 기반 통신에 대해 아래의 처리를 담당한다.
/app/chat 경로로 메시지를 보내면, 서버는 이 메시지를 처리하고 /topic/messages 구독자들에게 전송한다.@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue"); // 클라이언트 구독 가능 경로
config.setApplicationDestinationPrefixes("/app"); // 클라이언트가 서버에 메시지를 보낼 때 사용할 경로
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS(); // 클라이언트가 연결할 WebSocket 엔드포인트
}
}
package com.example.springstompchat.controller;
import com.example.springstompchat.model.ChatMessage;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
@MessageMapping("/chat")
@SendTo("/topic/messages")
public ChatMessage sendMessage(ChatMessage message) {
return message;
}
}
enableSimpleBroker("/topic", "/queue"): 메시지 브로커를 설정한다. 여기서 "/topic"과 "/queue"는 메시지가 전송되는 목적지다. 클라이언트는 이 경로를 구독해서 메시지를 받을 수 있다.setApplicationDestinationPrefixes("/app"): 클라이언트가 서버로 메시지를 보낼 때 사용할 경로를 설정한다. 클라이언트는 /app/chat 같은 경로로 메시지를 보낼 수 있으며, 서버에서 이 메시지를 처리한다.registerStompEndpoints: 클라이언트가 WebSocket에 연결하기 위한 엔드포인트를 설정한다. 예를 들어, 클라이언트는 /ws 경로로 연결을 시도할 수 있다./app/chat 경로로 메시지를 보내면, 서버는 이를 받아 처리한다./topic/messages와 같은 주제로 구독한 클라이언트에게 브로드캐스팅한다.이 설정은 메시지 브로커를 이용해 WebSocket 통신에서 메시지를 처리하고 라우팅하는 핵심적인 역할을 담당한다. 이때 보안적인 부분은 신경 쓰지 않으며, 오직 메시지의 경로와 라우팅 규칙만 처리한다.
@EnableWebSecurity 설정은 http 프로토콜 통신에서 보안설정을 한다면 @EnableWebSocketSecurity는 ws 프로토콜 통신에서의 보안설정을 담당한다.
연결된 후 WebSocket을 통해 주고받는 메시지에 대한 접근 권한을 제어한다. 예를 들어, 특정 사용자만 특정 경로의 메시지에 접근하도록 설정할 수 있다. 즉 STOMP 프로토콜 레벨에서의 권한 설정이 가능하다.
@Configuration
public class WebSocketSecurityConfig {
@Bean
public AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
messages
.nullDestMatcher().authenticated()
.simpSubscribeDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/app/**").hasRole("USER")
.simpSubscribeDestMatchers("/user/**", "/topic/friends/*").hasRole("USER")
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll()
.anyMessage().denyAll();
return messages.build();
}
}
해당 예제 코드는 WebSocket Security에서 제공하고 있다.
위 코드와 같이 세부적인 경로에 따른 권한 설정이 가능하다.
결론적으로 'org.springframework.boot:spring-boot-starter-websocket'는 WebSocket,STOMP 설정과 3-Way-Handshake 전후 처리를 할 수 있고, 'org.springframework.security:spring-security-messaging'는 STOMP기반 통신에 있어서 경로별로 권한설정을 할 수 있는 부분이 핵심이다.