
1. 문제 발생
웹소켓을 연결하고 채팅 테스트를 하던 중
특정 컴퓨터에서 메시지가 두 번 보내지는 오류가 있었다.
그런데 다시 새로고침을 하면 메시지가 한 개로 보였다.
DB의 채팅 메시지 테이블에도 메시지는 하나만 저장되어있었다.
2. 원인 추론
메시지의 상태를 저장하는 것으로 redux-toolkit 을 사용했다.
거기서 메시지를 보내고 받는 것을 setMessages 라는 액션을 이용해서
redux 의 messages 라고 지정된 state 에 저장했다.
그리고 그 messages 는 메시지를 렌더링 하는 컴포넌트에서 map 메서드를 이용해서
출력되도록 구성했다.
return (
<div className="chat-container">
{
messages?.map((message, index) => (
<ChatMessage messageInfo={message} key={index}/>
))
}
</div>
);
메시지 수신 로직
// 위의 코드 생략
onConnect: () => {
// 소켓 연결 시 호출 함수
// 채팅방 구독
clientRef.current?.subscribe(chatTopic, (message) => {
// 수신 메시지 처리
const chatMessage = JSON.parse(message.body).body.data;
// 메시지 리덕스에 저장
dispatch(setMessages([...messages, chatMessage]));
});
},
메시지 송신 로직
const sendMessage = (message: string) => {
const senderId = Number(extractUserIdFromCookie());
if (!chatroomId) return;
const chatSend = `/app/${chatroomId}/chatmsg`;
if (clientRef.current?.connected) {
const messageObj = {
chatroomId,
senderId,
chatmsgContent: message,
createdAt: new Date().toISOString(),
messageType : 'CHAT'
};
clientRef.current.publish({
destination: chatSend,
body: JSON.stringify(messageObj),
});
dispatch(setMessages([...messages, messageObj]));
}
};
문제는 메시지 수신 로직과 메시지 송신 로직 둘다 setMessages 액션을 사용하고 있기 때문이었다.
즉 송신할 때 dispatch 가 두 번 되었던 것이다.
3. 해결 방안
1차)
1차적으로는 그렇다면 송신 로직에서 보낸 사람이 '나' 임을 확인한 다음에
내가 맞다면 dispatch가 되지 않도록 조건문을 걸어주었다.
// 위의 코드 생략
onConnect: () => {
// 소켓 연결 시 호출 함수
// 채팅방 구독
clientRef.current?.subscribe(chatTopic, (message) => {
// 수신 메시지 처리
const chatMessage = JSON.parse(message.body).body.data;
if(chatMessage.senderId !== userId) { // 보낸 사람 ID와 나의 ID 가 일치하는지 확인
// 메시지 리덕스에 저장
dispatch(setMessages([...messages, chatMessage]));
}
});
},
그래서 일단은 두 번 보내지는 것을 고칠 수 있었다.
왜 1차라고 표현했냐면 다른 문제를 해결하다가 이럴 필요가 없었다는 것을 깨달았기 때문이다.
2차)
앞에서 말했다시피 다른 문제를 해결하다가 깨달았다.
(무슨 문제냐면 채팅 말풍선 옆에 시간을 표시했는데 송신 시간과 수신 시간의 불일치 문제였다. 다음 포스팅에서 다룰 예정이다.)
dispatch를 송신과 수신.. 둘 다 해줄 필요가 없다는 것을 말이다.
내가 사용한 stomp protocol 은 publish-subscribe 모델을 사용한다.
클라이언트가 특정 주제(topic)를 구독(subscribe) 하고,
서버는 이 특정 주제에 메시지를 발행(publish) 한다.
채팅방 생성 : topic 생성
채팅방 입장 : topic 구독
메시지 송수신 : 해당 topic 으로 메시지 송신(pub), 메시지 수신(sub)
이런 구조다.
즉, 메시지를 송신하고 있는 '나'도 해당 주제(topic) 을 구독(subscribe) 하고 있기 때문에
내가 보낸 메시지가 다른 사람에게 전달되는 것을 물론 나에게도 전달이 되는 것이다.
즉, 메시지 전송 함수에서 따로 처리를 안해줘도 되는 것이었다!!!
그래서 결국 로직을 변경해서
최종로직은
메시지 수신 로직
// 위의 코드 생략
onConnect: () => {
// 소켓 연결 시 호출 함수
// 채팅방 구독
clientRef.current?.subscribe(chatTopic, (message) => {
// 수신 메시지 처리
const chatMessage = JSON.parse(message.body).body.data;
// 메시지 리덕스에 저장
dispatch(setMessages([...messages, chatMessage]));
}
});
},
메시지 송신 로직
const sendMessage = (message: string) => {
const senderId = Number(extractUserIdFromCookie());
if (!chatroomId) return;
const chatSend = `/app/${chatroomId}/chatmsg`;
if (clientRef.current?.connected) {
const messageObj = {
chatroomId,
senderId,
chatmsgContent: message,
createdAt: new Date().toISOString(),
messageType : 'CHAT'
};
clientRef.current.publish({
destination: chatSend,
body: JSON.stringify(messageObj),
});
}
};
이런식으로 수신 로직에서 굳이 나를 확인하는 과정을 빼고 (어차피 화면 표시하는 곳에서 나를 표시하는 처리는 따로 해줬음)
송신 로직에서 setMessages 에 dispatch 하는 액션을 빼주었다.
4. 결과
결과는 아래처럼 내가 보낸 메시지가 중복되는 것 없이 정상적으로 전달된다는 것이다.

이 원인은 내가 stomp가 어떤 원리로 동작하는지 잘 모르고 썼기 때문에 발생한 문제였다.
그래도 이 문제를 겪고 나서 원인도 알고 어떤 원리로 동작하는지 알았기 때문에 얻은 것이 많다고 생각한다...
5. Reference
https://velog.io/@hoyun7443/WebSocket%EC%9D%98-Stomp
WebSocket의 Stomp
Websocket은 메시징 방식만 잘 정의한다면 좋은 Server/Client 소켓 서버를 완성할 수 있다.하지만, 단순한 통신 구조로 인해 WebSocket만을 이용해 소켓서버를 구현하면 해당 메세지가 어떤 요청인지, 어
velog.io
https://dev-gorany.tistory.com/235#google_vignette
[Spring Boot] WebSocket과 채팅 (3) - STOMP
[Spring Boot] WebSocket과 채팅 (2) - SockJS [Spring Boot] WebSocket과 채팅 (1) 일전에 WebSocket(웹소켓)과 SockJS를 사용해 Spring 프레임워크 환경에서 간단한 하나의 채팅방을 구현해본 적이 있다. [Spring MVC] Web Soc
dev-gorany.tistory.com
'Project > SeSAC 3차 팀 프로젝트' 카테고리의 다른 글
| 웹 개발자 부트캠프 과정 3차 팀 프로젝트 회고 #2 (0) | 2024.11.17 |
|---|---|
| 3차 팀 프로젝트 채팅 기능 트러블 슈팅 #2 (부제 : 채팅 송수신 시간이 이상해요..! ㅠ) (0) | 2024.11.14 |
| 3차 팀 프로젝트 리팩토링 #1 (채팅 페이지 UI 변경) (1) | 2024.11.10 |
| 웹 개발자 부트캠프 과정 3차 팀 프로젝트 회고 #1 (0) | 2024.11.08 |
| [React] WebSocket으로 단체 채팅방 만들기(feat. Spring boot) (1) | 2024.11.07 |