Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ssi02014/react-query-tutorial

๐Ÿ˜ƒ TanStack Query(aka. react query) ์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋… ์ •๋ฆฌ
https://github.com/ssi02014/react-query-tutorial

caching react react-query tanstack-query typescript

Last synced: 30 days ago
JSON representation

๐Ÿ˜ƒ TanStack Query(aka. react query) ์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋… ์ •๋ฆฌ

Awesome Lists containing this project

README

        

# ๐Ÿ’ป TanStack Query(React)

- ํ•ด๋‹น ์ €์žฅ์†Œ๋Š” TanStack Query(React)์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋…๋“ค์„ ์ •๋ฆฌํ•œ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค. TanStack Query(React)์˜ ๋ชจ๋“  ํ™œ์šฉ ๋ฐฉ๋ฒ•์ด ์ž‘์„ฑ๋œ ์ƒํƒœ๋Š” ์•„๋‹ˆ๋ฉฐ, ํ•„์š”ํ•œ ๋‚ด์šฉ์€ ์ถ”๊ฐ€, ๋ณด์™„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


## Contributors

- ๊ธฐ์—ฌํ•ด์ฃผ์‹  ๋ชจ๋“  ๋ถ„๊ป˜ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
- ์˜คํƒˆ์ž, ๊ฐ€๋…์„ฑ์ด ์ข‹์ง€ ์•Š์€ ๋ถ€๋ถ„ ๋˜๋Š” ์ถ”๊ฐ€ ๋‚ด์šฉ์€ `Pull Request`, `Issue` ๋“ฑ์„ ์ž์œ ๋กญ๊ฒŒ ๋‚จ๊ฒจ์ฃผ์‹œ๋ฉด ๊ฒ€ํ†  ํ›„์— ๋ฐ˜์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

