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

4. Vue3 프로젝트 실시간 채팅앱 VSignal - 실시간 업데이트

그랜파 개발자 2025. 3. 14. 03:57

채팅앱에 접속한 상태라면 메시지가 실시간으로 업데이트 되어야 합니다.
Firestore의 onSnapshot()을 이용하여 새로운 메시지가 추가될 때마다 자동으로 화면을 업데이트 할 수 있다고 하네요.

나의 말:

실시간 메시지 업데이트부터 구현해 보자

ChatGPT의 말:

좋아! Firestore의 onSnapshot()을 사용해서 실시간 메시지 업데이트를 구현해보자.

1. Firestore에서 실시간 메시지 수신

Firebase Firestore의 onSnapshot()을 이용하면 새로운 메시지가 추가될 때마다 자동으로 화면을 업데이트할 수 있어.

firebase.js에서 실시간 메시지 가져오기 함수 추가

// src/firebase.js
import { getFirestore, collection, addDoc, query, where, orderBy, onSnapshot } from "firebase/firestore";

const db = getFirestore();

// 🔹 특정 채팅방의 실시간 메시지 수신
const listenForMessages = (chatId, callback) => {
  const messagesRef = collection(db, `chats/${chatId}/messages`);
  const q = query(messagesRef, orderBy("timestamp"));

  return onSnapshot(q, (snapshot) => {
    const messages = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
    callback(messages);
  });
};

export { listenForMessages };

설명:

✔ onSnapshot(q, callback) → Firestore에서 실시간 데이터 수신
✔ 메시지가 변경될 때마다 callback 함수를 실행하여 UI 업데이트

2. Vue에서 실시간 메시지 적용

이제 ChatView.vue에서 실시간 메시지 업데이트를 적용해보자.

ChatView.vue 업데이트

<!-- src/views/ChatView.vue -->
<template>
  <v-container class="fill-height d-flex flex-column">
    <v-card class="pa-5" width="400">
      <v-card-title class="text-center text-h5">1:1 채팅</v-card-title>

      <v-select v-model="selectedUser" :items="users" label="대화 상대 선택" />
      <v-divider class="my-2"></v-divider>

      <v-list v-if="messages.length > 0">
        <v-list-item v-for="msg in messages" :key="msg.id">
          <v-list-item-content>
            <v-list-item-title>
              <strong>{{ msg.sender === currentUser ? "나" : "상대" }}:</strong>
              {{ msg.message }}
            </v-list-item-title>
          </v-list-item-content>
        </v-list-item>
      </v-list>

      <v-text-field v-model="newMessage" label="메시지 입력" @keyup.enter="handleSendMessage" />
      <v-btn color="primary" block class="mt-3" @click="handleSendMessage">전송</v-btn>
      <v-btn color="red" block class="mt-3" @click="handleLogout">로그아웃</v-btn>
    </v-card>
  </v-container>
</template>

<script>
import { ref, watch, onUnmounted } from "vue";
import { auth, getOrCreateChat, sendMessage, listenForMessages } from "@/firebase";
import { useRouter } from "vue-router";

export default {
  setup() {
    const router = useRouter();
    const currentUser = auth.currentUser?.email;
    const selectedUser = ref(""); // 대화할 사용자 선택
    const users = ref(["user1@example.com", "user2@example.com"]); // 예제 사용자 리스트
    const chatId = ref(null);
    const newMessage = ref("");
    const messages = ref([]);
    let unsubscribe = null; // Firestore 리스너 해제 함수

    // 로그아웃
    const handleLogout = async () => {
      await auth.signOut();
      router.push("/");
    };

    // 메시지 전송
    const handleSendMessage = async () => {
      if (newMessage.value.trim() === "" || !chatId.value) return;
      await sendMessage(chatId.value, currentUser, newMessage.value);
      newMessage.value = "";
    };

    // 채팅방 변경 감지 → Firestore 실시간 메시지 업데이트
    watch(selectedUser, async (newUser) => {
      if (unsubscribe) unsubscribe(); // 기존 리스너 해제
      if (newUser) {
        chatId.value = await getOrCreateChat(currentUser, newUser);
        unsubscribe = listenForMessages(chatId.value, (newMessages) => {
          messages.value = newMessages;
        });
      }
    });

    // 컴포넌트 언마운트 시 Firestore 리스너 해제
    onUnmounted(() => {
      if (unsubscribe) unsubscribe();
    });

    return { selectedUser, users, newMessage, handleSendMessage, messages, handleLogout, currentUser };
  },
};
</script>

변경사항:

✔ Firestore에서 실시간 메시지 수신 (listenForMessages())
✔ 채팅방 변경 시 기존 리스너 해제 후 새로 등록
✔ 컴포넌트 언마운트 시 onUnmounted()로 Firestore 리스너 해제

3. 테스트하기

  • 실시간 메시지 업데이트 테스트 방법
  • 두 개의 브라우저 창을 열고 로그인
  • 서로 다른 계정으로 대화 상대를 선택
  • 한쪽에서 메시지를 입력하면, 실시간으로 상대방 화면에 나타나는지 확인

마무리 & 다음 단계

🔥 이제 실시간 메시지 업데이트가 동작해! 🚀

다음 할 일:

🔹 채팅 목록에서 최근 메시지 표시
🔹 메시지 전송 시간 표시
🔹 온라인/오프라인 상태 표시

👉 어떤 기능부터 추가하고 싶어? 😊