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

홍홍님의 프로필 이미지
홍홍

작성한 질문수

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

Suspense로 Streaming하여 최적화하기(feat. loading.tsx, error.tsx)

react-query의 useSuspense.. 사용 시 클라이언트에서 suspense가 동작을 하지 않습니다.

작성

·

693

0

안녕하세요. 강사님

예제를 보고 하던 중 suspense 가 동작하지 않아 질문드립니다.

처음 예시로 알려주신 react-query의 isPending 을 사용한 로딩처리는 잘 동작하지만 마지막에 알려주신 useSuspense(useSuspenseInfiniteQuery, useSuspenseQuery)들을 사용하는 경우 동작하지 않네요..

*팔로우 중 을 선택해도 suspense에 설정한 로딩 컴포넌트가 나오지 않고 딜레이된 시간(5초) 후 데이터가 보여집니다.

로딩 컴포넌트도 회전하지 않고 멈춰있습니다.

어떤부분을 봐야할까요?ㅠㅠ

반대로 이런 증상을 경험하니 이전 데이터가 먼저 보여진 후 5초 뒤에 최신 데이터로 보여지므로 사용자가 잘 못된 데이터를 표시 할 수 있다는걸 배울 수 있었습니다.😎


소스코드

const HomePage = async () => {
  return (
    <HomeContextProvider>
      <HomeTopTab />
      <WriteForm />
      <Suspense fallback={<Loader />}>
        <TabDividerSuspense />
      </Suspense>
    </HomeContextProvider>
  );
};
const TabDividerSuspense = async () => {
  const queryClient = new QueryClient();
  await queryClient.prefetchInfiniteQuery({
    queryKey: ["tweet", "recommends"],
    queryFn: getPostRecommends,
    initialPageParam: 0,
  });
  const dehydratedState = dehydrate(queryClient);
  return (
    <HydrationBoundary state={dehydratedState}>
      <TabDivider />
    </HydrationBoundary>
  );
};
const TabDivider = () => {
  const { tab } = useContext(HomeContext);
  return tab === "recommended" ? <TweetList /> : <FollowingList />;
};
const TweetList = () => {
  const { ref, inView } = useInView();

  const { data, fetchNextPage, hasNextPage, isFetching, isPending } =
    useSuspenseInfiniteQuery<
      Post[],
      object,
      InfiniteData<Post[]>,
      [string, string],
      number
    >({
      queryKey: ["tweet", "recommends"],
      queryFn: getPostRecommends,
      initialPageParam: 0,
      getNextPageParam: (lastPage) => lastPage.at(-1)?.postId,
    });

  useEffect(() => {
    if (inView) {
      !isFetching && hasNextPage && fetchNextPage();
    }
  }, [fetchNextPage, hasNextPage, inView, isFetching]);

  const tweets = useMemo(() => {
    if (data) {
      return data.pages.flat();
    }
  }, [data]);

  return (
    <>
      {tweets?.map((tweet) => (
        <Tweet post={tweet} key={tweet.postId} />
      ))}
      {isPending && <Loader />}
      <div ref={ref} />
    </>
  );
};

추가 질문

빌드 후 네트워크 탭에서 home을 확인해보면 post 글 들이 모두 html로 변환되어 내려 오고 있습니다!

(dev에서는 템플릿?으로 표현되더라고요)

저는 html이 아닌 데이터 형태로 내려와 useQuery로 해당 키로 접근해서 그냥 데이터를 가져올 줄 알았는데..그게 아닌가보네요.

혹시 좀 더 자세히 설명 좀 부탁드려도 될까요?ㅠ

그리고 home미리보기 탭에서는 post글들이 아닌 로딩 컴포넌트가 보입니다. (위의 사진에 응답 탭에서는 post글 들이 존재하고요)

로딩 컴포넌트가 보이는 이유는 하이드레이션이 처리되기 전이라 그런게 맞나요?

지금 자료를 다시 찾으려니 못 찾고 있는데.. suspense를 사용할 경우 완성된 화면이 아닌 로딩화면을 먼저 내려주므로 seo에는 나쁠 수 있다라는 글을 본적이 있던 것 같은데..맞을까요?

*SEO 관련해서 추가로 궁금한건 강의가 따로 있다고 영상에서 말씀하셔서 거기까지 보고 필요할 경우 질문 한번 더 드리겠습니다.👍

 

