๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป DEV/React-Query

[ReactQuery] (2) Nextjs ํ”„๋กœ์ ํŠธ์— ReactQuery ์ ์šฉ

by Rising Oneโ˜… 2025. 3. 14.
728x90
๋ฐ˜์‘ํ˜•
SMALL

Tanstack (ReactQuery) ๋Œ€ํ‘œ์ด๋ฏธ์ง€

 

 

์ ‘๊ทผ (์š”์•ฝ)

Next.js ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋Š”๋ฐ, Web App์˜ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์ˆ˜์ ์ด๋‹ค.

๊ณต์‹ ์›น์‚ฌ์ดํŠธ๋ฅผ ํ™•์ธ ํ•ด๋ณด๋ฉด, npm ์—์„œ์˜ ๋ˆ„์  ๋‹ค์šด๋กœ๋“œ ์ˆ˜๊ฐ€ 18์–ต์— ์œก๋ฐ•ํ•˜๊ณ  ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ˆ˜์น˜๊ฐ€ ์ƒ์Šนํ•˜๋Š” ๊ฒƒ์„ ๋ณด๊ธฐ๋งŒ ํ•ด๋„

์–ผ๋งˆ๋‚˜ ํ•ซํ•œ ๊ธฐ์ˆ ์ธ์ง€ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์ด๋ฅผ ํ”„๋กœ์ ํŠธ์— ๋„์ž…ํ•˜์—ฌ ์น˜์—ดํ•˜๊ฒŒ ์—ฐ๊ตฌ๋ฅผ ํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

(์˜ˆ์ „์— ๊ฐœ๋ฐœ ๋•Œ์— ๋“œ๋ฌธ๋“œ๋ฌธ ํ™œ์šฉํ–ˆ๋˜ ๊ฒƒ ๊ทธ ์ด์ƒ์œผ๋กœ ์นœ์ˆ™ํ•ด์ง€๊ณ ์ž ํ•œ๋‹ค)

 

  1. ReactQuery ์„ค์น˜

- npm install @tanstack/react-query

- npm install @tanstack/react-query-devtools

 

  2. ESLint Plugin ์„ค์น˜

- npm i -D @tanstack/eslint-plugin-query

- ๊ธฐํƒ€ ์„ค์ • (.eslintrc.json)

 

  3. ReactQuery ์„ค์ •

- (1) queryClient.ts (index.ts) ์ž‘์„ฑ ํ›„ ๋‚ด๋ณด๋‚ด๊ธฐ (feat. layout.tsx)

- (2) (node_modules ์†) QueryClientProvider.tsx ๋‹ค๋ฃจ๊ธฐ (→QueryClientProviderWrapper.tsx) (๊ฐ์ข… import)

- (3) layout.tsx์— ReactQuery ์‚ฌ์šฉํ•˜๊ณ  ๊ตฌ๋™ ํ›„ ์ •์ƒํ™•์ธ

 


๋ฐฉ๋ฒ•

   1. React Query ์„ค์น˜

npm install @tanstack/react-query

npm install @tanstack/react-query-devtools

 

* devtools๋ฅผ dependency์„ฑ์ด ์•„๋‹Œ, ์ผ๋ฐ˜์œผ๋กœ ์„ค์น˜ํ•˜๋Š” ์ด์œ ?

- ์•ฑ ์ปดํฌ๋„ŒํŠธ๋“ค์—์„œ devtools๋ฅผ ๊ฐ€์ ธ์™€ ์ถ”๊ฐ€ํ•œ๋‹ค์Œ, devtools๋ฅผ ์‚ฌ์šฉํ•ด node ํ™˜๊ฒฝ์„ ํ…Œ์ŠคํŠธํ•˜๊ฒŒ ๋˜๋Š”๋ฐ,

- node ํ™˜๊ฒฝ์ด development์ธ ๊ฒฝ์šฐ ์ด๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

# ์ฐธ๊ณ 

https://tanstack.com/query/latest/docs/framework/react/devtools#devtools-in-production

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
By default, React Query Devtools are only included in bundles when process.env.NODE_ENV === 'development', so you don't need to worry about excluding them during a production build.
>> Lazy load ๋ฅผ ํ•  ๊ฒฝ์šฐ, Bundles์—์„œ ์ œ์™ธ๋˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

>> ๋˜ํ•œ ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์—์„œ window.toggleDevtools๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
- ์ด๋ฅผ ํ†ตํ•ด, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋„ dev tools๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

   2. ESLint Plugin ์„ค์น˜

npm i -D @tanstack/eslint-plugin-query

 

- ์„ค์น˜ ์ดํ›„, eslint.cjs (eslint config)์˜ extends array์— ํ•œ ์ค„ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ด ์žˆ์Œ

[eslintrc] 

To enable all of the recommended rules for our plugin, add plugin:@tanstack/query/recommended in extends:

[๊ณต์‹๋ฌธ์„œ์—์„œ๋Š”]
{
  "extends": ["plugin:@tanstack/query/recommended"]
}

→ [๊ฐ•์˜์—์„ ]     "plugin:@tanstack/eslint-plugin-query/recommended",

 

