1. 설치

<aside> 💡

npm install @tanstack/react-query @tanstack/react-query-devtools

</aside>

2. 기본 설정

최상위 컴포넌트에 provider로 감싸야 하는데,

client 방식이라 app/layout.ts에 직접 사용할 수 없다.

컴포넌트로 만든 후에 import 하여 사용해야함

2-1) 어떤 컴포넌트/QueryProvider.tsx

'use client'

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useState } from 'react';

export default function QueryProvier({ children }: { children: React.ReactNode }) {
    const [queryClient] = useState(() => new QueryClient({
        defaultOptions: {
            queries: {
                staleTime: 60 * 1000,
            }
        }
    }));

    return (
        <QueryClientProvider client={queryClient}>
            {children}
            <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
    )
}

1. 왜 set을 안 쓰나요?

QueryClient 인스턴스는 한 번 생성되면 애플리케이션이 실행되는 내내 단 하나만 존재해야 합니다. 만약 도중에 setQueryClient로 새로운 인스턴스를 갈아 끼워버리면, 이전에 쌓여있던 모든 캐시 데이터가 날아가 버립니다.

그래서 여기서는 **"컴포넌트가 처음 마운트될 때 딱 한 번만 만들고, 그다음 리렌더링 때는 새로 만들지 않고 기존 걸 재사용하겠다"**는 목적으로 useState를 사용하는 것입니다.


2. 일반 변수가 아니라 useState를 쓰는 이유

만약 layout.tsx나 Provider 안에서 그냥 변수로 선언하면 어떻게 될까요?

이 방식은 "불변하는 객체를 리액트의 생명주기 동안 안전하게 딱 하나만 유지하고 싶을 때" 사용하는 관용적인 패턴입니다.

2-2) 이후 app/layout에서 임포트하기

import QueryProvier from "@/lib/QueryProvider";
// ...
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased notoFont.className`}
      >
        <QueryProvier>
          {children}
        </QueryProvier>
      </body>
    </html>
  );
}