개발자

Next와 React-query의 prefetch에 대해

2024년 04월 05일조회 376

안녕하세요 ! Next14 버전과 tanstack-qeury를 이용해 프로젝트를 진행하던 도중 궁금한 점이 생겨 질문드립니다. Next의 서버 컴포넌트에서 data를 prefetching 하여 사용하려면 다음과 같은 단계를 밟아야 하는 것으로 알고 있습니다. 1. 서버 컴포넌트에서 queryClient.prefetchQuery를 사용해 데이터를 불러오고 이를 dehydrate하여 HydrationBoundary 내에 state로 넘겨준다. 2. 데이터를 사용하는 컴포넌트에서 useQuery로 동일한 데이터를 불러오면 해당 데이터는 prefetch 된 상태로 넘어와 이를 사용한다. 현재 이를 토대로 아래 예시코드와 같이 작성하여 사용중입니다. 이 때 하위 컴포넌트에서 prefetch한 데이터를 사용하려면 useQuery를 써야하기에, 'use client' 를 사용해야하고, 그렇게 되면 그 하위 컴포넌트도 전부 Client Boundary에 들어오게 되는 것으로 알고있는데, 그럼 "Next를 사용하며 얻을 수 있는 장점이 줄어들지 않나 ?" 라는 의문이 생겼습니다. 제가 사용법을 잘못 알고있다거나 Next의 개념에 대해 부족한걸까요 ? 또, HydrationBoundary로 감싸지 않고 queryClient.getQueryData를 이용해 직접 데이터를 가져와 이를 props로 전달해도 기능은 정상적으로 작동하는데, 이 둘의 차이점이 정확히 무엇인가요 ?

1// 서버 컴포넌트
2
3import {
4  HydrationBoundary,
5  dehydrate,
6  QueryClient,
7} from "@tanstack/react-query";
8import { QUERY_KEY } from "@/constants";
9import { requestGetPushAlarmList } from "@/apis/notification";
10import NotificationClient from "./Client";
11
12export default async function Page() {
13  const queryClient = new QueryClient();
14
15  await queryClient.prefetchQuery({
16    queryKey: [QUERY_KEY.PUSH_LIST],
17    queryFn: () => requestGetPushAlarmList({}),
18  });
19
20// const { rows: pushList } = queryClient.getQueryData([
21    // QUERY_KEY.PUSH_LIST,
22  // ]) as unknown as Rows<PushAlarm>; 예시 2. getQueryData로 데이터에 접근
23
24  return (
25    <HydrationBoundary state={dehydrate(queryClient)}>
26        <NotificationClient />
27    </HydrationBoundary>
28
29    // <NotificationList pushList={pushList} /> 예시2. props로 직접 데이터 전달
30  );
31}
32
33// 하위 컴포넌트
34
35"use client";
36
37import { useQuery } from "@tanstack/react-query";
38import { requestGetPushAlarmList } from "@/apis/notification";
39import { QUERY_KEY } from "@/constants";
40import NotificationList from "./_components/NotificationList";
41import ScreenBox from "@/components/ui/ScreenBox";
42import Empty from "@/components/ui/Empty";
43
44export default function NotificationClient() {
45  const { data } = useQuery({
46    queryKey: [QUERY_KEY.PUSH_LIST],
47    queryFn: () => requestGetPushAlarmList({}),
48    select: (data) => data.rows,
49  });
50
51  return (
52    <>
53      {!data?.length ? (
54        <ScreenBox.CenterBody>
55          <Empty message="알림이 없습니다." />
56        </ScreenBox.CenterBody>
57      ) : (
58        <NotificationList pushList={data} />
59      )}
60    </>
61  );
62}
이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.
profile picture
익명님의 질문

답변 1

허니님의 프로필 사진