[![contributors](https://contrib.rocks/image?repo=ssi02014/react-query-tutorial)](https://github.com/ssi02014/react-query-tutorial/graphs/contributors)


## TanStack Query(React) v5

- โญ๏ธ TanStack Query(React) `v5`๊ฐ€ 23.10.17์— ๋ฆด๋ฆฌ์ฆˆ๋์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฌธ์„œ๋Š” `v5` ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
- โญ๏ธ ๊ธฐ์กด `v4` ๋ฌธ์„œ๋Š” [react query tutorial v4 ๋ฌธ์„œ](https://github.com/ssi02014/react-query-tutorial/tree/master/README.v4.md)๋ฅผ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

![แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-10-18 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 2 09 09](https://github.com/ssi02014/react-query-tutorial/assets/64779472/84de2a61-7e39-4d52-aed8-b0ab67af95bc)

- `v3 -> v4`, `v4 -> v5` Migrating ์ •๋ฆฌ ๋ฌธ์„œ๋Š” ์•„๋ž˜ ๋ฌธ์„œ๋“ค์„ ํ™•์ธํ•ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.
- [Migrating to TanStack Query(React) v5](https://github.com/ssi02014/react-query-tutorial/tree/master/document/v5.md)

- [Migrating to TanStack Query(React) v4](https://github.com/ssi02014/react-query-tutorial/tree/master/document/v4.md)


## ์ฃผ์š” ์ปจ์…‰ ๋ฐ ๊ฐ€์ด๋“œ ๋ชฉ์ฐจ

1. [React Query ๊ฐœ์š” ๋ฐ ๊ธฐ๋Šฅ](#๊ฐœ์š”)
2. [๊ธฐ๋ณธ ์„ค์ •(QueryClientProvider, QueryClient)](#react-query-๊ธฐ๋ณธ-์„ค์ •)
3. [React Query Devtools](#devtools)
4. [React Query ์บ์‹ฑ ๋ผ์ดํ”„ ์‚ฌ์ดํด](#์บ์‹ฑ-๋ผ์ดํ”„-์‚ฌ์ดํด)
5. [useQuery](#usequery)
6. [useQuery ์ฃผ์š” ๋ฆฌํ„ด ๋ฐ์ดํ„ฐ](#usequery-์ฃผ์š”-๋ฆฌํ„ด-๋ฐ์ดํ„ฐ)
7. [staleTime๊ณผ gcTime](#staletime๊ณผ-gctime)
8. [๋งˆ์šดํŠธ ๋  ๋•Œ๋งˆ๋‹ค ์žฌ์š”์ฒญํ•˜๋Š” refetchOnMount](#refetchonmount)
9. [์œˆ๋„์šฐ๊ฐ€ ํฌ์ปค์‹ฑ ๋  ๋•Œ๋งˆ๋‹ค ์žฌ์š”์ฒญํ•˜๋Š” refetchOnWindowFocus](#refetchonwindowfocus)
10. [Polling ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ refetchInterval์™€ refetchIntervalInBackground)](#polling)
11. [์ž๋™ ์‹คํ–‰์˜ enabled์™€ ์ˆ˜๋™์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ์š”์ฒญํ•˜๋Š” refetch](#enabled-refetch)
12. [์‹คํŒจํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด ์žฌ์š”์ฒญํ•˜๋Š” retry](#retry)
13. [onSuccess, onError, onSettled](#onsuccess-onerror-onsettled) - ๐Ÿ’ก **v5 @Deprecated**
14. [select๋ฅผ ์ด์šฉํ•œ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜](#select)
15. [์ฟผ๋ฆฌ๊ฐ€ pending ์ƒํƒœ์ธ ๋™์•ˆ ๋ณด์—ฌ ์ค„ ์ˆ˜ ์žˆ๋Š” placeholderData](#placeholderdata)
16. [Paginated ๊ตฌํ˜„์— ์œ ์šฉํ•œ keepPreviousData](#keepPreviousData) - ๐Ÿ’ก **v5 @Deprecated**
17. [ํŠน์ • ์ฟผ๋ฆฌ ํ”„๋กœํผํ‹ฐ ๋ณ€๊ฒฝ ์‹œ์—๋งŒ ๋ฆฌ๋ Œ๋”๋ง์„ ํŠธ๋ฆฌ๊ฑฐ ํ•  ์ˆ˜ ์žˆ๋Š” notifyOnChangeProps](#notifyOnChangeProps)
18. [์ฟผ๋ฆฌ๋ฅผ ๋ณ‘๋ ฌ(Parallel) ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” useQueries](#parallel)
19. [์ข…์† ์ฟผ๋ฆฌ(Dependent Queries)](#dependent-queries)
20. [QueryClient ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” useQueryClient](#usequeryclient)
21. [์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” initialData](#initial-query-data)
22. [๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๋Š” PreFetching](#prefetching)
23. [Infinite Queries(๋ฌดํ•œ ์ฟผ๋ฆฌ) + useInfiniteQuery](#infinite-queries)
24. [์„œ๋ฒ„์™€ HTTP CUD๊ด€๋ จ ์ž‘์—…์„ ์œ„ํ•œ useMutation](#usemutation)
25. [์ฟผ๋ฆฌ ์ˆ˜๋™ ์ทจ์†Œ cancelQueries](#cancelqueries)
26. [์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์žˆ๋Š” queryClient.invalidateQueries](#์ฟผ๋ฆฌ-๋ฌดํšจํ™”)
27. [์บ์‹œ ๋ฐ์ดํ„ฐ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•œ queryClient.setQueryData](#์บ์‹œ-๋ฐ์ดํ„ฐ-์ฆ‰์‹œ-์—…๋ฐ์ดํŠธ)
28. [์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์„ ์˜ฌ๋ ค์ฃผ๋Š” Optimistic Updates(๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ)](#optimistic-update)
29. [์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ Fallback UI๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ErrorBoundary + useQueryErrorResetBoundary](#usequeryerrorresetboundary)
30. [์„œ๋ฒ„ ๋กœ๋”ฉ ์ค‘์ผ ๋•Œ Fallback UI๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ Suspense](#suspense)
31. [์•ฑ ์ „์ฒด์— ๋™์ผํ•œ ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๊ณต์œ ํ•˜๋Š” Default Query Function](#default-query-function)
32. [๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ์— ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ ์šฉ](#react-query-typescript)
33. [๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ ESLint ์ ์šฉ](#react-query-eslint-plugin)
34. [๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ ์ง€์› ๋ฒ„์ „](#์ง€์›-๋ฒ„์ „)


## ๐Ÿ“ƒ ๊ธฐํƒ€ ์ฐธ๊ณ  ๋ฌธ์„œ

1. [QueryClient ์ฃผ์š” ๋‚ด์šฉ ์ •๋ฆฌ ๋ฌธ์„œ](https://github.com/ssi02014/react-query-tutorial/tree/master/document/queryClient.md)
2. [๊ธฐ๋ณธ์ ์ธ React Query ์•„ํ‚คํ…์ฒ˜ ์‚ดํŽด๋ณด๊ธฐ: inside React Query](https://github.com/ssi02014/react-query-tutorial/tree/master/document/insideReactQuery.md)


## ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป ์ฃผ์š” ์ฐธ๊ณ  ํŽ˜์ด์ง€

- [TanStack/query ๊ณต์‹ ๊นƒํ—ˆ๋ธŒ](https://github.com/TanStack/query)
- [TkDodo ๋ธ”๋กœ๊ทธ(TanStack Query maintainer)](https://tkdodo.eu/blog/)


## ๐Ÿ“ƒ React Query ๊ฐœ์š” ๋ฐ ๊ธฐ๋Šฅ

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

### ๊ฐœ์š”

- react-query๋Š” ๋ฆฌ์•กํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ `์„œ๋ฒ„ ์ƒํƒœ ๊ฐ€์ ธ์˜ค๊ธฐ`, `์บ์‹ฑ`, `๋™๊ธฐํ™” ๋ฐ ์—…๋ฐ์ดํŠธ`๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ์™€ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์กŒ๋‹ค.
- react-query์—์„œ๋Š” ๊ธฐ์กด ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ `redux`, `mobX`๊ฐ€ `ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ ์ž‘์—…`์— ์ ํ•ฉํ•˜์ง€๋งŒ, `๋น„๋™๊ธฐ ๋˜๋Š” ์„œ๋ฒ„ ์ƒํƒœ ์ž‘์—…`์—๋Š” ๊ทธ๋‹ค์ง€ ์ข‹์ง€ ์•Š๋‹ค๊ณ  ์–ธ๊ธ‰ํ•œ๋‹ค.
- ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ(Client State)์™€ ์„œ๋ฒ„ ์ƒํƒœ(Server State)๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ๊ฐœ๋…์ด๋ฉฐ, ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋Š” ๊ฐ๊ฐ์˜ input ๊ฐ’์œผ๋กœ ์˜ˆ๋ฅผ ๋“ค ์ˆ˜ ์žˆ๊ณ , ์„œ๋ฒ„ ์ƒํƒœ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋กœ ์˜ˆ๋ฅผ ๋“ค ์ˆ˜ ์žˆ๋‹ค.


### ๊ธฐ๋Šฅ

- ์บ์‹ฑ
- ๋™์ผํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ค‘๋ณต ์š”์ฒญ์„ ๋‹จ์ผ ์š”์ฒญ์œผ๋กœ ํ†ตํ•ฉ
- ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ
- ๋ฐ์ดํ„ฐ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜๋˜์—ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
- ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ฐ€๋Šฅํ•œ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜
- ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋ฐ ๋ฐ์ดํ„ฐ ์ง€์—ฐ ๋กœ๋“œ์™€ ๊ฐ™์€ ์„ฑ๋Šฅ ์ตœ์ ํ™”
- ์„œ๋ฒ„ ์ƒํƒœ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ ๊ฐ€๋น„์ง€ ์ˆ˜์ง‘ ๊ด€๋ฆฌ
- ๊ตฌ์กฐ ๊ณต์œ ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจํ™”


## React Query ๊ธฐ๋ณธ ์„ค์ •

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [QueryClient ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/reference/QueryClient)
- [QueryClientProvider ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/QueryClientProvider)

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

const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
// ...
},
},
});
```

- QueryClient๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ `์บ์‹œ`์™€ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
- QueryClient์—์„œ ๋ชจ๋“  `query` ๋˜๋Š” `mutation`์— ๊ธฐ๋ณธ ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ข…๋ฅ˜๊ฐ€ ์ƒ๋‹นํ•˜๋ฏ€๋กœ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด ๋ณด์ž.

```tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient({ /* options */});

function App() {
return (

๋ธ”๋ผ๋ธ”๋ผ

;
);
}
```

- react-query๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” `QueryClientProvider`๋ฅผ ์ตœ์ƒ๋‹จ์—์„œ ๊ฐ์‹ธ์ฃผ๊ณ  `QueryClient` ์ธ์Šคํ„ด์Šค๋ฅผ client props๋กœ ๋„ฃ์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์—ฐ๊ฒฐํ•ด์•ผ ํ•œ๋‹ค.
- ์œ„ ์˜ˆ์‹œ์—์„œ App.js์— QueryClientProvider๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๊ณ , client props์—๋‹ค queryClient๋ฅผ ์—ฐ๊ฒฐํ•จ์œผ๋กœ์จ, ์ด context๋Š” ์•ฑ์—์„œ ๋น„๋™๊ธฐ ์š”์ฒญ์„ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” `background` ๊ณ„์ธต์ด ๋œ๋‹ค.


## Devtools

![แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2022-04-07 แ„‹แ…ฉแ„’แ…ฎ 11 53 32](https://user-images.githubusercontent.com/64779472/162228222-d1c7dd3e-ce62-484d-bfa0-8493f3e68cae.png)

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [React Query Devtools ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/devtools)
- react-query๋Š” `์ „์šฉ devtools`๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ ๋ณ„๋„์˜ ํŒจํ‚ค์ง€ ์„ค์น˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
- devtools๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด React Query์˜ ๋ชจ๋“  ๋‚ด๋ถ€ ๋™์ž‘์„ `์‹œ๊ฐํ™”`ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋ฉฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด `๋””๋ฒ„๊น… ์‹œ๊ฐ„์„ ์ ˆ์•ฝ`ํ•  ์ˆ˜ ์žˆ๋‹ค.
- devtools๋Š” ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ `process.env.NODE_ENV === "development"` ์ธ ๊ฒฝ์šฐ์—๋งŒ ์‹คํ–‰๋œ๋‹ค, ์ฆ‰ ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์ž‘๋™ํ•˜๋„๋ก ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ํ”„๋กœ์ ํŠธ ๋ฐฐํฌ ์‹œ์— Devtools ์‚ฝ์ž… ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•ด ์ค„ ํ•„์š”๊ฐ€ ์—†๋‹ค.
- Next 13+์˜ App Dir์—์„  dev dependency๋กœ ์„ค์น˜ํ•ด์•ผ ๋™์ž‘ํ•œ๋‹ค.

```bash
$ npm i @tanstack/react-query-devtools
# or
$ pnpm add @tanstack/react-query-devtools
# or
$ yarn add @tanstack/react-query-devtools
# or
$ bun add @tanstack/react-query-devtools
```

```tsx
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

function App() {
return (

{/* The rest of your application */}


);
}
```

### options

- initialIsOpen (Boolean)
- `true`์ด๋ฉด ๊ฐœ๋ฐœ ๋„๊ตฌ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์—ด๋ ค ์žˆ๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
- buttonPosition?: ("top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative")
- ๊ธฐ๋ณธ๊ฐ’: `bottom-right`
- devtools ํŒจ๋„์„ ์—ฌ๋‹ซ๊ธฐ ์œ„ํ•œ ๋กœ๊ณ  ์œ„์น˜
- `relative`์ผ ๋•Œ ๋ฒ„ํŠผ์€ devtools๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์œ„์น˜์— ๋ฐฐ์น˜๋œ๋‹ค.
- ์ผ๋ฐ˜์ ์œผ๋กœ initialIsOpen, buttonPosition์„ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋ฉฐ ๊ทธ ์™ธ์— position, client์™€ ๊ฐ™์€ ์˜ต์…˜๋“ค๋„ ์กด์žฌํ•œ๋‹ค.


## ์บ์‹ฑ ๋ผ์ดํ”„ ์‚ฌ์ดํด

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [React Query ์บ์‹œ ๋ผ์ดํ”„ ์‚ฌ์ดํด ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/caching)

```
* Query Instances with and without cache data(์บ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ์—†๋Š” ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค)
* Background Refetching(๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ฆฌํŒจ์นญ)
* Inactive Queries(๋น„ํ™œ์„ฑ ์ฟผ๋ฆฌ)
* Garbage Collection(๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜)
```

- `gcTime`์˜ ๊ธฐ๋ณธ๊ฐ’ 5๋ถ„, `staleTime` ๊ธฐ๋ณธ๊ฐ’ 0์ดˆ๋ฅผ ๊ฐ€์ •

1. `A`๋ผ๋Š” queryKey๋ฅผ ๊ฐ€์ง„ A ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ `mount`๋จ
2. ๋„คํŠธ์›Œํฌ์—์„œ ๋ฐ์ดํ„ฐ fetchํ•˜๊ณ , ๋ถˆ๋Ÿฌ์˜จ ๋ฐ์ดํ„ฐ๋Š” A๋ผ๋Š” queryKey๋กœ `์บ์‹ฑ`ํ•จ
3. ์ด ๋ฐ์ดํ„ฐ๋Š” `fresh`์ƒํƒœ์—์„œ `staleTime(๊ธฐ๋ณธ๊ฐ’ 0)` ์ดํ›„ `stale` ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋จ
4. A ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ `unmount`๋จ
5. ์บ์‹œ๋Š” `gcTime(๊ธฐ๋ณธ๊ฐ’ 5min)` ๋งŒํผ ์œ ์ง€๋˜๋‹ค๊ฐ€ `๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ(GC)`๋กœ ์ˆ˜์ง‘๋จ
6. ๋งŒ์ผ, gcTime ์ง€๋‚˜๊ธฐ ์ „์ด๊ณ , A ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค freshํ•œ ์ƒํƒœ๋ผ๋ฉด ์ƒˆ๋กญ๊ฒŒ mount๋˜๋ฉด ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.


## useQuery

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

### useQuery ๊ธฐ๋ณธ ๋ฌธ๋ฒ•

- [useQuery ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useQuery)
- useQuery๋Š” `v5`๋ถ€ํ„ฐ ์ธ์ž๋กœ ๋‹จ ํ•˜๋‚˜์˜ `๊ฐ์ฒด`๋งŒ ๋ฐ›๋Š”๋‹ค. ๊ทธ์ค‘์— ์ฒซ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ `queryKey`, `queryFn`๊ฐ€ ํ•„์ˆ˜ ๊ฐ’์ด๋‹ค.

```tsx
const result = useQuery({
queryKey, // required
queryFn, // required
// ...options ex) gcTime, staleTime, select, ...
});

result.data;
result.isLoading;
result.refetch;
// ...
```

```tsx
// ์‹ค์ œ ์˜ˆ์ œ
// ๐Ÿ’ก queryFn์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ง€์ •ํ•ด์ฃผ๋ฉด useQuery์˜ ํƒ€์ž… ์ถ”๋ก ์ด ์›ํ™œํ•ฉ๋‹ˆ๋‹ค.
const getAllSuperHero = async (): Promise> => {
return await axios.get("http://localhost:4000/superheroes");
};

const { data, isLoading } = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});
```


**1. queryKey**

```tsx
// (1) queryKey๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„์— ๋”ํ•ด ์ฟผ๋ฆฌ ํ•จ์ˆ˜์— ์•„๋ž˜์™€ ๊ฐ™์ด ํŽธ๋ฆฌํ•˜๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
const getSuperHero = async ({
queryKey,
}: {
queryKey: ["super-hero", number];
}): Promise> => {
const heroId = queryKey[1]; // ex) queryKey: ["super-hero", "3"]

return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};

const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: getSuperHero, // (*)
});
};
```

- useQuery์˜ queryKey๋Š” `๋ฐฐ์—ด`๋กœ ์ง€์ •ํ•ด ์ค˜์•ผ ํ•œ๋‹ค.
- ์ด๋Š” ๋‹จ์ผ ๋ฌธ์ž์—ด๋งŒ ํฌํ•จ๋œ ๋ฐฐ์—ด์ด ๋  ์ˆ˜๋„ ์žˆ๊ณ , ์—ฌ๋Ÿฌ ๋ฌธ์ž์—ด๊ณผ ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋กœ ๊ตฌ์„ฑ๋œ ๋ณต์žกํ•œ ํ˜•ํƒœ์ผ ์ˆ˜๋„ ์žˆ๋‹ค.

```tsx
// An individual todo
useQuery({ queryKey: ["todo", 5], ... })

// An individual todo in a "preview" format
useQuery({ queryKey: ["todo", 5, { preview: true }], ...})
```

- useQuery๋Š” `queryKey`๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ `์ฟผ๋ฆฌ ์บ์‹ฑ`์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ด๋‹ค.
- ๋งŒ์•ฝ, ์ฟผ๋ฆฌ๊ฐ€ ํŠน์ • ๋ณ€์ˆ˜์— `์˜์กด`ํ•œ๋‹ค๋ฉด ๋ฐฐ์—ด์—๋‹ค ์ด์–ด์„œ ์ค˜์•ผ ํ•œ๋‹ค. `ex: ["super-hero", heroId, ...]`
- **์ด๋Š” ์‚ฌ์‹ค ๊ต‰์žฅํžˆ ์ค‘์š”ํ•˜๋‹ค.** ์˜ˆ๋ฅผ ๋“ค์–ด, `queryClient.setQueryData` ๋“ฑ๊ณผ ๊ฐ™์ด ํŠน์ • ์ฟผ๋ฆฌ์— ์ ‘๊ทผ์ด ํ•„์š” ํ•  ๋•Œ `์ดˆ๊ธฐ์— ์„ค์ •ํ•ด๋‘” ํฌ๋งท`์„ ์ง€์ผœ์ค˜์•ผ ์ œ๋Œ€๋กœ ์ฟผ๋ฆฌ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
- ์•„๋ž˜ options ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด useSuperHeroData์˜ queryKey๋Š” `["super-hero", heroId]`์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด queryClient.setQueryData๋ฅผ ์ด์šฉํ•  ๋•Œ ๋˜‘๊ฐ™์ด `["super-hero", heroId]` ํฌ๋งท์„ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์›ํ•˜๋Š” ์ฟผ๋ฆฌ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค.


**2. queryFn**

- useQuery์˜ queryFn๋Š” `Promise`๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค.

```tsx
// (2) ์ƒ๋‹จ์˜ queryKey ์˜ˆ์ œ์™€ ๋ฐ˜๋Œ€๋กœ queryFn ์ž์ฒด์ ์œผ๋กœ ์ธ์ž๋ฅผ ๋ฐ›๋Š” ํ˜•ํƒœ
const getSuperHero = async (heroId: string): Promise> => {
return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};

const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId), // (*)
});
};
```


**3. options**

- [useQuery ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useQuery)
- useQuery์˜ `options`์— ๋งŽ์ด ์“ฐ์ด๋Š” ์˜ต์…˜๋“ค์€ ์ฐจ๊ทผ์ฐจ๊ทผ ์‚ดํŽด๋ณผ ์˜ˆ์ •์ด๋‹ค. ๋ฌธ์„œ ์™ธ์— ๋”์šฑ ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด ์œ„ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.


```tsx
const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId),
gcTime: 5 * 60 * 1000, // 5๋ถ„
staleTime: 1 * 60 * 1000, // 1๋ถ„
retry: 1,
// ... options
});
};
```


### useQuery ์ฃผ์š” ๋ฆฌํ„ด ๋ฐ์ดํ„ฐ

- [useQuery ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useQuery)

```tsx
const {
data,
error,
status,
fetchStatus,
isLoading,
isFetching,
isError,
refetch,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});
```

- **data**: ์ฟผ๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ๋ฆฌํ„ดํ•œ `Promise`์—์„œ `resolved`๋œ ๋ฐ์ดํ„ฐ
- **error**: ์ฟผ๋ฆฌ ํ•จ์ˆ˜์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์˜ค๋ฅ˜ ๊ฐ์ฒด
- **status**: `data`, ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ’์— ๋Œ€ํ•œ ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” status๋Š” ๋ฌธ์ž์—ด ํ˜•ํƒœ๋กœ `3๊ฐ€์ง€`์˜ ๊ฐ’์ด ์กด์žฌํ•œ๋‹ค.
- pending: ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๊ณ , ์ฟผ๋ฆฌ ์‹œ๋„๊ฐ€ ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์€ ์ƒํƒœ.
- `{ enabled: false }` ์ƒํƒœ๋กœ ์ฟผ๋ฆฌ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ด ์ƒํƒœ๋กœ ์‹œ์ž‘๋œ๋‹ค.
- [Dependent Queries ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/dependent-queries)
- error: ์—๋Ÿฌ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ƒํƒœ
- success: ์ฟผ๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ์˜ค๋ฅ˜ ์—†์ด ์š”์ฒญ ์„ฑ๊ณตํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•  ์ค€๋น„๊ฐ€ ๋œ ์ƒํƒœ.
- **fetchStatus**: `queryFn`์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋ƒ„
- fetching: ์ฟผ๋ฆฌ๊ฐ€ ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ƒํƒœ
- paused: ์ฟผ๋ฆฌ๋ฅผ ์š”์ฒญํ–ˆ์ง€๋งŒ, ์ž ์‹œ ์ค‘๋‹จ๋œ ์ƒํƒœ (network mode์™€ ์—ฐ๊ด€)
- idle: ์ฟผ๋ฆฌ๊ฐ€ ํ˜„์žฌ ์•„๋ฌด ์ž‘์—…๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ
- **isLoading**: `์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์„ ๋•Œ` ์ฆ‰, ์ฒ˜์Œ ์‹คํ–‰๋œ ์ฟผ๋ฆฌ์ผ ๋•Œ ๋กœ๋”ฉ ์—ฌ๋ถ€์— ๋”ฐ๋ผ `true/false`๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
- ์ด๋Š” ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋กœ๋”ฉ ์—ฌ๋ถ€์— ์ƒ๊ด€์—†์ด `false`๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
- `isFetching && isPending` ์™€ ๋™์ผํ•˜๋‹ค.
- **isFetching**: ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋”๋ผ๋„ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ๋กœ๋”ฉ ์—ฌ๋ถ€์— ๋”ฐ๋ผ `true/false`๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
- **isSuccess**: ์ฟผ๋ฆฌ ์š”์ฒญ์ด ์„ฑ๊ณตํ•˜๋ฉด `true`
- **isError**: ์ฟผ๋ฆฌ ์š”์ฒญ ์ค‘์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ `true`
- **refetch**: ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜.
- **๊ทธ ์™ธ ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ๋“ค์„ ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ์œผ๋ฉด useQuery ๊ณต์‹ ๋ฌธ์„œ ์ฐธ๊ณ **


### ๐Ÿ’ก status, fetchStatus ๋‚˜๋ˆ ์„œ ๋‹ค๋ฃจ๋Š” ๊ฑธ๊นŒ?

- [Why Two Different States ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/queries#why-two-different-states)
- fetchStatus๋Š” HTTP ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ƒํƒœ์™€ ์ข€ ๋” ๊ด€๋ จ๋œ ์ƒํƒœ ๋ฐ์ดํ„ฐ์ด๋‹ค.
- ์˜ˆ๋ฅผ ๋“ค์–ด, status๊ฐ€ `success` ์ƒํƒœ๋ผ๋ฉด ์ฃผ๋กœ fetchStatus๋Š” `idle` ์ƒํƒœ์ง€๋งŒ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ re-fetch๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ `fetching` ์ƒํƒœ์ผ ์ˆ˜ ์žˆ๋‹ค.
- status๊ฐ€ ๋ณดํ†ต `loading` ์ƒํƒœ์ผ ๋•Œ fetchStatus๋Š” ์ฃผ๋กœ `fetching`๋ฅผ ๊ฐ–์ง€๋งŒ, ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์ด ๋˜์–ด ์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ `paused` ์ƒํƒœ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.
- ์ •๋ฆฌํ•˜์ž๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
- status๋Š” `data`๊ฐ€ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€์— ๋Œ€ํ•œ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
- fetchStatus๋Š” ์ฟผ๋ฆฌ ์ฆ‰, `queryFn ์š”์ฒญ`์ด ์ง„ํ–‰ ์ค‘์ธ์ง€ ์•„๋‹Œ์ง€์— ๋Œ€ํ•œ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.


## useQuery ์ฃผ์š” ์˜ต์…˜

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [useQuery ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useQuery)


### staleTime๊ณผ gcTime

- stale์€ ์šฉ์–ด ๋œป๋Œ€๋กœ `์ฉ์€`์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. ์ฆ‰, ์ตœ์‹  ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.
- fresh๋Š” ๋œป ๊ทธ๋Œ€๋กœ `์‹ ์„ ํ•œ`์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. ์ฆ‰, ์ตœ์‹  ์ƒํƒœ๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.

```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
gcTime: 5 * 60 * 1000, // 5๋ถ„
staleTime: 1 * 60 * 1000, // 1๋ถ„
});
```


1. staleTime: `(number | Infinity)`
- staleTime์€ ๋ฐ์ดํ„ฐ๊ฐ€ `fresh์—์„œ stale` ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„, ๋งŒ์•ฝ staleTime์ด `3000`์ด๋ฉด fresh ์ƒํƒœ์—์„œ `3์ดˆ` ๋’ค์— stale๋กœ ๋ณ€ํ™˜
- `fresh` ์ƒํƒœ์ผ ๋•Œ๋Š” ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒˆ๋กญ๊ฒŒ mount ๋˜์–ด๋„ ๋„คํŠธ์›Œํฌ ์š”์ฒญ(fetch)์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.
- ์ฐธ๊ณ ๋กœ, staleTime์˜ ๊ธฐ๋ณธ๊ฐ’์€ `0`์ด๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์œผ๋กœ fetch ํ›„์— ๋ฐ”๋กœ stale์ด ๋œ๋‹ค.
2. gcTime: `(number | Infinity)`
- ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, `inactive` ์ƒํƒœ์ผ ๋•Œ `์บ์‹ฑ ๋œ ์ƒํƒœ๋กœ` ๋‚จ์•„์žˆ๋Š” ์‹œ๊ฐ„(๋ฐ€๋ฆฌ์ดˆ)์ด๋‹ค.
- ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ unmount ๋˜๋ฉด ๋ฐ์ดํ„ฐ๋Š” `inactive ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ`๋˜๋ฉฐ, ์บ์‹œ๋Š” `gcTime`๋งŒํผ ์œ ์ง€๋œ๋‹ค.
- gcTime์ด ์ง€๋‚˜๋ฉด `๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ`๋กœ ์ˆ˜์ง‘๋œ๋‹ค.
- gcTime์ด ์ง€๋‚˜๊ธฐ ์ „์— ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋‹ค์‹œ mount ๋˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ fetch ํ•˜๋Š” ๋™์•ˆ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
- gcTime์€ staleTime๊ณผ ๊ด€๊ณ„์—†์ด, ๋ฌด์กฐ๊ฑด `inactive` ๋œ ์‹œ์ ์„ ๊ธฐ์ค€์œผ๋กœ ์บ์‹œ ๋ฐ์ดํ„ฐ ์‚ญ์ œ๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.
- gcTime์˜ ๊ธฐ๋ณธ๊ฐ’์€ `5๋ถ„`์ด๋‹ค. SSR ํ™˜๊ฒฝ์—์„œ๋Š” `Infinity`์ด๋‹ค.

- ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ staleTime๊ณผ gcTime์˜ ๊ธฐ๋ณธ๊ฐ’์€ ๊ฐ๊ฐ `0๋ถ„`๊ณผ `5๋ถ„`์ด๋‹ค. ๋”ฐ๋ผ์„œ staleTime์— ์–ด๋– ํ•œ ์„ค์ •๋„ ํ•˜์ง€ ์•Š์œผ๋ฉด ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ(Observer)๊ฐ€ mount ๋์„ ๋•Œ ๋งค๋ฒˆ ๋‹ค์‹œ API๋ฅผ ์š”์ฒญํ•  ๊ฒƒ์ด๋‹ค.
- staleTime์„ gcTime๋ณด๋‹ค ๊ธธ๊ฒŒ ์„ค์ •ํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, staleTime๋งŒํผ์˜ ์บ์‹ฑ์„ ๊ธฐ๋Œ€ํ–ˆ์„ ๋•Œ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์ง€ ๋ชปํ•  ๊ฒƒ์ด๋‹ค. ์ฆ‰, ๋‘ ๊ฐœ์˜ ์˜ต์…˜์„ ์ ์ ˆํ•˜๊ฒŒ ์„ค์ •ํ•ด ์ค˜์•ผ ํ•œ๋‹ค.
- ์ฐธ๊ณ ๋กœ, [TkDodo์˜ reply](https://github.com/TanStack/query/discussions/1685#discussioncomment-1876723)์— ๋”ฐ๋ฅด๋ฉด TkDodo๋Š” `staleTime์„ gcTime๋ณด๋‹ค ์ž‘๊ฒŒ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.`๋Š” ์˜๊ฒฌ์— ๋™์˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค.
- ์˜ˆ์ปจ๋Œ€, staleTime์ด 60๋ถ„์ผ์ง€๋ผ๋„ ์œ ์ €๊ฐ€ ์ž์ฃผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋ผ๋ฉด ๊ตณ์ด gcTime์„ 60๋ถ„ ์ด์ƒ์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‚ญ๋น„ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.


### refetchOnMount

```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
refetchOnMount: true,
});
```

- refetchOnMount: `boolean | "always" | ((query: Query) => boolean | "always")`
- refetchOnMount๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ `stale` ์ƒํƒœ์ผ ๊ฒฝ์šฐ, mount๋งˆ๋‹ค `refetch`๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.
- refetchOnMount์˜ ๊ธฐ๋ณธ๊ฐ’์€ `true`์ด๋‹ค.
- `always`๋กœ ์„ค์ •ํ•˜๋ฉด ๋งˆ์šดํŠธ ์‹œ๋งˆ๋‹ค ๋งค๋ฒˆ refetch๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
- `false`๋กœ ์„ค์ •ํ•˜๋ฉด ์ตœ์ดˆ fetch ์ดํ›„์—๋Š” refetch ํ•˜์ง€ ์•Š๋Š”๋‹ค.


### refetchOnWindowFocus

```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
refetchOnWindowFocus: true,
});
```

- refetchOnWindowFocus: `boolean | "always" | ((query: Query) => boolean | "always")`
- refetchOnWindowFocus๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ `stale` ์ƒํƒœ์ผ ๊ฒฝ์šฐ `์œˆ๋„์šฐ ํฌ์ปค์‹ฑ` ๋  ๋•Œ๋งˆ๋‹ค refetch๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.
- refetchOnWindowFocus์˜ ๊ธฐ๋ณธ๊ฐ’์€ `true`์ด๋‹ค.
- ์˜ˆ๋ฅผ ๋“ค์–ด, ํฌ๋กฌ์—์„œ ๋‹ค๋ฅธ ํƒญ์„ ๋ˆŒ๋ €๋‹ค๊ฐ€ ๋‹ค์‹œ ์›๋ž˜ ๋ณด๋˜ ์ค‘์ธ ํƒญ์„ ๋ˆŒ๋ €์„ ๋•Œ๋„ ์ด ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•œ๋‹ค. ์‹ฌ์ง€์–ด F12๋กœ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ฐฝ์„ ์ผœ์„œ ๋„คํŠธ์›Œํฌ ํƒญ์ด๋“ , ์ฝ˜์†” ํƒญ์ด๋“  ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ฐฝ์—์„œ ๋†€๋‹ค๊ฐ€ ํŽ˜์ด์ง€ ๋‚ด๋ถ€๋ฅผ ๋‹ค์‹œ ํด๋ฆญํ–ˆ์„ ๋•Œ๋„ ์ด ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•œ๋‹ค.
- `always`๋กœ ์„ค์ •ํ•˜๋ฉด ํ•ญ์ƒ ์œˆ๋„์šฐ ํฌ์ปค์‹ฑ ๋  ๋•Œ๋งˆ๋‹ค refetch๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.


### Polling

```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
refetchInterval: 2000,
refetchIntervalInBackground: true,
});
```

```
Polling(ํด๋ง)์ด๋ž€?
์‹ค์‹œ๊ฐ„ ์›น์„ ์œ„ํ•œ ๊ธฐ๋ฒ•์œผ๋กœ "์ผ์ •ํ•œ ์ฃผ๊ธฐ(ํŠน์ •ํ•œ ์‹œ๊ฐ„)"๋ฅผ ๊ฐ€์ง€๊ณ  ์„œ๋ฒ„์™€ ์‘๋‹ต์„ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฐฉ์‹์ด ํด๋ง ๋ฐฉ์‹์ด๋‹ค.
react-query์—์„œ๋Š” "refetchInterval", "refetchIntervalInBackground"์„ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
```

1. refetchInterval: `number | false | ((data: TData | undefined, query: Query) => number | false)`

- refetchInterval์€ `์‹œ๊ฐ„(ms)`๋ฅผ ๊ฐ’์œผ๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ์ผ์ • ์‹œ๊ฐ„๋งˆ๋‹ค ์ž๋™์œผ๋กœ refetch๋ฅผ ์‹œ์ผœ์ค€๋‹ค.

2. refetchIntervalInBackground: `boolean`

- refetchIntervalInBackground๋Š” `refetchInterval`๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.
- ํƒญ/์ฐฝ์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์— ์žˆ๋Š” ๋™์•ˆ refetch ์‹œ์ผœ์ค€๋‹ค. ์ฆ‰, ๋ธŒ๋ผ์šฐ์ €์— focus ๋˜์–ด ์žˆ์ง€ ์•Š์•„๋„ refetch๋ฅผ ์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.


### enabled refetch

```tsx
const {
data,
refetch,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
enabled: false,
});

const handleClickRefetch = useCallback(() => {
refetch();
}, [refetch]);

return (


{data?.data.map((hero: Data) => (
{hero.name}

))}
Fetch Heroes

);
```

- enabled: `boolean`
- enabled๋Š” `์ฟผ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ํ•  ๋•Œ ์„ค์ •`ํ•  ์ˆ˜ ์žˆ๋‹ค.
- enabled๋ฅผ `false`๋ฅผ ์ฃผ๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ์ž๋™ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
- useQuery ๋ฐ˜ํ™˜ ๊ฐ’ ์ค‘ status๊ฐ€ `pending` ์ƒํƒœ๋กœ ์‹œ์ž‘ํ•œ๋‹ค.
- refetch๋Š” ์ฟผ๋ฆฌ๋ฅผ `์ˆ˜๋™`์œผ๋กœ ๋‹ค์‹œ ์š”์ฒญํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. ์ฟผ๋ฆฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ค๋ฅ˜๋งŒ ๊ธฐ๋ก๋œ๋‹ค.
- ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ ค๋ฉด `throwOnError` ์†์„ฑ์„ `true`๋กœ ํ•ด์„œ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.
- ๋ณดํ†ต ์ž๋™์œผ๋กœ ์ฟผ๋ฆฌ ์š”์ฒญ์„ ํ•˜์ง€ ์•Š๊ณ  `๋ฒ„ํŠผ ํด๋ฆญ`์ด๋‚˜ ํŠน์ • ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์š”์ฒญ์„ ์‹œ๋„ํ•  ๋•Œ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.
- ๐Ÿ’ก ์ฃผ์˜ํ•  ์ ์€, `enabled: false`๋ฅผ ์คฌ๋‹ค๋ฉด `queryClient`๊ฐ€ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ• ์ค‘ `invalidateQueries`์™€ `refetchQueries`๋ฅผ ๋ฌด์‹œํ•œ๋‹ค.


### retry

```tsx
const {
data,
refetch,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
retry: 10, // ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์ „์— ์‹คํŒจํ•œ ์š”์ฒญ์„ 10๋ฒˆ ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
});
```

- retry: `(boolean | number | (failureCount: number, error: TError) => boolean)`
- retry๋Š” ์ฟผ๋ฆฌ๊ฐ€ `์‹คํŒจ`ํ•˜๋ฉด useQuery๋ฅผ `ํŠน์ • ํšŸ์ˆ˜`๋งŒํผ ์žฌ์š”์ฒญํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.
- retry๊ฐ€ `false`์ธ ๊ฒฝ์šฐ, ์‹คํŒจํ•œ ์ฟผ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์‹œ ์‹œ๋„ํ•˜์ง€ ์•Š๋Š”๋‹ค. `true`์ธ ๊ฒฝ์šฐ์—๋Š” ์‹คํŒจํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด์„œ ๋ฌดํ•œ ์žฌ์š”์ฒญ์„ ์‹œ๋„ํ•œ๋‹ค.
- ๊ฐ’์œผ๋กœ `์ˆซ์ž`๋ฅผ ๋„ฃ์„ ๊ฒฝ์šฐ, ์‹คํŒจํ•œ ์ฟผ๋ฆฌ๊ฐ€ ํ•ด๋‹น ์ˆซ์ž๋ฅผ ์ถฉ์กฑํ•  ๋•Œ๊นŒ์ง€ ์š”์ฒญ์„ ์žฌ์‹œ๋„ํ•œ๋‹ค.
- ๊ธฐ๋ณธ๊ฐ’์€ ํด๋ผ์ด์–ธํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” `3`, ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋Š” `0`์ด๋‹ค.


### onSuccess, onError, onSettled

- _NOTE_: `v4`๊นŒ์ง€ ์žˆ๋˜ onSuccess, onError, onSettled Callback์€ `useQuery` ์˜ต์…˜์—์„œ [@Deprecated](https://github.com/TanStack/query/pull/5353) ๋๋‹ค. ๋‹จ, `useMutation`์—์„œ๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
- [Breaking React Query's API on purpose](https://velog.io/@cnsrn1874/breaking-react-querys-api-on-purpose) ์ฐธ๊ณ 


### select

```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
select: (data) => {
const superHeroNames = data.data.map((hero) => hero.name);
return superHeroNames;
},
});

return (


{data.map((heroName, idx) => (
{heroName}

))}

);
```

- select: `(data: TData) => unknown`
- select ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ํ•จ์ˆ˜์—์„œ `๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๋ฅผ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.`
- ์ฐธ๊ณ ๋กœ, ๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ ๊ฐ’์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€๋งŒ ์ฟผ๋ฆฌ ์บ์‹œ์— ์ €์žฅ๋˜๋Š” ๋‚ด์šฉ์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.


### placeholderData

```tsx
const placeholderData = useMemo(() => generateFakeHeroes(), []);

const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
placeholderData: placeholderData,
});
```

- placeholderData: `TData | (previousValue: TData | undefined; previousQuery: Query | undefined,) => TData`
- placeholderData๋ฅผ ์„ค์ •ํ•˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ `pending` ์ƒํƒœ์ธ ๋™์•ˆ ํŠน์ • ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ placeholder data๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
- placeholderData๋Š” ์บ์‹œ์— ์œ ์ง€๋˜์ง€ ์•Š์œผ๋ฉฐ, ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ์™€ ๊ด€๊ณ„์—†๋Š” ๋ณด์—ฌ์ฃผ๊ธฐ์šฉ ๊ฐ€์งœ ๋ฐ์ดํ„ฐ๋‹ค.
- placeholderData์— ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ด์ „์— ๊ด€์ฐฐ๋œ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ธ์ž๋Š” ์ด์ „ ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋œ๋‹ค.


### keepPreviousData

- v4๊นŒ์ง€ ์žˆ๋˜ keepPreviousData์€ `ํŽ˜์ด์ง€๋„ค์ด์…˜`๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋˜ ์˜ต์…˜์ด์—ˆ๋‹ค. ์บ์‹ฑ ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ๋ชฉ๋ก์ด `๊นœ๋นก๊ฑฐ๋ฆฌ๋Š” ํ˜„์ƒ์„ ๋ฐฉ์ง€`ํ•  ์ˆ˜ ์žˆ๋‹ค.
- **ํ•˜์ง€๋งŒ, v5๋ถ€ํ„ฐ `keepPreviousData`, `isPreviousData`์€ ์˜ต์…˜์€ ์ œ๊ฑฐ๋๋‹ค.**

- [Removed keepPreviousData in favor of placeholderData identity function](https://github.com/ssi02014/react-query-tutorial/blob/main/document/v5.md#9-%EF%B8%8F-removed-keeppreviousdata-in-favor-of-placeholderdata-identity-function)

- ์ด๋“ค์€ ๊ฐ๊ฐ `placeholderData`์™€ `isPlaceholderData` ํ”Œ๋ž˜๊ทธ์™€ ๊ฑฐ์˜ ์œ ์‚ฌํ•˜๊ฒŒ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
- ์•„๋ž˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ `placeholderData`๋ฅผ ํ™œ์šฉํ•˜๋ฉด์„œ ์ด์ „ ๋ฒ„์ „์—์„œ `keepPreviousData์˜ ๊ฐ’์„ "true"`๋กœ ์คฌ์„ ๋•Œ์™€ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

```tsx
import { useQuery, keepPreviousData } from "@tanstack/react-query";

const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
placeholderData: keepPreviousData,
});
```

- ์•„๋ž˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•ด์„œ ์œ„์˜ `keepPreviousData` ์˜ˆ์‹œ์™€ ๋™์ผํ•œ ๋™์ž‘์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

```tsx
import { useQuery } from "@tanstack/react-query";

const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
placeholderData: (previousData, previousQuery) => previousData,
});
```


### notifyOnChangeProps

```tsx
import { useQuery } from "@tanstack/react-query";

const { data, dataUpdatedAt } = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
notifyOnChangeProps: ["data"], // data ๊ฐ’ ๋ณ€๊ฒฝ์‹œ์—๋งŒ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•œ๋‹ค
});
```

- notifyOnChangeProps: `string[] | "all" | (() => string[] | "all")`
- ์ฟผ๋ฆฌ์˜ ํŠน์ • ํ”„๋กœํผํ‹ฐ๋“ค์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
- ๋ณ„๋„๋กœ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด, **์ปดํฌ๋„ŒํŠธ์—์„œ ์ ‘๊ทผํ•œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ** ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•œ๋‹ค (๊ธฐ๋ณธ ๋™์ž‘). ์ฆ‰, ์œ„ ์˜ˆ์‹œ์—์„œ `notifyOnChangeProps`์— ์„ค์ •๊ฐ’์„ ์ฃผ์ง€ ์•Š์•˜๋‹ค๋ฉด, `data`, `dataUpdatedAt` ์ค‘ ์–ด๋Š ํ•˜๋‚˜๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•œ๋‹ค.
- `"all"`๋กœ ์„ค์ •ํ•  ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ์˜ ์–ด๋–ค ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค.
- ์ฐธ๊ณ : ๊ธฐ๋ณธ ๋™์ž‘์€ [Object.defineProperty()](https://github.com/TanStack/query/pull/1578/files#diff-93f379800fc8abf895eba249b2e2371eda98740aa40fc9f284a8088d190f46c3R506-R514)๋ฅผ ํ™œ์šฉํ•œ๋‹ค.


## Parallel

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [useQueries ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useQueries)

```tsx
const { data: superHeroes } = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});

const { data: friends } = useQuery({
queryKey: ["friends"],
queryFn: getFriends,
});
```

- ๋ช‡ ๊ฐ€์ง€ ์ƒํ™ฉ์„ ์ œ์™ธํ•˜๋ฉด ์ฟผ๋ฆฌ ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ์„ ์–ธ๋œ ์ผ๋ฐ˜์ ์ธ ์ƒํ™ฉ์ผ ๋•Œ, ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋“ค์€ `๊ทธ๋ƒฅ ๋ณ‘๋ ฌ๋กœ ์š”์ฒญ๋ผ์„œ ์ฒ˜๋ฆฌ`๋œ๋‹ค.
- ์ด๋Ÿฌํ•œ ํŠน์ง•์€ ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ์˜ `๋™์‹œ์„ฑ`์„ ๊ทน๋Œ€ํ™”์‹œํ‚จ๋‹ค.

```tsx
const queryResults = useQueries({
queries: [
{
queryKey: ["super-hero", 1],
queryFn: () => getSuperHero(1),
staleTime: Infinity, // ๋‹ค์Œ๊ณผ ๊ฐ™์ด option ์ถ”๊ฐ€ ๊ฐ€๋Šฅ!
},
{
queryKey: ["super-hero", 2],
queryFn: () => getSuperHero(2),
staleTime: 0,
},
// ...
],
});
```

- ํ•˜์ง€๋งŒ, ์ฟผ๋ฆฌ ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๋ Œ๋”๋ง์ด ๊ฑฐ๋“ญ๋˜๋Š” ์‚ฌ์ด์‚ฌ์ด์— ๊ณ„์† ์ฟผ๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋กœ์ง์ด hook ๊ทœ์น™์— ์–ด๊ธ‹๋‚  ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Ÿด ๋•Œ๋Š” `useQueries`๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
- useQueries ํ›…์€ ๋ชจ๋“  ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ํฌํ•จ๋œ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋ฐ˜ํ™˜๋˜๋Š” ์ˆœ์„œ๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์ž…๋ ฅ๋œ ์ˆœ์„œ์™€ ๋™์ผํ•˜๋‹ค.

### Queries Combine

- [useQueries Combine ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useQueries#combine)

- useQueries ํ›…์ด ๋ฐ˜ํ™˜ํ•œ ๋ชจ๋“  ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ํฌํ•จ๋œ ๋ฐฐ์—ด์„ ๋‹จ์ผ ๊ฐ’์œผ๋กœ ๊ฒฐํ•ฉํ•˜๋ ค๋ฉด combine ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

```tsx
const ids = [1,2,3]
const combinedQueries = useQueries({
queries: ids.map(id => (
{ queryKey: ["post", id], queryFn: () => fetchPost(id) },
)),
combine: (results) => {
return ({
data: results.map(result => result.data),
pending: results.some(result => result.isPending),
})
}
})
```

- combinedQueries๋Š” `data`์™€ `pending` ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š”๋‹ค.
- _Note_: ์ฐธ๊ณ ๋กœ ๊ฒฐํ•ฉํ•˜๋ฉด ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์˜ ๋‚˜๋จธ์ง€ ๋‹ค๋ฅธ ํ”„๋กœํผํ‹ฐ๋“ค์€ ์†์‹ค๋œ๋‹ค.


## Dependent Queries

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [Dependent Queries ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/dependent-queries)
- `์ข…์† ์ฟผ๋ฆฌ`๋Š” ์–ด๋–ค A๋ผ๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์žˆ๋Š”๋ฐ ์ด A ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์‚ฌ์ „์— ์™„๋ฃŒ๋˜์–ด์•ผ ํ•˜๋Š” B ์ฟผ๋ฆฌ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋Ÿฌํ•œ B ์ฟผ๋ฆฌ์— ์˜์กดํ•˜๋Š” A ์ฟผ๋ฆฌ๋ฅผ ์ข…์† ์ฟผ๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค.
- react-query์—์„œ๋Š” `enabled` ์˜ต์…˜์„ ํ†ตํ•ด ์ข…์† ์ฟผ๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

```tsx
// ์‚ฌ์ „์— ์™„๋ฃŒ๋˜์–ด์•ผ ํ•  ์ฟผ๋ฆฌ
const { data: user } = useQuery({
queryKey: ["user", email],
queryFn: () => getUserByEmail(email),
});

const channelId = user?.data.channelId;

// user ์ฟผ๋ฆฌ์— ์ข…์† ์ฟผ๋ฆฌ
const { data: courses } = useQuery({
queryKey: ["courses", channelId],
queryFn: () => getCoursesByChannelId(channelId),
enabled: !!channelId,
});
```


## useQueryClient

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- useQueryClient๋Š” `QueryClient` ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
- `QueryClient`๋Š” ์บ์‹œ์™€ ์ƒํ˜ธ์ž‘์šฉํ•œ๋‹ค.
- QueryClient๋Š” ๋‹ค์Œ ๋ฌธ์„œ์—์„œ ์ž์„ธํ•˜๊ฒŒ ๋‹ค๋ฃฌ๋‹ค.
- [QueryClient](https://github.com/ssi02014/react-query-tutorial/tree/master/document/queryClient.md)

```tsx
import { useQueryClient } from "@tanstack/react-query";

const queryClient = useQueryClient();
```


## Initial Query Data

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [Initial Query Data ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/initial-query-data)
- ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ `์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ`๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ์ „์— ์บ์‹œ์— ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.
- initialData ์˜ต์…˜์„ ํ†ตํ•ด์„œ ์ฟผ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ์ฑ„์šฐ๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ดˆ๊ธฐ ๋กœ๋“œ ์ƒํƒœ๋„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜๋„ ์žˆ๋‹ค.

```tsx
const useSuperHeroData = (heroId: string) => {
const queryClient = useQueryClient();

return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId),
initialData: () => {
const queryData = queryClient.getQueryData(["super-heroes"]) as any;
const hero = queryData?.data?.find(
(hero: Hero) => hero.id === parseInt(heroId)
);

if (hero) return { data: hero };
},
});
};
```


- ์ฐธ๊ณ ๋กœ ์œ„ ์˜ˆ์ œ์—์„œ `queryClient.getQueryData` ๋ฉ”์„œ๋“œ๋Š” ๊ธฐ์กด ์ฟผ๋ฆฌ์˜ `์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ`๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋™๊ธฐ ํ•จ์ˆ˜์ด๋‹ค. ์ฟผ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด `undefined`๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


## Prefetching

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [prefetching ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/prefetching)
- prefetch๋Š” ๋ง ๊ทธ๋Œ€๋กœ ๋ฏธ๋ฆฌ fetchํ•ด์˜ค๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.
- ๋น„๋™๊ธฐ ์š”์ฒญ์€ ๋ฐ์ดํ„ฐ์–‘์ด ํด์ˆ˜๋ก ๋ฐ›์•„์˜ค๋Š” ์†๋„๊ฐ€ ๋Š๋ฆฌ๊ณ , ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ›์•„์™€์„œ ์บ์‹ฑํ•ด ๋†“์œผ๋ฉด? ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ธฐ ์ „์— ์‚ฌ์šฉ์ž๊ฐ€ ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์–ด `UX์— ์ข‹์€ ์˜ํ–ฅ`์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
- ์˜ˆ๋ฅผ ๋“ค์–ด ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ๊ตฌํ˜„ํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, ํŽ˜์ด์ง€1์—์„œ ํŽ˜์ด์ง€2๋กœ ์ด๋™ํ–ˆ์„ ๋•Œ ํŽ˜์ด์ง€3์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ›์•„๋†“๋Š” ๊ฒƒ์ด๋‹ค!
- react query์—์„œ๋Š” `queryClient.prefetchQuery`์„ ํ†ตํ•ด์„œ prefetch ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

### prefetchQuery

```tsx
const prefetchNextPosts = async (nextPage: number) => {
const queryClient = useQueryClient();
// ํ•ด๋‹น ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๋Š” ์ผ๋ฐ˜ ์ฟผ๋ฆฌ๋“ค์ฒ˜๋Ÿผ ์บ์‹ฑ ๋œ๋‹ค.
await queryClient.prefetchQuery({
queryKey: ["posts", nextPage],
queryFn: () => fetchPosts(nextPage),
// ...options
});
};

