[Flutter] firebase 데이터를 리스트로 야무지게 가져오기

mason.98·2023년 7월 14일
0

Flutter

목록 보기
8/8

이런식으로 채팅리스트를 db에서 가져오려고 한다.

개발단에서는
채팅리스트의 형식을 List<ChatModel>이라고 생각하고
ChatModel상대프사URL /상대닉네임 /마지막대화 /마지막대화시간 같은 정보가 들어가 있으면 좋을 것 같다.

DB(파베) 에는 아래와 같이 저장된다.

"chatRooms": {
	"chatRoomID": {
		"firstUID": ..., // 먼저 대화시작 유저 uid
      	"oppUID": ..., // 상대 유저 uid
        "lastText": ..., // 마지막 대화 텍스트
        "lastTime": ..., // 마지막 대화 텍스트 생성 시간
    },
	"chatRoomID": { ... },
  	"chatRoomID": { ... },
    .
    .
    .
}

원래 하려고 했던 방법 🥲

1. DB로부터 loginUID가 포함되어있는 채팅 QuerySnapshot 가져오기

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();
  }

2-1. 각각의 Collection 접근해서 상대방 정보 가져오기

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();

2-2. 상대방정보를 사용하여 ChatModel 재생성

// 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();
  1. 로그인사용자가 포함되어있는 채팅리스트를 가져온다.(DB접근)
  2. 각각의 채팅에서 상대방유저 정보를 가져온다. (DB접근)
  3. 새로운 채팅모델을 생성하여 새로운 채팅리스트로 변환한다.

생각대로 코드를 짜봤는데, DB접근을 좀 더 줄이고 과정을 간소화시켜보자.
어떻게 하면 좀 더 쉽게 로그인유저의 채팅리스트를 불러올 수 있을까..?

간소화 시킨 방법 😎

1. 유저 컬렉션(User)에 나의채팅 컬렉션(myChats) 생성 예정

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": { ... },
	.
    .
    .
},

2. 유저 컬렉션(User)에 나의채팅 컬렉션(myChats) 생성

채팅 컬렉션(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": ..., // ❌ 마지막 대화텍스트 생성 시간
         },


3. 나의채팅 컬렉션(myChats)에 마지막 대화내용 추가

유저가 채팅데이터를 생성할 때(키보드 입력)를 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한다.


4. case2 문제점 해결하기 (myChats 생성할 때)

case2가 set하는 경로를 Listen해서 아래와 같이 한번 더 update하는 방법을 사용하여 해결해보았다..!

  1. update(lastText, lastTime)
  2. set(chatRoomID, oppUID, oppDisplayname, oppAvatarURL)
  3. 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하는 방법도 있을 것 같다..!

profile
wannabe---ing

0개의 댓글