230915 Supabase realtime 이용해서 실시간 알림기능 구현하기

나윤빈·2023년 9월 14일
4

TIL

목록 보기
55/55

📌 Supabase는 데이터베이스 작업을 실시간으로 추적하고 애플리케이션에 업데이트를 자동으로 푸시할 수 있는 realtime을 제공한다. realtime은 Broadcast, Presence, Postgres Changes의 주요 기능을 포함한다.

1. Broadcast : 실시간 메시지 전송 매커니즘을 제공한다. 이를 사용하면 서버에서 클라이언트로 실시간 메시지를 푸시하고, 클라이언트 간에 메시지를 공유할 수 있다. 주로 채팅 애플리케이션에서 사용자 간에 메시지를 실시간으로 전송할 때 사용한다. 공식문서

2. Presence : 온라인 및 오프라인 상태를 추적하고, 애플리케이션의 사용자가 현재 활동중인지 여부를 알려준다. 주로 소셜 네트워크 앱에서 사용자가 온라인인지 여부를 파악하거나, 협업 도구에서 팀원의 활동을 실시간으로 추적하는 데 사용한다. 공식문서

3. Postgres Changes : 데이터베이스 테이블에서 발생하는 변경 사항을 실시간으로 감지하고, 변경 사항을 클라이언트에게 푸시한다. 데이터베이스 레벨에서 일어나는 추가, 수정, 삭제와 같은 모든 변경사항을 포함한다. 주로 온라인 쇼핑 앱에서 재고 수량이 변경될 때 실시간 알림을 받거나, 블로그 플랫폼에서 새로운 게시물이 게시될 때 이를 실시간으로 업데이트할 때 사용한다. 공식문서

👉🏻 나는 앞선 3가지 주요 기능 중에 마지막 기능인 Postgres Changes을 사용하여 유저가 구독한 사용자가 새로운 글을 작성했을 때 실시간 알림을 제공하는 기능을 구현해보고자 한다.

1. 테이블 구성

우선, 해당 기능을 구현하는데 필요한 테이블은 3가지이다.

  • post : 사용자가 작성한 게시글 데이터를 관리
  • subscribe : 사용자 간 구독 관계 데이터를 관리
  • alarm : 사용자에게 전달할 실시간 알림 데이터를 관리

각각의 테이블 구성은 다음과 같다.

  • post : id / created_at / updated_at / user_id / title / body / isDeleted
  • subscribe : subscribe_from / subscribe_to
  • alarm : id / created_at / targetUserId / post_id / content / isRead

2. 유저의 구독자 목록 가져오기

유저가 구독한 사용자가 새로운 글을 작성했을 때 실시간 알림을 제공하기 위해서는 첫번째로 현재 로그인 한 유저의 구독자 목록을 가져와야 한다. 따라서 현재 로그인 한 유저의 Id를 가지고 subscribe 테이블에서 구독자 목록을 조회한다.

1) api

// 구독자 목록 가져오기
export const getSubList = async (userId: string) => {
  const { data } = await supabase.from('subscribe').select('subscribe_to').eq('subscribe_from', userId);
  return data;
};

2) useQuery

  // 현재 유저 정보 가져오기
  const currentUser = useCurrentUser();
  const currentUserId = currentUser?.id;

  // 유저의 구독자 목록 가져오기
  const { data } = useQuery(['sublist'], () => getSubList(currentUserId ?? ''));
  let subList: any[] = [];
  if (data) {
    subList = data.map((item) => item.subscribe_to);
  }

3. Postgres Changes을 통해 실시간 데이터 가져오기

두번째로 Postgres Changes을 통해 유저가 구독한 사람이 새로운 글을 작성했을 때 해당 데이터를 실시간으로 가져온다. 따라서 post 테이블에 새로운 데이터가 추가될 때, 해당 게시글이 이전에 조회한 유저의 구독자 목록에 있는 사용자의 글인 경우를 필터링해서 실시간 데이터를 가져온다.

1) postgres_changes

  const [postData, setPostData] = useState();

  supabase
    .channel('db-changes')
    .on(
      'postgres_changes',
      {
        event: 'INSERT',
        schema: 'public',
        table: 'post',
        filter: `user_id=in.(${subList})`
      },
      (payload) => {
        setPostData(payload);
      }
    )