// ๋‹จ์ˆœ ์˜ˆ
useEffect(() => {
const nextPage = currentPage + 1;

if (nextPage < maxPage) {
prefetchNextPosts(nextPage);
}
}, [currentPage]);
```

- ์ฐธ๊ณ ๋กœ prefetchQuery๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ์˜ค๋Š” ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ `์ด๋ฏธ ์บ์‹ฑ ๋˜์–ด ์žˆ์œผ๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋Š”๋‹ค.`


### prefetchInfiniteQuery

- infinite ์ฟผ๋ฆฌ๋Š” ๋ฐ”๋กœ ์•„๋ž˜์— ๋‚˜์˜ค๊ฒ ์ง€๋งŒ ์ผ๋ฐ˜ ์ฟผ๋ฆฌ๋“ค์ฒ˜๋Ÿผ infinite ์ฟผ๋ฆฌ๋„ prefetch ํ•  ์ˆ˜ ์žˆ๋‹ค.
- ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฟผ๋ฆฌ์˜ `์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€๋งŒ prefetch` ๋˜๋ฉฐ, ๊ทธ ์ด์ƒ์„ prefetch ํ•˜๋ ค๋ฉด `pages ์˜ต์…˜`์„ ํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค.
- ์ด ๊ฒฝ์šฐ์—๋Š” `getNextPageParam` ํ•จ์ˆ˜๋ฅผ ๋ฌด์กฐ๊ฑด ์ œ๊ณตํ•ด ์ค˜์•ผ ํ•œ๋‹ค๋Š” ์ ์„ ์ฃผ์˜ํ•˜์ž.

```tsx
const prefetchTodos = async () => {
await queryClient.prefetchInfiniteQuery({
queryKey: ["projects"],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
pages: 3, // prefetch the first 3 pages
});
};
```


## Infinite Queries

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [Infinite Queries ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/infinite-queries)
- [useInfiniteQuery ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useInfiniteQuery)
- Infinite Queries(๋ฌดํ•œ ์ฟผ๋ฆฌ)๋Š” `๋ฌดํ•œ ์Šคํฌ๋กค`์ด๋‚˜ `load more(๋” ๋ณด๊ธฐ)`๊ณผ ๊ฐ™์ด ํŠน์ • ์กฐ๊ฑด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐ›์•„์˜ค๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์šฉํ•˜๋‹ค.
- react-query๋Š” ์ด๋Ÿฌํ•œ ๋ฌดํ•œ ์ฟผ๋ฆฌ๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด useQuery์˜ ์œ ์šฉํ•œ ๋ฒ„์ „์ธ `useInfiniteQuery`์„ ์ง€์›ํ•œ๋‹ค.

```tsx
import { useInfiniteQuery } from "@tanstack/react-query";

