์ฐธ๊ณ ์ฌ์ดํธ
โข Transtack Query ๊ณต์ ๋ฌธ์
TanStack Start Overview | TanStack Start React Docs
TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more using tools like and . It is ready to deploy...
tanstack.com
โข HEROPY.DEV ๋์ ๋ธ๋ก๊ทธ
TanStack Query(React Query) ํต์ฌ ์ ๋ฆฌ
TanStack Query๋ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ, ๋ฐ์ดํฐ ์บ์ฑ, ์บ์ ์ ์ด ๋ฑ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, React Query๋ผ๋ ์ด๋ฆ์ผ๋ก ์์ํ์ง๋ง, v4๋ถํฐ Vue๋ Svelte ๋ฑ์ ๋ค๋ฅธ
www.heropy.dev

๐ดTanStack Query
Tanstack Query(์ด์ React Query)๋ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ, ๋ฐ์ดํฐ ์บ์ฑ, ์ ์ด ๋ฑ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
๊ธฐ์กด์๋ React Query๋ผ๋ ์ด๋ฆ์ ์ฌ์ฉํ์ง๋ง, ์ดํ Vue, Solid, Svelete ๋ฑ ๋ค์ํ ํ๋ ์์ํฌ์์๋ ์ฌ์ฉํ๋ฉด์ React๋ฅผ ์ ์ธํ๊ณ ๊ฐ๋ฐ์๋ช ์ ๋ฐ์ Tanstack Query๋ก ์ด๋ฆ์ ๋ณ๊ฒฝ.

๊ณต์ ๋ฌธ์์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑ๋์ด ์๋ค.
์น ๊ฐ๋ฐ์๋ฅผ ์ํ ๋์ ํ๋ฆฌํฐ์ ์คํ์์ค.

