이런식으로 채팅리스트를 db에서 가져오려고 한다.
개발단에서는
채팅리스트의 형식을 List<ChatModel>
이라고 생각하고
ChatModel
은 상대프사URL /상대닉네임 /마지막대화 /마지막대화시간
같은 정보가 들어가 있으면 좋을 것 같다.
DB(파베) 에는 아래와 같이 저장된다.
"chatRooms": {
"chatRoomID": {
"firstUID": ..., // 먼저 대화시작 유저 uid
"oppUID": ..., // 상대 유저 uid
"lastText": ..., // 마지막 대화 텍스트
"lastTime": ..., // 마지막 대화 텍스트 생성 시간
},
"chatRoomID": { ... },
"chatRoomID": { ... },
.
.
.
}
DB에 저장된 firstUID
, oppUID
의 값이
로그인유저의 uid
의 값과 같은 채팅 리스트 데이터들만 모아서 가져왔다.
// =============================================
// 🚀 GET List<ChatList> Collection
// =============================================
Future<QuerySnapshot<Map<String, dynamic>>> getListChatCollection(
{required String loginUID}) async {
return await _db
.collection("chatRooms")
.where(
Filter.or(
Filter("firstUID", isEqualTo: loginUID),
Filter("oppUID", isEqualTo: loginUID),
),
)
.get();
}
firstUID
: 먼저 대화를 건 유저
oppUID
: 대화 상대 유저
로그인유저가 두 유저중에 어떤 유저인지를 구분하고,
그 유저의 정보를 DB에서 가져온다.
// snapshot
final snapshot =
await _chatRepository.getListChatCollection(
loginUID: loginUID);
// 각각의 채팅 Collection 접근
final chatList = snapshot.docs.map((doc) async {
// 상대방 UID
final otherUID =
newChatList.firstUID == loginUID ?
newChatList.oppUID : loginUID;
// 상대유저 정보
final otherUser = await ref.read(userRepo)
.getUserCollection(otherUID);
...
}).toList();
// ChatModel
class ChatModel {
final String otherUID,
final String otherNickname,
final String otherAvatarURL,
final String lastText,
final String lastTime,
}
상대방의 정보와 함께 DB정보를 ChatModel
클래스로 재생성한다.
그렇게 채팅리스트 데이터를 List<ChatModel>
형식으로 반환한다.
// ✅ chatList = List<Future<ChatListModel>>
final chatList = snapshot.docs.map((doc) async {
...
final newChatModel = ChatModel(
otherUID: otherUID, // 상대방 UID
otherNickname: otherUser.nickname, // 상대방 닉네임
otherAvatarURL: otherUser.avatarURL, // 상대방 프사 url
lastText: doc.data()["lastText"], // 마지막 텍스트
lastTime: doc.data()["lastTime"], // 마지막 텍스트 생성날짜
);
return newChatModel;
}).toList();
생각대로 코드를 짜봤는데, DB접근을 좀 더 줄이고 과정을 간소화시켜보자.
어떻게 하면 좀 더 쉽게 로그인유저의 채팅리스트를 불러올 수 있을까..?
Firebase 클라우드 함수(CloudFunctions)를 이용하여
처음 채팅방을 생성할 때, 두명의 유저 컬렉션에 각각의 채팅방에 대한 상대방 정보를 저장해놓으면 위와 같이 누가 로그인 사용자이고, 누가 상대방인지 구분하지 않아도 된다.
// ✅ 채팅 컬렉션 구조
"chatRooms": {
"chatRoomID": {
"firstUID": ..., // 먼저 대화시작 유저 uid
"oppUID": ..., // 상대 유저 uid
"lastText": ..., // 마지막 대화 텍스트
"lastTime": ..., // 마지막 대화 텍스트 생성 시간
},
"chatRoomID": { ... },
"chatRoomID": { ... },
.
.
.
},
// ✅ 유저 컬렉션 구조
"users": {
"userID": {
"uid": ...,
"displayName": ...,
...
// ✅ 나의 채팅 컬렉션 생성예정
"myChats": {
"chatRoomID:" ..., // 채팅방 id
"oppUID": ..., // 상대방 유저 id
"oppDisplayname": ..., // 상대방 닉네임
"oppAvatarURL": ..., // 상대방 프사 url
"lastText": ..., // 두 유저의 마지막 대화텍스트
"lastTime": ..., // 마지막 대화텍스트 생성 시간
},
},
"userID": { ... },
"userID": { ... },
.
.
.
},
채팅 컬렉션(chatRoom)이 생성 될 때 클라우드함수로 listen
하여
각각의 유저 컬렉션(User)
에 새로운 나의채팅 컬렉션(myChats)
을 추가해보자.
// 클라우드 함수 사용 (JavaScript)
// 🚀 [LISTEN] 채팅방 컬렉션 생성
export const listenAddChatRoom = functions
.region("asia-northeast3")
.firestore.document("chatRooms/{chatRoomID}")
.onCreate(async (snapshot, context) => {
const chatRoom = snapshot.data(); // 생성된 채팅방 컬렉션 데이터
const db = admin.firestore(); // firebase
// ✅ 대화시작유저, 상대유저 정보 가져오기
const firstUser = (
await db.collection("users").doc(chatRoom.firstUID).get()
).data();
const oppUser = (
await db.collection("users").doc(chatRoom.oppUID).get()
).data();
// ✅ 두 유저 컬렉션(User)에 나의채팅방 컬렉션(myChats) 생성
if (firstUser && oppUser) {
await db
.collection("users")
.doc(firstUser.uid)
.collection("myChats")
.doc(context.params.chatRoomID)
.set({
chatRoomID: context.params.chatRoomID,
oppUID: oppUser.uid,
oppDisplayname: oppUser.displayName,
oppAvatarURL: oppUser.avatarURL,
});
await db
.collection("users")
.doc(oppUser.uid)
.collection("myChats")
.doc(context.params.chatRoomID)
.set({
chatRoomID: context.params.chatRoomID,
oppUID: firstUser.uid,
oppDisplayname: firstUser.displayName,
oppAvatarURL: firstUser.avatarURL,
});
}
});
여기까지는 아래와 같은 데이터들만 생성되었다.
아직 추가하지 못한 마지막 대화 데이터들을 넣어보자.
// 나의 채팅 컬렉션 생성예정
"myChats": {
"chatRoomID:" ..., // ✅ 채팅방 id
"oppUID": ..., // ✅ 상대방 유저 id
"oppDisplayname": ..., // ✅ 상대방 닉네임
"oppAvatarURL": ..., // ✅ 상대방 프사 url
"lastText": ..., // ❌ 마지막 대화텍스트
"lastTime": ..., // ❌ 마지막 대화텍스트 생성 시간
},
유저가 채팅데이터를 생성할 때(키보드 입력)를 Listen
한다.
해당 채팅데이터를 나의채팅 컬렉션(myChats)
에 저장한다.
// 클라우드 함수 사용 (JavaScript)
// 🚀 [LISTEN] 채팅 텍스트 컬렉션 생성
export const listenAddChat = functions
.region("asia-northeast3")
.firestore.document("chatRooms/{chatRoomID}/texts/{textID}")
.onCreate(async (snapshot, context) => {
const newChat = snapshot.data(); // 새로 생성된 채팅 텍스트 컬렉션 데이터
const chatRoomID = context.params.chatRoomID; // 채팅방 id
const [firstUID, oppUID] = chatRoomID.split("000"); // 두 유저 id
const db = admin.firestore(); // db
// ✅ 채팅방 컬렉션 업데이트 (마지막 텍스트)
await db.collection("chatRooms").doc(chatRoomID).update({
lastText: newChat.text,
lastTime: newChat.createdAt,
});
// ✅ 대화시작유저 / 상대유저 나의채팅방 컬렉션 업데이트
await db
.collection("users")
.doc(firstUID) // 대화 시작 유저
.collection("myChats")
.doc(chatRoomID)
.update({
lastText: newChat.lastText, // 마지막 대화 내용
lastTime: newChat.lastTime, // 마지막 대화 시간
});
await db
.collection("users")
.doc(oppUID) // 대화 상대 유저
.collection("myChats")
.doc(chatRoomID)
.update({
lastText: newChat.lastText, // 마지막 대화 내용
lastTime: newChat.lastTime, // 마지막 대화 시간
});
});
맨 처음 채팅방을 생성할 때에는 두가지 케이스가 생기는데
case1) 나의채팅 컬렉션(myChats)
이 먼저 생성되거나
case2) 채팅 텍스트 컬렉션(texts)
이 먼저 생성된다.
DB에 값을 넣을 때 사용하는 update
함수는 기존에 데이터가 없으면
데이터를 넣지 않아버리고, set
함수는 새로운 데이터로 덮어버린다.
- case1의 경우)
1. set(chatRoomID, oppUID, oppDisplayname, oppAvatarURL)
2. update(lastText, lastTime)
이라서 상관없지만
- case2의 경우)
1. update(lastText, lastTime)
2. set(chatRoomID, oppUID, oppDisplayname, oppAvatarURL)
는
lastText,lastTime 데이터가 날라가버린다..
따라서
맨 처음 채팅방을 생성할 때 case2
상황을 고려하여 한가지 더 Listen
한다.
case2가 set
하는 경로를 Listen
해서 아래와 같이 한번 더 update
하는 방법을 사용하여 해결해보았다..!
update(lastText, lastTime)
set(chatRoomID, oppUID, oppDisplayname, oppAvatarURL)
update(lastText, lastTime)
// 클라우드 함수 사용 (JavaScript)
// 🚀 [LISTEN] 나의 채팅방 컬렉션 생성
export const listenAddMyChats = functions
.region("asia-northeast3")
.firestore.document("users/{uid}/myChats/{cid}")
.onCreate(async (snapshot, context) => {
const cid = context.params.cid; // 채팅방 id
const [firstUID, oppUID] = cid.split("000"); // 대화유저 id
const db = admin.firestore();
// ✅ 생성된 채팅방 정보
const chatRoom = (await db.collection("chatRooms").doc(cid).get()).data();
// ✅ 대화시작유저 / 상대유저 나의채팅방 컬렉션 업데이트
if (chatRoom) {
await db
.collection("users")
.doc(firstUID)
.collection("myChats")
.doc(cid)
.update({
lastText: chatRoom.lastText,
lastTime: chatRoom.lastTime,
});
await db
.collection("users")
.doc(oppUID)
.collection("myChats")
.doc(cid)
.update({
lastText: chatRoom.lastText,
lastTime: chatRoom.lastTime,
});
}
});
생각해본 다른 방법으로는
처음 채팅방이 생성되고 텍스트를 보낼 때마다
클라우드함수가 아닌 개발단에서 직접 두명의 나의채팅방 컬렉션(myChats)
에 update
하는 방법도 있을 것 같다..!