// useInfiniteQuery์˜ queryFn์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” `pageParam`์ด๋ผ๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.
const fetchColors = async ({
pageParam,
}: {
pageParam: number;
}): Promise> => {
return await axios.get(`http://localhost:4000/colors?page=${pageParam}`);
};

const InfiniteQueries = () => {
const { data, hasNextPage, isFetching, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ["colors"],
queryFn: fetchColors,
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
return allPages.length < 4 && allPages.length + 1;
},
// ...
});

return (


{data?.pages.map((group, idx) => ({
/* ... */
}))}

fetchNextPage()}>
LoadMore


{isFetching && !isFetchingNextPage ? "Fetching..." : null}


);
};
```

### ์ฃผ์š” ๋ฐ˜ํ™˜

- `useInfiniteQuery`๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ useQuery์™€ ์‚ฌ์šฉ๋ฒ•์€ ๋น„์Šทํ•˜์ง€๋งŒ, ์ฐจ์ด์ ์ด ์žˆ๋‹ค.
- useInfiniteQuery๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ `isFetchingNextPage`, `isFetchingPreviousPage`, `fetchNextPage`, `fetchPreviousPage`, `hasNextPage` ๋“ฑ์ด ์ถ”๊ฐ€์ ์œผ๋กœ ์žˆ๋‹ค.
- data.pages: ๋ชจ๋“  ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ฐฐ์—ด์ด๋‹ค.
- data.pageParams: ๋ชจ๋“  ํŽ˜์ด์ง€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•˜๋Š” ๋ฐฐ์—ด์ด๋‹ค.
- fetchNextPage: `๋‹ค์Œ ํŽ˜์ด์ง€`๋ฅผ fetch ํ•  ์ˆ˜ ์žˆ๋‹ค.
- fetchPreviousPage: `์ด์ „ ํŽ˜์ด์ง€`๋ฅผ fetch ํ•  ์ˆ˜ ์žˆ๋‹ค.
- isFetchingNextPage: `fetchNextPage` ๋ฉ”์„œ๋“œ๊ฐ€ ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ true์ด๋‹ค.
- isFetchingPreviousPage: `fetchPreviousPage` ๋ฉ”์„œ๋“œ๊ฐ€ ์ด์ „ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ true์ด๋‹ค.
- hasNextPage: ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” `๋‹ค์Œ ํŽ˜์ด์ง€`๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ true์ด๋‹ค.
- hasPreviousPage: ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” `์ด์ „ ํŽ˜์ด์ง€`๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ true์ด๋‹ค.


### ์ฃผ์š” ์˜ต์…˜

1. initialPageParam: `TPageParam`

- initialPageParam์„ ์ด์šฉํ•ด์„œ ์ฒซ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์‚ฌ์šฉํ•  ๊ธฐ๋ณธ ํŽ˜์ด์ง€ ๋งค๊ฐœ๋ณ€์ˆ˜์ด๋‹ค. `ํ•„์ˆ˜๊ฐ’`์ด๋‹ค.

2. getNextPageParam: `(lastPage, allPages, lastPageParam, allPageParams) => TPageParam | undefined | null`

- getNextPageParam ์„ ์ด์šฉํ•ด์„œ ํŽ˜์ด์ง€๋ฅผ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. `ํ•„์ˆ˜๊ฐ’`์ด๋‹ค.
- getNextPageParam์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž `lastPage`๋Š” fetch ํ•ด์˜จ ๊ฐ€์žฅ ์ตœ๊ทผ์— ๊ฐ€์ ธ์˜จ ํŽ˜์ด์ง€ ๋ชฉ๋ก์ด๋‹ค.
- ๋‘ ๋ฒˆ์งธ ์ธ์ž `allPages`๋Š” ํ˜„์žฌ๊นŒ์ง€ ๊ฐ€์ ธ์˜จ ๋ชจ๋“  ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ์ด๋‹ค.
- ์„ธ ๋ฒˆ์งธ ์ธ์ž `firstPageParam` ๋Š” ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์ด๋‹ค.
- ๋„ค ๋ฒˆ์งธ ์ธ์ž `allPageParams` ๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์ด๋‹ค.
- ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ ์—†์Œ์„ ํ‘œ์‹œํ•˜๋ ค๋ฉด `undefined` ๋˜๋Š” `null`์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋œ๋‹ค.
- `getPreviousPageParam`๋„ ์กด์žฌํ•˜๋ฉฐ, `getNextPageParam`์™€ ๋ฐ˜๋Œ€์˜ ์†์„ฑ์„ ๊ฐ–๊ณ  ์žˆ๋‹ค.

3. maxPages: `number | undefined`

- infinite ์ฟผ๋ฆฌ์— ์ €์žฅํ•  `์ตœ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜`์ด๋‹ค.
- ์ตœ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜์— ๋„๋‹ฌํ–ˆ๋Š”๋ฐ ์ƒˆ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด `์ง€์ •๋œ ๋ฐฉํ–ฅ(next, previous)`์— ๋”ฐ๋ผ ํŽ˜์ด์ง€ ๋ฐฐ์—ด์—์„œ ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€ ๋˜๋Š” ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€๊ฐ€ ์ œ๊ฑฐ๋œ๋‹ค.
- `0` ๋˜๋Š” `undefined`๋ผ๋ฉด ํŽ˜์ด์ง€ ์ˆ˜๋Š” ๋ฌด์ œํ•œ์ด๋‹ค.


### ๐Ÿ’ก pageParam

- `queryFn`์— ๋„˜๊ฒจ์ฃผ๋Š” pageParam๊ฐ€ ๋‹จ์ˆœํžˆ ๋‹ค์Œ page์˜ ๊ฐ’๋งŒ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.
- pageParam ๊ฐ’์€ `getNextPageParam`์—์„œ ์›ํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝ์‹œ์ผœ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
- ๋ฌด์Šจ ๋ง์ธ์ง€ ์˜ˆ์‹œ๋ฅผ ๋ณด๋ฉด ์ดํ•ด๊ฐ€ ์‰ฝ๋‹ค. ๐Ÿ‘ ์•„๋ž˜์™€ ๊ฐ™์ด getNextPageParam์—์„œ ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹จ์ˆœํžˆ ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ’์ด ์•„๋‹Œ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.

```tsx
const { data, hasNextPage, isFetching, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ["colors"],
queryFn: ({ pageParam }) => fetchColors(pageParam), // pageParam: { page: number; etc: string }
initialPageParam: {
page: number,
etc: "hi",
},
getNextPageParam: (lastPage, allPages) => {
return (
allPages.length < 4 && {
page: allPages.length + 1,
etc: "bye",
};
)
},
});
```

- ๊ทธ๋Ÿฌ๋ฉด `queryFn`์— ๋„ฃ์€ pageParam์—์„œ getNextPageParam์—์„œ ๋ฐ˜ํ™˜ํ•œ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

```tsx
const fetchColors = async ({
page,
etc,
}: {
page: number;
etc: string;
}): Promise> => {
return await axios.get(
`http://localhost:4000/colors?page=${page}?etc=${etc}`
);
};
```

- ์ฆ‰, getNextPageParam์˜ ๋ฐ˜ํ™˜ ๊ฐ’์ด pageParams๋กœ ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— pageParams๋ฅผ ์›ํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด getNextPageParam์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.


## useMutation

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [useMutation ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useMutation)
- react-query์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ Get ํ•  ๋•Œ๋Š” useQuery๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
- ๋งŒ์•ฝ ์„œ๋ฒ„์˜ data๋ฅผ post, patch, put, delete์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์ด๋•Œ๋Š” useMutation์„ ์ด์šฉํ•œ๋‹ค.
- ์š”์•ฝํ•˜์ž๋ฉด `R(read)๋Š” useQuery`, `CUD(Create, Update, Delete)๋Š” useMutation`์„ ์‚ฌ์šฉํ•œ๋‹ค.

```tsx
const mutation = useMutation({
mutationFn: createTodo,
onMutate() {
/* ... */
},
onSuccess(data) {
console.log(data);
},
onError(err) {
console.log(err);
},
onSettled() {
/* ... */
},
});

