아이디어
•
사용자의 메시지 ‘읽음’ 이벤트를 웹소켓을 통해 서버에 전달합니다.
•
서버는 메시지 ‘읽음’ 이벤트를 전달받으면, 해당 메시지의 is_read 상태를 True로 변경하고,
•
웹소켓 그룹 내 모든 클라이언트에게 메시지 ‘읽음’ 이벤트 발생을 전달합니다.
사용자의 메시지 읽음 기준은?
1.
채팅방을 불러올 때
2.
채팅방을 클릭했을 때
3.
채팅방이 있는 탭을 활성화했을 때
1. 채팅방을 처음 불러올 때
•
채팅방은 웹소켓 연결과 동시에 불러옵니다.
•
따라서 웹소켓이 연결되었을 때 불러와지는 .onopen에 ‘읽음’ 이벤트를 전달하도록 했습니다.
// WebSocket 연결 이벤트 리스너
chatSocket.onopen = function () {
console.log("WebSocket 연결이 열렸습니다.");
// 웹소켓이 연결되면 메시지 '읽음' 이벤트를 서버로 보냅니다
let chatRoomId = JSON.parse(document.getElementById('selected-chatroom-id').textContent);
let readerId = JSON.parse({{ user.id }})
chatSocket.send(JSON.stringify({
'type': 'page_loaded',
'message': 'The page is now loaded',
'chatroom_id': chatRoomId,
'reader_id': readerId
}));
};
JavaScript
복사
•
실행 화면
2. 채팅방을 클릭했을 때
// 페이지가 클릭되었을 때 웹소켓으로 메시지 '읽음' 여부 보내기
document.addEventListener("click", function(event) {
// 특정 요소를 클릭했을 때는 이벤트 처리를 중단합니다.
let clickedElement = event.target;
if (clickedElement.classList.contains('ignore-page-clicked')) {
return
}
console.log('Page is clicked');
let chatRoomId = JSON.parse(document.getElementById('selected-chatroom-id').textContent);
let readerId = JSON.parse({{ user.id }})
chatSocket.send(JSON.stringify({
'type': 'page_clicked',
'message': 'The page is clicked',
'chatroom_id': chatRoomId,
'reader_id': readerId
}));
})
})
JavaScript
복사
•
실행 화면
3. 채팅방 탭을 활성화 했을 때
// 페이지 활성화되었을 때 웹소켓으로 메시지 '읽음' 여부 보내기
document.addEventListener('visibilitychange', function () {
if (document.visibilityState === 'visible') {
console.log('Page is now visible');
let chatRoomId = JSON.parse(document.getElementById('selected-chatroom-id').textContent);
let readerId = JSON.parse({{ user.id }})
chatSocket.send(JSON.stringify({
'type': 'page_visible',
'message': 'The page is now visible',
'chatroom_id': chatRoomId,
'reader_id': readerId
}));
}
})
JavaScript
복사
•
실행 화면
서버에서는 메시지 읽음 이벤트를 수신하여 2가지를 처리합니다.
1.
데이터베이스에서 메시지 읽음 상태(is-read) → True로 업데이트
2.
클라이언트에 메시지 읽음 이벤트 전달
# ...
async def receive(self, text_data):
text_data_json = json.loads(text_data)
# '메시지 읽음' 이벤트인 경우
if text_data_json['type'] in ["page_clicked", "page_visible", "page_loaded"]:
chatroom_id = text_data_json['chatroom_id']
user_id = self.scope['user'].id
reader_id = text_data_json['reader_id']
# 1. (데이터베이스) 메시지 -> '읽음'으로 업데이트
await self.update_messages(chatroom_id, user_id)
# 2. room group에 이벤트 전달
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'read_message',
'reader_id': reader_id
}
)
# 수신자가 현재 로그인한 사용자인 메시지를 모두 '읽음'으로 처리합니다.
@database_sync_to_async
def update_messages(self, chatroom_id, user_id):
messages = Message.objects.filter(chatroom=chatroom_id, receiver=user_id)
messages.update(is_read=True)
Python
복사
클라이언트에서 ‘메시지 읽음’ 이벤트를 수신하면 아래 규칙에 따라 처리합니다.
1.
상대가 보낸 메시지를 내가 읽을 경우
let messageElements = document.getElementsByClassName('from-you');
if (data.reader_id === {{ user.id }}) {
Array.from(messageElements).forEach(messageElement => {
let isReadElement = messageElement.querySelector('.is-read');
if (isReadElement != null) {
isReadElement.remove();
}
})
}
JavaScript
복사
•
상대에게 온 메시지 중 isReadElement(● 표시)가 있을 경우 삭제합니다.
2.
내가 보낸 메시지를 상대가 읽을 경우
messageElements = document.getElementsByClassName('from-me');
if (data.reader_id === {{ chat_partner.id }}) {
Array.from(messageElements).forEach(messageElement => {
let isReadElement = messageElement.querySelector('.is-read');
if (isReadElement != null) {
isReadElement.remove();
}
})
}
JavaScript
복사
•
내가 보낸 메시지 중 isReadElement(● 표시)가 있을 경우 삭제합니다.