๊ฐ๋ ฅํ ๋น๋๊ธฐ ์ํ๊ด๋ฆฌ ๋๊ตฌ
๐ช๐ป ๋ํ ๊ธฐ๋ฅ
โ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ์บ์ฑ
โ ๋์ผ ์์ฒญ์ ์ค๋ณต ์ ๊ฑฐ
โ ์ ์ ํ ๋ฐ์ดํฐ ์ ์ง
โ ๋ฌดํ ์คํฌ๋กค, ํ์ด์ง๋ค์ด์ ๋ฑ์ ์ฑ๋ฅ ์ต์ ํ
โ ๋คํธ์ํฌ ์ฌ์ฐ๊ฒฐ ์์ฒญ, ์คํจ ๋ฑ์ ์๋ ๊ฐฑ์
๐ฆ ๋ฐ์ดํฐ ์บ์ฑ
TanStack Query๋ฅผ ํ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋๋ ํญ์ ์ฟผ๋ฆฌ ํค (Query-Key)๋ฅผ ์ง์ ํ๊ฒ ๋๋ค.
์ด ์ฟผ๋ฆฌ-ํค๋ ์บ์๋ ๋ฐ์ดํฐ์ ๋น๊ตํด ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ์ง, ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ง ๊ฒฐ์ ํ๋ ๊ธฐ์ค์ด ๋๋ค.
Query-key๋ฅผ ํตํด ์์ฒญ์ ๋ณด๋ผ ๋, ์บ์ฑ์ ํด๋น ํค๋ก ์ ๊ทผํ ๋ฐ์ดํฐ๊ฐ ์๋์ง ํ์ธ ํ, ์๋ค๋ฉด ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์บ์ฑํ๋ค.
๊ทธ ์ดํ, ์ฟผ๋ฆฌ ํค๋ก ์ผ์นํ๋ ๋ฐ์ดํฐ๊ฐ ์์ ๊ฒฝ์ฐ, ์๋ฒ์ ์์ฒญํ์ง ์๊ณ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ์ฌ์ฉํ๊ฒ ๋๋ค.
์ด๋ ๋ค๋ฉด ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์์ฒญ์ด ์ฌ๋ฌ ๋ฒ ๋ฐ์ํด๋, ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋์ด ์ค๋ณต ์์ฒญ์ ์ค์ผ ์ ์๋ค.
โจ ๋ฐ์ดํฐ์ ์ ์ ๋
TanStack Query๋ ์บ์ํ ๋ฐ์ดํฐ๋ฅผ ์ ์ (Fresh)ํ๊ฑฐ๋ ์ํ(Stale) ์ํ๋ก ๊ตฌ๋ถํ์ฌ ๊ด๋ฆฌํ๋ค.
์บ์๋ ๋ฐ์ดํฐ๊ฐ ์ ์ ํ๋ค๋ฉด ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ , ๋ง์ฝ ๋ฐ์ดํฐ๊ฐ ์ํ๋ค๋ฉด ์๋ฒ์ ๋ค์ ์์ฒญํด ์ ์ ํ(์๋ก์ด) ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
์ผ์ข ์ ๋ฐ์ดํฐ ์ ํต๊ธฐํ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ์ฝ๋ค.
๋ฐ์ดํฐ๊ฐ ์ํ๋๋ฐ๊น์ง ๊ฑธ๋ฆฌ๋ ์๊ฐ์ staleTime ์ต์ ์ ํตํด ์ง์ ํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ ์ ํ์ง ์ฌ๋ถ๋ isStale๋ก ํ์ธ ํ ์ ์๋ค.
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function Home() {
return (
<QueryClientProvider client={queryClient}>
<StaleData/>
</QueryClientProvider>
)
}
function StaleData() {
const { data, isStale } = useQuery({
queryKey: ['todo'],
queryFn: async () => (await fetch('https://jsonplaceholder.typicode.com/todos/1')).json(),
staleTime: 1000 * 3,
})
return <div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
}
ํด๋น ์ฝ๋๋ Stale์ 3์ด๋ก ์ง์ ํ๋ค.
๋ ๋๋ง ํ, 3์ด๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉด 3์ด ํ์ ์ฌ๊ณผ๊ฐ ์ํ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.


