[React] ๐ŸŒบReact Query ์‚ฌ์šฉ๋ฒ•

TATAยท2023๋…„ 3์›” 19์ผ
0

React

๋ชฉ๋ก ๋ณด๊ธฐ
16/32

โ–ท React Query

React Query๋Š” React์— ์ข…์†์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฉฐ,
React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค.

Redux์—์„œ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ post, put, patch ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด,
๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ๋‹ค์‹œ get๋ฉ”์†Œ๋“œ ์š”์ฒญ์„ ํ†ตํ•ด ์„œ๋ฒ„์™€์˜ ๋™๊ธฐํ™”๊ฐ€ ํ•„์š”ํ•ด์ง„๋‹ค.

์ด๋•Œ, React Query์˜ invalidateQueries๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
React Query๋Š” get์œผ๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ state์ฒ˜๋Ÿผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ ,
setState(post, put, patch)ํ•˜๋ฉด invalidateQueries๋ฅผ ํ˜ธ์ถœํ•ด์„œ
์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋กœ refetching ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰, React Query๋Š” ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ, API ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ๋ฐ›์•„์˜ค๋Š” ๊ฒƒ์€ ๋ฌผ๋ก , ์บ์‹œ๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์™€์˜ ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ.


๐ŸŒบ ์„ค์น˜

npm install react-query

๐ŸŒบ QueryClientProvider, QueryClient

// ๊ฐ€์ ธ์˜ค๊ธฐ
import { QueryClientProvider, QueryClient } from "react-query"

// ์ฟผ๋ฆฌ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ƒ์„ฑ
// ์ฟผ๋ฆฌ ํด๋ผ์ด์–ธํŠธ๋Š” ์ฟผ๋ฆฌ์™€ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ ์บ์‹œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ์ž„
const queryClient = new QueryClient();

function App () {
  <QueryClientProvider client={queryCient}>
    <Header />
    <Main />
    <Footer />
  </QueryClientProvider>
}

๐ŸŒบ useQuery

์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

useQuery์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž โ†’ QueryKey(์œ ๋‹ˆํฌํ•ด์•ผ ํ•จ)
useQuery์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž โ†’ QueryFunction(๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜)

/* Main.js */
// userQuery ๊ฐ€์ ธ์˜ค๊ธฐ
import { useQuery } from "react-query"

const Main = () => {
  // ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์˜ค๋Š” ํ•จ์ˆ˜
  const getInfo = () => {
    return axios.get("/user").then((res) => res.data)
  }
    
  // ๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น
  const { data, isLoading, isError } = useQuery("keynameUserInfo", getInfo);
  
  if (isError) return <span>ERROR</span>
  if (isLoading) return <span>Loading...</span>
  
  return (
  	<>
      <span>{data?.userName}</span>
    </>
  )
}

export default Main;

์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•œ data๋Š” axios๋กœ ๊ฐ€์ ธ์˜จ data์ด๋ฉฐ,
isLoading๊ณผ isError๋Š” queryFn์„ ์‹คํ–‰ํ•˜๋Š” ๋™์•ˆ์˜
๋กœ๋”ฉ, ์—๋Ÿฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ boolean ๊ฐ’์œผ๋กœ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

useQuery์—์„œ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ์ฒด๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

const result = useQuery("keynameUserInfo", getInfo);
console.log(result);

// ์ฝ˜์†”์ฐฝ์— ์ฐํž˜
data,
dataUpdatedAt,
error,
errorUpdatedAt,
failureCount,
isError,
isFetched,
isFetchedAfterMount,
isFetching, // ๋น„๋™๊ธฐ์ฟผ๋ฆฌ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜์Œ
isIdle,
isLoading, // ์ฟผ๋ฆฌํ•จ์ˆ˜๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜๊ณ , ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋„ ์—†๋‹ค
isLoadingError,
isPlaceholderData,
isPreviousData,
isRefetchError,
isRefetching,
isStale,
isSuccess,
refetch,
remove,
status,

๐ŸŒบ useMutation

useQuery์™€ ๋‹ค๋ฅด๊ฒŒ mutation์€
๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑ, ์—…๋ฐ์ดํŠธ, ์‚ญ์ œํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