2) payload

공식문서를 참조하여 위와 같이 작성할 경우 payload에 위와 같은 데이터가 담긴다. 특히 new에는 업로드 된 게시글의 데이터가 담기는데, 이 데이터는 유저에게 전달할 알림 메시지를 작성하는 데 사용하기 위해 useState를 이용하여 데이터를 담아준다.

4. alarm 테이블에 데이터 추가하기

이제 Postgres Changes 통해 가져온 실시간 데이터를 바탕으로 alarm 테이블에 데이터를 추가한다. 우선, 게시글을 작성자의 정보가 필요하므로 작성자의 Id를 가지고 작성자 데이터를 가져온다. 다음으로 알림 테이블에 추가할 데이터를 세팅하기 위해 이전에 payload로 받아서 useState로 담아둔 postDate를 활용한다.

    if (postData) {
      // 작성자 아이디 가져오기
      const writerId = postData.new.user_id;

      const postAlarm = async () => {
        // 작성자 정보 가져오기
        const { data: user } = await supabase.from('user').select('*').eq('id', writerId).single();

        if (user) {
          // 알림 메시지에 들어갈 내용 세팅
          const writerName = user.name;
          const newAlarm = {
            created_at: postData.commit_timestamp,
            targetUserId: currentUserId,
            content: `${writerName}님의 새 게시글: ${postData.new.title}`,
            post_id: postData.new.id
          };

          // 알림 테이블에 DB 추가
          await supabase.from('alarm').insert(newAlarm);
  • created_at : 실시간 알림이 전달되는 일시
  • targetUserId : 실시간 알림을 받을 사용자의 Id
  • content : 알림 메시지의 내용
  • post_id : 해당 게시글의 Id (이후에 알림을 통해 해당 게시글로 바로 이동하기 위함)

5. alarm 테이블 데이터를 바탕으로 실시간 알림 전달

마지막으로 alarm 테이블 데이터를 바탕으로 실시간 알림 전달한다. 로그인한 유저의 Id를 가지고 alarm 테이블에서 알림 데이터 목록을 오름차순으로 조회한다. 가져온 알림 데이터 목록 중 가장 최신 데이터를 실시간 알림으로 전달한다. 알림은 React-Toastify 라이브러리를 사용하였다.

          // 알림 테이블에서 알림 데이터 가져오기
          const { data: alarm } = await supabase
            .from('alarm')
            .select('*')
            .eq('targetUserId', currentUserId)
            .order('created_at', { ascending: true }); // 오름차순

          // 가장 최신 데이터를 실시간 알림으로 전달하기
          if (alarm) {
            toast.info(alarm[alarm.length - 1]?.content, {
              theme: 'light',
              icon: <BorderColorIcon />
            });
          }

6. 최종 코드

  // 내가 구독한 사용자(작성자)의 새 게시글 알림 메세지
  useEffect(() => {
    if (postData) {
      // 작성자 아이디 가져오기
      const writerId = postData.new.user_id;

      const postAlarm = async () => {
        // 작성자 정보 가져오기
        const { data: user } = await supabase.from('user').select('*').eq('id', writerId).single();

        if (user) {
          // 알림 테이블에 들어갈 내용 세팅
          const writerName = user.name;
          const newAlarm = {
            ctg_index: 1,
            created_at: postData.commit_timestamp,
            targetUserId: currentUserId,
            content: `${writerName}님의 새 게시글: ${postData.new.title}`,
            post_id: postData.new.id
          };

          // 알림 테이블에 데이터 추가
          await supabase.from('alarm').insert(newAlarm);

          // 알림 테이블에서 알람 목록 데이터 가져오기
          const { data: alarm } = await supabase
            .from('alarm')
            .select('*')
            .eq('targetUserId', currentUserId)
            .order('created_at', { ascending: true }); // 오름차순

          // 가장 최신 데이터를 실시간 알림으로 전달하기
          if (alarm) {
            toast.info(alarm[alarm.length - 1]?.content, {
              theme: 'light',
              icon: <BorderColorIcon />
            });
          }
        }
      };
      postAlarm();
    }
  }, [postData, currentUserId]);
profile
프론트엔드 개발자를 꿈꾸는

0개의 댓글