๐ ํต์ฌ ๊ธฐ๋ฅ
TanStack Query๋ฅผ ์์ํ๊ธฐ ์์, ํ๋ก์ ํธ ๋ฒ์๋ฅผ <QueryClientProvider>๋ก ๋ฉํํด์ผํ๋ค.
import {
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import Children from '~/components/Children'
const queryClient = new QueryClient()
export default function Home() {
return (
<QueryClientProvider client={queryClient}>
<Children/>
</QueryClientProvider>
)
}
๐ช๐ป useQuery
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ฟผ๋ฆฌ ํ ์ผ๋ก, ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์ฌ์ฉํ๋ค.
const result = useQuery<T>(option)
option์๋ ๊ฐ์ฒดํ์ผ๋ก ๋ง์ ์ต์ ๋ค์ด ์กด์ฌํ๋๋ฐ ์๋ ๋งํฌ์์ ํ์ธ ํ ์ ์๋ค.
@TanStack-Query/useQuery#Options
useQuery | TanStack Query React Docs
tsx const { data, dataUpdatedAt, error, errorUpdatedAt, failureCount, failureReason, fetchStatus, isError, isFetched, isFetchedAfterMount, isFetching, isInitialLoading, isLoading, isLoadingError, isPa...
tanstack.com
๐ช๐ป QueryKey
์ฟผ๋ฆฌ ํค(query key)๋ ์ฟผ๋ฆฌ๋ฅผ ์๋ณํ๋ ๊ณ ์ ํ ๊ฐ์ผ๋ก, ๋ฐฐ์ด ํํ๋ก ์ง์ ํ๋ค.
๋ค์ค ์์ดํ ์ฟผ๋ฆฌ ํค๋ฅผ ์ฌ์ฉํ ๋๋, ์์ดํ ์ ์์๊ฐ ์ค์ํ๋ค.
// ๋จ์ผ ์์ดํ
์ฟผ๋ฆฌ ํค
useQuery({ queryKey: ['hello'] })
// ๋ค์ค ์์ดํ
์ฟผ๋ฆฌ ํค
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2 }] })
// ์๋ก ๊ฐ์ ์ฟผ๋ฆฌ
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2 }] })
useQuery({ queryKey: ['hello', 'world', 123, { b: 2, c: undefined, a: 1 }] })
// ์๋ก ๋ค๋ฅธ ์ฟผ๋ฆฌ
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2 }] })
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2, c: 3 }] })
useQuery({ queryKey: ['hello', 'world'] })
useQuery({ queryKey: [123, 'world', { a: 1, b: 2, c: 3 }, 'hello'] })
ํ์์ ์์ฑ์ ๋ถ์
โ ๋จ์ผ ์์ดํ : ์ฟผ๋ฆฌ ํค ๋ฐฐ์ด์ ํ๋์ ํค๊ฐ ์กด์ฌํ ๋,
โ ๋ค์ค ์์ดํ : ์ฟผ๋ฆฌ ํค ๋ฐฐ์ด์ ์ฌ๋ฌ ํค๊ฐ ์กด์ฌํ ๋,
โ ์๋ก ๊ฐ์ ์ฟผ๋ฆฌ: ๋ฐฐ์ด ์์๊ฐ ๊ฐ๊ณ , ๊ฐ์ฒด๋ ์์์ ์๊ด์์ด ํค์์ด ๋ง์ ๋, (undefined๋ ์ ์ธ)
โ ์๋ก ๋ค๋ฅธ ์ฟผ๋ฆฌ: ๋ฐฐ์ด ์์๊ฐ ๋ค๋ฅด๊ณ , ๊ฐ์ฒด๋ ์์ ์๊ด ์์ง๋ง ํค์์ด ๋ค๋ฅผ ๊ฒฝ์ฐ
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<QueryClientProvider client={queryClient}>
<TanstackComponent id={1} />
<TanstackComponent id={2} />
</QueryClientProvider>
</div>
)
}
type JsonPlaceholder = {
userId: number
id: number
title: string
completed: boolean
}
type TanstackComponentProps = {
id?: number
}
function TanstackComponent({id}: TanstackComponentProps) {
const { data, isStale } = useQuery<JsonPlaceholder>({
queryKey: ['delay', id],
queryFn: async () => {
return (await fetch(`https://jsonplaceholder.typicode.com/todos${id ? `/${id}` : ''}`)).json();
},
staleTime: 1000 * 3,
})
return (
<div className='flex flex-col items-center justify-center'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
);
}
๋ค์๊ณผ ๊ฐ์ด jsonplaceholder ์์ ToDo ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์ฌ ๋, id๋ฅผ ๋์ ์ผ๋ก ์ ์ฉํ์ฌ ๊ฐ์ ธ์ค๊ณ ์ถ์ ๋, ์ฟผ๋ฆฌ ํค์ id๊ฐ์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.