invalidateQueries๋Š” queryKey ๊ฐ’ ํ˜น์€
ํ‚ค๋“ค์˜ ๋ฐฐ์—ด์„ ์ธ์ž๋กœ ๋ฐ›์œผ๋ฉฐ, ํ•ด๋‹น ์ฟผ๋ฆฌ๋“ค์„ ๋ฌดํšจํ™”์‹œํ‚จ๋‹ค.

(๋ฌดํšจํ™”ํ•œ ๋ชจ๋“  ์ฟผ๋ฆฌ๊ฐ€ ์žฌํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ,
ํ™”๋ฉด ๋ Œ๋”๋ง์— ์—ฐ๊ด€๋œ ์ฟผ๋ฆฌ๋งŒ ์žฌํ˜ธ์ถœ๋˜๊ณ 
๋‚˜๋จธ์ง€๋Š” stale ์ƒํƒœ๋กœ๋งŒ ์ „ํ™˜๋˜๋Š” ๊ฒƒ์ž„)

import { useQuery, useMutation, useQueryClient } from "react-query"

const Main = () => {
  ...
  const { data } = useQuery("keynameUserInfo", getInfo);
  
  // ์ฟผ๋ฆฌ ํด๋ผ์ด์–ธํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
  const queryClient = useQueryClient();
  
  // ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜
  const updateInfo = (userName) => {
    return axios.put(`/update-username?username=${username}`)
  }
  
  // useMutation ์ฒซ๋ฒˆ์งธ ์ธ์ž => QueryFunction
  // useMutation ๋‘๋ฒˆ์งธ ์ธ์ž => ์˜ต์…˜
  const mutation = useMutation(updateInfo, {
    onSuccess: () => {
      // ๋ฎคํ…Œ์ด์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚ฌ์„๋•Œ,
      // 'keynameUserInfo' ํ‚ค๊ฐ€ ๊ฐ€์ง„ ๋ชจ๋“  ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”์‹œํ‚ด
      queryClient.invalidateQueries("keynameUserInfo");
    },
  })
  
  mutation.mutate("๋ณ€๊ฒฝํ• _์ƒˆ๋กœ์šด_์œ ์ €_์ด๋ฆ„")
  
  return (
    <>
      <span>{data?.userName}</span>
    </>
  )
}

/*
* useMutation์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ๋Š” 3๊ฐ€์ง€ ์˜ต์…˜
* - optimisticUpdate: ์„œ๋ฒ„๋กœ ์ „์†ก๋˜๊ธฐ ์ „์—, ์ด๋ฏธ ๋ณ€ํ™”๋œ ์ƒํƒœ๋ฅผ ์ฆ‰์‹œ UI์— ๋ฐ˜์˜ํ•จ
* - invalidateQueries: ๋ฎคํ…Œ์ด์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์‹คํ–‰๋œ ํ›„, cache์—์„œ query๋ฅผ ์‚ญ์ œ
* - setQueryData: ๋ฎคํ…Œ์ด์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์‹คํ–‰๋œ ํ›„, cache์— ์žˆ๋Š” query ๊ฒฐ๊ณผ๋ฅผ ์—…๋ฐ์ดํŠธ
*/

/*
* useMutation hook์—์„œ ์ œ๊ณตํ•˜๋Š” ์†์„ฑ
* - onMutate: ๋ฎคํ…Œ์ด์…˜์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰
* - onError: ๋ฎคํ…Œ์ด์…˜์ด ์‹คํŒจํ•œ ๊ฒฝ์šฐ ํ˜ธ์ถœ
* - onSettled: ๋ฎคํ…Œ์ด์…˜์ด ์™„๋ฃŒ๋œ ํ›„์— ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
* - onSuccess: ๋ฎค๋ฐ์ด์…˜์ด ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
*/

๐ŸŒบ staleTime, cacheTime

์ด ๋‘ ์˜ต์…˜์€ ๊ฐ๊ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์–ธ์ œ ๊ฐ€์ ธ์˜ฌ์ง€์™€ ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ์œ ์ง€ํ• ์ง€๋ฅผ ์ œ์–ดํ•œ๋‹ค.

