728x90
problem
채팅을 구현 하고 있는데 Websoket 을 테스트 할 곳이 없었다.
웹소켓은 제약이 많아서 socketJS + stomp 를 같이 사용하는데 채팅 기능을 테스트 할 곳이 없어서
간단히 html ,css,js 로 만들어서 테스트 해보았다.
아래 주소와 응답 필드들을 수정해서 잘 사용 해보도록 하자.
solution
이 파일을 설명해 보자면
헤더에 들어갈 JWT 토큰을 넣으면서 연결을 시도 한다.
입맛에 맞게 필드 수정 후 사용 한다.
- html 세팅을 아예 모르는 사람들을 위한 설명 -
1. VScode 를 설치한다.
2. 왼쪽 extentions 버튼을 찾아서 Live sever 를 검색해서 설치한다
3. 하나의 디렉토리에 아래 세개의 파일을 만들어 넣어둔다 ( html 과 css 는 만질게 없다.)
4. 설정들 ( 엔드포인트/ 응답필드 ) 등을 수정한다.
5. 인덱스 파일 오른쪽 클릭 후 Open with live server 를 클릭하면 브라우저가 뜬다
6. ㄱㄱㄱㄱㄱ~
1.html
#index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>간단한 채팅 - 사용자 1과 사용자 2</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- 사용자 1 채팅 화면 -->
<div class="chat-container" id="user1-chat">
<h2>사용자 1</h2>
<div class="header-input">
<label for="header1">JWT 토큰:</label>
<input type="text" id="header1" placeholder="JWT 토큰 입력...">
<button onclick="connect('user1')" id="connectBtn1">연결</button>
<span id="status1" class="status">미연결</span> <!-- 연결 상태 표시 -->
</div>
<div class="message-container" id="messages1"></div>
<div class="input-container">
<!-- 입력 필드를 textarea로 변경 -->
<textarea id="messageInput1" placeholder="메시지를 입력하세요..." rows="4"></textarea>
<button onclick="sendMessage('user1')">전송</button>
</div>
</div>
<!-- 사용자 2 채팅 화면 -->
<div class="chat-container" id="user2-chat">
<h2>사용자 2</h2>
<div class="header-input">
<label for="header2">JWT 토큰:</label>
<input type="text" id="header2" placeholder="JWT 토큰 입력...">
<button onclick="connect('user2')" id="connectBtn2">연결</button> <!-- 연결 버튼 추가 -->
<span id="status2" class="status">미연결</span> <!-- 연결 상태 표시 -->
</div>
<div class="message-container" id="messages2"></div>
<div class="input-container">
<!-- 입력 필드를 textarea로 변경 -->
<textarea id="messageInput2" placeholder="메시지를 입력하세요..." rows="4"></textarea>
<button onclick="sendMessage('user2')">전송</button>
</div>
</div>
<!-- JavaScript 파일 연결 -->
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
<script src="script.js"></script>
</body>
</html>
2.CSS
#styles.css
* {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 20px;
font-family: Arial, sans-serif;
background-color: #f5f5f5;
padding: 20px;
margin: 0;
}
.chat-container {
width: 300px;
padding: 20px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.header-input, .input-container {
margin-bottom: 10px;
}
.message-container {
height: 200px;
overflow-y: auto;
border: 1px solid #ddd;
padding: 10px;
margin-bottom: 10px;
}
.message {
margin: 5px 0;
}
3.JS
let stompClient1 = null;
let stompClient2 = null;
function connect(userId) {
//채팅방Id
const chatRoomId = "f1001";
const jwtToken = document.getElementById(`header${userId === 'user1' ? '1' : '2'}`).value;
const socket = new SockJS(`http://localhost:8080/chat`);
const stompClient = Stomp.over(socket);
const statusElement = document.getElementById(`status${userId === 'user1' ? '1' : '2'}`);
const connectBtn = document.getElementById(`connectBtn${userId === 'user1' ? '1' : '2'}`);
stompClient.connect(
{ Authorization: `${jwtToken}`, chatRoomId: chatRoomId }, function (frame) {
console.log(`Connected as ${userId}: ${frame}`);
statusElement.textContent = "연결됨"; // 연결 상태 업데이트
connectBtn.disabled = true; // 연결 버튼 비활성화
stompClient.subscribe('/client', function (message) {
const parsedMessage = JSON.parse(message.body);
if (parsedMessage.newMessage) {
// 새 메시지 추가
showMessage(parsedMessage.newMessage, userId);
}
// 읽음 상태 업데이트
updateReadStatus(parsedMessage.updateReadMessage, userId);
}, { Authorization: `${jwtToken}` });
fetchChatHistory(userId, jwtToken);
}, function (error) {
console.error(`Connection error for ${userId}:`, error);
statusElement.textContent = "연결 실패";
}
);
if (userId === 'user1') {
stompClient1 = stompClient;
} else {
stompClient2 = stompClient;
}
}
// 채팅 기록을 가져오는 함수
function fetchChatHistory(userId, jwtToken) {
const chatRoomId = "f1001"; // 실제 채팅방 ID를 동적으로 설정 가능
const page = 0; // 페이지 번호
fetch(`http://localhost:8080/api/customer/chat?page=${page}&chatRoomId=${chatRoomId}`, {
method: 'GET',
headers: {
'Authorization': `${jwtToken}`,
'Content-Type': 'application/json'
}
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch chat history');
}
return response.json();
})
.then(data => {
// data.data에 포함된 각 메시지를 화면에 표시
data.data.forEach(message => showMessage(message, userId));
})
.catch(error => {
console.error('Error fetching chat history:', error);
});
}
// 읽음 상태가 업데이트된 메시지들을 화면에 반영하는 함수
function updateReadStatus(updatedMessages, userId) {
updatedMessages.forEach(message => {
const messageElement = document.getElementById(`message-${message.messageId}`);
if (messageElement) {
// 기존 메시지의 읽음 상태 업데이트
messageElement.querySelector(".read-count").textContent = `읽음: ${message.readCount}`;
} else {
// 새로운 메시지면 추가
showMessage(message, userId);
}
});
}
// 메시지를 화면에 표시하는 함수
function showMessage(message, userId) {
const messageContainer = document.getElementById(`messages${userId === 'user1' ? '1' : '2'}`);
const messageElement = document.createElement('div');
messageElement.className = 'message';
messageElement.id = `message-${message.messageId}`; // 메시지 ID 설정
// 메시지 내용 구조
messageElement.innerHTML = `
<div><strong>${message.nickname}</strong> (${message.senderId})</div>
<div>${message.content}</div>
<small class="read-count">읽음: ${message.readCount}</small>
`;
messageContainer.appendChild(messageElement);
}
function sendMessage(userId) {
const messageInput = document.getElementById(`messageInput${userId === 'user1' ? '1' : '2'}`);
const messageContent = messageInput.value;
const chatRoomId = "f1001";
const jwtToken = document.getElementById(`header${userId === 'user1' ? '1' : '2'}`).value;
if (messageContent.trim() === '') return; // 빈 메시지일 경우 전송하지 않음
// 서버로 전송할 메시지 DTO 형식에 맞게 데이터 구성
const payload = JSON.stringify({
chatRoomId: chatRoomId,
message: messageContent
});
// 사용자에 따라 stompClient 선택
const stompClient = userId === 'user1' ? stompClient1 : stompClient2;
if (stompClient && stompClient.connected) {
stompClient.send('/server/customer/sendMessage', { Authorization: `${jwtToken}` }, payload);
messageInput.value = ''; // 전송 후 입력 필드 초기화
} else {
console.error(`User ${userId} is not connected.`);
}
}
수정 사항
위는 JS 파일의 설정이고
아래는 Springboot 의 설정이다 매핑 시켜서 수정하자 !
1. const socket = new SockJS(`http://localhost:8080/chat`);
-> override fun registerStompEndpoints(registry: StompEndpointRegistry)
{ registry.addEndpoint("/chat/")
2. stompClient.connect( { Authorization: `${jwtToken}`, chatRoomId: chatRoomId }, function (frame) {
-> 필자는 헤더에 채팅방Id와 JWT 토큰을 보내기 때문에 헤더에 토큰 정보를 넣었다 필요 없으면 삭제 하도록 하자.
3. stompClient.subscribe('/client', function (message)
-> override fun configureMessageBroker(config: MessageBrokerRegistry)
{ config.enableSimpleBroker("/client")
4. stompClient.send('/server/customer/sendMessage', { Authorization: `${jwtToken}` }, payload)
-> override fun configureMessageBroker(config: MessageBrokerRegistry)
{ config.setApplicationDestinationPrefixes("/server") }
-> @MessageMapping("/customer/sendMessage")
fun send
-> 첫번째 DestinationPrefixes 인 /server 와 MessageMapping 어노테이션의 /customer/sendMessage
를 합쳐 /server/customer/sendMessage 라는 엔드포인트로 보낸다
여기서도 마찬가지로 헤더에 JWT 토큰을 사용한다.
응답필드
서버 endpoint
http://localhost:8080
-> 자신에게 맞는 포트로 변경하자
메세지 보내기
chatRoomId: String,
message: String
-> chatRoomId 는 위에서 f1001 로 설정 해두었으니 필요 하면 변경 하자
웹소켓 연결 후 채팅방 내용 불러오기
fetchChatHistory(userId, jwtToken);
/customer/chat?page=${page}&chatRoomId=${chatRoomId}
-> /customer/chat 에 page 번호와 chatRoomId 를 받아서 보내자
응답객체
stompClient.subscribe('/client', function (message) {
const parsedMessage = JSON.parse(message.body);
if (parsedMessage.newMessage) {
// 새 메시지 추가
showMessage(parsedMessage.newMessage, userId);
}
// 읽음 상태 업데이트
updateReadStatus(parsedMessage.updateReadMessage, userId);
-> parsedMessage
안에 newMessage / updateReadMessage 라는 객체가 있다
newMessage
{ messageId :String,
senderId: String,
content:String,
readCount : int }
updateReadMessage
{ messageId : String,
readCount : int }
- 자바스크립트는 타입이 없다 -
728x90
'개-발 > Java + Spring + Kotlin' 카테고리의 다른 글
[Spring] 상대방 채팅 읽음 감지 (1) | 2024.11.17 |
---|---|
[spring] Cache 조회 성능을 최적화 Redis + Kotlin (0) | 2024.11.14 |
[Spring Batch] 반복 오류 제어 (0) | 2024.09.01 |
[Java] 정규표현식 regex 패키지 (0) | 2024.07.29 |
[Java] 정규표현식 regex (0) | 2024.07.29 |