๐ช๐ปqueryFn
์ฟผ๋ฆฌ ํจ์(queryFn)๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋น๋๊ธฐ ํจ์๋ก, ๊ผญ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๊ฑฐ๋ ์ค๋ฅ๋ฅผ ๋์ ธ์ผ ํ๋ค.
๋์ ธ์ง ์ค๋ฅ๋ ๋ฐํ๋๋ error ๊ฐ์ฒด๋ก ํ์ธํ ์ ์๋ค.
error๋ ๊ธฐ๋ณธ์ ์ผ๋ก null ์ด๋ค.
API ์์ฒญ ๋์ค, ์๋ฌ๋ฅผ ๋์ ธ์ useQuery()๋ก ์๋ฌ๋ฅผ ๋ฐ์ ์ฌ์ฉํ๋ค.
์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<QueryClientProvider client={queryClient}>
<TanstackComponent id={1}/>
<TanstackComponent id={2}/>
</QueryClientProvider>
</div>
)
}
type JsonPlaceholder = {
userId: number
id: number
title: string
completed: boolean
}
type TanstackComponentProps = {
id?: number
}
function TanstackComponent({id}: TanstackComponentProps) {
const { data, isStale, isPending, error } = useQuery<JsonPlaceholder>({
queryKey: ['delay', id],
queryFn: async () => {
const result = await fetch(`https://jsonplaceholder.typicode.com/todos${id ? `/${id}` : ''}/์ค๋ฅ๋ฐ์`);
const data = await result.json();
if(data.id !== 3) {
throw new Error('์๋ฌ ๋ฐ์');
}
return data;
},
staleTime: 1000 * 3,
retry: false,
})
if(isPending) {
return <div className='text-green-400 p-3'>๐ข ๋ก๋ฉ์ค...</div>
}
if(error) {
return <div className='text-red-500 p-3 font-bold'>โ ๏ธ ์๋ฌ ๋ฐ์</div>
}
return (
<div className='flex flex-col items-center justify-center'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
);
}


๐ช๐ปselect
์ ํ ํจ์(select)๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ๋ณํ(์ ํ) ํ ์ ์๋ค.
์ฟผ๋ฆฌ ํจ์๊ฐ ๋ฐํํ๋ ๋ฐ์ดํฐ๋ฅผ ์ธ์๋ก ๋ฐ์ ์ ํ ํจ์์์ ์ฒ๋ฆฌํ๊ณ ๋ฐํํ์ฌ ์ต์ข ๋ฐ์ดํฐ๊ฐ ๋๋ค.
์ต์ข ๋ฐ์ดํฐ ํ์ ์ useQuery์ 3๋ฒ์งธ ์ ๋ค๋ฆญ ํ์ ์ผ๋ก ์ ์ธํ ์ ์๋ค.
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<QueryClientProvider client={queryClient}>
<TanstackComponent id={1}/>
<TanstackComponent id={2}/>
</QueryClientProvider>
</div>
)
}
type JsonPlaceholder = {
userId: number
id: number
title: string
completed: boolean
}
type TanstackComponentProps = {
id?: number
}
type returnType = {
[key: string] : number
}
function TanstackComponent({id}: TanstackComponentProps) {
const { data, isStale, isPending, error } = useQuery<JsonPlaceholder, Error, returnType>({
queryKey: ['delay', id],
queryFn: async () => {
const result = await fetch(`https://jsonplaceholder.typicode.com/todos${id ? `/${id}` : ''}`);
const data = await result.json();
if(!data.id) {
throw new Error('์๋ฌ ๋ฐ์');
}
return data;
},
staleTime: 1000 * 3,
select: data => ({ id: data.id }),
})
if(isPending) {
return <div className='text-green-400 p-3'>๐ข ๋ก๋ฉ์ค...</div>
}
if(error) {
return <div className='text-red-500 p-3 font-bold'>โ ๏ธ ์๋ฌ ๋ฐ์</div>
}
return (
<div className='flex flex-col items-center justify-center'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
);
}
useQuery()์ ๋ฐํ ๋๋ ํ์ ๋ค์ ์ ์ด์ฃผ๊ณ ์ธ ๋ฒ์งธ๋ ์ต์ข ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ค ๋ค
select๋ฅผ ํตํด ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฌ์ด ๋ฐํํ๋ค.