클라이언트 컴포넌트도 서버에서 초기 렌더링이 수행될 수 있습니다 1. 전체 페이지 로드 시 (ex. 첫 페이지 로드, 새로고침) prefetch를 사용 시 -> data를 서버에서 fetching하여 data를 가진 상태의 컴포넌트의 초기 html을 서버에서 렌더링합니다 prefetch를 사용하지 않을 시 -> 서버에서는 아직 data가 없으므로 데이터가 없을 때의 fallback ex. if(isLoading) return <p>loading...</p>을 표시합니다 2. 후속 탐색 시 (ex. Link를 통한 페이지 이동) 클라이언트 구성 요소는 클라이언트에서 완전히 렌더링 됩니다. https://nextjs.org/docs/app/building-your-application/rendering/client-components prefetch와 HydrationBoundary을 사용하면 서버에서 데이터를 fetching하고 클라이언트의 queryclient에 바로 전달할 수 있어 초기 렌더링 시 깜박임이 생기지 않습니다. 클라이언트 컴포넌트에서는 서버에서 Hydration을 했는지 여부를 알지 못해도 되고 그대로 useQuery와 같은 훅을 사용하면 됩니다 Hydration 대신 props를 통해 데이터를 전달할 수도 있습니다. 하지만 tanstack query가 필요한 컴포넌트는 지속적으로 데이터 관리가 필요할 가능성이 크다고 볼 수 있습니다. 따라서 해당 컴포넌트는 클라이언트 컴포넌트로서 props로 초기 상태를 전달 받고 훅을 통해 지속적으로 상태 관리를 해야 합니다. 또한 데이터가 필요한 위치까지 계속 내려줘야 하는 props drilling가 발생할 수 있습니다. 이와 관련해서는 공식 문서에서 자세히 찾아볼 수 있습니다 https://tanstack.com/query/latest/docs/framework/react/guides/ssr#get-started-fast-with-initialdata

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

AI 답변
커리어리 AI 봇 프로필 이미지

커리어리 AI 봇

GPT가 달아주는 답변입니다.

2024년 04월 05일

Next.js와 React Query를 함께 사용하실 때의 깊은 이해가 요구되는 질문이네요. 먼저, `useQuery`를 사용하면서 Client Boundary에 하위 컴포넌트들이 전부 들어오게 되어 Next의 서버사이드 렌더링(SSR) 같은 기능을 적극적으로 활용하지 못한다는 당황스러움을 느끼셨을 것 같아요. 다만, 이런 문제 상황은 "SSR과 CSR(Client-Side Rendering) 간에 데이터 패칭 전략을 어떻게 설정하느냐"에 따라 달라집니다. Next.js에서 `useQuery`를 사용하는 것이 반드시 SSR의 장점을 줄이지는 않습니다. 실제로, React Query 및 SWR 등의 도구는 초기 페이지 로드 시 SSR로 데이터를 패치하고, 이후에 발생하는 상호작용에 대해서는 클라이언트 사이드에서 데이터 변경을 처리합니다. 그래서 만약 특정 데이터가 페이지 초기 로딩 시 한 번만 패치되며 그 이후에 업데이트되지 않는 경우에는 Next.js의 `getServerSideProps`나 `getInitialProps`에서 데이터 패치 후 prop으로 내려주거나 해당 API 호출 코드를 외부 함수로 분리한 다음 공유하시면 됩니다. 또한, HydrationBoundary와 queryClient.getQueryData의 차이점에 대해 질문하셨는데요. `HydrationBoundary`는 서버에서 dehydrate 된 쿼리 상태를 다시 클라이언트에서 hydrate 하여 사용할 수 있도록 해주는 컴포넌트입니다. 이것을 통해 데이터를 prefetch 한 후 해당 상태를 클라이언트로 보내고 클라이언트가 그 데이터를 재사용할 수 있게 하는 것이죠. 반면에 `queryClient.getQueryData`는 현재 queryClient에서 유지되고 있는 쿼리 결과를 가져오는 메서드입니다. 즉, HydrationBoundary는 서버와 클라이언트 간의 state 전달을 위한 도구로 볼 수 있으며, `queryClient.getQueryData`는 해당 시점에서 queryClient가 가진 데이터에 접근하는 방법으로 이해하시면 됩니다. 마지막으로 Next.js와 React Query 사이에 괴리감을 느낀다면 서버 컴포넌트 환경에서 React Query의 `Hydrate` 기능을 활용해보시기 바랍니다. 이렇게 하면 서버사이드 패칭과 CSR 패칭간 효율적인 관리가 가능할 거에요.

목록으로

지금 가입하면 모든 질문의 답변을 볼 수 있어요!