고마구의 개발 블로그
240722 14주차 월요일 - Spring 06 본문
1:1 채팅 기능을 구현하기 위해서는 각 세션을 개별적으로 관리해야 합니다. 이를 위해 클라이언트와 서버 모두에서 사용자 구분과 메시지 전송 로직을 변경해야 합니다. 여기서는 Spring Boot와 WebSocket을 이용하여 1:1 채팅 기능을 구현하는 방법을 설명하겠습니다.
1. WebSocketConfig 클래스
기본 설정은 동일하지만, 사용자별 메시지 전송을 위해 사용자 ID를 사용할 수 있도록 합니다.
WebSocketConfig.java
java
package com.example.websocket;
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;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/Chatting").setAllowedOrigins("*");
}
}
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;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/Chatting").setAllowedOrigins("*");
}
}
2. WebSocket 핸들러
사용자별로 메시지를 관리할 수 있도록 WebSocket 핸들러를 수정합니다.
ChatWebSocketHandler.java
java
package com.example.websocket;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ChatWebSocketHandler extends TextWebSocketHandler {
private Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String userId = getUserId(session);
sessions.put(userId, session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String userId = getUserId(session);
String payload = message.getPayload();
// 메시지 형식: "toUserId:message"
String[] parts = payload.split(":", 2);
if (parts.length == 2) {
String toUserId = parts[0];
String msg = parts[1];
WebSocketSession toSession = sessions.get(toUserId);
if (toSession != null && toSession.isOpen()) {
toSession.sendMessage(new TextMessage(userId + ":" + msg));
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
String userId = getUserId(session);
sessions.remove(userId);
}
private String getUserId(WebSocketSession session) {
// 세션에서 사용자 ID를 가져오는 로직 구현
// 예: session.getAttributes().get("userId");
return session.getUri().getQuery().split("=")[1];
}
}
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ChatWebSocketHandler extends TextWebSocketHandler {
private Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String userId = getUserId(session);
sessions.put(userId, session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String userId = getUserId(session);
String payload = message.getPayload();
// 메시지 형식: "toUserId:message"
String[] parts = payload.split(":", 2);
if (parts.length == 2) {
String toUserId = parts[0];
String msg = parts[1];
WebSocketSession toSession = sessions.get(toUserId);
if (toSession != null && toSession.isOpen()) {
toSession.sendMessage(new TextMessage(userId + ":" + msg));
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
String userId = getUserId(session);
sessions.remove(userId);
}
private String getUserId(WebSocketSession session) {
// 세션에서 사용자 ID를 가져오는 로직 구현
// 예: session.getAttributes().get("userId");
return session.getUri().getQuery().split("=")[1];
}
}
3. 채팅 기록을 처리하는 ChatController
채팅 기록을 데이터베이스에서 불러오고 저장하는 컨트롤러를 작성합니다.
ChatController.java
java
package com.example.websocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
@RestController
public class ChatController {
@Autowired
private JdbcTemplate jdbcTemplate;
@GetMapping("/Chat_Select.do")
public String getChatHistory(String userId, String otherUserId) {
String sql = "SELECT CHAT_ARTICLE, CHAT_TIME FROM CHATTING WHERE (USER_ID = ? AND OTHER_USER_ID = ?) OR (USER_ID = ? AND OTHER_USER_ID = ?) ORDER BY CHAT_TIME";
List<Map<String, Object>> chats = jdbcTemplate.queryForList(sql, userId, otherUserId, otherUserId, userId);
StringBuilder response = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?><chats><chat>");
for (Map<String, Object> chat : chats) {
response.append("<chat_Article>")
.append(chat.get("CHAT_ARTICLE"))
.append("</chat_Article>");
}
response.append("</chat></chats>");
return response.toString();
}
@GetMapping("/Chat_Save.do")
public void saveChat(String userId, String otherUserId, String chatArticle) {
String sql = "INSERT INTO CHATTING (USER_ID, OTHER_USER_ID, CHAT_ARTICLE, CHAT_TIME) VALUES (?, ?, ?, ?)";
jdbcTemplate.update(sql, userId, otherUserId, chatArticle, new Timestamp(System.currentTimeMillis()));
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
@RestController
public class ChatController {
@Autowired
private JdbcTemplate jdbcTemplate;
@GetMapping("/Chat_Select.do")
public String getChatHistory(String userId, String otherUserId) {
String sql = "SELECT CHAT_ARTICLE, CHAT_TIME FROM CHATTING WHERE (USER_ID = ? AND OTHER_USER_ID = ?) OR (USER_ID = ? AND OTHER_USER_ID = ?) ORDER BY CHAT_TIME";
List<Map<String, Object>> chats = jdbcTemplate.queryForList(sql, userId, otherUserId, otherUserId, userId);
StringBuilder response = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?><chats><chat>");
for (Map<String, Object> chat : chats) {
response.append("<chat_Article>")
.append(chat.get("CHAT_ARTICLE"))
.append("</chat_Article>");
}
response.append("</chat></chats>");
return response.toString();
}
@GetMapping("/Chat_Save.do")
public void saveChat(String userId, String otherUserId, String chatArticle) {
String sql = "INSERT INTO CHATTING (USER_ID, OTHER_USER_ID, CHAT_ARTICLE, CHAT_TIME) VALUES (?, ?, ?, ?)";
jdbcTemplate.update(sql, userId, otherUserId, chatArticle, new Timestamp(System.currentTimeMillis()));
}
}
4. Spring Boot 애플리케이션 클래스
Spring Boot 애플리케이션 클래스를 작성합니다.
WebSocketApplication.java
java
package com.example.websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebSocketApplication {
public static void main(String[] args) {
SpringApplication.run(WebSocketApplication.class, args);
}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebSocketApplication {
public static void main(String[] args) {
SpringApplication.run(WebSocketApplication.class, args);
}
}
5. 클라이언트 HTML 파일 수정
클라이언트가 사용자 ID를 전송할 수 있도록 HTML 파일을 수정합니다.
Chatting.jsp
html
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.ui/1.12.1/jquery-ui.js"></script>
<style>
#messageWindow {
padding-left: 10px;
width: 275px;
height: 340px;
line-height: 20px;
resize: none;
outline: none;
border: 0px;
background-color: #fafbfc;
}
#inputMessage {
width: 227px;
height: 25px;
}
#submit_btn {
width: 40px;
height: 30px;
}
fieldset {
margin-top: 10px;
margin-left: 7px;
border: 0px;
}
</style>
</head>
<body>
<fieldset onkeydown="key(event.keyCode)">
<textarea id="messageWindow" readonly></textarea>
<br/>
<input id="inputMessage" type="text"/>
<input id="submit_btn" type="submit" value="전송" onclick="send()" />
</fieldset>
</body>
<script type="text/javascript">
var nick = '${login_Name}';
var toUserId = 'admin'; // 1:1 채팅 상대방의 사용자 ID
var textarea = document.getElementById("messageWindow");
var webSocket = new WebSocket('ws://localhost:8082/manager2/Chatting?userId=' + nick);
var inputMessage = document.getElementById('inputMessage');
function key(e){
if(e==13){
send();
}
}
webSocket.onerror = function(event) {
onError(event)
};
webSocket.onopen = function(event) {
onOpen(event)
};
webSocket.onmessage = function(event) {
onMessage(event)
};
function onMessage(event) {
textarea.value += event.data + "\n";
document.getElementById("messageWindow").scrollTop =
document.getElementById("messageWindow").scrollHeight;
}
function onOpen(event) {
$.ajax({
url: 'Chat_Select.do',
dataType: 'text',
data: {
userId: nick,
otherUserId: toUserId
},
success: function(data){
$(data).find('chat').each(function(){
var count = $('chat_Article', this).text();
textarea.value += count;
document.getElementById("messageWindow").scrollTop =
document.getElementById("messageWindow").scrollHeight;
})
}
})
}
function onError(event) {
alert(event.data);
}
function send() {
if(inputMessage.value!=""){
textarea.value += nick+": "+ inputMessage.value + "\n";
webSocket.send(toUserId + ":" + inputMessage.value);
$.ajax({
url: 'Chat_Save.do',
data: {
userId: nick,
otherUserId: toUserId,
chatArticle: inputMessage.value
}
});
inputMessage.value = "";
document.getElementById("messageWindow").scrollTop =
document.getElementById("messageWindow").scrollHeight;
}
}
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.ui/1.12.1/jquery-ui.js"></script>
<style>
#messageWindow {
padding-left: 10px;
width: 275px;
height: 340px;
line-height: 20px;
resize: none;
outline: none;
border: 0px;
background-color: #fafbfc;
}
#inputMessage {
width: 227px;
height: 25px;
}
#submit_btn {
width: 40px;
height: 30px;
}
fieldset {
margin-top: 10px;
margin-left: 7px;
border: 0px;
}
</style>
</head>
<body>
<fieldset onkeydown="key(event.keyCode)">
<textarea id="messageWindow" readonly></textarea>
<br/>
<input id="inputMessage" type="text"/>
<input id="submit_btn" type="submit" value="전송" onclick="send()" />
</fieldset>
</body>
<script type="text/javascript">
var nick = '${login_Name}';
var toUserId = 'admin'; // 1:1 채팅 상대방의 사용자 ID
var textarea = document.getElementById("messageWindow");
var webSocket = new WebSocket('ws://localhost:8082/manager2/Chatting?userId=' + nick);
var inputMessage = document.getElementById('inputMessage');
function key(e){
if(e==13){
send();
}
}
webSocket.onerror = function(event) {
onError(event)
};
webSocket.onopen = function(event) {
onOpen(event)
};
webSocket.onmessage = function(event) {
onMessage(event)
};
function onMessage(event) {
textarea.value += event.data + "\n";
document.getElementById("messageWindow").scrollTop =
document.getElementById("messageWindow").scrollHeight;
}
function onOpen(event) {
$.ajax({
url: 'Chat_Select.do',
dataType: 'text',
data: {
userId: nick,
otherUserId: toUserId
},
success: function(data){
$(data).find('chat').each(function(){
var count = $('chat_Article', this).text();
textarea.value += count;
document.getElementById("messageWindow").scrollTop =
document.getElementById("messageWindow").scrollHeight;
})
}
})
}
function onError(event) {
alert(event.data);
}
function send() {
if(inputMessage.value!=""){
textarea.value += nick+": "+ inputMessage.value + "\n";
webSocket.send(toUserId + ":" + inputMessage.value);
$.ajax({
url: 'Chat_Save.do',
data: {
userId: nick,
otherUserId: toUserId,
chatArticle: inputMessage.value
}
});
inputMessage.value = "";
document.getElementById("messageWindow").scrollTop =
document.getElementById("messageWindow").scrollHeight;
}
}
</script>
</html>
'KDT풀스택과정 공부' 카테고리의 다른 글
240724 14주차 수요일 - Spring 08 (0) | 2024.07.24 |
---|---|
240723 14주차 화요일 - Spring 07 (0) | 2024.07.23 |
240721 13주차 토요일 - 지도만들기 (0) | 2024.07.20 |
240719 금요일 (0) | 2024.07.19 |
240719 13주차 금요일 - Spring 05 (0) | 2024.07.19 |