๋๋ id๊ฐ๋ง ๊ฐ์ ธ์๋ดค๋ค.
์ฐธ๊ณ ์ฌ์ดํธ
โข Transtack Query ๊ณต์ ๋ฌธ์
TanStack Start Overview | TanStack Start React Docs
TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more using tools like and . It is ready to deploy...
tanstack.com
โข HEROPY.DEV ๋์ ๋ธ๋ก๊ทธ
TanStack Query(React Query) ํต์ฌ ์ ๋ฆฌ
TanStack Query๋ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ, ๋ฐ์ดํฐ ์บ์ฑ, ์บ์ ์ ์ด ๋ฑ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, React Query๋ผ๋ ์ด๋ฆ์ผ๋ก ์์ํ์ง๋ง, v4๋ถํฐ Vue๋ Svelte ๋ฑ์ ๋ค๋ฅธ
www.heropy.dev

๐ดTanStack Query
Tanstack Query(์ด์ React Query)๋ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ, ๋ฐ์ดํฐ ์บ์ฑ, ์ ์ด ๋ฑ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
๊ธฐ์กด์๋ React Query๋ผ๋ ์ด๋ฆ์ ์ฌ์ฉํ์ง๋ง, ์ดํ Vue, Solid, Svelete ๋ฑ ๋ค์ํ ํ๋ ์์ํฌ์์๋ ์ฌ์ฉํ๋ฉด์ React๋ฅผ ์ ์ธํ๊ณ ๊ฐ๋ฐ์๋ช ์ ๋ฐ์ Tanstack Query๋ก ์ด๋ฆ์ ๋ณ๊ฒฝ.

๊ณต์ ๋ฌธ์์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑ๋์ด ์๋ค.
์น ๊ฐ๋ฐ์๋ฅผ ์ํ ๋์ ํ๋ฆฌํฐ์ ์คํ์์ค.

๊ฐ๋ ฅํ ๋น๋๊ธฐ ์ํ๊ด๋ฆฌ ๋๊ตฌ
๐ช๐ป ๋ํ ๊ธฐ๋ฅ
โ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ์บ์ฑ
โ ๋์ผ ์์ฒญ์ ์ค๋ณต ์ ๊ฑฐ
โ ์ ์ ํ ๋ฐ์ดํฐ ์ ์ง
โ ๋ฌดํ ์คํฌ๋กค, ํ์ด์ง๋ค์ด์ ๋ฑ์ ์ฑ๋ฅ ์ต์ ํ
โ ๋คํธ์ํฌ ์ฌ์ฐ๊ฒฐ ์์ฒญ, ์คํจ ๋ฑ์ ์๋ ๊ฐฑ์
๐ฆ ๋ฐ์ดํฐ ์บ์ฑ
TanStack Query๋ฅผ ํ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋๋ ํญ์ ์ฟผ๋ฆฌ ํค (Query-Key)๋ฅผ ์ง์ ํ๊ฒ ๋๋ค.
์ด ์ฟผ๋ฆฌ-ํค๋ ์บ์๋ ๋ฐ์ดํฐ์ ๋น๊ตํด ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ์ง, ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ง ๊ฒฐ์ ํ๋ ๊ธฐ์ค์ด ๋๋ค.
Query-key๋ฅผ ํตํด ์์ฒญ์ ๋ณด๋ผ ๋, ์บ์ฑ์ ํด๋น ํค๋ก ์ ๊ทผํ ๋ฐ์ดํฐ๊ฐ ์๋์ง ํ์ธ ํ, ์๋ค๋ฉด ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์บ์ฑํ๋ค.
๊ทธ ์ดํ, ์ฟผ๋ฆฌ ํค๋ก ์ผ์นํ๋ ๋ฐ์ดํฐ๊ฐ ์์ ๊ฒฝ์ฐ, ์๋ฒ์ ์์ฒญํ์ง ์๊ณ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ์ฌ์ฉํ๊ฒ ๋๋ค.
์ด๋ ๋ค๋ฉด ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์์ฒญ์ด ์ฌ๋ฌ ๋ฒ ๋ฐ์ํด๋, ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ฒ ๋์ด ์ค๋ณต ์์ฒญ์ ์ค์ผ ์ ์๋ค.
โจ ๋ฐ์ดํฐ์ ์ ์ ๋
TanStack Query๋ ์บ์ํ ๋ฐ์ดํฐ๋ฅผ ์ ์ (Fresh)ํ๊ฑฐ๋ ์ํ(Stale) ์ํ๋ก ๊ตฌ๋ถํ์ฌ ๊ด๋ฆฌํ๋ค.
์บ์๋ ๋ฐ์ดํฐ๊ฐ ์ ์ ํ๋ค๋ฉด ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ , ๋ง์ฝ ๋ฐ์ดํฐ๊ฐ ์ํ๋ค๋ฉด ์๋ฒ์ ๋ค์ ์์ฒญํด ์ ์ ํ(์๋ก์ด) ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
์ผ์ข ์ ๋ฐ์ดํฐ ์ ํต๊ธฐํ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ์ฝ๋ค.
๋ฐ์ดํฐ๊ฐ ์ํ๋๋ฐ๊น์ง ๊ฑธ๋ฆฌ๋ ์๊ฐ์ staleTime ์ต์ ์ ํตํด ์ง์ ํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ ์ ํ์ง ์ฌ๋ถ๋ isStale๋ก ํ์ธ ํ ์ ์๋ค.
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function Home() {
return (
<QueryClientProvider client={queryClient}>
<StaleData/>
</QueryClientProvider>
)
}
function StaleData() {
const { data, isStale } = useQuery({
queryKey: ['todo'],
queryFn: async () => (await fetch('https://jsonplaceholder.typicode.com/todos/1')).json(),
staleTime: 1000 * 3,
})
return <div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
}
ํด๋น ์ฝ๋๋ Stale์ 3์ด๋ก ์ง์ ํ๋ค.
๋ ๋๋ง ํ, 3์ด๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉด 3์ด ํ์ ์ฌ๊ณผ๊ฐ ์ํ๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.


