๐Ÿš€ 09. Next.js App Router ํ™˜๊ฒฝ์—์„œ์˜ TanStack Query ์…‹์—…

2026๋…„ 3์›” 4์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

React Query๋ฅผ ์ตœ์‹  Next.js App Router(RSC) ํ™˜๊ฒฝ์— ํƒ‘์žฌํ•  ๋•Œ ํ•„์ˆ˜์ ์ธ QueryClientProvider ์‹ฑ๊ธ€ํ†ค ์„ธํŒ… ์ „๋žต๊ณผ ๋ฉ”๋ชจ๋ฆฌ ๋ฆญ ๋ฐฉ์ง€๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ

"์˜์ฒ  ๋‹˜, Layout.tsx ํŒŒ์ผ ์ตœ์ƒ๋‹จ์— use client ๋ถ™์—ฌ๋†“๊ณ  ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†” ํ•œ ๋ฒˆ ๋ณด์„ธ์š”. ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ํ„ฐ์ ธ๋‚˜๊ฐ€๊ณ  ์žˆ๋Š”์ง€."

โ˜•๏ธ ์˜์ฒ ์ด์˜ ๊ณ ๋ฏผ: "Next.js์—์„œ Query ์–ด๋–ป๊ฒŒ ์ผœ๋‚˜์š”?"

(์›”์š”์ผ ์•„์นจ, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ฒ„๋ฒ„๋ฒ…๊ฑฐ๋ฆฌ๋Š” ํ˜„์ƒ์„ ๊ฒช๋Š” ์˜์ฒ )

๐Ÿฃ ์˜์ฒ : ๋ฆฌ๋“œ ๋‹˜! ์ € ํ˜ผ์ž ์ฃผ๋ง์— Next.js 14 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์ด๋ฉด ์„œ๋ฒ„ ์บ์‹œ๊ฐ€ ํด๋ผ์ด์–ธํŠธ๋กœ ๋„˜์–ด๊ฐ€์ž๋งˆ์ž ์ƒํ•ด๋ฒ„๋ ค์„œ ๋ฐ”๋กœ ๋˜ ๋ฆฌํŒจ์น˜ ์นฉ๋‹ˆ๋‹ค.
        staleTime: 60 * 1000, 
      },
      dehydrate: {
        // ๋ณด๋ฅ˜ ์ค‘์ธ ํ”„๋กœ๋ฏธ์Šค๋Š” ์ ˆ๋Œ€ ํด๋ผ์ด์–ธํŠธ๋กœ Hydrate ์˜์ง€ ๋งˆ๋ผ๋Š” Next.js ๊ถŒ์žฅ ์˜ต์…˜
        shouldDehydrateQuery: (query) =>
          defaultShouldDehydrateQuery(query) ||
          query.state.status === 'pending',
      }
    },
  });
}
 
// ๐ŸŽ‰ ํด๋ผ์ด์–ธํŠธ ์‹ฑ๊ธ€ํ†ค ์•ˆ์ „๋ง ๋ณ€์ˆ˜
let browserQueryClient: QueryClient | undefined = undefined;
 
export function getQueryClient() {
  if (typeof window === 'undefined') {
    // 1๏ธโƒฃ ์ง€๊ธˆ ๋‚ด๊ฐ€ ์„œ๋ฒ„ ์ฃผ๋ฐฉ์ด๋ผ๋ฉด? 
    // ์ „์—ญ ๋ณ€์ˆ˜ ๊ณต์œ  ์ ˆ๋Œ€ ๊ธˆ์ง€! ์š”์ฒญ ํ•œ ๋ฒˆ๋งˆ๋‹ค ๋ฌด์กฐ๊ฑด ๋ผˆ๋Œ€ ์ƒˆ๋กœ ์ƒ์„ฑ!
    return makeQueryClient();
  } else {
    // 2๏ธโƒฃ ๋‚ด๊ฐ€ ์ง€๊ธˆ ์œ ์ €์˜ ๋ธŒ๋ผ์šฐ์ €(ํฐ) ์•ˆ์ด๋ผ๋ฉด?
    // ๋ฆฌ์•กํŠธ๊ฐ€ ๋ Œ๋”๋ง์„ ์ˆ˜๋ฐฑ ๋ฒˆ ๋‹ค์‹œ ๋Œ์•„๋„ ๋‹จ 'ํ•˜๋‚˜'์˜ ์ „์—ญ ์ธ์Šคํ„ด์Šค๋งŒ ๋ณด์กด!
    if (!browserQueryClient) browserQueryClient = makeQueryClient();
    return browserQueryClient;
  }
}

2. ๋ ˆ์ด์•„์›ƒ ํŒŒ๊ดด ๊ธˆ์ง€: Provider ๊ป๋ฐ๊ธฐ ๋ถ„๋ฆฌ (Boundary)

์œ„์—์„œ ์•ˆ์ „ํ•œ ์•ˆ์ „๋ชจ ํ˜ธ์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด, ์ด์ œ ์ด๊ฑธ ์œ ์ €์—๊ฒŒ ๊ฐ์‹ธ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ ˆ๋Œ€ 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 ํ›…์„ ๋นจ์•„๋จน์„ ์ˆ˜ ์žˆ๋Š” ์™„๋ฒฝํ•œ ์•„ํ‚คํ…์ฒ˜์ž…๋‹ˆ๋‹ค!


๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์™€, ์ง„์งœ ์†Œ๋ฆ„ ๋‹์•˜๋‹ค... ๋ฐฉ๊ธˆ ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์—์„œ ๋‚ด๊ฐ€ ๋งŒ๋“  "์ „์—ญ QueryClient" ํ•˜๋‚˜ ๋•Œ๋ฌธ์— ์˜†์ž๋ฆฌ ๋‚ด ๋™๊ธฐ์˜ ํ”„๋กœํ•„ ํ† ํฐ ์บ์‹œ๊ฐ€ ๋‚ด ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์— ๋˜‘๊ฐ™์ด ๋ Œ๋”๋ง๋˜๋Š” ๊ฑธ ์žฌํ˜„ํ•ด์ฃผ์…จ๋Š”๋ฐ ๋“ฑ๊ณจ์ด ์‹น ์„œ๋Š˜ํ•ด์กŒ๋‹ค;;

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "Next.js App Router ์‹œ๋Œ€์—๋Š” ๋‚ด๊ฐ€ ์ง  ์ฝ”๋“œ๊ฐ€ ์„œ๋ฒ„ ์ฃผ๋ฐฉ์—์„œ ๋Œ์•„๊ฐ€๋Š”์ง€, ์œ ์ €์˜ ํฐ์—์„œ ๋Œ์•„๊ฐ€๋Š”์ง€๋ฅผ ๊ตฌ๋ถ„ ๋ชป ํ•˜๋ฉด ๋Œ€ํ˜• ๋ณด์•ˆ ์‚ฌ๊ณ (Cross-Request Leak) ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. QueryClient๋Š” ํด๋ผ์ด์–ธํŠธ์ผ ๋• ์ „์—ญ ์žฌ์‚ฌ์šฉ ๋‹จ 1๊ฐœ, ์„œ๋ฒ„์ผ ๋• ๋ฌด์กฐ๊ฑด ์š”์ฒญ๋‹น 1๊ฐœ์”ฉ ์ฐ์–ด๋‚ด์ž(Factory)!"

๋‚œ ๊ทธ๋ƒฅ ์‰ฝ๊ฒŒ ๊ฐ€๋ ค๊ณ  const queryClient = new QueryClient() ํ•œ ์ค„ ๋ก ์นœ ๊ฒƒ ๋ฟ์ธ๋ฐ, ๊ทธ๊ฒŒ ์ˆ˜๋งŒ ๋ช…์˜ ์ ‘์†์ž๊ฐ€ ๊ฐ™์ด ํผ๋จน๋Š” ๊ฑฐ๋Œ€ํ•œ ๊ณต์šฉ ๋ƒ„๋น„๊ฐ€ ๋  ์ค„์ด์•ผ ใ…‹ใ…‹ใ…‹. ์˜ค๋Š˜ ์ง‘์— ๊ฐ€์„œ Next.js๋กœ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ ์„ธํŒ…ํ•  ๋•Œ ๋ฌด์กฐ๊ฑด ์ € getQueryClient ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ๋ณต๋ถ™ํ•ด๋†”์•ผ์ง€!


๐Ÿ“ ๋ฐฐ์šด ๋‚ด์šฉ ์ ๊ฒ€ํ•˜๊ธฐ (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() ์‹คํ–‰ ํ•จ์ˆ˜๋ฅผ ์ƒ๋‹จ ์ „์—ญ์— ๋‘๋ฉด ์„œ๋ฒ„์—์„  ๊ณต์œ  ์Šคํ† ๋ฆฌ์ง€ ์ŠคํŒŒ๊ฒŒํ‹ฐ(Leak)๊ฐ€ ๋‚˜๊ณ , ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— const queryClient = new QueryClient() ๋ƒ…๋‹ค ์น˜๋ฉด ํ”„๋ก ํŠธ์—์„œ ์กฐ๊ธˆ์˜ ๋ Œ๋”๋ง ํ„ฐ์งˆ ๋•Œ๋งˆ๋‹ค ์บ์‹œ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณ„์† ์ดˆ๊ธฐํ™”(ํŒŒ๊ดด)๋˜์–ด ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ ์•„๊ฐ‘๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ €๋ ‡๊ฒŒ useState(() => new ...) ์ฝœ๋ฐฑํ˜•์‹์œผ๋กœ ๋„˜๊ฒจ์ฃผ๋ฉด, ๊ฐ€์žฅ ์™„๋ฒฝํ•˜๊ฒŒ ๋ชจ๋“  ์œ ์ €(์š”์ฒญ๋‹น 1๊ฐœ)๋งˆ๋‹ค ๋…๋ฆฝ๋˜๋ฉด์„œ๋„, ์ปดํฌ๋„ŒํŠธ ์ƒ๋ช…์ฃผ๊ธฐ ๋‚ด๋‚ด(๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ผœ์ ธ์žˆ๋Š” ํ•œ) ์ ˆ๋Œ€ ์žฌํ• ๋‹น(ํŒŒ๊ดด) ์•ˆ ๋˜๋Š” 1ํšŒ์„ฑ ์ดˆ๊ธฐํ™” ๊ฐ€ ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค!
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, B๋ฒˆ์ฒ˜๋Ÿผ ๋ฆฌ๋ Œ๋”๋ง๋งˆ๋‹ค ์บ์‹œํ†ต(QueryClient) ์ด ์ฐข์–ด๋ฐœ๊ฒจ์ง€๋ฉด ํ™”๋ฉด ๊นœ๋นก์ž„์ด ์–ด๋งˆ์–ด๋งˆํ•  ๊ฒ๋‹ˆ๋‹ค. ์บ์‹œ๋Š” ์‚ด๋ ค๋‘๋Š” ๊ฒŒ ๋ชฉ์ ์ด์—์š”!"
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ์ธ์Šคํ„ด์Šค ์ƒ๋ช… ์—ฐ์žฅ์ˆ ์˜ ๊ฝƒ, useState(() => init) ์ง€์—ฐ ์ดˆ๊ธฐํ™”(Lazy Init)!