[Spring/Web Socket] 웹 소켓 세션을 이용한 웹상에서 통신 (2) - 단체톡방
2021. 3. 24. 10:15
웹소켓 세션 Channel(room)을 이용해서 개인간 통신이 아닌 channel에 등록되어 있는 멤버들끼리의 통신을 해보기로 한다.
필자는 일단 handler에서 모든걸 구현했다
다시 클래스를 나눠서 구현해보자!!
1.Java단
package ercg.common.websocket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import ercg.sys.log.cmm.UserDetailsHelper;
import ercg.sys.log.vo.SysLog0101VO;
public class EchoHandler extends TextWebSocketHandler{
//연결된 모든 sessions 저장
//List<WebSocketSession> sessions = new ArrayList<>();
//userId의 webSession을 저장한다
Map<String, WebSocketSession> userSessions = new HashMap<>();
//ROOM마다 연결되어있는 userId list를 저장
Map<String, List<String>> roomUsers = new HashMap<>();
//클라이언트 접속 성공 시 연결 성공시
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception{
System.out.println("afterConnectionEstablished:" + session);
//userId 알아내기
Map<String, Object> sessionVal = session.getAttributes();
SysLog0101VO sysLog0101VO = (SysLog0101VO) sessionVal.get("sysLog0101VO");
System.out.println(sysLog0101VO.getUserId());
String userId = sysLog0101VO.getUserId();
if(userSessions.get(userId) != null) {
//userId에 원래 웹세션값이 저장되어 있다면 update
userSessions.replace(userId, session);
} else {
//userId에 웹세션값이 없다면 put
userSessions.put(userId, session);
}
}
//소켓에 메시지를 보냈을때 js에서 on.message
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println("handleTextmessage: " + session + " : " + message);
//protocol: RoomNum, 보내는id, 내용
String msg = message.getPayload();
if(StringUtils.isNotEmpty(msg)) {
String[] strs = msg.split(",");
if(strs != null && strs.length == 3) {
String roomNum = strs[0];
String sendId = strs[1];
String content = strs[2];
//입장일시
if(content.equals("ENTER")) {
//해당 roomNum의 Map의 userId 리스트에 sendId를 넣어준다.
System.out.println("ENTER안에 있음");
if(roomUsers.get(roomNum) == null) {
List<String> list = new ArrayList<>();
roomUsers.put(roomNum, list);
}
roomUsers.get(roomNum).add(sendId);
System.out.println(sendId + "가 들어왔습니다.");
List<String> roomUserList = roomUsers.get(roomNum);
for(int i = 0; i < roomUserList.size(); i++) {
System.out.println(i + roomUserList.get(i) + " " + userSessions.get(roomUserList.get(i)));
}
}
//퇴장일시
else if(content.equals("OUT")) {
// room을 나갈시 Map의 userId 리스트에 sendId를 지운다.
roomUsers.get(roomNum).remove(sendId);
System.out.println("나갔습니다.");
List<String> roomUserList = roomUsers.get(roomNum);
for(int i = 0; i < roomUserList.size(); i++) {
System.out.println(i + roomUserList.get(i) + " " + userSessions.get(roomUserList.get(i)));
}
}
//해당 room의 userList를 가져옴
List<String> roomUserList = roomUsers.get(roomNum);
for(int i = 0; i < roomUserList.size(); i++) {
//room의 userId의 session에 보내기
userSessions.get(roomUserList.get(i)).sendMessage(new TextMessage(sendId + "," + content));
}
}
}
}
//소켓이 close 됐을 때
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception{
//sessions.remove(session);
System.out.println("afterHandleTextmessage: " + session + " : " + status);
}
}
2. JSP단
(1) ROOM 목록
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
<title>chats</title>
<link rel="stylesheet" type="text/css" href="/ercg/css/talk.css">
</head>
<script type="text/javascript" src="/ercg/js/jquery-3.1.1.min.js"></script>
<script language="javascript">
$(document).ready(function() {
$("#room1").on('click', function() {
location.replace("ROOM1.jsp");
});
$("#room2").on('click', function() {
location.replace("ROOM2.jsp");
});
});
</script>
<body>
<div class="talkTool">
<!-- Title 채팅 -->
<header class= "top-header">
<div class="header__column">
<span class="header__text">채팅</span>
</div>
</header>
<!-- 채팅방들 리스트 -->
<main class="chats">
<div class="search-bar">
<input type="text" placeholder="검색">
</div>
<ul class="chats__list">
<li class="chats__chat" id="room1">
<div class="chat__content">
<div class="chat__preview">
<h3 class="chat__user">ROOM1</h3>
</div>
</div>
</li>
<li class="chats__chat" id="room2">
<div class="chat__content">
<div class="chat__preview">
<h3 class="chat__user">ROOM2</h3>
</div>
</div>
</li>
</ul>
</main>
<nav class="tab-bar">
<a href="index.jsp" class="tab-bar__tab">
<i class="fa fa-user"></i>
<span class="tabl-bar_title">친구</span>
</a>
<a href="chats.jsp" class="tab-bar__tab--selected">
<i class="fas fa-commet"></i>
<span class="tab-bar__title">채팅</span>
</a>
<a href="" class="tab-bar__tab">
<i class="fa fa-search"></i>
<span class="tab-bar__title">채널</span>
</a>
<a href="" class="tab-bar__tab">
<i class="fa fa-ellipsis-h"></i>
<span class="tab-bar__title">더보기</span>
</a>
</nav>
</div>
</body>
</html>
(2) ROOM1(2)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
<title>Insert title here</title>
<%@ include file="/apps/common/include/ercgHeader.jsp"%>
<style>
.body-chat{
padding-bottom: 0;
}
.chat-header,
.chat-header a {
background-color: #a1c0d6;
color: black;
}
.chat-header {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.chat {
background-color: #a1c0d6;
height: 100vh;
padding-top: 15px;
padding-left:10px;
padding-right: 10px;
}
.chat .date-divider {
text-align: center;
font-size: 12px;
color: rgba(0, 0, 0, 0.5);
margin-bottom: 15px;
}
.chat__message{
margin-bottom: 10px;
display: flex;
}
.chat__message-from-me {
justify-content: flex-end;
align-items: flex-end;
}
.chat__message-to-me {
justify-content: flex-start;
align-items: flex-start;
}
.chat__message-time {
font-size: 10px;
color : rgba(0, 0, 0, 0.5);
}
.chat__message-from-me .chat__message-body{
background-color: #ffe934;
padding : 10px 5px;
border-radius: 2px;
margin-right: 10px;
margin-left: 10px;
font-size: 15px;
}
.chat__message-to-me img{
height: 35px;
border-radius: 50%;
margin-right: 10px;
}
.chat__message-username{
font-size: 12px;
font-weight: 600;
margin-bottom: 5px;
}
.chat__message-to-me .chat__message-body {
background-color: white;
padding : 10px 5px;
border-radius: 2px;
font-size: 15px;
}
.chat__message-center {
display: flex;
flex-direction: column;
}
.chat__message-time {
margin-top: 50px;
margin-left: 5px;
}
.type-message {
width: 100%;
bottom:0;
position: fixed;
height: 45px;
background-color: #eeeeee;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 10px;
padding-right: 5px;
}
.type-message .fa-plus {
color: #b4b4b4;
}
.type-message__input {
width: 100%;
display: flex;
align-items: center;
}
.type-message__input input {
width: 100%;
padding: 10px;
border-style: none;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
}
.type-message__input #btnSend {
color: #523737;
background-color: #ffe934;
padding: 10px;
margin-right: 5px;
width: 13%;
}
.type-message__input .record-message i {
display: flex;
align-items: center;
justify-content: center;
}
.header__column {
height: 20px;
}
#receiver {
text-align: center;
margin-left: 233px;
width: 5em;
background-color: #a1c0d6;
height: 1.5em;
}
.enter {
text-align: center;
color: gray;
}
</style>
<script language="javascript">
var socket = null;
connect();
function connect() {
var ws = new WebSocket("ws://localhost:8080/ercg/echo.do");
socket = ws;
//이벤트 헨들러
ws.onopen = function() {
console.log('Info: connection opened.');
socket.send("1" + "," + gUserId + "," + "ENTER");
};
//소켓한테 메시지 받아옴
ws.onmessage = function (event) {
var sm = event.data;
//sl에는 sendId, content가 들어있음
var sl = sm.split(',');
let sendId = sl[0];
let content = sl[1];
let html = $("#nextMsg").html();
if(content == "ENTER") {
html += "<div class='enter'>" + sendId + "님이 들어오셨습니다.</div>";
} else if(content == "OUT") {
html += "<div class='enter'>" + sendId + "님이 나가셨습니다.</div>";
} else if(sendId != gUserId){
let currT = new Date().getHours() + ":" + new Date().getMinutes();
html += '<div class="chat__message chat__message-to-me"><div class="chat__message-center"><h3 class="chat__message-username">' + sendId
+ '</h3><span class="chat__message-body">' + content + '</span></div><span class="chat__message-time">'
+ currT + '</span></div>';
}
$("#nextMsg").html(html);
console.log("ReceiveMessage:" + event.data+'\n');
};
ws.onclose = function (event) {
console.log('Info: connection closed');
//setTimeout( function() {connect(); }, 1000); // retry connection!!
};
ws.onerror = function (err) { console.log('Error:', err); };
};
$(document).ready(function() {
$('#backChats').on('click', function(evt) {
location.replace("chats.jsp");
socket.send("1," + gUserId + "," + "OUT");
});
$('#btnSend').on('click', function(evt) {
let currT = new Date().getHours() + ":" + new Date().getMinutes();
let msg = $('#msg').val();
evt.preventDefault();
if (socket.readyState !== 1 ) return;
//protocol: RoomNum, 보내는id, 내용
socket.send("1," + gUserId + "," + msg);
let html = $("#nextMsg").html() + '<div class="chat__message chat__message-from-me"><span class="chat__message-time">'
+ currT + '</span><span class="chat__message-body">' + msg + '</span></div>';
$("#nextMsg").html(html);
$("#msg").val("");
});
$('#wsClose').on('click', function(e) {
socket.onclose();
});
});
</script>
</head>
<body class="body-chat">
<header class= "top-header chat-header">
<div class="header__column">
<i class="fa fa-chevron-left fa-lg" id="backChats"></i>
</div>
<div class="header__column">
<span class="header__text">ROOM1</span>
</div>
</header>
<main class="chat">
<div class="date-divider">
<span class="date-divider__text">화요일, 2018년 6월 19일</span>
</div>
<div id="nextMsg"></div>
</main>
<div class="type-message">
<div class="type-message__input">
<input type="text" id="msg"/>
<span id="btnSend">전송</span>
</div>
</div>
</body>
</html>
3. 결과화면
(1) Chats.jsp (채팅목록)
(2) ROOM1 대화방 모습
3개의 ID(dev, t1, t2)를 이용한 대화이다.
'Web > spring' 카테고리의 다른 글
[Spring] Class Not found / Class 'org.springframework.*' not found [config set: web-context] 오류 해결 (0) | 2021.04.05 |
---|---|
[Websocket] 웹 소켓 세션을 이용한 웹상에서 통신 (3) - RabbitMQ 띄우기 (0) | 2021.04.01 |
[Spring/Web Socket] 웹 소켓 세션을 이용한 웹상에서 통신 (1) (1) | 2021.03.19 |
[sqlMap] There is no statement named / sqlMap.xml 오류 (0) | 2021.03.02 |
[Spring] No mapping found for HTTP request with URI (0) | 2021.01.15 |