๐ ํต์ฌ ๊ธฐ๋ฅ
TanStack Query๋ฅผ ์์ํ๊ธฐ ์์, ํ๋ก์ ํธ ๋ฒ์๋ฅผ <QueryClientProvider>๋ก ๋ฉํํด์ผํ๋ค.
import {
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import Children from '~/components/Children'
const queryClient = new QueryClient()
export default function Home() {
return (
<QueryClientProvider client={queryClient}>
<Children/>
</QueryClientProvider>
)
}
๐ช๐ป useQuery
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ฟผ๋ฆฌ ํ ์ผ๋ก, ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์ฌ์ฉํ๋ค.
const result = useQuery<T>(option)
option์๋ ๊ฐ์ฒดํ์ผ๋ก ๋ง์ ์ต์ ๋ค์ด ์กด์ฌํ๋๋ฐ ์๋ ๋งํฌ์์ ํ์ธ ํ ์ ์๋ค.
@TanStack-Query/useQuery#Options
useQuery | TanStack Query React Docs
tsx const { data, dataUpdatedAt, error, errorUpdatedAt, failureCount, failureReason, fetchStatus, isError, isFetched, isFetchedAfterMount, isFetching, isInitialLoading, isLoading, isLoadingError, isPa...
tanstack.com
๐ช๐ป QueryKey
์ฟผ๋ฆฌ ํค(query key)๋ ์ฟผ๋ฆฌ๋ฅผ ์๋ณํ๋ ๊ณ ์ ํ ๊ฐ์ผ๋ก, ๋ฐฐ์ด ํํ๋ก ์ง์ ํ๋ค.
๋ค์ค ์์ดํ ์ฟผ๋ฆฌ ํค๋ฅผ ์ฌ์ฉํ ๋๋, ์์ดํ ์ ์์๊ฐ ์ค์ํ๋ค.
// ๋จ์ผ ์์ดํ
์ฟผ๋ฆฌ ํค
useQuery({ queryKey: ['hello'] })
// ๋ค์ค ์์ดํ
์ฟผ๋ฆฌ ํค
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2 }] })
// ์๋ก ๊ฐ์ ์ฟผ๋ฆฌ
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2 }] })
useQuery({ queryKey: ['hello', 'world', 123, { b: 2, c: undefined, a: 1 }] })
// ์๋ก ๋ค๋ฅธ ์ฟผ๋ฆฌ
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2 }] })
useQuery({ queryKey: ['hello', 'world', 123, { a: 1, b: 2, c: 3 }] })
useQuery({ queryKey: ['hello', 'world'] })
useQuery({ queryKey: [123, 'world', { a: 1, b: 2, c: 3 }, 'hello'] })
ํ์์ ์์ฑ์ ๋ถ์
โ ๋จ์ผ ์์ดํ : ์ฟผ๋ฆฌ ํค ๋ฐฐ์ด์ ํ๋์ ํค๊ฐ ์กด์ฌํ ๋,
โ ๋ค์ค ์์ดํ : ์ฟผ๋ฆฌ ํค ๋ฐฐ์ด์ ์ฌ๋ฌ ํค๊ฐ ์กด์ฌํ ๋,
โ ์๋ก ๊ฐ์ ์ฟผ๋ฆฌ: ๋ฐฐ์ด ์์๊ฐ ๊ฐ๊ณ , ๊ฐ์ฒด๋ ์์์ ์๊ด์์ด ํค์์ด ๋ง์ ๋, (undefined๋ ์ ์ธ)
โ ์๋ก ๋ค๋ฅธ ์ฟผ๋ฆฌ: ๋ฐฐ์ด ์์๊ฐ ๋ค๋ฅด๊ณ , ๊ฐ์ฒด๋ ์์ ์๊ด ์์ง๋ง ํค์์ด ๋ค๋ฅผ ๊ฒฝ์ฐ
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<QueryClientProvider client={queryClient}>
<TanstackComponent id={1} />
<TanstackComponent id={2} />
</QueryClientProvider>
</div>
)
}
type JsonPlaceholder = {
userId: number
id: number
title: string
completed: boolean
}
type TanstackComponentProps = {
id?: number
}
function TanstackComponent({id}: TanstackComponentProps) {
const { data, isStale } = useQuery<JsonPlaceholder>({
queryKey: ['delay', id],
queryFn: async () => {
return (await fetch(`https://jsonplaceholder.typicode.com/todos${id ? `/${id}` : ''}`)).json();
},
staleTime: 1000 * 3,
})
return (
<div className='flex flex-col items-center justify-center'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
);
}
๋ค์๊ณผ ๊ฐ์ด jsonplaceholder ์์ ToDo ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์ฌ ๋, id๋ฅผ ๋์ ์ผ๋ก ์ ์ฉํ์ฌ ๊ฐ์ ธ์ค๊ณ ์ถ์ ๋, ์ฟผ๋ฆฌ ํค์ id๊ฐ์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.