const onCreateTodo = (e) => {
e.preventDefault();
mutate({ title });
};
```

- useMutation์˜ ๋ฐ˜ํ™˜ ๊ฐ’์ธ mutation ๊ฐ์ฒด์˜ `mutate` ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์š”์ฒญ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
- mutate๋Š” `onSuccess`, `onError` ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์„ฑ๊ณตํ–ˆ์„ ์‹œ, ์‹คํŒจํ–ˆ์„ ์‹œ response ๋ฐ์ดํ„ฐ๋ฅผ ํ•ธ๋“ค๋งํ•  ์ˆ˜ ์žˆ๋‹ค.
- `onMutate`๋Š” mutation ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋˜๊ณ , mutation ํ•จ์ˆ˜๊ฐ€ ๋ฐ›์„ ๋™์ผํ•œ ๋ณ€์ˆ˜๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค.
- `onSettled`๋Š” try...catch...finally ๊ตฌ๋ฌธ์˜ `finally`์ฒ˜๋Ÿผ ์š”์ฒญ์ด ์„ฑ๊ณตํ•˜๋“  ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋“  ์ƒ๊ด€์—†์ด ๋งˆ์ง€๋ง‰์— ์‹คํ–‰๋œ๋‹ค.

```tsx
const mutation = useMutation(addTodo);