(1). exhaustive dependencies ๊ทœ์น™
{
  "plugins": ["@tanstack/query"],
  "rules": {
    "@tanstack/query/exhaustive-deps": "error"
  }
}

- ์œ„์™€ ๊ฐ™์ด ์„ค์ •ํ•ด์ฃผ๋ฉด, QueryKey(์ฟผ๋ฆฌํ‚ค)์— QueryFn(์ฟผ๋ฆฌํ•จ์ˆ˜)์˜ ๋ชจ๋“  dependencies๊ฐ€ ํฌํ•จ๋˜์–ด

- dependencies๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ์šฐ, Query๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.


   3. ReactQuery ์„ค์ •

(1). queryClient.ts (→ index.ts) ์ž‘์„ฑ ํ›„ ๋‚ด๋ณด๋‚ด๊ธฐ (feat. layout.tsx)

 

1. root/react-query ํด๋”๐Ÿ—‚๏ธ ์ƒ์„ฑ

2. root/react-query/index.ts ํŒŒ์ผ๐Ÿ“„ ์ƒ์„ฑ

 

[./react-query/index.ts]

import {QueryClient} from "@tanstack/react-query";

export const queryClient = new QueryClient();

- queryClient๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๋ณด๋‚ด, ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘

 

(2). (node_modules ์†) QueryClientProvider.tsx ๋‹ค๋ฃจ๊ธฐ (→ QueryClientProviderWrapper.tsx) (๊ฐ์ข… import)

 

(ใ„ฑ-1). QueryClientProviderWrapper.tsx ์ƒ์„ฑ

'use client';

import {queryClient} from "@/react-query/index";
import {QueryClientProvider} from "@tanstack/react-query";
import {ReactQueryDevtools} from "@tanstack/react-query-devtools";

export default function QueryClientProviderWrapper({children}: { children: React.ReactNode }) {

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

- QueryClientProvider queryClientReactQueryDevTools 

 

<ReactQueryDevTools /> ๋Š” QueryClientProvider ๋‚ด๋ถ€์—๋งŒ ์กด์žฌํ•œ๋‹ค๋ฉด ์–ด๋””๋“  OK

 


(ใ„ด-1). (node_modules ์ œ๊ณต๋˜๋Š”) (if) QueryClientProvider.tsx ์‚ฌ์šฉ

'use client'
import * as React from 'react'

import type { QueryClient } from '@tanstack/query-core'

export const QueryClientContext = React.createContext<QueryClient | undefined>(
  undefined,
)

export const useQueryClient = (queryClient?: QueryClient) => {
  const client = React.useContext(QueryClientContext)

  if (queryClient) {
    return queryClient
  }

  if (!client) {
    throw new Error('No QueryClient set, use QueryClientProvider to set one')
  }

  return client
}

export type QueryClientProviderProps = {
  client: QueryClient
  children?: React.ReactNode
}

export const QueryClientProvider = ({
  client,
  children,
}: QueryClientProviderProps): React.JSX.Element => {
  React.useEffect(() => {
    client.mount()
    return () => {
      client.unmount()
    }
  }, [client])

  return (
    <QueryClientContext.Provider value={client}>
      {children}
    </QueryClientContext.Provider>
  )
}

 

2. ์ œ๊ณต๋œ ๊ฒƒ์„ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์‚ฌ์šฉํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

import {QueryClientProvider} from "@tanstack/react-query";
import {ReactQueryDevtools} from "@tanstack/react-query-devtools";
import {queryClient} from "@/react-query/queryClient";

export function App() {
  return (
      <QueryClientProvider client={queryClient}>
            <Routes>
              <Route path="/" element={<Home/>}/>
              <Route path="/Staff" element={<AllStaff/>}/>
              <Route path="/Calendar" element={<Calendar/>}/>
              <Route path="/Treatments" element={<Treatments/>}/>
              <Route path="/signin" element={<Signin/>}/>
              <Route path="/user/:id" element={<UserProfile/>}/>
            </Routes>
        <ReactQueryDevtools/>
      </QueryClientProvider>
  );
}

 

(3). layout.tsx์— ReactQuery ์‚ฌ์šฉํ•  ์ค€๋น„ํ•˜๊ธฐ (๊ฐ์ข… import)

 

1. root/app/layout.tsx ๐Ÿ“„ ์ ‘๊ทผ

[./app/layout.tsx]

import type {Metadata} from "next";
import {Geist, Geist_Mono} from "next/font/google";
import "./globals.css";
import QueryClientProviderWrapper from "@/react-query/QueryClientProviderWrapper";

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
                                     children,
                                   }: Readonly<{
  children: React.ReactNode;
}>) {

  return (
    <html lang="en">
    <body>
    <QueryClientProviderWrapper>
      {children}
    </QueryClientProviderWrapper>
    </body>
    </html>
  );
}

- ๋งŒ๋“ค์–ด๋†“์€ <QueryClientProviderWapper>๋กœ ๊ฐ์‹ผ๋‹ค


Devtools๊ฐ€ ์ ์šฉ๋œ ๋ชจ์Šต

 

728x90
๋ฐ˜์‘ํ˜•
LIST

๋Œ“๊ธ€