React Query (v4) 설치 및 사용법

hodu·2023년 11월 2일
1

Library

목록 보기
2/5

✅ 개요

최근 2주를 정신없이 보냈다.
RQ(React Query)를 사용했는데, 공부와 사용을 동시에 하니 정신이 없었고,
이를 정리하면서 복습하려고 한다.

현재 버전은 V5까지 나왔는데, 나는 V4.7.1를 사용하였다.
V5가 아직 5.0.3 초반 버전이고, RQ sample 버전이 V4.7.1이기 때문에 선택하였다.

이번 블로깅에서는 공식 문서에서 말하는 설치 방법과 3가지로 나누어 RQ 사용법을 정리할 것이다.

React Query는 서버 상태 관리 라이브러리로,
캐싱, 동일 데이터 단일 요청, 데이터 업데이트 관리, 등의 문제를 해결할 수 있고,
서버를 관리하기 까다로운 것을 쉽게 할 수 있도록 도와준다.
ReactQuery 개요


✅ 설치

$ npm i @tanstack/react-query
# or
$ pnpm add @tanstack/react-query
# or
$ yarn add @tanstack/react-query

npm 설치 방법이다.
이 중에서도 권장사항이 있는데,
eslint 플러그인이다. 추천한다.

👍 @tanstack/eslint-plugin-query

ESLint를 사용하면 깔끔한 사용법과 일반적인 실수를 방지하는 데 도와준다.
(사용할 때는 이를 확인하지 못해 아쉬웠다.😥)

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

설치한 이후에 .eslintrc(esLint 설정 파일)에 플러그인과 확장을 추가하면 된다.

{
  "plugins": ["@tanstack/query"]
  "extends": ["plugin:@tanstack/eslint-plugin-query/recommended"]
}

만약 확장이 부담스럽다면 일부 룰만 적용해서 간소화해보자

{
  "rules": {
    "@tanstack/query/exhaustive-deps": "error",
    "@tanstack/query/prefer-query-object-syntax": "error",
    "@tanstack/query/stable-query-client": "error"
  }
}

공식문서 설치


✅ 사용법

QueryClientProvider로 먼저 감싸주어야한다.
QueryClient는 query와 상호작용하기 위해 넣어줘야한다.

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

const queryClient = new QueryClient()

function App() {
  return (
    // Provide the client to your App
    <QueryClientProvider client={queryClient}>
      <Todos />
    </QueryClientProvider>
  )
}

next.js에서는 아래와 다르니 공식 문서를 참고하길 바란다.
SSR 및 Next.js

이렇게 감아주면 이제 React Query는 3가지 핵심 개념을 살펴보아야한다.


✨ Queries

Query를 요약하면 데이터 조회 및 캐싱의 역할을 한다.

먼저 Queries는 서버 데이터를 가져오고 Query Key에 저장하는 역할을 한다.

예시 코드 1

  // Queries
 function Todos() {
  const { isLoading, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,//api 함수
  })

예시 코드 2

const { isLoading, error, data, isFetching, isError } = useQuery({
  queryKey: ['repoData'],
  queryFn: () =>
    axios
      .get('https://api.github.com/repos/tannerlinsley/react-query')
      .then((res) => res.data),
})

여기서 useQuery 훅은 단일 객체 인자를 받는다.
이 객체 안에는 필수 옵션으로 queryKey와 queryFn라는 두 개의 Property를 받는다.

각각 데이터 쿼리의 고유 식별자(캐싱 데이터 식별자)
데이터를 비동기적으로 가져올 함수를 정의합니다.

그리고 객체를 return 하는데,
객체 상태에 맞게 로딩중, 패치중, 에러, 데이터를 뱉는다.

  const { isLoading, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,//api 함수
  })

그러면 이 데이터를 아래처럼 활용할 수 있다.

  if (isLoading) {
    return <span>Loading...</span>
  }

  if (isError) {
    return <span>Error: {error.message}</span>
  }

  // We can assume by this point that `isSuccess === true`
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )

기타 옵션들에 대해서는 아래 공식문서를 참고하길 바란다.

공식 문서 Queries


✨ Mutations

Mutation을 요약하면 데이터 생성/수정/삭제 및 동기화 역할을 한다.

Query와 달리 Mutation은 일반적으로 데이터를 생성/업데이트/삭제를 수행하는 데 사용됩니다. 이를 위해 useMutation hook을 내보낸다.

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