try {
const todo = await mutation.mutateAsync(todo);
console.log(todo);
} catch (error) {
console.error(error);
} finally {
console.log("done");
}
```

- ๋งŒ์•ฝ, useMutation์„ ์‚ฌ์šฉํ•  ๋•Œ promise ํ˜•ํƒœ์˜ response๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋ผ๋ฉด `mutateAsync`๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์–ป์–ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.


### ๐Ÿ’ก mutate์™€ mutateAsync๋Š” ๋ฌด์—‡์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์„๊นŒ?

- ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์— ์šฐ๋ฆฌ๋Š” mutate๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์œ ๋ฆฌํ•˜๋‹ค. ์™œ๋ƒํ•˜๋ฉด mutate๋Š” ์ฝœ๋ฐฑ(onSuccess, onError)๋ฅผ ํ†ตํ•ด data์™€ error์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ ํŠน๋ณ„ํžˆ ํ•ธ๋“ค๋งํ•ด ์ค„ ํ•„์š”๊ฐ€ ์—†๋‹ค.
- ํ•˜์ง€๋งŒ mutateAsync๋Š” Promise๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๊ฐ™์€ ๋ถ€๋ถ„์„ ์ง์ ‘ ๋‹ค๋ค„์•ผ ํ•œ๋‹ค.
- ๋งŒ์•ฝ ์ด๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š์œผ๋ฉด `unhandled promise rejection` ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
- [TkDodo Blog: Mutate or MutateAsync](https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutate-or-mutateasync)


### ๐Ÿ’ก useMutation callback๊ณผ mutate callback์˜ ์ฐจ์ด

- useMutation์€ onSuccess, onError, onSettled์™€ ๊ฐ™์€ Callback ํ•จ์ˆ˜๋“ค์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.
- ๊ทธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, mutate ์—ญ์‹œ ์œ„์™€ ๊ฐ™์€ Callback ํ•จ์ˆ˜๋“ค์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.
- ๋‘˜์˜ ๋™์ž‘์€ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์•ฝ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
- useMutation์˜ Callback ํ•จ์ˆ˜์™€ mutate์˜ Callback ํ•จ์ˆ˜๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.
- ์ˆœ์„œ๋Š” `useMutation์˜ Callback -> mutate์˜ Callback` ์ˆœ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.
- mutation์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ์ปดํฌ๋„ŒํŠธ๊ฐ€ unmount๋œ๋‹ค๋ฉด mutate์˜ Callback์€ ์‹คํ–‰๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.
- `TkDodo`๋Š” ์œ„์™€ ๊ฐ™์€ ์ด์œ ๋กœ ๋‘˜์„ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.
- ๊ผญ ํ•„์š”ํ•œ ๋กœ์ง(ex. `์ฟผ๋ฆฌ ์ดˆ๊ธฐํ™”`)์€ useMutation์˜ Callback์œผ๋กœ ์‹คํ–‰์‹œํ‚จ๋‹ค.
- ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ๋ฐ UI ๊ด€๋ จ ์ž‘์—…์€ mutate Callback์—์„œ ์‹คํ–‰์‹œํ‚จ๋‹ค.
- [TkDodo Blog: Some callbacks might not fire](https://tkdodo.eu/blog/mastering-mutations-in-react-query#some-callbacks-might-not-fire)


## cancelQueries

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [Query Cancellation ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/query-cancellation)
- ์ฟผ๋ฆฌ๋ฅผ `์ˆ˜๋™์œผ๋กœ ์ทจ์†Œ`ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ๋‹ค.
- ์˜ˆ๋ฅผ ๋“ค์–ด ์š”์ฒญ์„ ์™„๋ฃŒํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๊ฐ€ ์ทจ์†Œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์š”์ฒญ์„ ์ค‘์ง€ํ•˜๋„๋ก ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
- ๋˜๋Š”, ์•„์ง HTTP ์š”์ฒญ์ด ๋๋‚˜์ง€ ์•Š์•˜์„ ๋•Œ, ํŽ˜์ด์ง€๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ๋„ ์ค‘๊ฐ„์— ์ทจ์†Œํ•ด์„œ ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
- ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์ทจ์†Œํ•˜๊ณ  ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ ์œ„ํ•ด `queryClient.cancelQueries(queryKey)`๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ react-query๋Š” ์ฟผ๋ฆฌ ์ทจ์†Œ๋ฟ๋งŒ์•„๋‹ˆ๋ผ queryFn์˜ Promise๋„ ์ทจ์†Œํ•œ๋‹ค.

```tsx
const query = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});

const queryClient = useQueryClient();

const onCancelQuery = (e) => {
e.preventDefault();

queryClient.cancelQueries({
queryKey: ["super-heroes"],
});
};

return Cancel;
```


## ์ฟผ๋ฆฌ ๋ฌดํšจํ™”

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- invalidateQueries์€ ํ™”๋ฉด์„ ์ตœ์‹  ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.
- ์˜ˆ๋ฅผ ๋“ค๋ฉด, ๊ฒŒ์‹œํŒ ๋ชฉ๋ก์—์„œ ์–ด๋–ค ๊ฒŒ์‹œ๊ธ€์„ `์ž‘์„ฑ(Post)`ํ•˜๊ฑฐ๋‚˜ ๊ฒŒ์‹œ๊ธ€์„ `์ œ๊ฑฐ(Delete)`ํ–ˆ์„ ๋•Œ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ๊ฒŒ์‹œํŒ ๋ชฉ๋ก์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ตœ์‹ ํ™”ํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค.
- ํ•˜์ง€๋งŒ ์ด๋•Œ, `query Key`๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฐ•์ œ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”ํ•˜๊ณ  ์ตœ์‹ ํ™”๋ฅผ ์ง„ํ–‰ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋Ÿฐ ๊ฒฝ์šฐ์— `invalidateQueries()` ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
- ์ฆ‰, query๊ฐ€ ์˜ค๋ž˜๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ํŒ๋‹จํ•˜๊ณ  ๋‹ค์‹œ `refetch`๋ฅผ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค!

```tsx
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";

const useAddSuperHeroData = () => {
const queryClient = useQueryClient();

return useMutation(addSuperHero, {
onSuccess(data) {
queryClient.invalidateQueries({ queryKey: ["super-heroes"] }); // ์ด key์— ํ•ด๋‹นํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋ฌดํšจํ™”!
console.log(data);
},
onError(err) {
console.log(err);
},
});
};
```

- ์ฐธ๊ณ ๋กœ, queryKey์— `["super-heroes"]`์„ ๋„˜๊ฒจ์ฃผ๋ฉด queryKey์— "super-heroes"๋ฅผ ํฌํ•จํ•˜๋Š” ๋ชจ๋“  ์ฟผ๋ฆฌ๊ฐ€ ๋ฌดํšจํ™”๋œ๋‹ค.

```tsx
queryClient.invalidateQueries({ queryKey: ["super-heroes"] });

