thinkanddoit / coupang-clone-2

쿠팡 클론 코딩을 통한 좋은 코드 작성하는 능력 기르기
0 stars 0 forks source link

useQuery 공식문서 익히기 #5

Open thinkanddoit opened 1 year ago

thinkanddoit commented 1 year ago

서두

우선 서두에 개인적인 포부를 적고 모든 것 시리즈를 시작하겠다. 모든 것 시리즈는 프로젝트에 기술을 도입하기 전 팀원 혹은 나를 위한 문서화 작업이다. 이는 좋은 개발자가 되기 위한 필자의 습관 다지기의 일련이다.

TanStack Query v4 공식문서

React Query란?

TanStack 이라는 오픈소스에서 제공하는 라이브러리 중 한가지. v3까지는 React Query 명칭을 사용했으나 TanStack Query(v4)라는 이름으로 브랜드 변경 및 mono repo로 구조 조정되었다. Announcing TanStack Query v4 !본 글에서는 React Query명칭을 사용하겠음.

개요

그래서 무슨 라이브러리인가?

it makes fetching, caching, synchronizing and updating server state in your React applications a breeze. 리액트에서 데이터 패칭, 캐싱, 동기화, 서버상태 업데이트를 쉽게 해주는 라이브러리

왜 사용해야하는가?

리액트에서는 데이터 패칭 & 업데이트 기능을 제공하지 않는다. 우리는 리액트 훅을 통해서 컴포넌트 단위로 상태를 직접 관리하거나 전역 라이브러리를 사용해서 전체 상태관리 및 비동기 데이터를 관리한다.

기존 상태 관리 라이브러리는 클라이언트 상태관리에는 적합하지만 비동기 또는 서버상태 관리에는 제약이 있다.

React Query vs SWR vs Apollo vs RTK Query vs React Router

서버상태?

  • 원격에서 유지되는 상태로서 패칭 및 업데이트를 위한 비동기 API 필요하다.
  • 다른 사용자가 데이터를 변경하더라도 우리는 다시 패칭을 하지 않는 이상 모른다. (이것은 데이터가 out of date 가 될 수 있음을 의미한다.)

서버 상태를 파악하기 위해 다음과 같은 까다로운 기능구현이 필요하다.

그렇다면 React Query는 어떤 방식을 통해 위와 같은 서버상태 관리를 효율적으로 도와주는지 기능 분석을 통해서 알아보자

code snippet을 통해 3가지 개념 미리보기

  1. Queries
  2. Mutations
  3. Query Invalidation
    
    import {
    useQuery,
    useMutation,
    useQueryClient,
    QueryClient,
    QueryClientProvider,
    } from '@tanstack/react-query'
    import { getTodos, postTodo } from '../my-api'

// Client를 생성함. const queryClient = new QueryClient()

function App() { return ( // App(최상단)에서 Provider을 통해 Client 전달.

) }

function Todos() { // Client에 접근 const queryClient = useQueryClient()

// Queries const query = useQuery(['todos'], getTodos)

// Mutations const mutation = useMutation(postTodo, { onSuccess: () => { // 쿼리 데이터 무효화 및 refetch (Query Invalidation) queryClient.invalidateQueries(['todos']) }, })

return (

    {query.data?.map(todo => (
  • {todo.title}
  • ))}

) }

render(, document.getElementById('root'))

**이제 구체적인 기능 하나하나에 대해 알아보자**

### [개발도구](https://tanstack.com/query/v4/docs/devtools)

$ npm i @tanstack/react-query-devtools

or

$ pnpm add @tanstack/react-query-devtools

or

$ yarn add @tanstack/react-query-devtools

