프로젝트를 진행하면서 웹소켓과 관련해서 개발할 일이 많아 꼭 구현해야하는 부분 중 하나가 웹소켓 연결 그리고 끊김을 감지하는 부분이었다. (모두 웹소켓 연결과 끊김을 감지하는 로직을 토대로 진행된다고 생각함)
*개발 ToDo*
1️⃣ 채팅 목록 unreadCount 숫자 구현
2️⃣ 채팅 메세지별 unreadCount 구현
목차
1. 웹소켓 연결, 끊김 코드 구현
2. 채팅 목록 unreadCount 로직
3. 2번 코드 구현
1. 웹소켓 연결, 끊김 감지 코드
: WebSocketListenerConfig 클래스에 구현을 진행했다. (생략한 부분에 원하는 로직을 넣으면 된다.)
@Component를 빼놓지 않고 넣어고 웹소켓 연결이 발생했을 때 이어지는 코드가 실행될 수 있도록 마무리해준다.
➕@Component는 왜 필요하지?
: 해당 어노테이션을 사용하면 스프링 컨테이너가 해당 클래스를 자동으로 빈으로 등록해 웹소켓 연결(이벤트)이 발생하면 @EventListener 가 정상적으로 동작할 수 있게 된다.
@Component
public class WebSocketListenerConfig {
// 연결
@EventListener(SessionConnectEvent.class)
public void onConnect(SessionConnectEvent event) {
(생략)
}
// 연결 종료
@EventListener
public void onDisconnectEvent(SessionDisconnectEvent event) {
(생략)
}
2. 채팅 목록 unreadCount 로직
1️⃣번의 경우 웹소켓 끊김 감지만 된다면 로직을 구현하는 것은 어렵지 않다.
*로직*
→ 웹소켓 연결이 끊겼을 때 당시 채팅방의 전체 메세지를 사용자의 '읽은 메세지 수' 칼럼에 저장한다.
(연결은 여기에서 쓰이지 않음)
→ 채팅방 목록을 조회할 때 조회 당시에 각 채팅방별 전체 메세지 수를 가져오고, 사용자의 채팅방 별 '읽은 메세지 수' 를 가져와 두 수의 차를 구해 프론트에 전달한다.
이렇게 되면 각 채팅방별 안읽은 채팅 메세지 수(unreadCount)를 구할 수 있다.
❓읽은 메세지 수는 어느 테이블에 저장되어 있나?
: chatlog 테이블을 두어 사용자의 읽은 메세지 수를 read_count 칼럼에 저장하고 있다.
웹소켓의 끊김을 기준으로 업데이트하며 로직을 구현한다.
❓꼭 웹소켓이 끊길 때를 기준으로 해야 하나?
: 사실 채팅방 나가기 api를 개발하여 진행해도 되는 부분이고 이렇게 하는 것이 쉽다.
하지만, 이렇게 구현했을 때 만약 사용자가 나가기 버튼이 아닌 뒤로 가기를 통해 나가게 된다면 이는 반영이 되지 않는다.
따라서 이러한 경우도 모두 빼놓지 않고 고려하기 위해선 웹소켓 끊김을 통해 구현하는 것이 좋다고 생각했다.
그럼 위 로직을 구현하기 위한 코드를 살펴보자.
3. 채팅 목록 unreadCount 로직 구현 코드
WebSocketListenerConfig
@EventListener
public void onDisconnectEvent(SessionDisconnectEvent event) {
String sessionId = (String) event.getMessage().getHeaders().get("simpSessionId");
chattingService.leaveChatRoom(sessionId);
}
ChattingService
→ 여기에서 세션을 사용해서 정보를 저장해둔 것을 볼 수 있는데 이 이유는 채팅메세지별 unreadCount 구현을 위해서 필요한 부분이라 다음 포스팅에서 좀 더 설명해보도록 하겠다.
// 채팅방에 퇴장했을 때 (웹소켓 끊김)
public void leaveChatRoom(String sessionId) {
for (Map.Entry<String, ChatSession> entry : chatParticipantInfos.entrySet()) {
if (entry.getKey().equals(sessionId)) {
Long userId = entry.getValue().getUserId();
Long roomId = entry.getValue().getRoomId();
readMessageCnt(roomId, userId);
chatParticipantInfos.remove(sessionId); // 채팅목록 unreadCount에선 필요없음
break;
}
}
}
@Transactional
public void readMessageCnt(Long chatRoomId, Long userPk) {
// 채팅방 나갈 시점에서의 메세지 개수 조회
int messageCnt = chattingRepository.countMessageByChatRoomId(chatRoomId);
// chatlog 테이블 속 readCount 업데이트
chatlogService.updateReadCount(chatRoomId, userPk, messageCnt);
}
ChatlogService
@Transactional
public void updateReadCount(Long chatRoomId, Long userPk, int messageCnt) {
chatLogRepository.updateCount(chatRoomId, userPk, messageCnt);
}
ChatlogRepository
@Modifying
@Query(
"UPDATE ChatLog c SET c.readCount=:readCount WHERE c.user.id=:userId AND c.chatRoom.id=:chatRoomId")
void updateCount(
@Param("chatRoomId") Long chatRoomId,
@Param("userId") Long userId,
@Param("readCount") int readCount);
다음은
2️⃣ 채팅 메세지별 unreadCount 구현
이어서 포스팅하도록 하겠습니다 :)
*참고 자료*
web-socket 연결 끊김 감지하기
🐧 Summary 클라이언트A가 서버와 소켓 연결을 끊을 경우(Disconnect) 서버에 해당 소식을 전달하고, 소식을 접한 서버가 나머지 클라이언트들에게 클라이언트A의 연결이 끊겼음을(퇴장했음을) 알리
hyeon9mak.github.io
WebSocket과 Stomp를 이용해서 채팅 구현하기
"모두의 텃밭"이라는 텃밭 중개 플랫폼에 채팅 기능을 추가하게 되면서 겪었던 문제들과그 방법들을 정리해보려고 합니다.HTTP와 비교해서 정리합니다.HTTP비연결성매번 연결 맺고 끊는 과정의
velog.io
(Spring) Stomp로 채팅 동시접속자 구현해보기
팀 프로젝트에서 채팅에 동시접속자를 데이터를 실시간으로 보내달라는 요청이 왔다. 동시접속자를 어떻게 실시간으로 보내야할까.. 많은 고민을 했고 구글링을 해봤는데 자료가 거의..없었다.
malangcow.tistory.com
'프로젝트' 카테고리의 다른 글
[프로젝트] 코드 리펙토링 도전기 step 1. 코드 분리하기 (0) | 2025.04.07 |
---|---|
[회고] 프로젝트 회고 <너쫌친당> (0) | 2025.02.17 |
웹소켓 STOMP로 채팅구현하기 (with Spring) +웹소켓 테스트 사이트 추천 (0) | 2024.11.17 |
[데이터베이스 설계] Null을 피해야 하는 이유? (4) | 2024.10.13 |
헥사고날 아키텍처? (Hexagonal Architecture) (1) | 2024.09.16 |