2024.03.21 TIL - 좋아요 로직 (Next.js Palette Ground 프로젝트)

Innes·2024년 3월 21일
0

TIL(Today I Learned)

목록 보기
95/147
post-thumbnail

📝 치열한 고민의 흔적... 좋아요가 이렇게 어려운 것일 줄이야

Task TODOLIST

  • 화면 렌더링시 1 : 로그인 상태일때, 유저가 좋아요한 그림인지 체크하기(좋아요한 그림이면 하트 색 채워놓기)
  • 화면 렌더링시 2 : 해당 그림의 좋아요 개수 조회
  • 좋아요 클릭시 1 : likes테이블에 해당 user email과 그림 정보(id, url) row 추가
  • 좋아요 클릭시 2 : 좋아요 개수 +1
  • 좋아요 취소시 1 : likes테이블에서 해당 user email과 그림id 일치하는 row 삭제
  • 좋아요 취소시 2 : 좋아요 개수 -1

📝 아직 수정이 필요한 부분 : 현재 로그인한 유저 email 확인하는 방식

  • 기존 : email이 필요한 api에서 await supabase.auth로 email을 매번 가져옴
  • 변경 : zustand에 저장해놓은 current user Info 를 가지고 인자로 email을 넘겨주는 방식으로 변경하기
    (zustand로 관리하는 전역상태는 화면 렌더링시 useQuery보다 늦게 값을 가져오기 때문에, useQuery에서 enabled 옵션 추가 및 isPending을 사용하자)


    🤍 (참고) isLoadingisPending의 차이 : isLoading은 query가 실행중인 상태를 확인해주는 것, isPending은 query가 실행되기 전의 상태를 확인해주는 것
    -> enabled 옵션을 추가해놨으면 query가 실행되기 전의 상태를 확인해주는 로직이 필요하기 때문에 isLoading이 아닌 isPending을 사용해주어야 한다!

✨ 개발 내용

🧡 좋아요 로직 정리 🧡

  1. detail1페이지 화면 렌더링시
  • 로그인 상태인 경우 - 유저가 이미 좋아요한 그림인지 체크하기 (좋아요한 그림이면 하트 색 채워놓기)
  • 로그아웃 상태인 경우 - 하트 색 비워놓기
  • 좋아요 개수 보여주기 - useQuery로 가져와야 함 (좋아요 및 취소시 mutation으로 바로 개수 업데이트가 필요함)
// 컴포넌트

  const [isLike, setIsLike] = useState(false);
  const isLoggedIn = useAuthStore((state) => state.isLoggedIn);

  // 화면 렌더링시 - 로그인 상태일때만 이미 좋아요한 그림인지 미리 확인하기
  useEffect(() => {
    if (isLoggedIn) {
      const fetchData = async () => {
        try {
          const response = await isCheckLikeState(id);
          setIsLike(response);
        } catch (error) {
          console.error(error);
        }
      };
      fetchData();
    } else if (!isLoggedIn) {
      setIsLike(false);
    }
  }, [id, isLoggedIn]);
// api

// 유저가 좋아요 했는지 미리 확인하기 - likes에서 email, drawing_id 존재하는지 확인
export const isCheckLikeState = async (drawingId: number) => {
  // current loggedIn user의 email 가져오기
  const { data: user, error: userError } = await supabase.auth.getUser();
  if (userError) {
    throw userError;
  }
  const email = user.user.email;

  // likes테이블에서 email, drawingId 모두 일치하는값 있는지 확인하기
  const { data: like, error } = await supabase
    .from("likes")
    .select()
    .eq("user_email", email)
    .eq("drawing_id", drawingId);

  if (error) {
    throw error;
  }

  return like.length > 0;
};
// 좋아요 개수 가져오기 api

export const countLikesNumber = async (id: number) => {
  const { data, error } = await supabase
    .from("likes")
    .select()
    .eq("drawing_id", id);
  if (error) {
    throw error;
  }
  return data.length;
};
  1. 좋아요 하트 클릭시
  • '좋아요'인 경우

    • 로그인 상태인 경우 : likes 테이블에 현재 로그인 유저의 email, 좋아요한 그림 id를 추가 + 좋아요 하트 색 채우기
    • 로그아웃 상태인 경우 : '로그인이 필요합니다 alert', 로그인 모달창 띄우기
  • '좋아요 취소'인 경우

    • likes 테이블에서 유저 id와 좋아요한 그림 id가 일치하는 열을 삭제하기 + 좋아요 하트 색 비우기