๐ช๐ปqueryFn
์ฟผ๋ฆฌ ํจ์(queryFn)๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋น๋๊ธฐ ํจ์๋ก, ๊ผญ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๊ฑฐ๋ ์ค๋ฅ๋ฅผ ๋์ ธ์ผ ํ๋ค.
๋์ ธ์ง ์ค๋ฅ๋ ๋ฐํ๋๋ error ๊ฐ์ฒด๋ก ํ์ธํ ์ ์๋ค.
error๋ ๊ธฐ๋ณธ์ ์ผ๋ก null ์ด๋ค.
API ์์ฒญ ๋์ค, ์๋ฌ๋ฅผ ๋์ ธ์ useQuery()๋ก ์๋ฌ๋ฅผ ๋ฐ์ ์ฌ์ฉํ๋ค.
์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<QueryClientProvider client={queryClient}>
<TanstackComponent id={1}/>
<TanstackComponent id={2}/>
</QueryClientProvider>
</div>
)
}
type JsonPlaceholder = {
userId: number
id: number
title: string
completed: boolean
}
type TanstackComponentProps = {
id?: number
}
function TanstackComponent({id}: TanstackComponentProps) {
const { data, isStale, isPending, error } = useQuery<JsonPlaceholder>({
queryKey: ['delay', id],
queryFn: async () => {
const result = await fetch(`https://jsonplaceholder.typicode.com/todos${id ? `/${id}` : ''}/์ค๋ฅ๋ฐ์`);
const data = await result.json();
if(data.id !== 3) {
throw new Error('์๋ฌ ๋ฐ์');
}
return data;
},
staleTime: 1000 * 3,
retry: false,
})
if(isPending) {
return <div className='text-green-400 p-3'>๐ข ๋ก๋ฉ์ค...</div>
}
if(error) {
return <div className='text-red-500 p-3 font-bold'>โ ๏ธ ์๋ฌ ๋ฐ์</div>
}
return (
<div className='flex flex-col items-center justify-center'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
);
}