function App() {
  const mutation = useMutation({
    mutationFn: (newTodo) => {
      return axios.post('/todos', newTodo)
    },
  })

useMutation은 useQuery와 같이 인자로 객체를 받는데, mutationFn 키로 함수를 전달받는다.

다른 점은 useMutation을 사용하는 함수는 서버의 상태를 변경하는 함수를 받는다.

return (
  <div>
    {mutation.isLoading ? (
      'Adding todo...'
    ) : (
      <>
        {mutation.isError ? (
          <div>An error occurred: {mutation.error.message}</div>
        ) : null}

        {mutation.isSuccess ? <div>Todo added!</div> : null}

        <button
          onClick={() => {
            mutation.mutate({ id: new Date(), title: 'Do Laundry' })
          }}
        >
          Create Todo
        </button>
      </>
    )}
  </div>
)
}

위 값을 통해 업데이트 중 여러 화면을 렌더링 해줄 수 있다.

공식문서 Mutations

✨ Query Invalidation

invalidateQueries 함수를 주로 사용하는데, 요약하면 캐시 데이터가 더 이상 최신이 아닐 가능성이 있을 때 해당 캐시를 무효화하고, 필요에 따라 데이터를 다시 가져오는 역할을 한다.

정리하면,

  • staleTime(데이터 신선도)에서 사용되는 모든 구성을 재정의합니다.
  • useQuery 쿼리가 현재 useQuery 또는 관련 후크를 통해 렌더링되고 있는 경우 백그라운드에서도 다시 가져옵니다.
    (위 내용이 이해가 안간다면 공식 문서 Queries staleTime을 참고하자)

이 방법은 데이터가 변경을 알고 있을 때 유용하게 사용할 수 있습니다.
예를 들어, 사용자가 데이터를 변경하는 작업을 수행했고, 이로 인해 쿼리 데이터가 업데이트가 필요할 때 사용할 수 있습니다.

사용 방법 예시를 보자

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

// Get QueryClient from the context
const queryClient = useQueryClient()

// Invalidate every query in the cache
queryClient.invalidateQueries()
// Invalidate every query with a key that starts with `todos`
queryClient.invalidateQueries({ queryKey: ['todos'] })

위처럼 넘겨주는 객체 없이 선언을 하면 모든 query 초기화가 이루어지고,
키값을 넘겨주는 그 키에 해당되는 애들이 무효화가 된다.

구체적인 사용 법을 확인하기 위해 예시 코드들을 비교해보자

예시 코드 1

// Get QueryClient from the context
const queryClient = useQueryClient();

// 'todos'로 시작하는 쿼리 키를 가진 쿼리를 무효화합니다.
queryClient.invalidateQueries({ queryKey: ['todos'] });

// 아래 두 쿼리는 무효화됩니다.
const todoListQuery = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodoList,
});
const todoPageQuery = useQuery({
  queryKey: ['todos', { page: 1 }],
  queryFn: fetchTodoList,
});

예시 코드 2

// Get QueryClient from the context
const queryClient = useQueryClient();

// 'todos'로 시작하는 쿼리 키를 가진 쿼리를 무효화합니다.
queryClient.invalidateQueries({ queryKey: ['todos'] });

// 아래 두 쿼리는 무효화됩니다.
const todoListQuery = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodoList,
});
const todoPageQuery = useQuery({
  queryKey: ['todos', { page: 1 }],
  queryFn: fetchTodoList,
});

위처럼 구체적으로 지정해줄 수 있고 조건을 줄 수도 있다.

예시 코드 3

queryClient.invalidateQueries({
  predicate: (query) =>
    query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
});

// 아래 쿼리는 무효화됩니다.
const recentTodosQuery = useQuery({
  queryKey: ['todos', { version: 20 }],
  queryFn: fetchTodoList,
});

// 하지만 아래 쿼리는 무효화되지 않습니다.
const olderTodosQuery = useQuery({
  queryKey: ['todos', { version: 5 }],
  queryFn: fetchTodoList,
});

이렇게 invalidateQueries를 사용함으로써, 앱의 다양한 부분에서 데이터가 변경되었을 때 적절하게 쿼리를 새로고침하고 사용자에게 최신 정보를 제공할 수 있다.

공식문서 Query Invalidation


🚀 끝으로

React Query를 짧은 시간 안에 구현하려다보니 놓치던 부분들이 많았다.
공식 문서를 천천히 읽어보면서 곱씹어보니 깨달은 것이 많았다.

ESLint, Invalidation 활용법, 등을 알았다면 더 효율적으로 로직을 구성할 수 있었을 것이다.
어떠한 라이브러리를 사용하더라도 공식문서를 면밀히 살펴보는 것은 중요하다.

profile
잘부탁드립니다.

0개의 댓글