본문 바로가기
매일 해내는 개발/Develog

[Develog] 좋아요 기능 구현2

by 해야지 2023. 3. 1.
반응형

이전 글

 

[Develog] 좋아요 기능 구현(with firebase, useQuery, useMutation)

현재 데이터 구조상 모든 게시글을 저장하는 post 컬렉션(문서는 각 게시글 데이터)과 내가 좋아요한 게시글이 저장되는 myprojects 컬렉션(문서는 각 유저)으로 나눠져있고 좋아요로 증가 또는 감

subtlething.tistory.com

 

React Query를 처음 쓰는데 생각보다 옵션들이 너무 많아서 제대로 활용하지 못하는 것 같았다.

그리고 내가 짰던 좋아요 로직에 대한 의심이 계속 들었던 와중에 좋아요가 적용이 안되는 오류가 발생했다.

그래서 하루종일 좋아요만 붙들고 수정했다.

커밋 링크

 

[PR] 프로젝트 상세보기 페이지 수정 by jeLee94 · Pull Request #261 · Team-Detto/Detto

개요 🔎 Close #239 Close #240 Close #260 작업사항 📝 수정하기 페이지 데이터 전달 오류 수정 좋아요 기능 로직 수정(하루종일 걸림...) 삭제 후 메인 페이지 이동-> 이전 페이지로 이동 지원하기 모달

github.com

이전 코드와 달라진 점 (코드는 아래쪽에)

1. useMutation에 onSettled 함수를 추가해서 데이터 업데이트가 성공하든 말든 invalidate를 수행하게 함
=> 이 부분은 나중에 확인해보니 꼭 필요하진 않은 부분이어서 삭제했다.

2. mutate를 useEffect 안에 배치해서 isLike가 변할때마다 업데이트 되도록 했는데, 이를 deps를 삭제하고 useEffect, return을 사용해서 컴포넌트가 언마운트 될 때만 업데이트 되도록 쓰로틀링을 적용해 서버 요청을 더 줄일 수 있도록 구현했다.
=> 이 부분도 나중에 확인해보니 좋아요 누른 후 새로고침을 수행하면 좋아요가 적용되지 않아서 실시간 업데이틀 변경했다..

3. 가장 중요한 것은 76~82번째 줄의 useEffect였다.
이 두개는 useQuery로 요청한 데이터가 조금씩 늦게 불러질 때 데이터 일관성이 떨어지기 때문에 추가했다.
그래서 이 둘 중 하나라도 삭제되면 좋아요 수, 하트 유지 여부가 일관성이 떨어지기 때문에 꼭 필요했다.

수정된 코드

import styled from '@emotion/styled';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import { AiOutlineHeart, AiFillHeart } from 'react-icons/ai';
import { updateLike, updateMyProject } from '../../apis/postDetail'; //여기서 에러 발생 :모듈 또는 해당 형식 선언을 찾을 수 없습니다.
import { findWithCollectionName } from 'apis/findWithCollectionName';
import { useAuth, useGlobalModal } from 'hooks';
import COLORS from 'assets/styles/colors';

const Likes = ({ pid, version = 'web' }: any) => {
  const { uid } = useAuth();
  const { openModal } = useGlobalModal();

  const handleLikeButton = async (event: any) => {
    event.preventDefault();
    if (isLike) {
      setIsLike(false);
      setCountLike(countLike - 1);
    } else {
      setIsLike(true);
      setCountLike(countLike + 1);
    }
  };

  //좋아요한 프로젝트 조회
  const { data: myProjects } = useQuery({
    queryKey: ['myProjects', uid],
    queryFn: () => findWithCollectionName('myprojects', uid),
  });

  const { data: projectLike } = useQuery({
    queryKey: ['post', pid],
    queryFn: () => findWithCollectionName('post', pid),
  });

  const [countLike, setCountLike] = useState(projectLike?.like);
  const [isLike, setIsLike] = useState(myProjects?.likedProjects.includes(pid));

  const queryClient = useQueryClient();
  const { mutate: updateLikeMutate } = useMutation(
    () => updateLike(pid, countLike),
    {
      onSettled: () => {
        queryClient.invalidateQueries(['post', pid]);
      },
      onSuccess: () => {
        queryClient.invalidateQueries(['post', pid]);
        setCountLike(projectLike?.like);
      },
    },
  );

  const { mutate: updateMyProjectMutate } = useMutation(
    () => updateMyProject(uid, pid, isLike),
    {
      onSettled: () => {
        queryClient.invalidateQueries(['myProjects', uid]);
      },
      onSuccess: () => {
        queryClient.invalidateQueries(['myProjects', uid]);
      },
    },
  );
  useEffect(() => {
    setCountLike(projectLike?.like);
    return () => {
      updateMyProjectMutate();
      updateLikeMutate();
      setIsLike(myProjects?.likedProjects.includes(pid));
    };
  }, []);
  
  useEffect(() => {
    setCountLike(projectLike?.like);
  }, [projectLike?.like]);

  useEffect(() => {
    setIsLike(myProjects?.likedProjects.includes(pid));
  }, [myProjects?.likedProjects]);

  return (
    <IconButton
      onClick={(event) => {
        if (!uid) {
          openModal('login', 0);
          return;
        }
        handleLikeButton(event);
      }}
    >
      {isLike ? (
        <FillHeart version={version} />
      ) : (
        <LineHeart version={version} />
      )}
      관심 {countLike ?? ' 0'}
    </IconButton> // 로그아웃인 경우 관심 버튼 클릭 시 likedProjects에 데이터가 없어서 로직 에러 발생: 예외처리 필요
  );
};

export default Likes;

const IconButton = styled.button`
  display: flex;
  align-items: center;
  gap: 0.5rem;
`;

const FillHeart = styled(AiFillHeart)<{ version: string }>`
  font-size: ${(props) => (props.version === 'mobile' ? '1rem' : '1.5rem')};
  color: ${COLORS.pink};
`;

const LineHeart = styled(AiOutlineHeart)`
  font-size: ${(props: { version: string }) =>
    props.version === 'mobile' ? '1rem' : '1.5rem'};
  color: ${COLORS.gray750};
`;

 

반응형

댓글