[SpringBoot] WebSocket을 활용한 채팅 구현하기.
[SpringBoot] WebSocket을 활용한 채팅 구현하기
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile("org.springframework.boot:spring-boot-starter-websocket")
compile("org.webjars:sockjs-client:1.0.2")
compile("org.webjars:stomp-websocket:2.3.3")
runtime('org.springframework.boot:spring-boot-devtools')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
코드에서 어떻게 구현되는지 살펴보자.
WebSocketConfig.java
package com.mapia.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websockethandler").withSockJS();
}
}
`WebSocketConfig`클래스는 Message broker에 대한 설정을 한다. 코드에서 볼 수 있듯이 `AbstractWebSocketMessageBorkerConfigurer` 추상클래스를 상속하며 두 가지의 메서드를 `Override`하고 있다.
`enableSimpleBroker()` 메소드는 메모리 기반 메세지 브로커가 해당 api를 구독하고 있는 클라이언트에게 메세지를 전달한다.
`setApplicationDestinationPrefixes()` 메소드는 서버에서 클라이언트로부터의 메세지를 받을 api의 prefix를 설정한다.
`registerStompEndpoints()` 메소드는 클라이언트에서 WebSocket을 연결할 api를 설정한다. parameter로 넘겨받는 `StompEndpointRegistry`의 메소드인 `addEndpoint()` 메소드를 통해서 여러 가지 end point 를 설정할 수 있다.
MessageHandler.java
package com.mapia.websocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageHandler {
@MessageMapping("/hello")
@SendTo("/topic/roomId")
public Message broadcasting(ClientMessage message) throws Exception {
return new Message(message.getContent());
}
}
이 클래스는 Message-handling controller 이다. 해당 클래스는 @MessageMapping 어노테이션에 의해 "/hello"라는 api로 mapping 되어있다. 만약에 클라이언트에서 "../hello"라는 api로 메세지를 보내면 `broadcasting()` 메소드가 호출된다. 클라이언트로부터 오는 메세지는 `broadcasting()` 메소드의 파라미터와 binding 되어 있다. return value는 @SendTo 어노테이션에 mapping 되어있는 api를 구독하고 있는 클라이언트들에게 브로드캐스팅 된다.
서버사이드 코드는 이것이 전부이다. 클라이언트 코드를 살펴보자.
index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<textarea id="chatOutput" name="" class="chatting_history" rows="24"></textarea>
<div class="chatting_input">
<input id="chatInput" type="text" class="chat">
</div>
<script src="/webjars/sockjs-client/1.0.2/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/2.3.3/stomp.min.js"></script>
<script src="js/main.js"></script>
<script src="js/socket.js"></script>
</body>
</html>
socket.js
document.addEventListener("DOMContentLoaded", function() {
WebSocket.init();
});
let WebSocket = (function() {
const SERVER_SOCKET_API = "/websockethandler";
const ENTER_KEY = 13;
let stompClient;
let textArea = document.getElementById("chatOutput");
let inputElm = document.getElementById("chatInput");
function connect() {
let socket = new SockJS(SERVER_SOCKET_API);
stompClient = Stomp.over(socket);
stompClient.connect({}, function () {
stompClient.subscribe('/topic/roomId', function (msg) {
printMessage(JSON.parse(msg.body).content);
});
});
}
function printMessage(message) {
textArea.value += message + "\n";
}
function chatKeyDownHandler(e) {
if (e.which === ENTER_KEY && inputElm.value.trim() !== "") {
sendMessage(inputElm.value);
clear(inputElm);
}
}
function clear(input) {
input.value = "";
}
function sendMessage(text) {
stompClient.send("/app/hello", {}, JSON.stringify({'content': text}));
}
function init() {
connect();
inputElm.addEventListener("keydown", chatKeyDownHandler);
}
return {
init : init
}
})();
자바스크립트 코드에서는 클라이언트 소켓을 생성하는데 SockJS를 사용한다. 그리고 그 소켓을 `Stomp.over()`메소드를 사용하여 사용하게 되는 구조이다. .subscribe()메소드를 통해서 broadcasting 받을 api를 설정할 수 있고 `.send()`메소드를 통해서 서버 측에 message를 보낼 수 있다.
Spring WebSocket 으로 검색하면 나오는 공식 홈페이지에 설명이 너무 잘되있어서 생각보다 금방 해결할 수 있었다.
Reference> https://spring.io/guides/gs/messaging-stomp-websocket/
end