VSignal - 1:1 채팅
로그인을 하고 채팅을 시작합니다.
가장 단순한 1:1 채팅에 대해 생각해 봅니다.
채팅이란 것이 상대가 있어야 하는데,
상대가 접속하기를 마냥 기다리는 것도 그리 좋아 보이지 않습니다.
실시간 채팅이라고는 하지만,
SMS 처럼 상대가 접속하지 않았더라도 메시지를 보내는 것도 고려해 봅시다.
실시간 채팅앱 - 1:1 채팅
나의 말:
로그인 성공 후 채팅방 페이지로 이동하는 기능을 추가해줘
ChatGPT의 말:
좋아! 로그인 성공 후 채팅방 페이지(/chat)로 이동하도록 기능을 추가해볼게.
1. 채팅방 페이지(ChatView.vue) 생성
먼저, 로그인 후 이동할 채팅방 페이지를 만들어야 해.
src/views/ChatView.vue 파일을 생성하고 아래 코드를 추가해줘.
<!-- src/views/ChatView.vue -->
<template>
<v-container class="fill-height d-flex justify-center align-center">
<v-card class="pa-5" width="400">
<v-card-title class="text-center text-h5">채팅방</v-card-title>
<v-btn color="red" block class="mt-3" @click="handleLogout">로그아웃</v-btn>
</v-card>
</v-container>
</template>
<script>
import { logout } from "@/firebase";
import { useRouter } from "vue-router";
export default {
setup() {
const router = useRouter();
const handleLogout = async () => {
await logout();
router.push("/"); // 로그아웃 시 로그인 페이지로 이동
};
return { handleLogout };
},
};
</script>
기능:
✔️ 로그인 후 채팅방 페이지 /chat으로 이동
✔️ 로그아웃 버튼 추가 (클릭 시 로그인 페이지로 이동)
2. 로그인 후 채팅방 페이지로 이동
이제 AuthView.vue에서 로그인 성공 시 채팅방으로 이동하도록 변경할게.
<!-- src/views/AuthView.vue -->
<template>
<v-container class="fill-height d-flex justify-center align-center">
<v-card class="pa-5" width="400">
<v-card-title class="text-center text-h5 mb-4">
{{ isLogin ? "로그인" : "회원가입" }}
</v-card-title>
<v-form @submit.prevent="handleSubmit">
<v-text-field v-model="email" label="이메일" type="email" required />
<v-text-field v-model="password" label="비밀번호" type="password" required />
<v-btn type="submit" color="primary" block class="mt-3">
{{ isLogin ? "로그인" : "회원가입" }}
</v-btn>
</v-form>
<v-btn variant="text" block class="mt-2" @click="isLogin = !isLogin">
{{ isLogin ? "회원가입 하러가기" : "로그인 하러가기" }}
</v-btn>
</v-card>
</v-container>
</template>
<script>
import { ref } from "vue";
import { register, login } from "@/firebase";
import { useRouter } from "vue-router";
export default {
setup() {
const email = ref("");
const password = ref("");
const isLogin = ref(true);
const router = useRouter();
const handleSubmit = async () => {
try {
if (isLogin.value) {
await login(email.value, password.value);
alert("로그인 성공!");
} else {
await register(email.value, password.value);
alert("회원가입 성공!");
}
router.push("/chat"); // 로그인 또는 회원가입 성공 시 채팅방으로 이동
} catch (error) {
alert(error.message);
}
};
return { email, password, isLogin, handleSubmit };
},
};
</script>
변경사항:
✔️ useRouter()를 사용하여 router.push("/chat") 추가
✔️ 로그인 또는 회원가입 성공 시 자동으로 /chat 페이지로 이동
3. 라우터에 채팅방 경로 추가
이제 router.js에 채팅방 페이지 경로를 추가해야 해.
// src/router.js
import { createRouter, createWebHistory } from "vue-router";
import AuthView from "@/views/AuthView.vue";
import ChatView from "@/views/ChatView.vue";
const routes = [
{ path: "/", component: AuthView },
{ path: "/chat", component: ChatView },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
변경사항:
✔️ /chat 경로를 추가하여 채팅방 페이지 연결
4. 실행 및 테스트
npm run dev
이메일 & 비밀번호 입력 후 로그인하면 자동으로 채팅방 페이지(/chat)로 이동해야 해!
로그아웃을 누르면 다시 로그인 페이지로 돌아와야 해!
🎯 다음 단계: 로그인한 사용자만 채팅방 접근 가능하게 만들기!
지금은 누구나 /chat 경로에 직접 접속 가능한 상태야.
Firebase Auth 상태를 확인해서 로그인한 사용자만 채팅방에 접근하도록 만들까? 🚀
나의 말:
그래, 그리고 1:1 채팅 기능도 만들어 보자.
ChatGPT의 말:
좋아! 먼저 로그인한 사용자만 채팅방에 접근 가능하도록 설정한 후, 1:1 채팅 기능을 구현해보자. 🚀
1. 로그인한 사용자만 채팅방 접근 가능하게 만들기
Firebase에서 로그인 상태 확인
Vue Router의 beforeEach 훅을 이용해서, 로그인하지 않은 사용자는 /chat에 접근할 수 없도록 설정할 거야.
// src/router.js
import { createRouter, createWebHistory } from "vue-router";
import AuthView from "@/views/AuthView.vue";
import ChatView from "@/views/ChatView.vue";
import { getAuth, onAuthStateChanged } from "firebase/auth";
const routes = [
{ path: "/", component: AuthView },
{ path: "/chat", component: ChatView, meta: { requiresAuth: true } },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// 네비게이션 가드 추가 (로그인한 사용자만 채팅방 접근 가능)
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
next();
} else {
next("/");
}
});
} else {
next();
}
});
export default router;
변경사항:
✔️ meta: { requiresAuth: true } 설정 → 로그인한 사용자만 접근 가능
✔️ onAuthStateChanged()를 사용해 로그인 상태를 체크
✔️ 로그인하지 않았다면 /(로그인 페이지)로 리디렉션
2. 1:1 채팅 기능 구현하기
1. Firestore에 채팅 데이터 저장 구조
Firestore에 채팅 데이터를 저장할 거야. 1:1 채팅을 위해 다음과 같은 구조를 사용하자.
/chats/{chatId}/messages/{messageId}
각 1:1 채팅은 고유한 chatId를 가지며, 그 안에 메시지가 저장됨.
2. 채팅 데이터 Firestore에 저장
firebase.js에 Firestore 관련 로직을 추가하자.
// src/firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore, collection, addDoc, query, where, getDocs, orderBy } 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,
};
// Firebase 초기화
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
// 🔹 특정 유저와의 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`);
await addDoc(messagesRef, {
sender,
message,
timestamp: new Date(),
});
};
export { auth, db, getOrCreateChat, sendMessage };
추가된 기능:
✔️ getOrCreateChat(user1, user2) → 기존 1:1 채팅방을 찾거나 새로 생성
✔️ sendMessage(chatId, sender, message) → Firestore에 채팅 메시지 저장
3. 채팅 UI 추가
이제 1:1 채팅 UI를 만들어보자.
src/views/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 } from "vue";
import { auth, getOrCreateChat, sendMessage } 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([]);
// 로그아웃
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 = "";
};
// 채팅방 변경 감지
watch(selectedUser, async (newUser) => {
if (newUser) {
chatId.value = await getOrCreateChat(currentUser, newUser);
}
});
return { selectedUser, users, newMessage, handleSendMessage, messages, handleLogout, currentUser };
},
};
</script>
추가된 기능:
✔️ selectedUser를 선택하면 1:1 채팅방 자동 생성
✔️ handleSendMessage()로 Firestore에 메시지 저장
✔️ watch(selectedUser, ...)로 채팅방 자동 변경
🎯 마무리 & 다음 단계
지금까지 로그인한 사용자만 채팅방 접근 가능하도록 만들었고, 1:1 채팅 기능도 추가했어! 🚀
'Vue3, Firebase 프로젝트 - 채팅앱 VSignal' 카테고리의 다른 글
2. Vue3 프로젝트 - vite 빠른 프론트엔드 빌드 도구 (0) | 2025.03.11 |
---|---|
1. Vue3와 Firebase 프로젝트 - 실시간 채팅앱 VSignal 개발 (0) | 2025.03.10 |