๐ช๐ปselect
์ ํ ํจ์(select)๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ๋ณํ(์ ํ) ํ ์ ์๋ค.
์ฟผ๋ฆฌ ํจ์๊ฐ ๋ฐํํ๋ ๋ฐ์ดํฐ๋ฅผ ์ธ์๋ก ๋ฐ์ ์ ํ ํจ์์์ ์ฒ๋ฆฌํ๊ณ ๋ฐํํ์ฌ ์ต์ข ๋ฐ์ดํฐ๊ฐ ๋๋ค.
์ต์ข ๋ฐ์ดํฐ ํ์ ์ useQuery์ 3๋ฒ์งธ ์ ๋ค๋ฆญ ํ์ ์ผ๋ก ์ ์ธํ ์ ์๋ค.
"use client"
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<div className='flex flex-col items-center justify-center h-screen bg-gray-900 text-white'>
<QueryClientProvider client={queryClient}>
<TanstackComponent id={1}/>
<TanstackComponent id={2}/>
</QueryClientProvider>
</div>
)
}
type JsonPlaceholder = {
userId: number
id: number
title: string
completed: boolean
}
type TanstackComponentProps = {
id?: number
}
type returnType = {
[key: string] : number
}
function TanstackComponent({id}: TanstackComponentProps) {
const { data, isStale, isPending, error } = useQuery<JsonPlaceholder, Error, returnType>({
queryKey: ['delay', id],
queryFn: async () => {
const result = await fetch(`https://jsonplaceholder.typicode.com/todos${id ? `/${id}` : ''}`);
const data = await result.json();
if(!data.id) {
throw new Error('์๋ฌ ๋ฐ์');
}
return data;
},
staleTime: 1000 * 3,
select: data => ({ id: data.id }),
})
if(isPending) {
return <div className='text-green-400 p-3'>๐ข ๋ก๋ฉ์ค...</div>
}
if(error) {
return <div className='text-red-500 p-3 font-bold'>โ ๏ธ ์๋ฌ ๋ฐ์</div>
}
return (
<div className='flex flex-col items-center justify-center'>
<div>๋ฐ์ดํฐ {isStale ? '๐์ ์ ํ์ง ์์' : '๐์ ์ ํจ'}</div>
<div>{JSON.stringify(data)}</div>
</div>
);
}
useQuery()์ ๋ฐํ ๋๋ ํ์ ๋ค์ ์ ์ด์ฃผ๊ณ ์ธ ๋ฒ์งธ๋ ์ต์ข ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ค ๋ค
select๋ฅผ ํตํด ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฌ์ด ๋ฐํํ๋ค.

๋๋ id๊ฐ๋ง ๊ฐ์ ธ์๋ดค๋ค.