인프런 커뮤니티 질문&답변

챠챠_님의 프로필 이미지

작성한 질문수

Next + React Query로 SNS 서비스 만들기

하트 누를 때 optimistic update 적용하기

찜하기하고 해당글 상세페이지 이동시 찜 정보제대로 안내려오는 현상

24.06.02 13:55 작성

·

141

0

안녕하세요 선생님

홈에서 찜했다, 안했다 잘 작동하고

상세페이지로 이동하면 찜하기 데이터가 제대로 내려오지 않는 부분을 확인했습니다.

호출은 아래와 같이 하고있습니다.
/src/app/(afterLogin)/[username]/status/[id]/page.tsx

import BackButton from "@/app/(afterLogin)/_component/BackButton";
import style from './singlePost.module.scss';
import Post from "@/app/(afterLogin)/_component/Post";
import CommentForm from "@/app/(afterLogin)/[username]/status/[id]/_component/CommentForm";
import SinglePost from '@/app/(afterLogin)/[username]/status/[id]/_component/SinglePost';
import Comments from '@/app/(afterLogin)/[username]/status/[id]/_component/Comments';
import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query';
import { getSinglePost } from '@/app/(afterLogin)/[username]/status/[id]/_lib/getSinglePost';
import { getComments } from '@/app/(afterLogin)/[username]/status/[id]/_lib/getComments';

type Props = {
  params: { id: string}
}

export default async function Pasge({ params }: Props) {
  console.log('----------------------------- single post params', params);
  const { id } = params;
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({ queryKey: ['posts', id], queryFn: getSinglePost });
  await queryClient.prefetchQuery({ queryKey: ['posts', id, 'comments'], queryFn: getComments });
  const dehydratedState = dehydrate(queryClient);
  return (
    <div className={style.main}>
      <HydrationBoundary state={dehydratedState}>
        <div className={style.header}>
          <BackButton/>
          <h3 className={style.headerTitle}>게시하기</h3>
        </div>
        <SinglePost id={id} />
        <CommentForm id={id} />
        <div>
          <Comments id={id} />
        </div>
      </HydrationBoundary>
    </div>
  )
}


/src/app/(afterLogin)/[username]/status/[id]/_component/SinglePost.tsx

'use client';

import { Post as IPost } from '@/models/Post'
import { useQuery } from '@tanstack/react-query'
import { getSinglePost } from '@/app/(afterLogin)/[username]/status/[id]/_lib/getSinglePost';
import Post from '@/app/(afterLogin)/_component/Post';

export default function SinglePost({id, noImage}: {id: string, noImage?: boolean}) {
  const { data: post, error } = useQuery<IPost, Object, IPost, [_1: string,  _2: string]>({
    queryKey: ['posts', id],
    queryFn: getSinglePost,
    staleTime: 60 * 1000,
    gcTime: 300 * 100,
  });
  console.log(post, '--------------------------single post');
  if (error) {
    return (
      <div style={{
        height: 100, alignItems: 'center', fontSize: 31, fontWeight: 'bold', display: 'flex', justifyContent: 'center'
      }}>게시글을 찾을 수 없습니다.</div>
    )
  }
  if (!post) {
    return null;
  }
  return <Post post={post} key={post.postId} noImage={noImage} />
}

찜하기 코드

export default function ActionButtons({ white, post }: Props) {
  const queryClient = useQueryClient();
  const { data: session } = useSession();
  const commented = !!post.Comments?.find(d => d.userId === session?.user?.email);
  const reposted = !!post.Reposts?.find(d => d.userId === session?.user?.email);
  const liked = !!post.Hearts?.find(d => d.userId === session?.user?.email);
  const { postId } = post;

  const heart = useMutation({
    mutationFn: () => {
      return fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/posts/${postId}/heart`, {
        method: 'post',
        credentials: 'include',
      })
    },
    onMutate() {
      const queryCache = queryClient.getQueryCache();
      const queryKeys = queryCache.getAll().map(cache => cache.queryKey);
      console.log('queryKey', queryKeys);
      queryKeys.forEach((queryKey) => {
        if (queryKey[0] === 'posts') {
          const value: Post | InfiniteData<Post[]> | undefined = queryClient.getQueryData(queryKey);
          if (value && 'pages' in value) {
            const obj = value.pages.flat().find(d => d.postId === postId);
            if (obj) { // 존재는 하는지?
              const pageIndex = value.pages.findIndex(page => page.includes(obj));
              const index = value.pages[pageIndex].findIndex(d => d.postId === postId);
              const shallow = produce(value, draft => {
                draft.pages[pageIndex][index].Hearts = [{ userId: session?.user?.email as string }];
                draft.pages[pageIndex][index]._count.Hearts += 1;
              })
              queryClient.setQueryData(queryKey, shallow);
            }
          } else if (value) {
            // 싱글 포스트인 경우
            if (value.postId === postId) {
              const shallow = {
                ...value,
                Hearts: [...value.Hearts, { userId: session?.user?.email as string }],
                _count: {
                  ...value._count,
                  Hearts: value._count.Hearts + 1,
                }
              }
              queryClient.setQueryData(queryKey, shallow);
            }
          }
        }
      })
    },
    onError() {

    },
    onSettled() {

    }
  });

 

 

다른 찜하기 질문에서

키를가지고 호출하지 않해서라고 하신걸 봤었는데,

위와 같은 경우에는 클라이언트 서버에서 쿼리키를 가지고 호출했는데 데이터가 잘 안내려오는 것을 확인했습니다.


찜을 눌렀을때

찜을 누르고 해당글 상세로 이동했을때

이러한 경우에는 찜하기를 누르고 추가적인 작업이 필요한지 궁금합니다. 예를들면 찜하기를 누르고 해당 쿼리키의 데이터를 호출해야한다는지... 혹은 제가 잘못호출한것이라면 알려주시면 감사하겠습니다.

 

 

답변 1

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 06. 02. 14:05

음, 지금 해당글 상세에서는 하트가 1인데 찜을 누를 때는 3이네요? 3이 맞나요 1이 맞나요?

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 06. 02. 19:01

3이 맞습니다.
홈 화면에서 각각 다른 계정으로 3번 눌렀는데,
상세화면갔을때 저렇게 나왔습니다.

새로고침하면 제대로 나옵니다.

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 06. 02. 22:24

보통 저렇게 과거 데이터가 오는 것은 캐싱 문제라서 캐싱 비활성화해서 요청 보내봐야합니다.

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 06. 02. 23:31

답변감사합니다.

참고해서 다시 해보겠습니다!

챠챠_님의 프로필 이미지

작성한 질문수

질문하기