// 컴포넌트

const [isLike, setIsLike] = useState(false);
const isLoggedIn = useAuthStore((state) => state.isLoggedIn);
const setIsLoginOpen = useAuthStore((state) => state.setIsLoginOpen);

const queryClient = useQueryClient();

// 현재 그림의 url
const drawingUrl = post.drawing_url;

const { mutate: insertLikeMutation } = useMutation({
    mutationFn: ({ id, drawingUrl }: { id: number; drawingUrl: string }) =>
      insertLike(id, drawingUrl),
    onSuccess: () => {
      queryClient.invalidateQueries({
        queryKey: ["countLikesNumber"],
      });
    },
  });

  // 좋아요 클릭시
  // - 좋아요 : likes 테이블에 email, id, url 추가, 하트색 변경
  // - 좋아요 취소 : likes 테이블에서 email, id가 같은 열 삭제, 하트색 변경
  const handleLikeOnClick = async () => {
    if (!isLike) {
      if (isLoggedIn) {
        insertLikeMutation({ id, drawingUrl });
        setIsLike((prev) => !prev);
      } else if (!isLoggedIn) {
        alert("로그인이 필요한 기능입니다.");
        setIsLoginOpen(true);
      }
    } else if (isLike) {
      deleteLikeMutation(id);
      setIsLike((prev) => !prev);
    }
  };
// api

// 좋아요! - likes테이블에 email, id, url 추가하기
export const insertLike = async (id: number, url: string) => {
  // current loggedIn user의 email 가져오기
  const { data: user, error: userError } = await supabase.auth.getUser();
  if (userError) {
    throw userError;
  }
  const email = user.user.email;

  // likes 테이블에 insert
  const { data, error } = await supabase.from("likes").insert([
    {
      user_email: email,
      drawing_id: id,
      drawing_url: url,
    },
  ]);
  if (error) {
    throw error;
  }
  return data;
};

// 좋아요 취소 - likes 테이블에서 그림id가 같은 열 삭제
export const deleteLike = async (id: number) => {
  // current loggedIn user의 email 가져오기
  const { data: user, error: userError } = await supabase.auth.getUser();
  if (userError) {
    throw userError;
  }
  const email = user.user.email;

  // likes테이블에서 email, id 같은 열 삭제
  const { data, error } = await supabase
    .from("likes")
    .delete()
    .eq("drawing_id", id)
    .eq("user_email", email)
    .select();
  if (error) {
    throw error;
  }
  return data;
};

TroubleShotting

  1. 로그아웃 상태일 때 좋아요 하트 부분에 'Error'가 출력됨
  • 원인 : 화면 렌더링시 유저가 좋아요 해놓은 그림인지 미리 체크하는 useQuery가 있었는데, 로그인 여부는 상관없이 바로 query가 실행되니까 만약 로그인한 유저정보가 없는 경우 해당 query가 error를 반환한 것이었다.
  • 해결 : query로 체크하던 좋아요 상태를 useEffect를 사용하여 isLoggedIn이 true이면 좋아요 상태 체크하기, false이면 하트 색만 비워놓기 로직으로 변경
  1. 좋아요 클릭시 짧은시간 연속으로 클릭하면 좋아요 수가 무한 증가됨
  • 원인 : query로 체크해놓은 좋아요 상태가 false이면 좋아요하게, true이면 좋아요 취소하게 만들었다. 이렇게 하니 같은 그림id가 무한정 likes 테이블에 추가되었다.
  • 해결 : isLike 상태(하트 색이 채워져있냐, 비워져있냐)로 좋아요할지 취소할지를 결정하니 해결!
  1. 좋아요 취소시 좋아요 수가 한번에 0으로 바뀜
  • 원인 : likes테이블에서 열을 delete할 때, 그림id만 체크해서 열을 delete했더니 해당 유저가 아닌 다른 유저들이 좋아요 했던 기록들도 한번에 함께 삭제되어 좋아요가 0으로 바뀐 것이었다.
  • 해결 : 유저의 email도 함께 체크하여 email과 그림id 둘 다 일치하는 열만 삭제하도록 수정함
profile
꾸준히 성장하는 우상향 개발자

0개의 댓글