첫번째 강좌에서는 1개의 채팅방만 가능했는데, 실제 채팅 사이트 처럼 여러개의 채팅방을 만드는 방법입니다.
1. 각 채팅방은 bang_id라는 파라미터에 따라서 구분 됩니다.
채팅방A : http://localhost/chat_test/chat.do?bang_id=aaaaaaaa
채팅방B : http://localhost/chat_test/chat.do?bang_id=bbbbbbbb
2. 프로토콜
첫번째 강좌처럼 단순 메세지를 주고 받는 형식이 아닌, 프로토콜 형식으로 변경 했습니다.
그리고 이 객체는 웹소켓 구간에서는 JSON으로 변환되어서 전송 됩니다.
var msgData = {
bang_id : bang_id,
cmd : cmd,
msg : msg
};
3. 웹소켓 핸들러 (HandlerChat.java)
COMMAND에 따라서 분기해서 메세지를 처리를 합니다.
참고로 세션관리를 위한 자료구조 sessionList는 개선이 필요하고, 쓰레드 동기화 처리도 해야 합니다.
package com.chat.handler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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 com.fasterxml.jackson.databind.ObjectMapper;
@Component
public class HandlerChat extends TextWebSocketHandler {
// (<"bang_id", 방ID>, <"session", 세션>) - (<"bang_id", 방ID>, <"session", 세션>) - (<"bang_id", 방ID>, <"session", 세션>) 형태
private List<Map<String, Object>> sessionList = new ArrayList<Map<String, Object>>();
// 클라이언트가 서버로 메세지 전송 처리
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
// JSON --> Map으로 변환
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> mapReceive = objectMapper.readValue(message.getPayload(), Map.class);
switch (mapReceive.get("cmd")) {
// CLIENT 입장
case "CMD_ENTER":
// 세션 리스트에 저장
Map<String, Object> map = new HashMap<String, Object>();
map.put("bang_id", mapReceive.get("bang_id"));
map.put("session", session);
sessionList.add(map);
// 같은 채팅방에 입장 메세지 전송
for (int i = 0; i < sessionList.size(); i++) {
Map<String, Object> mapSessionList = sessionList.get(i);
String bang_id = (String) mapSessionList.get("bang_id");
WebSocketSession sess = (WebSocketSession) mapSessionList.get("session");
if(bang_id.equals(mapReceive.get("bang_id"))) {
Map<String, String> mapToSend = new HashMap<String, String>();
mapToSend.put("bang_id", bang_id);
mapToSend.put("cmd", "CMD_ENTER");
mapToSend.put("msg", session.getId() + "님이 입장 했습니다.");
String jsonStr = objectMapper.writeValueAsString(mapToSend);
sess.sendMessage(new TextMessage(jsonStr));
}
}
break;
// CLIENT 메세지
case "CMD_MSG_SEND":
// 같은 채팅방에 메세지 전송
for (int i = 0; i < sessionList.size(); i++) {
Map<String, Object> mapSessionList = sessionList.get(i);
String bang_id = (String) mapSessionList.get("bang_id");
WebSocketSession sess = (WebSocketSession) mapSessionList.get("session");
if (bang_id.equals(mapReceive.get("bang_id"))) {
Map<String, String> mapToSend = new HashMap<String, String>();
mapToSend.put("bang_id", bang_id);
mapToSend.put("cmd", "CMD_MSG_SEND");
mapToSend.put("msg", session.getId() + " : " + mapReceive.get("msg"));
String jsonStr = objectMapper.writeValueAsString(mapToSend);
sess.sendMessage(new TextMessage(jsonStr));
}
}
break;
}
}
// 클라이언트가 연결을 끊음 처리
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
ObjectMapper objectMapper = new ObjectMapper();
String now_bang_id = "";
// 사용자 세션을 리스트에서 제거
for (int i = 0; i < sessionList.size(); i++) {
Map<String, Object> map = sessionList.get(i);
String bang_id = (String) map.get("bang_id");
WebSocketSession sess = (WebSocketSession) map.get("session");
if(session.equals(sess)) {
now_bang_id = bang_id;
sessionList.remove(map);
break;
}
}
// 같은 채팅방에 퇴장 메세지 전송
for (int i = 0; i < sessionList.size(); i++) {
Map<String, Object> mapSessionList = sessionList.get(i);
String bang_id = (String) mapSessionList.get("bang_id");
WebSocketSession sess = (WebSocketSession) mapSessionList.get("session");
if (bang_id.equals(now_bang_id)) {
Map<String, String> mapToSend = new HashMap<String, String>();
mapToSend.put("bang_id", bang_id);
mapToSend.put("cmd", "CMD_EXIT");
mapToSend.put("msg", session.getId() + "님이 퇴장 했습니다.");
String jsonStr = objectMapper.writeValueAsString(mapToSend);
sess.sendMessage(new TextMessage(jsonStr));
}
}
}
}
4. 클라이언트 (view_chat.jsp)
전송과 수신 COMMAND에 따라서 분기해서 메세지를 처리를 합니다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>웹소켓 채팅</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.4.0/sockjs.js"></script>
<script type="text/javascript">
var webSocket = {
init: function(param) {
this._url = param.url;
this._initSocket();
},
sendChat: function() {
this._sendMessage('${param.bang_id}', 'CMD_MSG_SEND', $('#message').val());
$('#message').val('');
},
sendEnter: function() {
this._sendMessage('${param.bang_id}', 'CMD_ENTER', $('#message').val());
$('#message').val('');
},
receiveMessage: function(msgData) {
// 정의된 CMD 코드에 따라서 분기 처리
if(msgData.cmd == 'CMD_MSG_SEND') {
$('#divChatData').append('<div>' + msgData.msg + '</div>');
}
// 입장
else if(msgData.cmd == 'CMD_ENTER') {
$('#divChatData').append('<div>' + msgData.msg + '</div>');
}
// 퇴장
else if(msgData.cmd == 'CMD_EXIT') {
$('#divChatData').append('<div>' + msgData.msg + '</div>');
}
},
closeMessage: function(str) {
$('#divChatData').append('<div>' + '연결 끊김 : ' + str + '</div>');
},
disconnect: function() {
this._socket.close();
},
_initSocket: function() {
this._socket = new SockJS(this._url);
this._socket.onopen = function(evt) {
webSocket.sendEnter();
};
this._socket.onmessage = function(evt) {
webSocket.receiveMessage(JSON.parse(evt.data));
};
this._socket.onclose = function(evt) {
webSocket.closeMessage(JSON.parse(evt.data));
}
},
_sendMessage: function(bang_id, cmd, msg) {
var msgData = {
bang_id : bang_id,
cmd : cmd,
msg : msg
};
var jsonData = JSON.stringify(msgData);
this._socket.send(jsonData);
}
};
</script>
<script type="text/javascript">
$(window).on('load', function () {
webSocket.init({ url: '<c:url value="/chat" />' });
});
</script>
</head>
<body>
<div style="width: 800px; height: 700px; padding: 10px; border: solid 1px #e1e3e9;">
<div id="divChatData"></div>
</div>
<div style="width: 100%; height: 10%; padding: 10px;">
<input type="text" id="message" size="110" onkeypress="if(event.keyCode==13){webSocket.sendChat();}" />
<input type="button" id="btnSend" value="채팅 전송" onclick="webSocket.sendChat()" />
</div>
</body>
</html>
결과
무료채팅 사이트를 만들었는데, 이 사이트의 기초가 되는 내용들이었습니다.
'개발 - 웹소켓 채팅 강좌' 카테고리의 다른 글
스프링+웹소켓 채팅 예제 : 기초 (3) | 2020.05.24 |
---|