// ์•„๋ž˜ query๋“ค ๋ชจ๋‘ ๋ฌดํšจํ™” ๋œ๋‹ค.
const query = useQuery({
queryKey: ["super-heroes", "superman"],
queryFn: fetchSuperHero,
});
const query = useQuery({
queryKey: ["super-heroes", { id: 1 }],
queryFn: fetchSuperHero,
});
```

- ์œ„์— `enabled/refetch`์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ์ง€๋งŒ `enabled: false` ์˜ต์…˜์„ ์ฃผ๋ฉด`queryClient`๊ฐ€ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ• ์ค‘ `invalidateQueries`์™€ `refetchQueries`๋ฅผ ๋ฌด์‹œํ•œ๋‹ค.
- [Disabling/Pausing Queries ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/disabling-queries) ์ฐธ๊ณ 
- ์ž์„ธํ•œ ๋‚ด์šฉ์€ [queryClient.invalidateQueries ์ •๋ฆฌ](https://github.com/ssi02014/react-query-tutorial/blob/master/document/queryClient.md#invalidateQueries)๋ฅผ ์ฐธ๊ณ ํ•˜์ž.


## ์บ์‹œ ๋ฐ์ดํ„ฐ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [queryClient.setQueryData ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetquerydata)
- ๋ฐ”๋กœ ์œ„์—์„œ `queryClient.invalidateQueries`๋ฅผ ์ด์šฉํ•ด ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์‹ ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค๋Š”๋ฐ `queryClient.setQueryData`๋ฅผ ์ด์šฉํ•ด์„œ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.
- `queryClient.setQueryData`๋Š” ์ฟผ๋ฆฌ์˜ ์บ์‹œ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” `๋™๊ธฐ ํ•จ์ˆ˜`์ด๋‹ค.
- setQueryData์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋Š” `updater` ํ•จ์ˆ˜์ด๋‹ค. ํ•ด๋‹น ํ•จ์ˆ˜์˜ ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” `oldData`๋กœ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

```tsx
const useAddSuperHeroData = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: addSuperHero,
onSuccess(data) {
queryClient.setQueryData(["super-heroes"], (oldData: any) => {
return {
...oldData,
data: [...oldData.data, data.data],
};
});
},
onError(err) {
console.log(err);
},
});
};
```


## Optimistic Update

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- `Optimistic Update(๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ)`๋ž€ ์„œ๋ฒ„ ์—…๋ฐ์ดํŠธ ์‹œ UI์—์„œ๋„ ์–ด์ฐจํ”ผ ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ด๋ผ๊ณ (๋‚™๊ด€์ ์ธ) ๊ฐ€์ •ํ•ด์„œ `๋ฏธ๋ฆฌ UI๋ฅผ ์—…๋ฐ์ดํŠธ` ์‹œ์ผœ์ฃผ๊ณ  ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ๊ฒ€์ฆ๋ฐ›๊ณ  ์—…๋ฐ์ดํŠธ ๋˜๋Š” ๋กค๋ฐฑํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.
- ์˜ˆ๋ฅผ ๋“ค์–ด facebook์— ์ข‹์•„์š” ๋ฒ„ํŠผ์ด ์žˆ๋Š”๋ฐ ์ด๊ฒƒ์„ ์œ ์ €๊ฐ€ ๋ˆ„๋ฅธ๋‹ค๋ฉด, ์ผ๋‹จ client ์ชฝ state๋ฅผ ๋จผ์ € ์—…๋ฐ์ดํŠธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋งŒ์•ฝ์— ์‹คํŒจํ•œ๋‹ค๋ฉด, ์˜ˆ์ „ state๋กœ ๋Œ์•„๊ฐ€๊ณ  ์„ฑ๊ณตํ•˜๋ฉด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ fetch ํ•ด์„œ ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ์™€ ํ™•์‹คํžˆ ์—ฐ๋™์„ ์ง„ํ–‰ํ•œ๋‹ค.
- Optimistic Update๊ฐ€ ์ •๋ง ์œ ์šฉํ•  ๋•Œ๋Š” ์ธํ„ฐ๋„ท ์†๋„๊ฐ€ ๋Š๋ฆฌ๊ฑฐ๋‚˜ ์„œ๋ฒ„๊ฐ€ ๋Š๋ฆด ๋•Œ์ด๋‹ค. ์œ ์ €๊ฐ€ ํ–‰ํ•œ ์•ก์…˜์„ ๊ธฐ๋‹ค๋ฆด ํ•„์š” ์—†์ด ๋ฐ”๋กœ ์—…๋ฐ์ดํŠธ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX) ์ธก๋ฉด์—์„œ ์ข‹๋‹ค.

```tsx
const useAddSuperHeroData = () => {
const queryClient = useQueryClient();
return useMutation({
mutateFn: addSuperHero,
onMutate: async (newHero: any) => {
await queryClient.cancelQueries(["super-heroes"]);

// ์ด์ „ ๊ฐ’
const previousHeroData = queryClient.getQueryData(["super-heroes"]);

// ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ ์ง„ํ–‰
queryClient.setQueryData(["super-heroes"], (oldData: any) => {
return {
...oldData,
data: [
...oldData.data,
{ ...newHero, id: oldData?.data?.length + 1 },
],
};
});

// ๊ฐ’์ด ๋“ค์–ด์žˆ๋Š” context ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
return { previousHeroData };
},
// mutation์ด ์‹คํŒจํ•˜๋ฉด onMutate์—์„œ ๋ฐ˜ํ™˜๋œ context๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กค๋ฐฑ ์ง„ํ–‰
onError(error, hero, context: any) {
queryClient.setQueryData(["super-heroes"], context.previousHeroData);
},
// ์˜ค๋ฅ˜ ๋˜๋Š” ์„ฑ๊ณต ํ›„์—๋Š” ํ•ญ์ƒ refetch
onSettled() {
queryClient.invalidateQueries(["super-heroes"]);
},
});
};
```

- ์ฐธ๊ณ ๋กœ ์œ„ ์˜ˆ์ œ์—์„œ `cancelQueries`๋Š” ์ฟผ๋ฆฌ๋ฅผ `์ˆ˜๋™์œผ๋กœ ์ทจ์†Œ`์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ์ทจ์†Œ์‹œํ‚ฌ query์˜ queryKey๋ฅผ cancelQueries์˜ ์ธ์ž๋กœ ๋ณด๋‚ด ์‹คํ–‰์‹œํ‚จ๋‹ค.
- ์˜ˆ๋ฅผ ๋“ค์–ด, ์š”์ฒญ์„ ์™„๋ฃŒํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์ทจ์†Œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์š”์ฒญ์„ ์ค‘์ง€ํ•˜๋Š” ๊ฒฝ์šฐ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


## useQueryErrorResetBoundary

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [useQueryErrorResetBoundary ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/reference/useQueryErrorResetBoundary)
- react-query์—์„œ ErrorBoundary์™€ useQueryErrorResetBoundary๋ฅผ ๊ฒฐํ•ฉํ•ด `์„ ์–ธ์ `์œผ๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ Fallback UI๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.
- ErrorBoundary์— ๋Œ€ํ•œ ์„ค๋ช…์€ ํ•ด๋‹น ๋ฌธ์„œ ์ฐธ๊ณ  [ErrorBoundary](https://github.com/ssi02014/react-query-tutorial/blob/master/document/errorBoundary.md)


- `useQueryErrorResetBoundary`๋Š” `ErrorBoundary`์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š”๋ฐ ์ด๋Š”, ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฆฌ์•กํŠธ ๊ณต์‹ ๋ฌธ์„œ์—์„œ ๊ธฐ๋ณธ ์ฝ”๋“œ ๋ฒ ์ด์Šค๊ฐ€ ์ œ๊ณต๋˜๊ธด ํ•˜์ง€๋งŒ ์ข€ ๋” ์‰ฝ๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” `react-error-boundary` ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜๊ณ , react-query ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์„ ์˜ˆ์‹œ๋กœ ์ œ๊ณตํ•ด ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— `react-error-boundary`๋ฅผ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•ด ๋ณด์ž.

```bash
$ npm i react-error-boundary
# or
$ pnpm add react-error-boundary
# or
$ yarn add react-error-boundary
# or
$ bun add react-error-boundary
```

- ์„ค์น˜ ํ›„์— ์•„๋ž˜์™€ ๊ฐ™์€ QueryErrorBoundary๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ ๋‚ด๋ถ€์— `useQueryErrorResetBoundary` ํ›…์„ ํ˜ธ์ถœํ•ด `reset` ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
- ์•„๋ž˜ ์ฝ”๋“œ ๋‚ด์šฉ์€ ๋‹จ์ˆœํ•˜๋‹ค.
- Error๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ErrorBoundary์˜ `fallbackRender` prop์œผ๋กœ ๋„˜๊ธด ๋‚ด์šฉ์ด ๋ Œ๋”๋ง ๋˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด children ๋‚ด์šฉ์ด ๋ Œ๋”๋ง ๋œ๋‹ค.
- ๋˜ํ•œ, fallbackRender์— ๋„ฃ์–ด์ฃผ๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ `resetErrorBoundary`๋ฅผ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น์„ ํ†ตํ•ด ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์ฟผ๋ฆฌ ์—๋Ÿฌ๋ฅผ `์ดˆ๊ธฐํ™”`ํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” button์„ ํด๋ฆญํ•˜๋ฉด ์—๋Ÿฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ฒŒ๋” ์ž‘์„ฑํ–ˆ๋‹ค.

```tsx
import { useQueryErrorResetBoundary } from "@tanstack/react-query"; // (*)
import { ErrorBoundary } from "react-error-boundary"; // (*)

interface Props {
children: React.ReactNode;
}

const QueryErrorBoundary = ({ children }: Props) => {
const { reset } = useQueryErrorResetBoundary(); // (*)

return (
(


Error!!
resetErrorBoundary()}>Try again

)}
>
{children}

);
};

export default QueryErrorBoundary;
```

- ๊ทธ๋ฆฌ๊ณ  App.js์—๋‹ค QueryErrorBoundary ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ queryClient ์˜ต์…˜์—๋‹ค `{ throwOnError: true }`๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ๊ทธ๋ž˜์•ผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ `ErrorBoundary` ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

```tsx
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import QueryErrorBoundary from "./components/ErrorBoundary"; // (*)

const queryClient = new QueryClient({
defaultOptions: {
queries: {
throwOnError: true, // (*) ์—ฌ๊ธฐ์„œ๋Š” ๊ธ€๋กœ๋ฒŒ๋กœ ์„ธํŒ…ํ–ˆ์ง€๋งŒ, ๊ฐœ๋ณ„ ์ฟผ๋ฆฌ๋กœ ์„ธํŒ… ๊ฐ€๋Šฅ
},
},
});

function App() {
return (

{/* ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค */}

);
}
```


## Suspense

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- ErrorBoundary๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ณด์—ฌ์ฃผ๋Š” Fallback UI๋ฅผ `์„ ์–ธ์ `์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ , ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋Š” `Suspense`์™€๋„ ๊ฒฐํ•ฉํ•ด์„œ `์„œ๋ฒ„ ํ†ต์‹  ์ƒํƒœ๊ฐ€ ๋กœ๋”ฉ ์ค‘`์ผ ๋•Œ Fallback UI๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ์„ ์–ธ์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
- ์ฐธ๊ณ ๋กœ, Suspense ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ์•กํŠธ v16๋ถ€ํ„ฐ ์ œ๊ณต๋˜๋Š” `Component Lazy Loading`์ด๋‚˜ `Data Fetching` ๋“ฑ์˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ๋•Œ, ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ Fallback UI(ex: Loader)๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋‹ค.

```tsx
import { Suspense } from "react";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
// suspense: true, - ๐Ÿ’ก v5๋ถ€ํ„ฐ Deprecated
// useQuery/useInfiniteQuery์™€ ๊ฐ™์€ ์ผ๋ฐ˜ ํ›… ๋Œ€์‹  useSuspenseQuery/useSuspenseInfiniteQuery์™€ ๊ฐ™์€ suspense ํ›… ์‚ฌ์šฉ
throwOnError: true,
},
},
});

function App() {
return (

}>{/* ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค */}
;
);
}
```

- ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์šฐ๋ฆฌ๋Š” ์„œ๋ฒ„ ์ƒํƒœ๊ฐ€ ๋กœ๋”ฉ์ผ ๋•Œ Loader ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด์—ฌ์ฃผ๊ฒ ๋‹ค!๋ผ๊ณ  ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
- Suspense์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์–ด๋–ค ๋กœ์ง์ด ๋™์ž‘ํ•˜๋Š”์ง€ ์šฐ๋ฆฌ๋Š” ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„๋œ๋‹ค. ์ด์ฒ˜๋Ÿผ `๋‚ด๋ถ€ ๋ณต์žก์„ฑ์„ ์ถ”์ƒํ™”`ํ•˜๋Š” ๊ฒŒ ๋ฐ”๋กœ `์„ ์–ธํ˜• ์ปดํฌ๋„ŒํŠธ`์ด๋‹ค.
- ์œ„์™€ ๊ฐ™์ด `react-query(useSuspenseQuery)`์™€ ๊ฒฐํ•ฉํ•œ `Suspense`๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ณผ์ •์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

```
1. Suspense mount
2. MainComponent mount
3. MainComponent์—์„œ useSuspenseQuery ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์š”์ฒญ
4. MainComponent unmount, fallback UI์ธ Loader mount
5. ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๋ฉด fallback UI์ธ Loader unmount
6. MainComponent mount
```


### ๐Ÿ’ก New hooks for suspense

- [new hooks for suspense](https://github.com/ssi02014/react-query-tutorial/blob/main/document/v5.md#21-%EF%B8%8F-new-hooks-for-suspense)
- v5์—์„œ๋Š” `data fetching`์— ๋Œ€ํ•œ `suspense`๊ฐ€ ๋งˆ์นจ๋‚ด ์•ˆ์ •ํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
- `useSuspenseQuery`, `useSuspenseInfiniteQuery`, `useSuspenseQueries` 3๊ฐ€์ง€ ํ›…์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
- ๊ธฐ์กด์˜ `suspense ์˜ต์…˜`์€ ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Suspense๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ์œ„ ํ›…๋“ค์„ ํ™œ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
- ์œ„ 3๊ฐ€์ง€ ํ›…์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ํƒ€์ž… ๋ ˆ๋ฒจ์—์„œ `data`๊ฐ€ `undefined` ์ƒํƒœ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

```tsx
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";

const fetchGroups = async (): Promise<{ data: Group[] }> => {
const res = await axios.get("/groups");
return res;
};

// as-is
const { data } = useQuery({
queryKey: ["groups"],
queryFn: fetchGroups,
select: (data) => data.data,
});

// to-be
const { data } = useSuspenseQuery({
queryKey: ["groups"],
queryFn: fetchGroups,
select: (data) => data.data,
});
```


### ๐Ÿ’ก @suspensive/react-query

- TanStack Query(React) ๊ณต์‹ ๋ฌธ์„œ์˜ `Community Resources`์—์„œ๋Š” Suspense๋ฅผ ๋” `ํƒ€์ž… ์„ธ์ดํ”„`ํ•˜๊ฒŒ ์ž˜ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด [useSuspenseQuery](https://suspensive.org/ko/docs/react-query/useSuspenseQuery), [useSuspenseQueries](https://suspensive.org/ko/docs/react-query/useSuspenseQueries), [useSuspenseInfiniteQuery](https://suspensive.org/ko/docs/react-query/useSuspenseInfiniteQuery)๋ฅผ ์ œ๊ณตํ•˜๋Š” [@suspensive/react-query](https://tanstack.com/query/v4/docs/react/community/suspensive-react-query)๋ฅผ ์†Œ๊ฐœํ•˜๊ณ  ์žˆ๋‹ค.

- suspensive/react-query์˜ ํ›…(useSuspenseQuery, useSuspenseQueries, useSuspenseInfiniteQuery)์€ @tanstack/react-query v5 ๋ฒ„์ „์— ์ถ”๊ฐ€([๊ด€๋ จ Pull Request](https://github.com/TanStack/query/pull/5739))๋˜๊ณ  ๊ณต์‹ API๋กœ [์ด ํŽ˜์ด์ง€](https://tanstack.com/query/v5/docs/react/guides/suspense)์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


## Default Query Function

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- [Default Query Function ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/guides/default-query-function)
- ์•ฑ ์ „์ฒด์—์„œ ๋™์ผํ•œ ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๊ณต์œ ํ•˜๊ณ , `queryKey`๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ€์ ธ์™€์•ผ ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด `QueryClient`์— `queryFn` ์˜ต์…˜์„ ํ†ตํ•ด Default Query Function์„ ์ง€์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.

```tsx
// ๊ธฐ๋ณธ ์ฟผ๋ฆฌ ํ•จ์ˆ˜
const getSuperHero = async ({ queryKey }: any) => {
const heroId = queryKey[1];

return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};

