양방향
네트워크 프로토콜 기반으로 동작Binary 데이터
를 포함 할 수 있다.pub/sub
구조로 동작@MessagingMapping
어노테이션을 사용해 메시지 발행 시 엔드포인트를 별로도 분리해서 관리할 수 있다.특정 topic
에 메세지를 보내면 해당 topic을 구독
하고 있는 subsciber 모두
에게 메세지가 전달
되는 구조IntelliJ : 2022.1.3.Ultimate
spring boot : 2.6.1
implementation 'org.springframework.boot:spring-boot-starter-websocket'
@Configuration
@EnableWebSocketMessageBroker
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger( WebSocketConfig.class );
// 세션 관리
final StompHandler stompHandler;
// 클라이언트가 웹 소켓 서버에 연결하는데 사용할 웹 소켓 엔드포인트 등록
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
// client가 sockjs로 개발되어 있을 때만 필요, client가 java면 필요없음
.withSockJS()
;
}
/*한 클라이언트에서 다른 클라이언트로 메시지를 라우팅하는데 사용될 메시지 브로커*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//sub으로 시작되는 요청을 구독한 모든 사용자들에게 메시지를 broadcast한다.
registry.enableSimpleBroker("/sub");
// pub로 시작되는 메시지는 message-handling methods로 라우팅된다.
registry.setApplicationDestinationPrefixes("/pub");
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
// connect / disconnect 인터셉터
registration.interceptors(stompHandler);
}
}
@Component
public class StompHandler extends ChannelInterceptorAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger( StompHandler.class );
@Override
public void postSend(Message message, MessageChannel channel, boolean sent) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
String sessionId = accessor.getSessionId();
switch ((accessor.getCommand())) {
case CONNECT:
// 유저가 Websocket으로 connect()를 한 뒤 호출됨
LOGGER.info("세션 들어옴 => {}", sessionId);
break;
case DISCONNECT:
// 유저가 Websocket으로 disconnect() 를 한 뒤 호출됨 or 세션이 끊어졌을 때 발생
LOGGER.info("세션 끊음 => {}", sessionId);
break;
default:
break;
}
}
}
@RestController
@RequiredArgsConstructor
public class SocketController {
private static final Logger LOGGER = LoggerFactory.getLogger( SocketController.class );
private final SimpMessageSendingOperations simpleMessageSendingOperations;
// 새로운 사용자가 웹 소켓을 연결할 때 실행됨
// @EventListener은 한개의 매개변수만 가질 수 있다.
@EventListener
public void handleWebSocketConnectListener(SessionConnectEvent event) {
LOGGER.info("Received a new web socket connection");
}
// 사용자가 웹 소켓 연결을 끊으면 실행됨
@EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
StompHeaderAccessor headerAccesor = StompHeaderAccessor.wrap(event.getMessage());
String sessionId = headerAccesor.getSessionId();
LOGGER.info("sessionId Disconnected : " + sessionId);
}
// /pub/cache 로 메시지를 발행한다.
@MessageMapping("/cache")
@SendTo("/sub/cache")
public void sendMessage(Map<String, Object> params) {
// /sub/cache 에 구독중인 client에 메세지를 보낸다.
simpleMessageSendingOperations.convertAndSend("/sub/cache/" + params.get("channelId"), params);
}
}
📌 여담
📚 참고