Next.js에서 prefetch의 필요성
Next.js는 동적 데이터도 빌드 시점에 미리 fetch해서 로딩 속도를 향상시킬 수 있다. (SSG)
하지만 빌드 시점의 상태가 아닌 최신 상태를 받아야하면서도, 로딩 속도를 향상시키고 싶다면 prefetch 를 활용할 수 있다!

json-server 옵션으로 user 목록을 json-server에서 로드해오는 fetch에 2초 딜레이를 준 상태다
메인 페이지에서 사용자 목록을 prefetch 해두었기 때문에 other 페이지에서 사용자 목록을 로딩 없이 바로 확인할 수 있다!
예제
Query Provider 만들기
하위 컴포넌트들이 queryClient에 접근할 수 있도록 Query Provider를 생성한다
// src/components/providers/TQProvider.tsx
"use client";
import {
isServer,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
},
});
}
let browserQueryClient: QueryClient | undefined = undefined;
function getQueryClient() {
if (isServer) {
// Server: always make a new query client
return makeQueryClient();
} else {
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
}
export default function Providers({ children }: { children: React.ReactNode }) {
// NOTE: Avoid useState when initializing the query client if you don't
// have a suspense boundary between this and the code that may
// suspend because React will throw away the client on the initial
// render if it suspends and there is no boundary
const queryClient = getQueryClient();
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
// app/layout.tsx
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
prefetch 하기
prefetch를 수행할 queryClient를 생성하고, queryClient.prefetchQuery를 호출한다.
<HydrationBoundary/>에 state에 dehydrate(queryClient)를 넘겨 queryClient에 캐싱된 값을 직렬화해 넘겨준다. (json 형태)
(HydrationBoundary 하위의 요소들 외에도 다른 queryClient가 캐싱된 값을 공유하게되어, 다른 queryClient에서도 해당 값을 사용할 수 있게 된다.)
// app/page.tsx
import {
dehydrate,
HydrationBoundary,
QueryClient,
} from "@tanstack/react-query";
import Link from "next/link";
export default async function Home() {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 3000,
},
},
});
const fetchUsers = async () => {
const response = await fetch("http://localhost:4000/users");
const data = await response.json();
return data;
};
await queryClient.prefetchQuery({
queryKey: ["users"],
queryFn: () => fetchUsers(),
});
return (
<div className="flex flex-col gap-3 justify-center items-center min-h-screen font-[family-name:var(--font-geist-sans)]">
<div className="flex gap-3">
<button className="shadow shadow-gray-500/50 rounded p-2">
Increment
</button>
<button className="shadow shadow-gray-500/50 rounded p-2">
Decrement
</button>
</div>
<HydrationBoundary state={dehydrate(queryClient)}>
<Link href="/other">Other 페이지로 이동하기</Link>
</HydrationBoundary>
</div>
);
}
prefetch된 값 사용하기
다른 페이지에서 prefetch와 동일한 queryKey의 값을 사용하면 이미 캐싱되어있는 데이터를 바로 사용할 수 있다!
"use client";
import { User } from "@/query/useUser";
import { useQuery } from "@tanstack/react-query";
const Page = () => {
const { data, isLoading } = useQuery({
queryKey: ["users"],
queryFn: async () => {
const response = await fetch("http://localhost:4000/users");
const data: User[] = await response.json();
return data;
},
});
if (isLoading) return <div>Loading...</div>;
return (
<div className="w-full min-h-screen flex flex-col justify-center items-center">
<h1 className="font-bold text-xl">other 페이지 입니다</h1>
{data?.map((user) => {
return <div key={user.id}>{user.name}</div>;
})}
</div>
);
};
export default Page;

성공!
'Next.js' 카테고리의 다른 글
[Next.js] SSR 이해하기 (1) | 2024.10.07 |
---|---|
[Next.js] 개인과제 트러블슈팅 (동적 라우팅 경로, 클라이언트 컴포넌트 환경변수, Module not found) (0) | 2024.10.07 |
[Next.js] Auth와 Middleware (0) | 2024.09.30 |
[Next.js] Parallel Routes & Intercepting Routes (1) | 2024.09.29 |
[Next.js] Caching (1) | 2024.09.28 |