- 패키지 설치를 통해 사용한다.
![](https://velog.velcdn.com/images/thinkanddoit/post/90aafb50-8a43-4342-b306-d84e6e22417e/image.png)

- React Query의 모든 내부 작동을 시각화해서 디버깅 시간 절약한다.

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

function App() { return (

{/* The rest of your application */} //프로젝트 최상단(App)에 추가해서 사용한다.

) }


- 2가지 모드, option들을 제공한다. 
- 기본적으로 React Query Devtools는 `process.env.NODE_ENV === 'development'`에서만 번들에 포함되니 배포시 제외하는 것에 대해 고려하지 않아도 된다.
- 자세한 사항은 공식문서 및 참고를 확인하자.[(참고)](https://daily-dev-tips.com/posts/make-your-life-easier-with-react-query-devtools/#thank-you-for-reading-and-lets-connect)

### [TypeScript](https://tanstack.com/query/v4/docs/typescript)

- React Query는 TypeScript로 작성되어 있어서 type-safe를 보장한다.

#### Type Inference
- React Query에서 타입추론이 잘되기 때문에 타입지정을 따로 할 필요는 없다.

const { data } = useQuery(['test'], () => Promise.resolve(5)) // ^? const data: number | undefined

- 타입 추론은 `queryFn`에서 반환 타입에 대한 정의가 잘 되어있을때 효과가 있다. 보통 data fetching 라이브러리에서는 기본적으로 any타입을 결과로 반환하기 때문에 적절한 반환타입을 지정해야한다.

const fetchGroups = (): Promise<Group[]> => axios.get('/groups').then(response => response.data)

const { data } = useQuery(['groups'], fetchGroups) // ^? const data: Group[] | undefined

#### Type Narrowing
- React Query는 query 결과로 `state 필드`와 `파생된 state boolean flags`로 구분되는 [discriminated union](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) type을 사용한다.

const { data, isSuccess } = useQuery(['test'], () => Promise.resolve(5)) // data의 정의여부 확인을 위해 isSucess를 확인해서 Type Narrowing을 수행. if (isSuccess) { data // ^? const data: number (not undefined) }


#### Typing the error field

- error의 타입은 기본적으로`unknown`이다. [참고)useUnknownInCatchVariables](https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/#use-unknown-catch-variables)
- 안전하게 error를 처리하려면 runtime에서 error를 체크하거나 명시적으로 error type을 지정해야한다.

// runtime에서 error 체크

const { error } = useQuery(['groups'], fetchGroups) // ^? const error: unknown

if (error instanceof Error) { error // ^? const error: Error

}

// 명시적 Error type 지정

const { error } = useQuery<Group[], Error>(['groups'], fetchGroups) // ^? const error: Error | null


[React Query and TypeScript 읽을 거리](https://tkdodo.eu/blog/react-query-and-type-script)

### [Important Defaults](https://tanstack.com/query/v4/docs/guides/important-defaults)

- React Query는 `default 값들`이 존재한다.
#### staleTime
- `useQuery`와 `useInfiniteQuery`를 한 Query instance는 캐시된 데이터를 `stale`로 여긴다.
- `stale`은 오래된 데이터라는 개념인 것 같음.
- `staleTime` option을 통해서 전역 or 쿼리별로 refetch 타이밍을 설정할 수 있다.

`stale` 상태인 query는 다음과 같은 경우 refetch를 자동적으로 수행한다:
>
- 새로운 query instance가 마운트 될때
- window가 다시 포커스될 때
- network가 다시 연결될 때
- 선택적으로 설정된 refetch interval 지점 때

*주의 : 개발시에는 `refetchOnWindowFocus`가 설정되어 있기때문에 DevTools과 app에서 포커스를 이동 할 때마다 refetch가 수행됨.
#### refetchOnMount, refetchOnWindowFocus, refetchOnReconnect, refetchInterval
- `refetchOnMount`, `refetchOnWindowFocus`, `refetchOnReconnect`, `refetchInterval` 옵션 설정을 통해 refetch 타이밍은 제어가 가능하다.

#### cacheTime

- 더이상 활성화되지 않은 `useQuery`와 `useInfiniteQuery`의 쿼리 결과는 `inactive`상태가 된다.
- `inactive`상태에서는 다시 사용할 경우를 대비해 캐시에 남아있는다.
- default로 `inactive`상태의 쿼리가 garbage collected되기까지 5분이 설정되어 있다.(`cacheTime` = 1000 * 60 * 5)
- `cacheTime` 옵션 설정을 통해 제어가 가능하다.

#### retry, retryDelay
- default로 쿼리는 실패시 3번의 재시도를 수행한다.
- `retry`, `retryDelay` 옵션 설정을 통해 제어가 가능하다.

### [Queries](https://tanstack.com/query/v4/docs/guides/queries)