์๊ฐ
์ WebSocket์ ์ฌ์ฉํ์๋?
Spring-boot WebSocket ์ฌ์ฉ๋ฒ(์ค์ ๋ฒ)
์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์์ฑํด๋ณด๊ธฐ
๋ฐ์ํ ๋ฌธ์ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ
ใด HTTP session ๊ฐ์ ธ์ค๊ธฐ -> interception ์ฌ์ฉ
ใด DB๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๊ธฐ์กด ๋ฉ์์ง ์ ์ฅํ๊ธฐ -> Session Storage ์ฌ์ฉํ๊ธฐ
์ต๊ทผ ์์ฑ ์ค์ธ ๊ฐ์ธ ํ๋ก์ ํธ์์ ์น์ ์ ์ํ ํด๋ผ์ด์ธํธ์ ์ค์๊ฐ ์ฑํ ์ํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด๋ ค๊ณ ํ๋ค.
๐ค ์ WebSocket์ ์ฌ์ฉํ์๋?
์ฌ๊ธฐ์ ๋งํ๋ ์น ์์ผ์ Spring Framework์ STOMP๊ฐ ์๋ ์์ JavaScript๋ฅผ ํ์ฉํ ์์ ์ ๋๋ค.
jquery๋ ์ฌ์ฉํ์ง ์์ต๋๋ค.
WebSocket์ด ํ์ํ๋ ์ด์
์์๋งํ๋ฏ ์ค์๊ฐ ๋ผ์ด๋ธ ์ฑํ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ณ ์ถ์๋ค. ํ์ง๋ง, ๊ธฐ๋ณธ์ ์ผ๋ก HTTP ํต์ ์์๋ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์์ฒญ์ํ๋ฉด ์๋ฒ๊ฐ ์๋ตํด์ค ์ ์์๋ค. ๋ผ์ด๋ธ ์ฑํ ๊ธฐ๋ฅ์ด ๊ตฌํ๋๊ธฐ ์ํด์๋ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ์์ฒญ ์์ด๋ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ฃผ์ด์ผ ํ๋ค.
์น ์์ผ์ HandShake(์ ์)๋ผ๋ ๊ณผ์ ์ผ๋ก ํตํด์ HTTP์ WS(WebSocket)ํ๋กํ ์ฝ๋ก ์ ํ ํด์ฃผ๋๋ฐ ์ดํ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๋จผ์ ๋ฉ์ธ์ง๋ฅผ ์ ํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํด์ค๋ค.
๐ฅธ Spring-boot WebSocket ์ฌ์ฉ๋ฒ(์ธํ ๋ฐฉ๋ฒ)
1. WebSocket ์์กด์ฑ์ ์ถ๊ฐ
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-websocket'
}
gradle.build ํ์ผ dependencies ์์ ํด๋น ์ฝ๋๋ฅผ ์ฝ์ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
๋น๋ ๋๊ตฌ๋ก๋ gradle์ ์ฌ์ฉํ์์ต๋๋ค. Maven์ ์ฌ์ฉํ์ ๋ค๋ฉด ๊ทธ์ ๋ง๊ฒ ์ค์ ํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
2. Handler class ์์ฑ
WebSocket์ ํตํด์ message๋ฅผ ์ ๋ฌํ๊ธฐ ์ํด์๋ WebSocketHandler ์ธํฐํ์ด์ค๋ฅผ ์์ ๋ฐ์ TextWebSocketHandler๋ฅผ ์์ ๋ฐ์์ผ ํฉ๋๋ค. ๋ ์์ ๊ฐ์ฒด๋ก๋ AbstractWebSocketHandler๊ฐ ์์ต๋๋ค.
์์น๋ core package ํ์์๋ง ์์ผ๋ฉด ๋ฉ๋๋ค.
package com.spacedev.board.handler;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import lombok.extern.log4j.Log4j2;
@Component
@Log4j2
public class ChatHandler extends TextWebSocketHandler {
// message
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
}
// connection established
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
}
// connection closed
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
}
}
TextWebSocketHandler๋ฅผ ์์ ๋ฐ์ผ๋ฉด handleTextMessage, afterConnectionEstablished, afterConnectionClosed ์ถ์ํ๋ 3๊ฐ์ง ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ ํด์ผํฉ๋๋ค.
์ด๋ฆ์์ ์ ์ถํ ์ ์๋ฏ์ด ์ธ ๋ฉ์๋์ ๊ธฐ๋ฅ์ ์๋์ ๊ฐ์ต๋๋ค.
handleTextMessage: websocket์ ํตํด์ ๋ฐ์ ๋ฉ์ธ์ง๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฉ์๋
afterConnectionEstablished: websocket์ session์ด ์ ์ํ์ ๋, ์ฒ๋ฆฌํ๋ ๋ฉ์๋
afterConnectionClosed: websocket์ session์ด ์ ์์ ํด์ ํ์ ๋, ์ฒ๋ฆฌํ๋ ๋ฉ์๋
3. WebSocketConfigurer ์์ฑ
package com.spacedev.board;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import com.spacedev.board.handler.ChatHandler;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private final ChatHandler chatHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatHandler, "/ws/chat").setAllowedOrigins("*")
.addInterceptors(new HttpSessionHandshakeInterceptor()); // interceptor for adding httpsession into websocket session
}
}
WebSecketConfigurer ๋ฅผ ์์ ๋ฐ์ ํด๋์ค์์ ์ฌ์ฉํ hanlder ํด๋์ค์ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ฃผ์ด์ผํ๋ค.
.setAllowedOrigins("*")๋ wsํ๋กํ ์ฝ /ws/chat ํ์์ ๋ชจ๋ uri์์ chatHandler๋ฅผ ์ฌ์ฉํ๋ค๋ ์๋ฏธ์ด๋ค.
.addInterceptors(new HttpSessionHandshakeInterceptor()) ๋ ํธ๋ ์์ดํฌ๋ฅผ ํ ๋, http์ session์ ๊ฐ์ ธ์ค๊ธฐ ์ํ ์ธํฐ์ ํฐ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ธ๋ฐ ์์ด๋ ์๊ด์ต๋๋ค. ์ฌ์ฉํ ์ด์ ๋ ์๋์์ ์ค๋ช ํฉ๋๋ค.
์ด๋ก์จ, spring boot์์ websocket์ ์ฌ์ฉํ๊ธฐ ์ํ ๊ธฐ๋ณธ์ ์ธ ์ค์ ์ ํ ์ ์์ต๋๋ค.
๐ง ์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์์ฑํด๋ณด๊ธฐ
<script>
// websocket ์์ฑ
const websocket = new WebSocket("ws://localhost:8080/ws/chat");
websocket.onmessage = onMessage; // ์์ผ์ด ๋ฉ์ธ์ง๋ฅผ ๋ฐ์ ๋
websocket.onopen = onOpen; // ์์ผ์ด ์์ฑ๋ ๋(ํด๋ผ์ด์ธํธ ์ ์)
websocket.onclose = onClose; // ์์ผ์ด ๋ซํ๋(ํด๋ผ์ด์ธํธ ์ ์ํด์ )
//on exit chat room
function onClose(evt) {
console.log("close event : " + evt);
}
//on entering chat room
function onOpen(evt) {
console.log("open event : " + evt);
}
// on message controller
function onMessage(msg) {
var data = JSON.parse(msg.data); // msg๋ฅผ ๋ฐ์ผ๋ฉด data ํ๋ ์์ Json String์ผ๋ก ์ ๋ณด๊ฐ ์์
// ํ์ํ ์ ๋ณด๋ฅผ Json data์์ ์ถ์ถ
var senderId = data.senderId;
var message = data.message;
var time = data.time;
var newOne = data.newOne;
var outOne = data.outOne;
}
// send a message
function send(){
var message = document.getElementById("msg").value;
// don't send when content is empty
// ์ฑํ
์
๋ ฅ ์นธ์ด ๋น์ด์์ง ์์ ๊ฒฝ์ฐ๋ง ์ ๋ณด๋ฅผ Jsonํํ๋ก ์ ์ก
if(message != "") {
let msg = {
'receiverId' : receiverId,
'message' : message
}
if(message != null) {
websocket.send(JSON.stringify(msg)); // websocket handler๋ก ์ ์ก(์๋ฒ๋ก ์ ์ก)
}
document.getElementById("msg").value = '';
}
}
</script>
์ ์คํฌ๋ฆฝํธ๋ ์์์ฉ ์คํฌ๋ฆฝํธ ์ ๋๋ค.
ํด๋น ์คํฌ๋ฆฝํธ๊ฐ ํฌํจ๋์ด์๋ view์ ํ์ผ์ url์ ํด๋ผ์ด์ธํธ๊ฐ ์ ์ํ ๋ ์น ์์ผ์ด ์์ฑ์ด๋๋ค.
const websocket = new WebSocket("ws://localhost:8080/ws/chat");
http ํ๋กํ ์ฝ์ ws๋ก ๋ฐ๊พธ๊ณ config์์ ์ค์ ํ url๋ก ์ ์ํด์ฃผ๋ฉด ๋๋ค.(8080 ํ์ ์ฃผ์ ex: ws/chat -> ๋ณธ์ธ์ด ์ค์ ํ ์ฃผ์)
WebSocket์ผ๋ก ์ฑํ ๋ฐฉ์ ๊ตฌํํ ๋ ์ฌ์ฉ๋๋ ๋ฉ์๋๋ 4๊ฐ์๋ค.
1. ์ ์(Session connection): onopen
2. ์ฐ๊ฒฐํด์ (Session disconnection): onclose
3. ๋ฉ์ธ์ง๋ฅผ ์์ (get): onmessage
4. ๋ฉ์ธ์ง๋ฅผ ์ก์ (send): send
์ฃผ๋ก ๋ณผ ๊ฒ์ ๋ฉ์ธ์ง๋ฅผ ๋ฐ์ ๋์ ์ ์กํ ๋์ด๋ค.
์ฑํ ์ ๊ตฌํํ๊ธฐ ์ํด์๋ ๋ณด๋ธ์๊ฐ, ๋ฐ๋์ฌ๋, ๋ณด๋ธ์ฌ๋, ๋ฉ์ธ์ง ๋ด์ฉ ๋ฑ ๋ค์ํ ์ ๋ณด๊ฐ ํ์ํ๋ฐ ์น ์์ผ์ ๊ฒฝ์ฐ String ํ์ ์ message๋ง ์๋ฒ์ ํด๋ผ์ด์ธํธ ๋จ์์ ์ฃผ๊ณ ๋ฐ์ ์ ์์๋ค.
๊ทธ๋ ๊ธฐ์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ Json ํ์์ผ๋ก ์๋ฒ์ ์ฃผ๊ณ ๋ฐ์ ์ ์๋๋ก ๊ตฌ์ฑํ์๋ค.
๐ฑ๋ฐ์ํ ๋ฌธ์ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ
๊ตฌํ ์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋๋ฐ ๋ฐ๋ก HTTP session์ ๊ฐ์ ธ์ค๊ธฐ์ด๋ค.
ํด๋ผ์ด์ธํธ๊ฐ ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด์ ์น ์ฌ์ดํธ์ ์ ์ํ๋ฉด ์๋ฒ๋ ํด๋ผ์ด์ธํธ๋ค์ ๊ตฌ๋ถํ ์ ์๊ฒ ๊ฐ์ ๊ณ ์ ์ ๊ฐ์ด session์ ์์ฑํด์ ์ ์ ์๊ฒ ์ ์กํ๋๋ฐ ์ด session์ HTTP์์ session์ด๋ค.
์ ๋ฌธ์ ๊ฐ ๋ ๊น?
Session ์์ ๋ค์ด์๋ seesion id๋ฅผ ๊ฐ๊ณตํด์ ์ฌ์ฉํ ํ์๊ฐ ์์๋๋ฐ WS์ด HTTP๋ ์๋ก ๋ค๋ฅธ session์ ์์ฑํ๋ค๋ ๊ฒ์ด๋ค.
์ฆ, ์น ํ์ด์ง์ ํ session id๋ฅผ ๊ฐ๊ณตํ์ฌ attribute์ ์ ์ฅํด๋์ ๊ฒ์ WS์์ ๊ฐ์ ธ์ฌ ์ ์์๋ค๋ ๊ฒ.
โถ ํด๊ฒฐ ๋ฐฉ๋ฒ: HTTP session ๊ฐ์ ธ์ค๊ธฐ -> interception ์ฌ์ฉ
๋ด๊ฐ ํ์ํ๊ฑด ๋ง์ธ ํ๋ฅญํ ๋ถ๋ค๊ป์ ๋ค ๋ง๋ค์ด ๋์ผ์ ๊ฒฝ์ฐ๊ฐ ๋๋ถ๋ถ์ด์๊ธฐ์ ์ด๋ฒ์๋ ํด๋ต์ ์ฐพ์๋ค.
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private final ChatHandler chatHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatHandler, "/ws/chat").setAllowedOrigins("*")
.addInterceptors(new HttpSessionHandshakeInterceptor()); // interceptor for adding httpsession into websocket session
}
}
Java์์ ์น ์์ผ์ ์ค์ ํ ๋ WebSocketConfigurer ์ธํฐํ์ด์ค๋ฅผ ์์ ๋ฐ๊ณ resigerWebsocketHandlers ๋ฉ์๋๋ฅผ ์์ ๋ฐ์์ผํ๋ค.
์ ๋ฉ์๋์ ์ธ์ registry์ .addInterceptors(new HttpSessionHandshakeInterceptor()๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด HTTP๋ฅผ WS์ผ๋ก ๋ณ๊ฒฝํ ๋ interceptor๊ฐ ์์ฉํด HTTP session์ attrubutes๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
@Component
public class ChatHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String senderId = (String) session.getAttributes().get("sessionId");
}
handler ๋ด์ WebSocketSession์์ httpSession์์ ์ค์ ํ attribute๋ฅผ "getAttrubutes().get(์ ์ฅํ ์ ํธ๋ฆฌ๋ทฐํธ์ด๋ฆ)์ ํตํด์ ๊ฐ์ ธ์ฌ ์ ์์๋ค.
โถ DB๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๊ธฐ์กด ๋ฉ์์ง ์ ์ฅํ๊ธฐ -> Session Storage ์ฌ์ฉํ๊ธฐ
<script>
// save chat history
function setChatHistory(name){
var value =[];
document.querySelectorAll('.message-li').forEach(item => {
var time = item.querySelector('.message-data > .message-data-time').textContent;
var message = item.querySelector('.message').textContent;
var senderId;
var type = item.querySelector('.message').classList[1];
if(type == 'my-message'){
senderId = myId;
} else {
senderId = name;
}
data = {
"time":time,
"message":message,
"senderId":senderId
}
value.push(data);
})
sessionStorage.setItem(name, JSON.stringify(value));
};
// insert pre chat history
function getChatHistory(name){
var data = JSON.parse(sessionStorage.getItem(name));
if(data != null) {
data.forEach(item => {
var time = item.time;
var message = item.message;
var senderId = item.senderId;
insertMessage(senderId, time, message);
})
}
};
</script>
์๋๋ฐฉ์ id๋ฅผ session stroage์ key๋ก ์๋๋ฐฉ๊ณผ์ ๋ํ ๋ด์ฉ์ Json String์ผ๋ก ๋ง๋ค์ด value๋ก ๋ฃ์ด์คฌ๋ค.
sessionStorage๋ฅผ ์ฌ์ฉํ ์ด์ ?
๋ด๊ฐ ํ์ํ ๋ถ๋ถ์ ์ค์๊ฐ ๋ผ์ด๋ธ ์ฑํ ์ด์ง๋ง, 1:1 ์ฑํ ์ด์๊ธฐ์ ์ฑํ ์๋๋ฅผ ๋ฐ๊พธ๋ ๊ณผ์ ์ด ์๊ธฐ์ ์ฑํ ํ์คํ ๋ฆฌ๋ฅผ ์ ์ฅํ ๊ณต๊ฐ์ด ํ์ํ๋ค.
sessionStorage๋ localStorage๋ DB ์ ์ฅ๊ณผ๋ ๋ค๋ฅด๊ฒ session์ด ๋ง๋ฃ๋๋ฉด ์ฌ๋ผ์ง๋ ์ ๋ณด์ด๋ค. ํด๋น ์น ํ์ด์ง๋ ์ต๋ช ์ผ๋ก ์ฑํ ์ ์ฐธ์ฌํ๋ ๊ฒ์ด๊ณ ์ค์ํ ์ ๋ณด๋ฅผ ์ ์ฅํ ํ์๊ฐ ์๊ธฐ์ ์ธ์ ์ด ์ข ๋ฃ์ ์ฌ๋ผ์ง๋ ํ๋ฐ์ฑ ์ ์ฅ ๊ณต๊ฐ์ด ํ์ํ๊ธฐ์ ์ฌ์ฉํ๊ธฐ ์ ์ ํด ์ฌ์ฉํ์๋ค.
โถ sessionStorage ์ฌ์ฉ๋ฒ
<script>
if(sessionStorage.getItem(senderId) != null) {
container = JSON.parse(sessionStorage.getItem(senderId));
console.log('stagine message container >>> ', container);
container.push(data);
} else {
container.push(data);
}
sessionStorage.setItem(senderId, JSON.stringify(container));
</script>
sessionStorage๋ key(String), value(String)๋ก ์ด๋ค์ง๋๋ค.
setItem(key, value) ๋ฉ์๋๋ฅผ ํตํด์ ๊ฐ์ ์ ์ฅํ๊ณ getItem(key)๋ฅผ ํตํด์ ์ ์ฅํ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค.
์์ ์์ ๋ด์ฉ
๋ฉ์ธ์ง๋ฅผ ๋ฐ์์ ๋, senderId(๋ณด๋ด๋ ์ฌ๋)์ key๋ฅผ ๊ฐ์ง sessionStorage๊ฐ null์ด ์๋ ๋
-> ์ด๋ฏธ senderId์ ๋ฉ์ธ์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์์ ์ด ์๋ ๊ฒฝ์ฐ
=> ๋ฉ์ธ์ง๋ฅผ ์ ์ฅํด ๋์ container ๋ฐฐ์ด ๋ค์ ์ถ๊ฐ ํด์ค๋ค.
sessionStorage์ senderId์ ์ผ์นํ๋ key๊ฐ ์๋ ๊ฒฝ์ฐ
-> ๋ฉ์ธ์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์์ ์ด ์๋ค.
=> ๋น์ด์๋ container์ ์ถ๊ฐ ํด์ค๋ค.
์ ์ฝ๋์ ๋ฌธ์ ์
host์ ๊ฒฝ์ฐ ๋ํ์๋๋ฅผ ๋ฐ๊ฟ ๋๋ง๋ค sessionStorage์ ๊ธฐ์กด์ ๋ํ ๋ด์ฉ์ ๋ชจ๋ ์ ์ฅํ๊ณ ๋ค์ ๋ชจ๋ ๋ถ๋ฌ์์ผ ํ๋ค.
๋ํ ๋ด์ฉ๊ณผ ๋ํ ์๋๊ฐ ๋งค์ฐ ๋ง์ ์ง๋ค๋ฉด host(master)์ ๋ธ๋ผ์ฐ์ ์ ๋ถํ๊ฐ๊ฑธ๋ฆด ํ๋ฅ ์ด ๋๋ค.
ํด๊ฒฐ์ฑ ?
1. host์ ๋ํํ ์ ์๋ session ์ ํํ๊ธฐ
2. ์ ์ฅํ ๋ฉ์ธ์ง์ ๊ฐ์๋ฅผ ์ ํํ๊ธฐ -> ํ์ฅ: ์ ์ฅํ ๋ํ ๋ด์ฉ์ ์์ host๊ฐ ์ง์ ์กฐ์ ํ ์ ์๋ ๊ธฐ๋ฅ ์ ๊ณต
2๋ฒ ๋ฐฉ์์ด ์กฐ๊ธ ๋ ์ ์ฉํ ์ ์์ง๋ง, ๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ์ฑ ์ด๋ผ๊ณ ๋ณด๊ธฐ๋ ์ด๋ ค์ธ ๊ฒ ๊ฐ๋ค.
๋ ์ข์ ํด๊ฒฐ๋ฐฉ์์ด ์๋ค๋ฉด ๋๊ธ๋ก ์๊ฒฌ์ฃผ์๋ฉด ๊ฐ์ฌํฉ๋๋ค.
๐ถ๐ซ๏ธ์๋ฌดํผ ๊ฒฐ๊ณผ...
์ฑํ ๋ฐฉ์ guest๋ก ์ ์ํ๋ master๋ก ์ ์ํ๋๋ก ๋๋ฉ๋๋ค. ๋๊ตฐ๊ฐ ์ ์ํ๊ฒ๋๋ฉด ์ ์ ๋ฉ์ธ์ง๋ฅผ ๋จ๊ธฐ๊ฒ๋๋๋ฐ ๋ง์คํฐ์ ๊ฒฝ์ฐ ๋ชจ๋ ๊ฒ์คํธ๋ค์๊ฒ ์ ์ ๋ฉ์ธ์ง๋ฅผ ๋ ๋ฆฌ๊ฒํฉ๋๋ค.
์ข: guest ์ฐ: master(host)
๋ง์คํฐ์ ๊ฒฝ์ฐ ๊ฒ์คํธ๊ฐ ๋ง์ ์ ์์ผ๋ฏ๋ก ๊ฒ์คํธ๋ฅผ ์ ํ์ ์ผ๋ก ๋ํํ ์ ์์ต๋๋ค.
๋ํ ์ค์ด ์๋ ๊ฒ์คํธ๊ฐ ๋ง์คํฐ์๊ฒ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ธ๋ค๋ฉด ๋ณด๋ธ ๋งํผ ์นด์ดํ ๋๊ณ ๋ง์คํฐ ํ๋ฉด์ ํ์๋๊ฒ ํด๋จ์ต๋๋ค. ์ฌ๊ธฐ์ seesionStorage์ ์๋์ ๋ฉ์ธ์ง๋ฅผ ์ ์ฅ์์ผ ๋์ต๋๋ค.
ํ๋ฉด์ ํ์๋๋ ๋๊ทธ๋ ํ์๋ canvas๋ฅผ ์ด์ฉํด์ ๊ทธ๋ ธ์ต๋๋ค.
๊ฒ์คํธ๋ฅผ ์ ํํ๋ ์๊ฐ ํด๋น ๊ฒ์คํธ์ ์์ด๋์ ๋์ผํ sessionStorage์์ ๋ฉ์ธ์ง๋ฅผ ๊ฐ์ ธ์ ํ์์ ๋ง๊ฒ ๋ณด์ฌ์ค๋๋ค.
์ด๋ ๊ฒ ์ค์๊ฐ ์ฑํ ์ ๊ธฐ๋ฅ์ ๋น์ทํ๊ฒ ๊ตฌํํด๋ดค์ต๋๋ค.
JS์ Java ์ ์ฒด์ฝ๋๋ฅผ ์ ๋๊ฑด ๋๋ฌด ๊ธธ์ด์ ํ์ํ ํ๋ง ์์ฑํ์ผ๋ ๋ด์ฉ์ด ํ์ํ์ ๋ถ๋ถ์ ๊นํ์์ ํ์ธํด์ฃผ์ธ์.
chatHandler.java, chat.html(<script>)๋ฅผ ์ฐพ์์ ํ์ธ ํ์๋ฉด ๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋ถ์กฑํ ๋ถ๋ถ์ด ๋ง์ํ ๋ฐ ์ด์๊ฐ ๋ ๋งํ ๋ถ๋ถ์ ๊นํ ์ด์์ ๋จ๊ฒจ์ฃผ์๋ฉด ๊ฐ์ฌํฉ๋๋ค.
ํ๋ก์ ํธ ์ ์ฒด ์์ค: https://github.com/wjgin/Spring-boot-drill/tree/master/itsme