북마크 구현 (optimistic update)

Jtiiin:K·2024년 1월 16일
0
post-thumbnail

북마크 구현하기 (optimistic update)

1. 서버에서 데이터 넣고 빼기

DB는 supabase를 사용하기 때문에
일단 bookmark 테이블에 데이터를 넣고 빼는 함수부터 만들었다

// 북마크 추가
export const insertBookmark = async ({ userId, placeId }: Props) => {
  const { data, error } = await supabase
    .from('bookmarks')
    .insert({ user_id: userId, place_id: placeId })
    .select();
  if (error) {
    throw error;
  }
  return data;
};

// 북마크 삭제
export const deleteBookmark = async ({ userId, placeId }: Props) => {
  const { error } = await supabase
    .from('bookmarks')
    .delete()
    .eq('user_id', userId)
    .eq('place_id', placeId);
  if (error) {
    console.log(error);
  }
};

2. 북마크 가져오기

// 북마크 가져오기
export const getBookmark = async ({ userId, placeId }: Props) => {
  const { data, error } = await supabase
    .from('bookmarks')
    .select()
    .eq('place_id', placeId)
    .eq('user_id', userId);
  if (error) {
    throw error;
  }
  return data;
};

접근법 : 현재 장소에 현재 로그인 한 유저가 북마크를 했는지 안했는지 알기 위해
두 개의 eq를 써서 데이터가 나오면 true, 없으면 false 로 받아
isBookmarked 상태로 쓰려고 했다

문제 1. 처음에 user_id를 eq 대신 match로 가져왔는데 이렇게하면
user_id가 'abc' 와 'abcd'인 사람이 있을 경우 두 개를 다 가져온다고..!
그래서 eq를 두 번 해서 필터링 했다

문제 2. supabase 의 서버 통신으로 나오는 데이터의 값이
[[{data}]] 형식으로 되어있기 때문에 항상 data[0] 으로
접근해야하는 귀찮음이 있었다
그래서 처음부터 single() 로 가져오려고 했는데 쿼리에서 계속 에러 발생! 🥺
이유는 single() 메서드는 뭐라도 1개는 값으로 내보내겠다! 라는 친구라
값이 없는 경우(undefined) 서버통신에 실패한 것으로 보고
몇 번 재시도를 하다가 결국 에러를 뿜었던 것!

그래서 결국 single()을 빼고 값을 console.log에 찍어본 결과
값이 있는 경우 => [[{data}]]
값이 없는 경우 => undefined

이렇게 useQuery로 가져온 결과를 bookmarkState라는
useState의 상태값으로 만들어(boolean) 쓰기 위해
useEffect안에 담아서 초기값을 세팅해줬다

  useEffect(() => {
    setIsBookmarked(bookmarkState ? bookmarkState.length > 0 : false);
  }, [bookmarkState]);

3. toggle 함수 만들기

상태값으로 만든 bookmarkState 값에 따라서
아이콘을 조건부 렌더링 해주고
해당 아이콘을 눌렀을 때 북마크를 체크/해제 할 수 있게
toggle 함수를 만들어서 달아줬다

  const toggleBookmark = () => {
    if (isBookmarked) {
      setIsBookmarked(false);
      delBookmark.mutate({ userId: userInfo.userId, placeId });
    } else {
      setIsBookmarked(true);
      addBookmark.mutate({ userId: userInfo.userId, placeId });
    }
  };

4. 낙관적 업데이트 추가 (optimistic update)

아래와 같은 방법으로 삭제도 만들고
좋아요 기능도 해치웠다! 😎

  // 낙관적 업데이트 (추가)
  const addBookmark = useMutation({
    mutationFn: insertBookmark, // 북마크 추가함수 
    onMutate: async () => {
      await queryClient.cancelQueries({
        queryKey: ['bookmark', userInfo.userId, placeId],
      }); 
      const prev = queryClient.getQueryData([
        'bookmark',
        userInfo.userId,
        placeId,
      ]); // 쿼리에 있었던 이전 값
      const updateBookmark = [{ user_id: userInfo.userId, place_id: placeId }]; // 새로 넣을 값 
      queryClient.setQueryData(
        ['bookmark', userInfo.userId, placeId],
        updateBookmark,
      ); // 쿼리에 넣어주기 
      return { prev };  
    },
    onError: (error, updateReviewParams, context) => {
      if (context?.prev) {
        queryClient.setQueryData(
          ['bookmark', userInfo.userId, placeId],
          context.prev,
        );
      }
    }, // 실패하면 이전 값으로 되돌리기
    onSettled: () => {
      queryClient.invalidateQueries({
        queryKey: ['bookmark', userInfo.userId, placeId],
      });
    }, // 실패하거나 성공하거나 실행됨
  });

결과물

profile
호기심 많은 귀차니즘의 공부 일기

0개의 댓글