const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryFn: getSuperHero,
// ...queries options
},
},
});

function App() {
return (
{/* ... */}
);
}
```

- `QueryClient`์— ์•ฑ ์ „์ฒด์—์„œ ์‚ฌ์šฉํ•  ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•ด ์ค€๋‹ค.

```tsx
// ์‚ฌ์šฉ ์˜ˆ์‹œ
const useSuperHeroData = (heroId: string) => {
return useQuery({ queryKey: ["super-hero", heroId] });
};
```

```tsx
// ๋‹ค์Œ ํ˜•ํƒœ๋Š” ๋ถˆ๊ฐ€๋Šฅ
const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId),
});
};
```

- useQuery์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ `queryKey`๋งŒ ๋„ฃ์–ด์ฃผ๋ฉด ๋‘ ๋ฒˆ์งธ ์ธ์ž์— ๋“ค์–ด๊ฐˆ `queryFn`์€ ์ž๋™์œผ๋กœ ์„ค์ •๋œ ๊ธฐ๋ณธ ์ฟผ๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.
- ์ผ๋ฐ˜์ ์œผ๋กœ `useQuery`๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์™€ ๋‹ฌ๋ฆฌ `queryFn`์„ ์ง€์ •ํ•˜์ง€ ์•Š๊ธฐ์— ์ฟผ๋ฆฌ ํ•จ์ˆ˜์— ์ง์ ‘ ์ธ์ž๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ํ˜•ํƒœ์˜ ์‚ฌ์šฉ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.


## React Query Typescript

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- React Query๋Š” TypeScript์˜ `์ œ๋„ค๋ฆญ(Generics)`์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๊ณ  API๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์œ ํ˜•์„ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


### useQuery

ํ˜„์žฌ useQuery๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” ์ œ๋„ค๋ฆญ์€ `4๊ฐœ`์ด๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1. **TQueryFnData**: useQuery๋กœ ์‹คํ–‰ํ•˜๋Š” query function์˜ `์‹คํ–‰ ๊ฒฐ๊ณผ`์˜ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
2. **TError**: query function์˜ `error` ํ˜•์‹์„ ์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
3. **TData**: useQuery์˜ `data์— ๋‹ด๊ธฐ๋Š” ์‹ค์งˆ์ ์ธ ๋ฐ์ดํ„ฐ`์˜ ํƒ€์ž…์„ ๋งํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ œ๋„ค๋ฆญ๊ณผ์˜ ์ฐจ์ด์ ์€ `select`์™€ ๊ฐ™์ด query function์˜ ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ ํ•ธ๋“ค๋ง์„ ํ†ตํ•ด ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์ข‹๋‹ค.
4. **TQueryKey**: useQuery์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž `queryKey`์˜ ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด ์ฃผ๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.

```tsx
// useQuery์˜ ํƒ€์ž…
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>
```

```tsx
import { AxiosError } from "axios";

// useQuery ํƒ€์ž… ์ ์šฉ ์˜ˆ์‹œ
const { data } = useQuery<
AxiosResponse,
AxiosError,
string[],
["super-heroes", number]
>({
queryKey: ["super-heroes", id],
queryFn: getSuperHero,
select: (data) => {
const superHeroNames = data.data.map((hero) => hero.name);
return superHeroNames;
},
});

/**
์ฃผ์š” ํƒ€์ž…
* data: string[] | undefined
* error: AxiosError
* select: (data: AxiosResponse): string[]
*/
```


### useMutation

useMutation๋„ useQuery์™€ ๋™์ผํ•˜๊ฒŒ ํ˜„์žฌ 4๊ฐœ์ด๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1. **TData**: useMutation์— ๋„˜๊ฒจ์ค€ mutation function์˜ `์‹คํ–‰ ๊ฒฐ๊ณผ`์˜ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
- data์˜ ํƒ€์ž…๊ณผ onSuccess(1๋ฒˆ์งธ ์ธ์ž) ์ธ์ž์˜ ํƒ€์ž…์œผ๋กœ ํ™œ์šฉ๋œ๋‹ค.
2. **TError**: useMutation์— ๋„˜๊ฒจ์ค€ mutation function์˜ `error` ํ˜•์‹์„ ์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
3. **TVariables**: `mutate ํ•จ์ˆ˜`์— ์ „๋‹ฌ ํ•  ์ธ์ž๋ฅผ ์ง€์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
- onSuccess(2๋ฒˆ์งธ ์ธ์ž), onError(2๋ฒˆ์งธ ์ธ์ž), onMutate(1๋ฒˆ์งธ ์ธ์ž), onSettled(3๋ฒˆ์งธ ์ธ์ž) ์ธ์ž์˜ ํƒ€์ž…์œผ๋กœ ํ™œ์šฉ๋œ๋‹ค.
4. **TContext**: mutation function์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์ˆ˜ํ–‰ํ•˜๋Š” `onMutate ํ•จ์ˆ˜์˜ return๊ฐ’`์„ ์ง€์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
- onMutate์˜ ๊ฒฐ๊ณผ๊ฐ’์˜ ํƒ€์ž…์„ onSuccess(3๋ฒˆ์งธ ์ธ์ž), onError(3๋ฒˆ์งธ ์ธ์ž), onSettled(4๋ฒˆ์งธ ์ธ์ž)์—์„œ ํ™œ์šฉํ•˜๋ ค๋ฉด ํ•ด๋‹น ํƒ€์ž…์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

```tsx
export function useMutation<
TData = unknown,
TError = DefaultError,
TVariables = void,
TContext = unknown
>
```

```tsx
// useMutation ํƒ€์ž… ์ ์šฉ ์˜ˆ์‹œ
const { mutate } = useMutation({
mutationFn: postTodo,
onSuccess: (res, id, nextId) => {},
onError: (err, id, nextId) => {},
onMutate: (id) => id + 1,
onSettled: (res, err, id, nextId) => {},
});

const onClick = () => {
mutate(5);
};

/**
์ฃผ์š” ํƒ€์ž…
* data: Todo
* error: AxiosError
* onSuccess: (res: Todo, id: number, nextId: number)
* onError: (err: AxiosError, id: number, nextId: number)
* onMutate: (id: number)
* onSettled: (res: Todo, err: AxiosError, id: number, nextId: number),
*/
```


### useInfiniteQuery

ํ˜„์žฌ useInfiniteQuery ๊ฐ–๊ณ  ์žˆ๋Š” ์ œ๋„ค๋ฆญ์€ `4๊ฐœ`์ด๋ฉฐ, useQuery์™€ ์œ ์‚ฌํ•˜๋‹ค.

1. **TQueryFnData**: useInfiniteQuery๋กœ ์‹คํ–‰ํ•˜๋Š” query function์˜ `์‹คํ–‰ ๊ฒฐ๊ณผ`์˜ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
2. **TError**: query function์˜ `error` ํ˜•์‹์„ ์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
3. **TData**: useInfiniteQuery์˜ `data์— ๋‹ด๊ธฐ๋Š” ์‹ค์งˆ์ ์ธ ๋ฐ์ดํ„ฐ`์˜ ํƒ€์ž…์„ ๋งํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ œ๋„ค๋ฆญ๊ณผ์˜ ์ฐจ์ด์ ์€ `select`์™€ ๊ฐ™์ด query function์˜ ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ ํ•ธ๋“ค๋ง์„ ํ†ตํ•ด ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์ข‹๋‹ค.
4. **TQueryKey**: useInfiniteQuery์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž `queryKey`์˜ ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด ์ฃผ๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.

```tsx
export function useInfiniteQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = InfiniteData,
TQueryKey extends QueryKey = QueryKey
>
```

```tsx
const {
data,
hasNextPage,
fetchNextPage,
//...
} = useInfiniteQuery<
AxiosResponse,
AxiosError,
InfiniteData, number>,
["colors"]
>({
queryKey: ["colors"],
queryFn: fetchColors,
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
return allPages.length < 4 && allPages.length + 1;
},
// ...options
});

/**
์ฃผ์š” ํƒ€์ž…
* data: InfiniteData, number> | undefined
* error: AxiosError
* select: (data: InfiniteData, number>): InfiniteData, number>
* getNextPageParam: GetNextPageParamFunction>
*/
```


### ๐Ÿ’ก Typescript Best Practice

- [TypeScript ๊ณต์‹ ๋ฌธ์„œ](https://tanstack.com/query/v5/docs/react/typescript)
- ์œ„์˜ ์ œ๋„ค๋ฆญ์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฑด ์ฝ”๋“œ์˜ ๋ณต์žก๋„๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค. ํ•˜์ง€๋งŒ react query๋Š” ํƒ€์ž…์„ ์ž˜ ์ „๋‹ฌํ•˜๋ฏ€๋กœ ๊ตณ์ด ์ œ๋„ค๋ฆญ์„ ๋ชจ๋‘ ์ง์ ‘ ์ œ๊ณตํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
- ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ `queryFn`์˜ ํƒ€์ž…์„ ์ž˜ ์ •์˜ํ•ด์„œ `ํƒ€์ž… ์ถ”๋ก `์ด ์›ํ™œํ•˜๊ฒŒ ๋˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

```tsx
const fetchGroups = async (): Promise => {
return await axios.get("/groups");
};

const { data } = useQuery({
queryKey: ["groups"],
queryFn: fetchGroups,
select: (data) => data.data,
});

/**
์ฃผ์š” ํƒ€์ž…
* data: AxiosResponse | undefined
* error: Error | null
* select: (data: AxiosResponse): Group[]
*/
```


## React Query ESLint Plugin

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- Tanstack Query๋Š” `์ž์ฒด ESLint Plugin`์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ†ตํ•ด ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์ ์šฉํ•˜๊ณ , ์ผ๋ฐ˜์ ์ธ ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

### ์„ค์น˜

```bash
$ npm i -D @tanstack/eslint-plugin-query
# or
$ pnpm add -D @tanstack/eslint-plugin-query
# or
$ yarn add -D @tanstack/eslint-plugin-query
# or
$ bun add -D @tanstack/eslint-plugin-query
```


### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•(1)

- ํ”Œ๋Ÿฌ๊ทธ์ธ์— ๋Œ€ํ•œ `๊ถŒ์žฅํ•˜๋Š” ๋ชจ๋“  rule`์„ ์ ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด `.eslintrc.js` ํŒŒ์ผ์˜ `extends`๋ฐฐ์—ด ์•ˆ์— `plugin:@tanstack/eslint-plugin-query/recommended`์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

```js
module.exports = {
// ...
extends: ["plugin:@tanstack/eslint-plugin-query/recommended"],
rules: {
/* ์ˆ˜์ •ํ•˜๊ณ ์ž ํ•˜๋Š” rule ์ถ”๊ฐ€ ๊ฐ€๋Šฅ... */
},
};
```

- ๋ฌผ๋ก , rule์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด rules์— ์•„๋ž˜ `์‚ฌ์šฉ๋ฐฉ๋ฒ•(2)`์™€ ๊ฐ™์ด rule์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.


### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•(2)

- ์›ํ•˜๋Š” `rule`์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์„ค์ •ํ•ด์„œ ์ ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด `.eslintrc.js` ํŒŒ์ผ์˜ `plugins`๋ฐฐ์—ด ์•ˆ์— `@tanstack/query`๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ์ ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” `rules`์— ๊ทœ์น™์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

```js
module.exports = {
// ...
plugins: ["@tanstack/query"],
rules: {
"@tanstack/query/exhaustive-deps": "error",
"@tanstack/query/no-rest-destructuring": "warn",
"@tanstack/query/stable-query-client": "error",
},
};
```


## ์ง€์› ๋ฒ„์ „

[๋ชฉ์ฐจ ์ด๋™](#์ฃผ์š”-์ปจ์…‰-๋ฐ-๊ฐ€์ด๋“œ-๋ชฉ์ฐจ)

- Tanstack Query v5์— ํ•„์š”ํ•œ TypeScript ์ตœ์†Œ ๋ฒ„์ „์€ `v4.7`์ž…๋‹ˆ๋‹ค.
- Tanstack Query v5์— ํ•„์š”ํ•œ React ์ตœ์†Œ ๋ฒ„์ „์€ `v18`์ž…๋‹ˆ๋‹ค.

- React v18 ์ด์ƒ์—์„œ ์ง€์›ํ•˜๋Š” `useSyncExternalStore` ํ›…์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

- Tanstack Query v5์˜ ๋ธŒ๋ผ์šฐ์ €๋ณ„ ์ง€์› ๋ฒ„์ „์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

```
Chrome >= 91
Firefox >= 90
Edge >= 91
Safari >= 15
iOS >= 15
opera >= 77
```