반응형
WebSocket
- Http처럼 서버와 클라이언트 간 양방향 통신을 제공하는 프로토콜.
- 한 번 connection이 맺어지면, 서버 또는 클라이언트가 connection을 종료하기 전까지 계속 통신이 가능하다.
- 따라서 지속적으로 서버와 클라이언트가 high frequency / low latency로 통신해야 하는 경우 http보다 websocket 프로토콜이 유리하다.
- url로 topic을 지정한 채 메시지를 전송
- 해당 메시지가 Message Broker로 도달 (Simple Broker)
- Message Broker는 해당 토픽에 대응되는 response channel로 Route
- receiver가 메시지 수신.
이 과정을 진행하기 위해서는 Stomp라는 프로토콜이 추가로 필요.
Stomp
- WebSocket은 단지 통신 프로토콜일 뿐이다. 특정 토픽의 subscriber에게만 / 특정 user에게만 메시지를 보내는 방법을 담당하는 프로토콜이 Stomp
- Streaming Text Oriented Messaging Protocol로, 프로토콜을 지원하는 Message Broker와 stomp client 간 통신을 지원한다.
- Spring은 default로 Support. 다른 messaging protocol - RabbitMQ / ActiveMQ 등도 사용 가능함.
먼저, WebSocket Config 클래스를 생성한다.
package com.inspirit941.websocketexample.config;
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 WsConfig implements WebSocketMessageBrokerConfigurer {
// 웹소켓 config. @Enable... 어노테이션 + WebSockekMsgBrokerConfigurer 구현체.
// 메시지를 중개 / 라우팅하는 브로커 설정.
// 아래 두 개의 메소드 오버라이딩이 필요함.
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// stomp에서 prefix URL 적용하는 부분
registry.addEndpoint("/testEndpoint").withSockJS();
// withSockJS 사용시의 장점:
// websocket 형태로 연결이 불가능한 경우 http를 사용해서 연결이 지속되도록 만든다는 듯.
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
- EndPoint : 서버와 클라이언트가 WebSocket 통신을 하기 위한 엔드포인트라고 생각하면 된다. 클라이언트 측에서 Socket을 생성할 때, 여기에 정의한 문자열로 생성해야 통신이 된다.
- enableSimpleBroker: 일종의 topic. 해당 문자열로 시작하는 message 주소값을 받아서 처리하는 Broker를 활성화한다
- ApplicationDesinationPrefixes: 클라이언트가 서버로 메시지를 보낼 때 붙여야 하는 url prefix.
간단한 메시지 클래스를 생성한다.
package com.inspirit941.websocketexample.model;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class Message {
private String content;
private String sender;
private MessageType type;
public enum MessageType {
CHAT, LEAVE, JOIN
};
}
이제, 클라이언트에게서 메시지를 받아 처리할 Controller를 생성한다.
package com.inspirit941.websocketexample.controller;
import com.inspirit941.websocketexample.model.Message;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
// add user to the chat / send message
// @Payload : WebSocket에서 전송하는 데이터를 받기 위한 어노테이션.
// capture the username (who will chat / currently in join state)
@MessageMapping("/chat.register") // map the same url from client to server
@SendTo("/topic/public")
// specify queue. (request / response. based on url). 여기서는 config에서 enableSimpleBroker에서 등록한 거
public Message register(
@Payload Message msg,
SimpMessageHeaderAccessor headerAccessor
) {
// 누가 보냈는지 정보 담기
headerAccessor.getSessionAttributes().put("username", msg.getSender());
return msg;
}
@MessageMapping("/chat.send")
@SendTo("/topic/public")
public Message sendMessage(@Payload Message msg) {
return msg;
}
}
- /chat.register 라는 url로 message가 올 경우, 해당 메시지를 포함한 세션에 username을 key값으로, sender를 value값으로 저장한 뒤 리턴한다.
- @SendTo로 static하게 토픽을 구독한 사람 전원에게 메시지를 보낼 수도 있지만, SimpMessageSendingOperations의 convertAndSend 메소드를 사용하면 @Payload로 넘어온 메시지 내부의 값을 토픽으로 활용할 수 있다.
- 위의 로직은
- /chat.register로 '00님이 입장하였습니다' 라는 채팅을 참여자 전체에게,
- /chat.send로 참여한 사람들에게 입력한 메시지를 전송하는 거라고 보면 된다.
프론트에서의 코드 예시를 보면
function connect(event) {
username = document.querySelector('#name').value.trim();
if(username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');
var socket = new SockJS('/testEndpoint');
stompClient = Stomp.over(socket);
stompClient.connect({}, onConnected, onError);
}
event.preventDefault();
}
function onConnected() {
// Subscribe to the Public Topic
stompClient.subscribe('/topic/public', onMessageReceived);
// Tell your username to the server
stompClient.send("/app/chat.register",
{},
JSON.stringify({sender: username, type: 'JOIN'})
)
connectingElement.classList.add('hidden');
}
- 처음에 /testEndpoint로 socket을 연결하고
- /topic/public을 subscribe한 후
- /app/char.register 메시지로 메시지를 보낸다.
function send(event) {
var messageContent = messageInput.value.trim();
if(messageContent && stompClient) {
var chatMessage = {
sender: username,
content: messageInput.value,
type: 'CHAT'
};
stompClient.send("/app/chat.send", {}, JSON.stringify(chatMessage));
messageInput.value = '';
}
event.preventDefault();
}
추가로 메시지를 보낼 때에는 /app/chat.send 로 보내는 것을 확인할 수 있다.
원본 영상은 이곳을 참고했다. 영어발음이 쾌적하지 못해서 알아듣기는 힘들지만, 설명한 내용은 거의 다 담았다.
www.youtube.com/watch?v=4Hyv4M1kFeM
원본 소스코드는 여기
github.com/Java-Techie-jt/Spring-Boot-WebSocket
반응형
'프로그래밍 > 이것저것_개발일지' 카테고리의 다른 글
화상 모의면접 연습 플랫폼 개발 프로젝트 (2) - KeyCloak 활용해서 서비스 DB에 OAuth 인증 붙이기 (0) | 2021.04.18 |
---|---|
화상 모의면접 연습 플랫폼 개발 프로젝트 (1) - 채팅 DB 아키텍처 고민하기 (0) | 2021.02.20 |
IBM Kubernetes Cluster에 SpringBoot Application 구동 실습하기 - 2. deploy (0) | 2020.11.10 |
IBM Kubernetes Cluster에 SpringBoot Application 구동 실습하기 - 1. dockerizing and 환경설정 (0) | 2020.11.09 |
Ubuntu 인스턴스에 MySQL 올리기 (0) | 2020.11.04 |