답변 2

0

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

로딩 컴포넌트 회전은

https://github.com/ZeroCho/next-app-router-z/blob/master/ch3-2/src/app/(afterLogin)/home/home.module.css#L22

부분 추가되어 있어야 하고요.

지금 뭔가 이상하네요. 지금 상태는 SSR이 되면 안 되고 로딩이 떠야하는 상태입니다. 지금 빌드된 코드랑 dev 코드랑 서로 다른 코드 같습니다.

0

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

Loading.tsx 없고 prefetch 있는 경우

  1. 컨텐츠 SSR 됨(다만 handlers.ts에서 delay를 걸어둔 경우 첫 로딩이 오래걸림)

  2. SSR이 제일 잘 되나 prefetch가 끝나야 화면이 보이므로 사용자가 답답해할 수 있음

Loading.tsx 없고 useSuspenseQuery만 있는 경우

  1. fallback 부분이 없어서 그런지 무한 요청 보내짐(사용X)

Loading.tsx 있고 prefetch 있는 경우는

  1. Loading.tsx가 뜨고 3초 뒤에 게시글이 뜸. 따라서 SSR 안 됨

  2. metadata나 generateMetadata로 SSR 정보 넣어줘야 함

Loading.tsx 있는데 Suspense도 하나 더 있고 prefetch는 있는 경우

  1. Suspense fallback이 뜨고 3초 뒤에 게시글이 뜸. 따라서 SSR안 됨

  2. 컨텐츠 SSR 안 됨. metadata나 generateMetadata로 SSR 정보 넣어줘야 함

     

Loading.tsx 있는데 prefetch는 없는 경우

  1. 컨텐츠 SSR 안 됨. metadata나 generateMetadata로 SSR 정보 넣어줘야 함

  2. isPending: true일 때 로딩 보여주게 직접 코딩해야 함

여기까지 알 수 있는 점: Loading.tsx도 Suspense의 일종이고, Suspense는 prefetchQuery가 있는 경우 작동한다.

Loading.tsx와 prefetchQuery 중간에 Suspense가 있으면 그게 작동한다. 그 이유는 Loading.tsx도 Suspense라서 Suspense구조가 Loading.tsx->중간 Suspense->prefetchQuery이면 prefetchQuery는 중간 Suspense에 걸림.

Loading.tsx 있고 Suspense + useQuery 있고 prefetch 없는 경우

  1. Loading.tsx 작동 안하고 Suspense 작동 안 함. isPending: true의 로딩이 보여짐

Loading.tsx 있고 Suspense + useSuspenseQuery 있고 prefetch 없는 경우

  1. Loading.tsx 작동 안하고 Suspense fallback의 로딩이 보여짐

  2. 서버에서 한번, 프론트에서 한 번 총 두 번 요청하므로 비효율

     

     

    Loading.tsx 있고 Suspense 없이 useSuspenseQuery 쓰는 경우

  1. Loading.tsx 씀.

  2. 서버에서 한번, 프론트에서 한 번 총 두 번 요청하므로 비효율

결론:

  1. SSR이 전체적으로 완벽하게 되길 원하면, Loading.tsx & 별도 Suspense 없이 prefetchQuery만 사용

  2. useSuspense 시리즈 문제 있으므로 쓰지 말 것

  3. SSR을 metadata로 대신 할 수 있다면 Suspense를 두고 prefetchQuery하거나 Suspense 없이 useQuery 쓰면 됨.

  4. Loading.tsx를 쓸지 별도의 Suspense를 둘지 선택하는 기준: Loading.tsx는 페이지 전체를 로딩하므로 부분만 로딩하고 싶다면 별도의 Suspense 사용

 

홍홍님의 프로필 이미지
홍홍
질문자

강사님 자세한 설명 감사합니다.

말씀하신 generateMetadatametadata부분을 보면 SEO(검색 엔진 최적화)를 말씀해주신것 같은데.. SSR이 서버사이드 랜더링 말고 다른 단어?가 있나요?

 

"컨텐츠 SSR 안 됨. metadata나 generateMetadata로 SEO(SSR) 정보 넣어줘야 함" 일까요?

 

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

아 네네 SEO입니다.

홍홍님의 프로필 이미지
홍홍

작성한 질문수

질문하기