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

홍홍님의 프로필 이미지

작성한 질문수

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

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

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

24.06.03 12:35 작성

·

485

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

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

2024. 06. 04. 15:11

로딩 컴포넌트 회전은

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

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

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

0

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

2024. 06. 04. 14:48

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 사용

 

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

2024. 06. 04. 23:15

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

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

 

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

 

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

2024. 06. 05. 01:15

아 네네 SEO입니다.

홍홍님의 프로필 이미지

작성한 질문수

질문하기