최종 프로젝트_실시간 채팅, 알림 완료

Rock Kyun·2024년 1월 10일
2

오늘 했던 것

  • supabase 리얼타임을 사용하여 실시간 채팅, 알림 구현

어제의 문제 리뷰

  • 어제 있었던 invalid input에 대해 알아봤지만
    데이터를 직렬화 하여 insert 하지 않아도
    다른 DB에는 잘 insert 되는 것을 확인하였고
    결론은 테이블 셋팅 단계에서 문제가 있던 거 같다.

  • 메세지 테이블과, 채팅방 테이블 설정을 다시 하는 것으로
    문제를 해결했다!

오늘의 구현 과정

1. 테이블 설정

  • 보편적인 실시간 채팅 설정에 따라
    메세지 테이블, 채팅방 테이블을 설정했다.
  • 이때 메세지 테이블은 채팅방의 room_id 컬럼을 foreign key로 설정하고
    채팅방의 participants 필드에는 참여자 정보를 담아
    유저가 채팅방을 read 하면 채팅방에 따라 메세지를 read 하도록 연결했다.

2. 로그인/회원가입

  • 간단하게 Email Provider만을 사용하여 로그인, 회원가입 구현.
  • 회원가입 시 user 테이블에 회원가입한 사용자의 정보를 저장.

3. 채팅방 생성하기 & 채팅방 리스트 UI 그리기

  • 2번 테스트 유저로 로그인 하여
    1번 테스트 유저에게 채팅하기 버튼을 클릭 시
    아래 함수가 작동하며 채팅방 목록이 생긴다.
const makeChatRoom = async (e: MouseEvent<HTMLButtonElement>) => {
    // 클릭하는 버튼에 있는 상대방 유저 id
    const id = e.currentTarget.id;
  
    try {
      // 현재 로그인 된 유저가 있고, 로그인 유저의 identities가 undefined가 아닐 때
      if (curUser && curUser.identities !== undefined) {
        // chat_room 테이블의 participants 필드에 row를 insert한다
        // row에 들어가는 속성은 테스트를 위해 간소하게
        // 로그인 된 유저의 id와 관련 정보, 상대방 유저의 id와 관련 정보를 담았다..
        const { data, error } = await supabase.from("chat_room").insert([
          {
            participants: [
              { user_id: id, user_name: "test1" },
              {
                user_id: curUser.id,
                user2_name: "test2",
              },
            ],
          },
        ]);
      }
    } catch (err) {
      console.log("failed", err);
    }
  };

생성과 동시에 채팅방을 가져와 필터하여 UI를 그린다.

// 현재 로그인 된 유저의 채팅방을 가져오는 함수
const getRoomsforUser = async () => {
    // 일단 모든 채팅방을 가져온 후
    try {
      const { data: chat_room, error } = await supabase
        .from("chat_room")
        .select("*");

      if (error) {
        console.error("채팅방 가져오기 실패", error);
        return;
      }

      // chat_room과 현재 로그인 유저 정보가 있다면
      if (chat_room && curUser) {
        // 가져온 채팅방 목록의 participants를 순회하며
        const filtered = chat_room.filter((room: any) => {
          // 현재 로그인 된 유저가 속한 채팅방만 filter한다.
          return room.participants.some(
            (participant: any) => participant.user_id === curUser.id
          );
        });
        setRooms(filtered);
      }
    } catch (error) {
      console.error("소속 된 채팅방이 없습니다", error);
    }
  };

4. 채팅하기

  • 메세지 전송 시 chat_room_id를 현재 열린 채팅방의 id로 하여
    메세지 테이블에 insert 한다!
const sendMessage = async (e: FormEvent) => {
    e.preventDefault();
    if (curUser) {
      const { data, error } = await supabase.from("chat_messages").insert([
        {
          sender_id: curUser?.id,
          // 채팅방을 클릭 시, 클릭 된 채팅방 id를 저장하는 state가 clicked이다.
          chat_room_id: clicked,
          content: chatInput,
        },
      ]);
    }
  };

5. 실시간으로 업데이트 하기

  • useEffect를 통해 열려있는 채팅방의 id와 같은 chat_room_id를 가진
    메세지 테이블을 구독하여 실시간으로 업데이트 한다.

6. 실시간 알림 확인

  • 채팅방을 끈 뒤 다른 페이지로 이동한 후
    useEffect를 사용하여 구독해둔 채팅방에 대한 실시간 알림을 받는다

결과물

로그인

채팅방 생성

채팅해보기

어려웠던 부분

1. 데이터간의 연결이 어려웠다

  • 어떤 데이터가 어떤 데이트를 참조하여 수월하게 데이터를 다룰지 고민을 많이 했다.

2. 새로고침 시 뜨지 않는 채팅방

  • 채팅방 생성 후 새로고침을 하면 useEffect 속 채팅방 가져오는
    함수가 작동하지 않았는데.
    useEffcet를 하나만 사용해야 한다는 이상한 개념이 있었던 탓이다.
    1번 useEffect를 통해 유저 정보를 가져오고
    2번 useEffect에서 user정보를 의존성 배열에 두고
    현재 로그인 된 사용자의 채팅방 목록을 가져오도록 하였다.
useEffect(() => {
  // mount 시 유저 정보를 가져오는 함수
  const getUserData = async () => {
    const { data, error } = await supabase.auth.getUser();
    if (data) {
      setCurUser(data.user);
      } else {
        console.log("user data is empty");
      }
    }
  };

    getUserData();
  }, []);

// 로그인 된 유저 정보가 변하고, 그 정보가 있을 경우 채팅방 fetch
useEffect(() => {
  if (curUser) {
    getRoomsforUser();
  }
}, [curUser]);

느낀점

  • 새로운 기술 supabase 강력하다.
    뉴비인 나에게는 장점만 보인다.

0개의 댓글