Vue3, Firebase 프로젝트 - 채팅앱 VSignal

12. Vue3 Firebase 프로젝트 채팅앱 VSignal - 실시간 메시지 읽음 확인

그랜파 개발자 2025. 3. 23. 04:30

🔥 실시간 메시지 읽음 확인 기능 추가

상대방이 채팅방을 열면 읽지 않은 메시지가 자동으로 "읽음" 처리하고,
읽음 메시지 UI를 실시간으로 업데이트함으로써
사용자는 메시지 읽음 상태를 확인할 수 있습니다.

 

1. 메시지 읽음 기능 개요

상대방이 채팅방을 열면 메시지를 "읽음" 상태로 변경
Firebase에서 메시지 상태를 "unread" → "read" 로 업데이트
사용자가 메시지를 읽었는지 실시간 감지하여 UI 업데이트

2. Firebase에 메시지 읽음 상태 저장

Firebase의 채팅 메시지 구조 예시:

"messages": {
  "chatRoomId": {
    "messageId1": {
      "text": "안녕!",
      "sender": "user1",
      "receiver": "user2",
      "timestamp": 1710020000000,
      "status": "unread"
    }
  }
}

✔ "status" 필드를 추가하여 "unread" → "read" 로 변경

3. Store에서 메시지 읽음 처리

📌 store/chatStore.js 생성

import { defineStore } from "pinia";
import { getDatabase, ref, onValue, update } from "firebase/database";
import { auth } from "@/firebase";

export const useChatStore = defineStore("chat", {
  state: () => ({
    messages: [],
  }),

  actions: {
    // ✅ 채팅방 메시지 가져오기
    listenForMessages(chatRoomId) {
      const db = getDatabase();
      const messagesRef = ref(db, `messages/${chatRoomId}`);

      onValue(messagesRef, (snapshot) => {
        if (snapshot.exists()) {
          this.messages = Object.entries(snapshot.val()).map(([id, msg]) => ({
            id,
            ...msg,
          }));
        } else {
          this.messages = [];
        }
      });
    },

    // ✅ 메시지를 읽음 상태로 변경
    markMessagesAsRead(chatRoomId) {
      const db = getDatabase();
      const user = auth.currentUser;
      if (!user) return;

      this.messages.forEach((message) => {
        if (message.receiver === user.uid && message.status === "unread") {
          const messageRef = ref(db, `messages/${chatRoomId}/${message.id}`);
          update(messageRef, { status: "read" });
        }
      });
    },
  },
});

✔ listenForMessages(chatRoomId) → 실시간 메시지 가져오기
✔ markMessagesAsRead(chatRoomId) → 내가 받은 메시지를 "read"로 변경

4. 채팅 UI에서 읽음 여부 표시

📌 ChatRoom.vue 수정

<script setup>
import { useChatStore } from "@/store/chatStore";
import { useRoute } from "vue-router";
import { onMounted, watch } from "vue";

const chatStore = useChatStore();
const route = useRoute();
const chatRoomId = route.params.chatRoomId;

onMounted(() => {
  chatStore.listenForMessages(chatRoomId);
});

// ✅ 메시지 목록이 업데이트될 때 읽음 처리 실행
watch(() => chatStore.messages, () => {
  chatStore.markMessagesAsRead(chatRoomId);
});
</script>

<template>
  <v-card>
    <v-card-title>1:1 채팅</v-card-title>
    <v-divider></v-divider>

    <v-card-text>
      <v-list>
        <v-list-item
          v-for="message in chatStore.messages"
          :key="message.id"
          :class="{ 'read-message': message.status === 'read' }"
        >
          <v-list-item-content>
            <v-list-item-title>{{ message.text }}</v-list-item-title>
            <v-list-item-subtitle>
              {{ message.sender === 'me' ? "보낸 메시지" : "받은 메시지" }}
            </v-list-item-subtitle>
          </v-list-item-content>

          <v-list-item-action v-if="message.sender === 'me'">
            <v-icon :color="message.status === 'read' ? 'blue' : 'grey'">
              mdi-check
            </v-icon>
          </v-list-item-action>
        </v-list-item>
      </v-list>
    </v-card-text>
  </v-card>
</template>

<style>
.read-message {
  background-color: #e3f2fd;
}
</style>

✔ 상대방이 읽음 처리한 메시지는 파란색 체크 표시 (mdi-check)
✔ 읽은 메시지는 배경색 변경 (파란색으로 강조)

🎯 결론

✅ 상대방이 채팅방을 열면 읽지 않은 메시지가 자동으로 "읽음" 처리
✅ 읽음 메시지 UI를 실시간으로 업데이트
✅ 보낸 메시지에 읽음 확인 아이콘 (mdi-check) 추가