그룹웨어 - 채팅 구현(7)

김채영·2024년 10월 17일

채팅

목록 보기
7/8

채팅 안읽음 처리

  • 채팅방 별 사용자가 읽지 않은 개수를 목록에 출력
  • 전송한 사용자는 메시지에 대해 읽음 처리가 바로 가능해야 함
  • 그룹 채팅방은 속한 사용자가 보낸 메시지 개수를 전부 확인 해야 함

채팅 안읽음 개수

  • 사용자가 페이지에 진입과 동시에 채팅방 목록에 해당 안읽음 개수를 검색
    <!-- 채팅방 별 안읽은 개수-->
    <select id="getUnreadCounts" parameterType="java.lang.Long" resultType="java.util.Map">
        SELECT COUNT(cm.chat_message_no) AS unread_count, cm.chat_room_no
        FROM fl_chat_message cm
        LEFT JOIN fl_chat_read_status cr ON cm.chat_message_no = cr.chat_message_no AND cr.member_no = #{memberNo}
        JOIN fl_chat_member ctm ON cm.chat_room_no = ctm.chat_room_no
        WHERE cr.chat_read_no IS NULL
        AND cm.chat_room_no IS NOT NULL
        AND ctm.member_no = #{memberNo}
        AND cm.chat_message_create_date >= ctm.chat_member_join_date AND cm.chat_sender_no NOT IN (0)
        GROUP BY cm.chat_room_no;

    </select>
  • 위의 쿼리문을 통해 현재 사용자의 번호를 가지고 채팅방 별 안읽은 개수를 모두 체크하여 서비스쪽으로 값을 넘김
  • Group By를 통해 같은 채팅방 번호끼리 개수를 확인 가능

실시간 반영

  • 페이지 진입 후, 실시간으로 보낸 메시지 개수 또한 증가 시켜야 함
  • 같은 방 사용자가 메시지를 전송과 동시에 실시간으로 같은 방 사용자들에게 안읽음 개수를 증가시켜 일괄 목록에 표현하도록 구현

채팅 읽음 처리 디비

//채팅 읽음 디비
CREATE TABLE fl_chat_read_status (
    chat_read_no INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '읽음 상태 번호',
    chat_message_no INT UNSIGNED NOT NULL COMMENT '메시지 번호',
    member_no INT UNSIGNED NOT NULL COMMENT '사원 번호',
    chat_read_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '읽은 시간',
    CONSTRAINT chat_read_message_no_fk
    FOREIGN KEY (chat_message_no) 
    REFERENCES fl_chat_message(chat_message_no)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    CONSTRAINT chat_read_emp_no_fk
    FOREIGN KEY (member_no) 
    REFERENCES fl_member(member_no)
    ON DELETE CASCADE
    ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT '읽음 상태';

//발신자 채팅 읽음 트리거

DELIMITER //

CREATE TRIGGER after_chat_message_insert
AFTER INSERT ON fl_chat_message
FOR EACH ROW
BEGIN
    IF NEW.chat_sender_no <> 0 THEN
        INSERT INTO fl_chat_read_status (chat_message_no, member_no, chat_read_date)
        VALUES (NEW.chat_message_no, NEW.chat_sender_no, CURRENT_TIMESTAMP);
    END IF;
END;
//
DELIMITER ;
  • 메시지 전송과 동시에 해당 사용자의 번호와 메시지 번호를 가지고 읽음 테이블에 값을 입력
  • 이를 통해 발신자는 읽음 처리가 동시에 가능

채팅방 진입 시 읽음 처리

  • 채팅방 진입 시, 읽지 않은 메시지를 일괄 읽음 처리하고 개수를 차감
  • 채팅방 번호와 현재 사용자 번호, 메시지 번호를 지니고 채팅 읽음 테이블에 값을 INSERT하여 읽음 처리 진행
	// 읽음 처리(js)
    function markAllMessagesAsRead(chatRoomNo) {
        const message = {
            type: 'markAsRead',
            chatRoomNo: chatRoomNo,
            currentMember: currentMember
        };

        socket.send(JSON.stringify(message));
    }
    
    ----------------------------------------------------------------------------------------------
    
    //채팅 읽음 처리(socket)
    private void handleChatRead(Map<String, Object> jsonMap, WebSocketSession session ,String type) throws Exception {
        Long currentMemberNo = Long.parseLong((String) jsonMap.get("currentMember"));
        Long roomNo = ((Number) jsonMap.get("chatRoomNo")).longValue();
        List<Long> messageNo = chatMessageService.markMessagesAsReadForChatRoom(currentMemberNo, roomNo);
        if (!messageNo.isEmpty()) {
            for (Long i : messageNo) {
                ChatReadDto chatReadDto = new ChatReadDto();
                chatReadDto.setMember_no(currentMemberNo);
                chatReadDto.setChat_message_no(i);

                chatMessageService.insertReadStatus(chatReadDto);
           }

        }
        Map<String, Object> response = new HashMap<>();
        response.put("type", "updateUnreadCount");
        response.put("chatRoomNo", roomNo);
       response.put("unreadCount", 0);

        session.sendMessage(new TextMessage(new ObjectMapper().writeValueAsString(response)));
    }
  • 해당 채팅방 목록을 클릭과 동시에 실시간으로 웹소켓으로 해당 정보를 가지고 감
  • 웹소켓 핸들러에서 읽음 처리 메소드로 이동
  • 해당 메소드에서 안읽음 메시지를 모두 조회
  • 조회한 메시지 번호와 사용자 번호를 가지고 읽음 처리 디비에 값 INSERT

채팅방 이용 시, 개수 증가 제한

  • 현재 채팅방을 이용하고 , 실시간 소통 중이라면 바로바로 확인 처리를 구성
  • 보냄과 동시에 사용자에게 해당 정보가 도착하고 그 채팅방 번호와 사용자가 이용 중인 채팅방 번호가 동일하면 디비에 읽음 처리 진행
  • 전역 변수로 채팅방 번호를 가지고 있다가 동작이 변화하면 이를 판단하여 진행

트러블 슈팅

  • 발신자의 읽음 처리를 고민하지 않고, 디비를 설계했음
  • 보낸 사용자도 읽음 개수가 실시간 반영된다는 점을 깨닫고 동시에 읽음 처리를 서비스에서 진행할까 고민
  • 하지만 구조상 너무 비효율적인 작업이라 생각함
  • 디비에 트리거를 추가해서 바로 읽음 처리될 수 있도록 구성해야 겠다고 생각
  • 위처럼 추가 후, 발신자는 개수 증가를 제한

깨달은 점

  • 실시간 반영과 디비 반영을 동시에 진행해야 하므로 어떤 순서로 구성해야 합리적일지 고민을 많이 함
  • 어느 부분에선 실시간 반영을 먼저하고 디비에 값을 넣으면 값 반영에 대해 오류가 발생하는 경우가 생김
  • 구성하는 부분과 설계순서에 대해 많이 고민할 필요가 있음
profile
백엔드 개발⭐

0개의 댓글