staleTime
๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ์—์„œ ๋งŒ๋ฃŒ๋˜์—ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ Œ๋”๋ง์„ ํ—ˆ์šฉํ•˜๋Š” ์‹œ๊ฐ„(๋ฐ€๋ฆฌ ์ดˆ)์„ ์„ค์ •ํ•œ๋‹ค. ์ด ์‹œ๊ฐ„ ๋‚ด์—๋Š” ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ ์˜ค์ง€ ์•Š๊ณ  ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ์‚ฌ์šฉํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ณ  ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์‚ฌ์šฉ ์˜ˆ์‹œ - useQuery์—์„œ ๊ฐœ๋ณ„์ ์œผ๋กœ ์„ค์ •ํ•จ
// (queryClient์—์„œ ์ „์—ญ์ ์œผ๋กœ ์„ค์ •๋„ ๊ฐ€๋Šฅ)
const { data } = useQuery("keynameUserInfo", getInfo, { staleTime: Infinity });

cacheTime
๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ์—์„œ ๋งŒ๋ฃŒ๋˜์–ด ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ์‹œ๊ฐ„(๋ฐ€๋ฆฌ ์ดˆ)์„ ์„ค์ •ํ•œ๋‹ค. ์ด ์‹œ๊ฐ„ ์ดํ›„์—๋Š” ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŒ๋ฃŒ๋˜๊ณ  ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

๋ณดํ†ต ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์˜ค๋ž˜ ์œ ์ง€ํ•˜๊ณ  ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด
staleTime์„ ๋” ๊ธธ๊ฒŒ, cacheTime์„ ๋” ์งง๊ฒŒ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๋‹ค.


๐ŸŒบ Suspense

์ „์—ญ์œผ๋กœ Suspense๋ฅผ ์„ค์ •ํ•ด๋„ ๋˜๊ณ 

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      suspense: true,
    },
  },
})

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Main />
    </QueryClientProvider>
  )
}

๊ฐ๊ฐ์˜ useQuery์— ์„ค์ •ํ•ด ์ค˜๋„ ๋œ๋‹ค.

import { useQuery } from '@tanstack/react-query'

useQuery(queryKey, queryFn, { suspense: true })

Router์—์„œ suspense์™€ react-query๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
Home๊ณผ Aboute ์ปดํฌ๋„ŒํŠธ์˜ useQuery์—์„œ ๋ฝ‘์•„๋‚ธ
isLoading์œผ๋กœ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋กœ๋”ฉ ์ฒ˜๋ฆฌ๊ฐ€ ์ž˜ ๋œ๋‹ค.

import { BrowserRouter, Routes, Route } from "react-router-dom"
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { Suspense } from "react"
...

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      suspense: true,
    },
  },
})

function Loading = () => {
  return <div>Loading...</div>
}

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Suspense fallback={<Loading />}>
        <BrowserRouter>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
          </Routes>
        </BrowserRouter>
      </Suspense>
    </QueryClientProvider>
  )
}

๐ŸŒบ useQueries

ํ•˜์ง€๋งŒ react-query์—์„œ Suspense๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ‘๋ ฌ์ ์œผ๋กœ ๋™์ž‘ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.
๋ณ‘๋ ฌ์ ์œผ๋กœ ๋™์ž‘์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„  useQuery๊ฐ€ ์•„๋‹ˆ๋ผ useQueries๋ผ๋Š” ํ›…์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

โ—๏ธ์ฐธ๊ณ ) ์—ฌ๋Ÿฌ ๊ฐœ์˜ query๋ฅผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” useQueries๋Š” Suspense์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ์‹œ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ์ด์Šˆ๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ, ์ด๋ฒˆ v4.5.0 ํŒจ์น˜์— ํ•ด๋‹น ์‚ฌํ•ญ์ด ์ˆ˜์ •๋˜์—ˆ๋‹ค.

import { useQueries } from "react-query"

const results = useQueries({
  queries: [
    { queryKey: ["getUser"], queryFn: getUser, staleTime: Infinity},
    { queryKey: ["getHistory"], queryFn: getHistory, staleTime: Infinity}
  ]
})



๐Ÿ”ซSuspense ์‚ฌ์šฉ๋ฒ• ๋ณด๋Ÿฌ๊ฐ€๊ธฐ
๐Ÿ‘‰ React Query ๊ณต์‹๋ฌธ์„œ ๋ณด๋Ÿฌ๊ฐ€๊ธฐ
๐Ÿ‘‰ Reacr-query(๊ณต์‹๋ฌธ์„œ) - Suspense ๏ผ‹ Parallel Queries ๏ผ‹ useQueries

profile
๐Ÿพ

0๊ฐœ์˜ ๋Œ“๊ธ€