실시간 메시지 업데이트 기능 구현
새로운 메시지가 추가될 때마다 자동으로 화면을 업데이트하여
대화의 내용을 실시간으로 볼 수 있게 기능을 구현합니다.
🔥 onSnapshot이란?
onSnapshot은 Firebase Firestore에서 실시간 데이터 변화를 감지하는 함수입니다.
채팅 앱, 1:1 대화, 알림 기능 등 실시간 동기화 기능을 구현할 때 사용됩니다.
🔥 unsubscribe란?
unsubscribe는 Firebase Firestore에서 onSnapshot으로 구독한 실시간 데이터 감지를 중지하는 함수입니다.
즉, Firestore의 실시간 감지를 멈추고, 메모리 누수를 방지하기 위해 사용합니다.
Firestore의 onSnapshot()을 사용해서 실시간 메시지 업데이트를 구현해봅시다.
Firebase Firestore의 onSnapshot()을 이용하면
새로운 메시지가 추가될 때마다 자동으로 화면을 업데이트할 수 있습니다.
1. 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 업데이트
firebase.js
// src/firebase.js
import { initializeApp } from "firebase/app";
import { getAuth, createUserWithEmailAndPassword,
signInWithEmailAndPassword, signOut } from "firebase/auth";
import { getFirestore, collection, addDoc, query,
where, getDocs, orderBy, onSnapshot} from "firebase/firestore";
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID,
databaseURL: import.meta.env.VITE_FIREBASE_DATABASE_URL,
};
console.log(import.meta.env.VITE_FIREBASE_PROJECT_ID);
// Firebase 초기화
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
// 회원가입 함수
const register = (email, password) => {
return createUserWithEmailAndPassword(auth, email, password);
};
// 로그인 함수
const login = (email, password) => {
return signInWithEmailAndPassword(auth, email, password);
};
// 로그아웃 함수
const logout = () => {
return signOut(auth);
};
// 특정 유저와의 1:1 채팅 찾기 또는 생성
const getOrCreateChat = async (user1, user2) => {
const chatRef = collection(db, "chats");
const q = query(chatRef, where("users", "array-contains", user1));
const snapshot = await getDocs(q);
for (const doc of snapshot.docs) {
const chatData = doc.data();
if (chatData.users.includes(user2)) {
return doc.id; // 기존 채팅방 ID 반환
}
}
// 새로운 채팅방 생성
const newChat = await addDoc(chatRef, { users: [user1, user2] });
return newChat.id;
};
// 채팅 메시지 전송
const sendMessage = async (chatId, sender, message) => {
const messagesRef = collection(db, `chats/${chatId}/messages`);
console.log(messagesRef);
await addDoc(messagesRef, {
sender,
message,
timestamp: new Date(),
});
};
// 특정 채팅방의 실시간 메시지 수신
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 { auth, db, register, login, logout,
getOrCreateChat, sendMessage, listenForMessages
};
2. 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-title>
<strong>{{ msg.sender === currentUser ? "나" : "상대" }}:</strong>
{{ msg.message }}
</v-list-item-title>
</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(["myblog@email.com", "my-pwa@email.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. 실시간 메시지 업데이트 테스트
1️⃣ 두 개의 브라우저 창을 열고 로그인
2️⃣ 서로 다른 계정으로 대화 상대를 선택
3️⃣ 한쪽에서 메시지를 입력하면, 실시간으로 상대방 화면에 나타나는지 확인


'Vue3, Firebase 프로젝트 - 채팅앱 VSignal' 카테고리의 다른 글
24. [개발] Vue3 Firebase 프로젝트 - 실시간 1:1 채팅 앱 VSignal 개발 계획 (0) | 2025.04.06 |
---|---|
23. [개발] 실시간 메시지 업데이트 분석 - Vue3 Firebase 프로젝트 채팅앱 VSignal (0) | 2025.04.05 |
21. [개발] 채팅 기능 구현 - Vue3 Firebase 프로젝트 채팅앱 VSignal (0) | 2025.04.02 |
20. [개발] 로그인 후 채팅방 이동 - Vue3 Firebase 프로젝트 채팅앱 VSignal (0) | 2025.04.01 |
8. Pinia에서의 Composition API 스타일과 Options API 스타일 (0) | 2025.03.30 |