๐ 09. Next.js App Router ํ๊ฒฝ์์์ TanStack Query ์ ์
๐ ๊ฐ์
React Query๋ฅผ ์ต์ Next.js App Router(RSC) ํ๊ฒฝ์ ํ์ฌํ ๋ ํ์์ ์ธ QueryClientProvider ์ฑ๊ธํค ์ธํ ์ ๋ต๊ณผ ๋ฉ๋ชจ๋ฆฌ ๋ฆญ ๋ฐฉ์ง๋ฒ์ ๋ค๋ฃน๋๋ค.
๐ ๋ชฉ์ฐจ
- โ๏ธ ์์ฒ ์ด์ ๊ณ ๋ฏผ: "Next.js์์ Query ์ด๋ป๊ฒ ์ผ๋์?"
- ๐ค ์ ์์์ผ ํ๋๊ฐ: ๋ ์ธ๊ณ์ ์ถฉ๋
- 1. ์ ์ญ ๋์ ๋ฐฉ์ง: Request-Scoped QueryClient
- 2. ๋ ์ด์์ ์์ ์ฑ ์ ์ง: Provider ๊ป๋ฐ๊ธฐ ๋ถ๋ฆฌ (Boundary)
- ๐ ๋ฐฐ์ด ๋ด์ฉ ์ ๊ฒํ๊ธฐ (Quiz)
"์์ฒ ๋, Layout.tsx ํ์ผ ์ต์๋จ์
use client๋ฅผ ๋ถ์ด๋ฉด ๋ธ๋ผ์ฐ์ ๋ฒ๋ค๊ณผ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ด๋ป๊ฒ ๋ฌ๋ผ์ง๋์ง ๋จผ์ ํ์ธํด๋ด ์๋ค."
โ๏ธ ์์ฒ ์ด์ ๊ณ ๋ฏผ: "Next.js์์ Query ์ด๋ป๊ฒ ์ผ๋์?"
(์์์ผ ์์นจ, ๋ธ๋ผ์ฐ์ ๊ฐ ๋ฒ๋ฒ๋ฒ ๊ฑฐ๋ฆฌ๋ ํ์์ ๊ฒช๋ ์์ฒ )
๐ฃ ์์ฒ : ๋ฆฌ๋ ๋! ์ ํผ์ ์ฃผ๋ง์ Next.js App Router๋ก ํ๋ก์ ํธ ๋ผ๋ ๋ค ๋ง๋ค์ด ๋จ๊ฑฐ๋ ์? ๊ทผ๋ฐ React Query๋ฅผ app/layout.tsx์ Provider๋ก ๊ฐ์ธ๋ ค๋๊น ์๋ฌ๊ฐ ๋จ๋๋ผ๊ณ ์. Provider๋ ๋ฆฌ์กํธ ํ
์ ์ฐ๋๊น ๋น์ฐํ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์์? ๊ทธ๋์ layout.tsx ๊ผญ๋๊ธฐ์ 'use client' ์ง์์ด๋ฅผ ๋ถ์ด๊ณ ์์ํ์ฃ !
๐ฆ ์ํธ: ์์ฒ ๋, ๊ทธ๊ฑด App Router์ ์๋ฒ ์ปดํฌ๋ํธ(RSC) ์ด์ ์ ์ค์ด๋ ์ํํ ์ ํ์
๋๋ค. ์ต์๋จ ๋ ์ด์์ ์ ์ฒด๋ฅผ ํด๋ผ์ด์ธํธ๋ก ๋ฐ์ด๋ฒ๋ฆฌ์
จ๊ตฐ์.
๋ ์ฌ๊ฐํ ๊ฑด, const queryClient = new QueryClient() ๋ฅผ ๊ทธ๋ฅ ์ ์ญ ๋ณ์๋ก ์ ์ธํ์
จ๋ค์? ์๋ฒ์์ ์ด ๋ฐฉ์์ ๋ค๋ฅธ ์ ์ A์ ์บ์๊ฐ ์ ์ B์ ์์ฒญ์ ์์ผ ์ ์๋ ์ ์ญ ๋ฉ๋ชจ๋ฆฌ ์ค์ผ(Cross-Request State Leak) ์ํ์ด ์์ต๋๋ค. ๋จผ์ ๊ตฌ์กฐ๋ถํฐ ๋ถ๋ฆฌํด์ผ ํฉ๋๋ค.
๐ค ์ ์์์ผ ํ๋๊ฐ: ๋ ์ธ๊ณ์ ์ถฉ๋
SPA ์์ React ๊ฐ๋ฐ์๋ค์ index.tsx ์ต์๋จ ํ์ผ ๋ฐ์ const queryClient = new QueryClient() ํ๋๋ฅผ ์ ์ธํด๋๊ณ ๋๋ ค์ผ์ต๋๋ค. ๋ธ๋ผ์ฐ์ ๋ ๋ณดํต ํ ์ฌ์ฉ์์ ์ธ์
์์์๋ง ๋์ํ๋๊น์.
ํ์ง๋ง Next.js 13+ (App Router) ๋ ๊ฐ๋ ฅํ Node.js ์๋ฒ ์ฃผ๋ฐฉ์์ ์ปดํฌ๋ํธ๊ฐ ๊ตฌ์์ง๋๋ค. ์๋ฒ ๊ณต๊ฐ์ ์๋ง ๋ช
์ ์ ์ ์ ์ ์์ฒญ์ด ๋์์ ๋น๋ฐ์นฉ๋๋ค. ์ด ์ํฉ์์ ์๋ฒ ๋ฉ๋ชจ๋ฆฌ ์์ ์ ์ญ ๋ณ์(์ฑ๊ธํค)๋ก QueryClient ์ธ์คํด์ค๋ฅผ ํ๋ ๋ฉ๊ทธ๋ฌ๋ ๋ง๋ค์ด๋๋ฉด?
A ์ฌ์ฉ์๊ฐ ํ์นญํด ์จ [user, me] ์ ์ ์ ๋ณด ์บ์๊ฐ 0.1์ด ๋ค ์ ์ํ B ์ฌ์ฉ์์ ํ๋ฉด์ ๊ทธ๋๋ก ํํธ(Cache Hit) ๋๋ฒ๋ฆฌ๋ ์น๋ช
์ ๊ฐ์ธ์ ๋ณด ์ ์ถ ์ฌ๊ณ ๊ฐ ๋ฐ์ํฉ๋๋ค.
์ด ๋จ์์์๋ RSC ์ธ๊ณ๊ด์์ ์๋ฒฝํ๊ณ ์์ ํ๊ฒ React Query๋ฅผ ์ค์นํ๋ '์ ์(Boilerplate)' ์ ์ ๋ฒ์ ์ตํ๋๋ค.
1. ์ ์ญ ๋์ ๋ฐฉ์ง: Request-Scoped QueryClient
์ ๋ต์ "ํด๋ผ์ด์ธํธ ํ๊ฒฝ(๋ธ๋ผ์ฐ์ )์ผ ๋๋ ์ ์ญ ์ฑ๊ธํค์ ์ฐ๋, ์๋ฒ ํ๊ฒฝ์ผ ๋๋ ๋ฐ๋์ ์ฌ์ฉ์์ ์์ฒญ(Request) 1๋ฒ๋น 1๊ฐ์ QueryClient๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์ฐ์ด๋ธ๋ค(Factory)" ์ ๋๋ค.
์ด๋ Next.js์ TanStack Query ๊ณต์ ๋ฌธ์์์ ์๊ธฐ๋ฅผ ๋ฐ์ ํฉํ ๋ฆฌ ํจํด์
๋๋ค.
๋จผ์ ํ
๊ธฐ๋ฐ ์์ฑ๊ธฐ ํ์ผ์ ๋ถ๋ฆฌํด์ผ ํฉ๋๋ค.
// ๐ app/providers/get-query-client.ts
import { QueryClient, defaultShouldDehydrateQuery } from '@tanstack/react-query';
// ๋ฑ ์ด ์ต์
๋ฉ์ด๋ฆฌ ํ๋๋ง ์ ๋ง๋ค์ด ๋ก๋๋ค.
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// SSR ํ๊ฒฝ์์๋ ๋ณดํต 0 ์ด์์ผ๋ก ๋ก๋๋ค.
// 0์ด๋ฉด ์๋ฒ ์บ์๊ฐ ํด๋ผ์ด์ธํธ๋ก ๋์ด๊ฐ์๋ง์ stale ์ํ๊ฐ ๋์ด ๋ฐ๋ก ๋ค์ ์์ฒญํ ์ ์์ต๋๋ค.
staleTime: 60 * 1000,
},
dehydrate: {
// ๋ณด๋ฅ ์ค์ธ ํ๋ก๋ฏธ์ค๊น์ง ํด๋ผ์ด์ธํธ๋ก Hydrateํ ์ ์๊ฒ ํ๋ ์ต์
shouldDehydrateQuery: (query) =>
defaultShouldDehydrateQuery(query) ||
query.state.status === 'pending',
}
},
});
}
// ๐ ํด๋ผ์ด์ธํธ ์ฑ๊ธํค ์์ ๋ง ๋ณ์
let browserQueryClient: QueryClient | undefined = undefined;
export function getQueryClient() {
if (typeof window === 'undefined') {
// ์๋ฒ ํ๊ฒฝ์์๋ ์์ฒญ ํ ๋ฒ๋ง๋ค QueryClient๋ฅผ ์๋ก ๋ง๋ ๋ค.
return makeQueryClient();
} else {
// ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์๋ ๋ ๋๋ง์ด ๋ฐ๋ณต๋์ด๋ ํ๋์ ์ธ์คํด์ค๋ฅผ ์ ์งํ๋ค.
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
}2. ๋ ์ด์์ ์์ ์ฑ ์ ์ง: Provider ๊ป๋ฐ๊ธฐ ๋ถ๋ฆฌ (Boundary)
์์์ ์์ ํ QueryClient ์์ฑ๊ธฐ๋ฅผ ๋ง๋ค์๋ค๋ฉด, ์ด์ ์ด๊ฑธ ์ฑ์ ์ฃผ์
ํด์ผ ํฉ๋๋ค.
layout.tsx ๊ผญ๋๊ธฐ์ 'use client'๋ฅผ ๋ถ์ด๋ฉด ๋ฃจํธ ๋ ์ด์์์ ์๋ฒ ์ปดํฌ๋ํธ ์ด์ ์ ์๊ธฐ ์ฝ์ต๋๋ค.
๋์ , Provider ๊ป๋ฐ๊ธฐ ์ ์ฉ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ ๋ฅผ ์๊ฒ ํ๋ ๋ถ๋ฆฌํด์ ํด๋ผ์ด์ธํธ ๊ฒฝ๊ณ๋ฅผ ์ต์ํํฉ๋๋ค.
// ๐ app/providers/react-query-provider.tsx
'use client' // โค๏ธ ์ด๊ฒ์ Provider ์ ์ฉ ๊ป์ง์ด๋ค!
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { getQueryClient } from './get-query-client';
export default function ReactQueryProvider({ children }: { children: React.ReactNode }) {
// useState์ ์ด๊ธฐํ๊ฐ(Initializer) ํจํด์ ํตํด,
// ๋ ๋๋ง์ด ์ผ์ด๋ ๋๋ง๋ค ํด๋ผ์ด์ธํธ ๊ฐ์ฒด๋ฅผ ๋ค์ ๊น์ง ์๋๋ก ๋ณด์ฅํฉ๋๋ค!
const queryClient = getQueryClient();
return (
<QueryClientProvider client={queryClient}>
{children}
{/* ๋ค์ผ๋ก ๊ฐ๋ฐ ๋๊ตฌ๋ ์ฌ๊ธฐ์ ์ฌ์ด์ค๋ค */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}์ด ์์ ๊ป์ง์ ์์ฑํ์ผ๋, ๋๋์ด ๋๋ง์ ์ต์๋จ ์๋ฒ ์ฃผ๋ฐฉ์ธ layout.tsx ์ ๋ฌด์ฌ๊ณ ํ์ฌํ ์ ์์ต๋๋ค.
// ๐ app/layout.tsx
// (์ฌ๊ธด ์์ Server Component ์์ญ์
๋๋ค)
import ReactQueryProvider from './providers/react-query-provider';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
{/* ๐ ์๋ฒ ์ปดํฌ๋ํธ์ ๊ฑฐ๋ํ ํธ๋ฆฌ๋ฅผ Provider ๋ฐ๊ตฌ๋๋ก ๋ด์์ฃผ๊ธฐ๋ง ํ๋ฉด ๋! */}
<ReactQueryProvider>
{children}
</ReactQueryProvider>
</body>
</html>
);
}์ด ๊ตฌ์กฐ๋ layout.tsx ์์ฒด๋ฅผ ํด๋ผ์ด์ธํธ ๋ฒ๋ค๋ก ๋ฐ์ด ๋ฃ์ง ์์ผ๋ฉด์, ํ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๊ฐ ํ์ํ ๊ณณ์์ useQuery๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ํด์ค๋๋ค.
๐ ๋ฐฐ์ด ๋ด์ฉ ์ ๊ฒํ๊ธฐ (Quiz)
Q. ๋ค์๊ณผ ๊ฐ์ด ์ปดํฌ๋ํธ ๋ด๋ถ์ useState๋ฅผ ์ด์ฉํด QueryClient ํด๋ผ์ด์ธํธ๋ฅผ ์ด๊ธฐํ(์ด๊น๊ฐ)ํ์ฌ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ์ด๊ฒ์ด ํ
์ค ์๋ฆฌ์ ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ์ด์ ์์ ํ ์ด์ ๋ ๋ฌด์์
๋๊น?
'use client'
import { QueryClientProvider, QueryClient } from '@tanstack/react-query'
import { useState } from 'react'
export default function Providers({ children }: { children: React.ReactNode }) {
// ์ปดํฌ๋ํธ ๋ด๋ถ์์ ์์ฑ!
const [queryClient] = useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}- A)
useState์์์ ์ฝ๋ฐฑ ํจ์๋ก ๊ฐ์ธ ์ง์ฐ ์ด๊ธฐํ(Lazy Initialization)๋ฅผ ํ์ผ๋ฏ๋ก ์ปดํฌ๋ํธ๊ฐ ์๋ฌด๋ฆฌ ๋ฆฌ๋ ๋๋ง์ ํ๋๋ผ๋ ์ ๋ QueryClient๋ฅผ ์ฌ์์ฑํ์ง ์๊ณ ๋ ํผ๋ฐ์ค๋ฅผ ์บ์ฑ(์ ์ง)ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. - B) ๋ธ๋ผ์ฐ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ฌดํ์ ์บ์๊ฐ ์์ด๋ ๊ฑธ ๋ง๊ธฐ ์ํด, ๋ฆฌ๋ ๋๋ง๋ง๋ค ๋ก์ QueryClient๊ฐ ๋ฎ์ด์จ์ ธ ์ด๊ธฐํ๋๋๋ก ์๋ช ์ ์ ํํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
- C) ์ ์ฝ๋๋ ํ๋ฆฐ ์ฝ๋์
๋๋ค.
useMemo๋ฅผ ๋ฐ๋์ ์ฌ์ฉํด์ผ ๋์ผํ ์ธ์คํด์ค๊ฐ ์ ์ง๋ฉ๋๋ค.
โ
์ ๋ต: A
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
:
new QueryClient()๋ฅผ ์๋ฒ ๋ชจ๋ ์ ์ญ์ ๋๋ฉด ์์ฒญ ๊ฐ ์บ์ ๊ณต์ ์ํ์ด ์๊ธฐ๊ณ , ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ ๋ณธ๋ฌธ์์ ๋งค ๋ ๋๋ง๋ค ๋ง๋ค๋ฉด ์บ์ ์ธ์คํด์ค๊ฐ ๊ณ์ ์ด๊ธฐํ๋ฉ๋๋ค.useState(() => new QueryClient())์ฒ๋ผ ์ง์ฐ ์ด๊ธฐํํ๋ฉด ๋ธ๋ผ์ฐ์ ์๋ช ์ฃผ๊ธฐ ๋์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ ์งํ ์ ์์ต๋๋ค. - ์ค๋ต ํผ๋๋ฐฑ: "์์ฒ ๋, B๋ฒ์ฒ๋ผ ๋ฆฌ๋ ๋๋ง๋ง๋ค QueryClient๊ฐ ์ด๊ธฐํ๋๋ฉด ์บ์๋ฅผ ์ ์งํ ์ ์์ต๋๋ค. ์บ์๋ ์์ ์ ์ธ ์ธ์คํด์ค ์์์ ์๋ฏธ๊ฐ ์๊น๋๋ค."
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ์ธ์คํด์ค ์๋ช
์ฐ์ฅ์ ์ ๊ฝ,
useState(() => init)์ง์ฐ ์ด๊ธฐํ(Lazy Init)!
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. Next.js App Router์์ QueryClient๋ฅผ ์ปดํฌ๋ํธ ๋ ๋๋ง๋ง๋ค ์๋ก ๋ง๋ค๋ฉด ์ด๋ค ๋ฌธ์ ๊ฐ ์๊ธฐ๋์?
โ ์ ๋ต: ๋ ๋๋ง ๋๋ง๋ค ์บ์ ์ ์ฅ์๊ฐ ๋ฐ๋์ด ๊ธฐ์กด ์บ์๊ฐ ์ ์ง๋์ง ์๊ณ ๋ถํ์ํ ์ฌ์์ฒญ๊ณผ ์ํ ์ด๊ธฐํ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
๐ก ์์ธ ํด์ค:
QueryClient๋ ์บ์ ๊ธ๊ณ ์
๋๋ค. ํด๋ผ์ด์ธํธ์์๋ ์์ ์ ์ธ ์ธ์คํด์ค๋ฅผ ์ ์งํด์ผ ๊ฐ์ ํค์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. Provider ์ปดํฌ๋ํธ ์์์ useState(() => new QueryClient()) ๊ฐ์ ํจํด์ ์ฐ๋ ์ด์ ๋ ์ธ์คํด์ค ์์ ์ฑ ๋๋ฌธ์
๋๋ค.
Q2. App Router์์ TanStack Query Provider ํ์ผ์ 'use client'๊ฐ ํ์ํ ์ด์ ๋ ๋ฌด์์ธ๊ฐ์?
โ
์ ๋ต: QueryClientProvider์ useQuery ๊ธฐ๋ฐ ์บ์ ๊ตฌ๋
์ ํด๋ผ์ด์ธํธ ๋ฐํ์์์ ๋์ํด์ผ ํ๋ฏ๋ก ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ ๊ฒฝ๊ณ๊ฐ ํ์ํฉ๋๋ค.
๐ก ์์ธ ํด์ค:
์๋ฒ ์ปดํฌ๋ํธ๋ ๋ธ๋ผ์ฐ์ ์ํ๋ฅผ ๊ตฌ๋
ํ์ง ์์ต๋๋ค. Query Provider๋ ํด๋ผ์ด์ธํธ ์บ์์ ํ
์ ์ํ ๊ธฐ๋ฐ์ด๋ฏ๋ก ํด๋ผ์ด์ธํธ ๊ฒฝ๊ณ ์์ ๋ฌ์ผ ํฉ๋๋ค. ๋ค๋ง ์๋ฒ์์ ํ๋ฆฌํ์นํ ๊ฒฐ๊ณผ๋ ์ดํ HydrationBoundary๋ก ๋๊ธธ ์ ์์ต๋๋ค.
Q3. ์์ฒ ์ด์ ํ ์คํธ ํ์: ์๋ฒ ์ปดํฌ๋ํธ์์ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๊ฐ ํด๋ผ์ด์ธํธ ์ง์ ์งํ ๋ค์ fetch๋ฉ๋๋ค. ๊ฐ์ฅ ๋จผ์ ์ ๊ฒํ ์ค์ ์ ๋ฌด์์ธ๊ฐ์?
โ
์ ๋ต: ํ๋ฆฌํ์น๋ ๋ฐ์ดํฐ์ ๋ง๋ staleTime์ด ์ค์ ๋์ด ์๋์ง ํ์ธํฉ๋๋ค.
๐ก ์์ธ ํด์ค:
TanStack Query๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ฆ์ stale์
๋๋ค. SSR ์งํ ๊ณง๋ฐ๋ก ์ฌ์์ฒญ์ด ๋ ์๊ฐ๋ ๊ฒ์ด ํญ์ ๋ฒ๊ทธ๋ ์๋์ง๋ง, โ๋ฐฉ๊ธ ์๋ฒ์์ ๋ฐ์์จ ๋ฐ์ดํฐโ๋ผ๋ฉด ์ ์ ํ ์ ์ ๋ ์๊ฐ์ ์ฃผ์ด ์ค๋ณต ์์ฒญ์ ์ค์ผ ์ ์์ต๋๋ค.
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ QueryClient ์ธ์คํด์ค์ ์์น๊ฐ ๋ณด์๊ณผ ์ฑ๋ฅ์ ๋์์ ์ข์ฐํ๋ค๋ ๊ฑธ ๋ฐฐ์ ๋ค. ๋ธ๋ผ์ฐ์ ์์๋ ํ๋์ ์์ ์ ์ธ ํด๋ผ์ด์ธํธ๋ฅผ ์ฌ์ฌ์ฉํด์ผ ์บ์๊ฐ ์ ์ง๋์ง๋ง, ์๋ฒ์์๋ ์์ฒญ๋ณ๋ก ์ ํด๋ผ์ด์ธํธ๋ฅผ ๋ง๋ค์ด์ผ ์ฌ์ฉ์ ๋ฐ์ดํฐ๊ฐ ์์ด์ง ์๋๋ค.
๐ก ์ค๋์ ๊ตํ: "์ฝ๋๊ฐ ์๋ฒ์์ ๋๋์ง ๋ธ๋ผ์ฐ์ ์์ ๋๋์ง ๋จผ์ ํ์ธํ์. QueryClient๋ ํด๋ผ์ด์ธํธ์์๋ ์์ ์ ์ผ๋ก ์ฌ์ฌ์ฉํ๊ณ , ์๋ฒ์์๋ ์์ฒญ๋ง๋ค ์๋ก ๋ง๋ ๋ค."
๋ด์ผ์ Provider๋ฅผ ๋ง๋ค ๋ layout.tsx์ ๋ฐ๋ก 'use client'๋ฅผ ๋ถ์ด์ง ๋ง๊ณ , ์์ provider ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ก ๋ง๋ ๋ค getQueryClient()์ ์๋ฒ/๋ธ๋ผ์ฐ์ ๋ถ๊ธฐ๋ฅผ ๋จผ์ ํ์ธํด์ผ๊ฒ ๋ค.