React Query 공식문서 번역본

April 14, 2023

Overview

TanStack Query(= React Query)는 웹 애플리케이션에서 서버 상태 fetching, caching, 동기화 및 업데이트를 쉽게 만듭니다.

대부분의 기존 상태 관리 라이브러리는 클라이언트 상태 작업에 적합하지만 비동기 또는 서버 상태 작업에는 적합하지 않습니다.

서버 상태가 완전히 다르기 때문입니다.

애플리케이션에서 서버 상태의 특성을 파악하고 나면 더 많은 문제가 발생합니다. 예를 들면 다음과 같습니다.

  • 캐싱

  • 동일한 데이터에 대한 여러 요청을 단일 요청으로 중복 제거

  • 백그라운드에서 “out of date”한 데이터 업데이트 데이터가 “out of date”된 시점 알기

  • 데이터 업데이트를 최대한 신속하게 반영

  • pagination 및 lazy loading 데이터와 같은 성능 최적화

  • 서버 상태의 메모리 및 가비지 수집 관리

React Query는 서버 상태를 관리하기 위한 최고의 라이브러리 중 하나입니다.

zero-config로 즉시 사용 가능하며 애플리케이션이 커짐에 따라 원하는 대로 사용자 정의할 수 있습니다.

설치

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

React Query는 React v16.8+와 호환되며 ReactDOM 및 React Native와 함께 작동합니다.

Queries

Query는 unique key에 연결된 데이터의 비동기 소스에 대한 선언적 종속성입니다.

Promise 기반 메서드(GET 및 POST 메서드 포함)와 함께 쿼리를 사용하여 서버에서 데이터를 가져올 수 있습니다.

(메서드가 서버의 데이터를 수정하는 경우 Mutations를 사용하는 것이 좋습니다.)

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

function App() {
  const info = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList
  })
}

Query Keys

TanStack Query는 Key를 기반으로 쿼리 caching을 관리합니다. 쿼리 키는 최상위 수준의 배열이어야 하며 단일 문자열이 포함된 배열처럼 단순하거나 많은 문자열 및 중첩 개체의 배열처럼 복잡할 수 있습니다.

쿼리 키가 직렬화 가능하고 쿼리 데이터에 고유한 한 사용할 수 있습니다!

function Todos({ todoId }) {
  const result = useQuery({
    queryKey: ['todos', todoId],
    queryFn: () => fetchTodoById(todoId),
  })
}})

Array Keys with variables

쿼리가 데이터를 unique하게 설명하기 위해 추가 정보가 필요한 경우 문자열과 serializable한 여러 object를 사용할 수 있습니다.

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

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

// A list of todos that are "done"
useQuery({ queryKey: ['todos', { type: 'done' }], ... })

Query Keys are hashed deterministically!

즉, object의 key 순서에 관계없이 다음 쿼리는 모두 동일한 것으로 간주됩니다.

useQuery({ queryKey: ['todos', { status, page }], ... })
useQuery({ queryKey: ['todos', { page, status }], ...})
useQuery({ queryKey: ['todos', { page, status, other: undefined }], ... })

그러나 다음 쿼리 키는 동일하지 않습니다. 배열 항목 순서가 중요합니다!

useQuery({ queryKey: ['todos', status, page], ... })
useQuery({ queryKey: ['todos', page, status], ...})
useQuery({ queryKey: ['todos', undefined, page, status], ...})

Query Function

Query function은 문자 그대로 Promise를 반환하는 모든 함수가 될 수 있습니다. 반환되는 Promise는 데이터를 resolve하거나 error를 throw해야 합니다.

다음은 모두 유효한 쿼리 기능 구성입니다.

useQuery({
  queryKey: ['todos'],
  queryFn: fetchAllTodos
})

useQuery({
  queryKey: ['todos', todoId],
  queryFn: () => fetchTodoById(todoId)
})

useQuery({
  queryKey: ['todos', todoId],
  queryFn: async () => {
    const data = await fetchTodoById(todoId)
    return data
  },
})

useQuery({
  queryKey: ['todos', todoId],
  queryFn: ({ queryKey }) => fetchTodoById(queryKey[1]),
})

Query가 error가 있는지 확인하려면 query function이 reject된 Promise을 throw하거나 return 해야 합니다.

const { error } = useQuery({
  queryKey: ['todos', todoId],
  queryFn: async () => {
    if (somethingGoesWrong) {
      throw new Error('Oh no!')
    }
    if (somethingElseGoesWrong) {
      return Promise.reject(new Error('Oh no!'))
    }

    return data
  },
})

axios 또는 graphql-request와 같은 대부분은 실패한 HTTP 호출에 대해 자동으로 오류를 발생시키지만 fetch와 같은 일부 유틸리티는 기본적으로 오류를 발생시키지 않습니다.

그런 경우에는 직접 던져야 합니다.

useQuery({
  queryKey: ['todos', todoId],
  queryFn: async () => {
    const response = await fetch('/todos/' + todoId)
    if (!response.ok) {
      throw new Error('Network response was not ok')
    }
    return response.json()
  },
})

Mutations

Mutations는 일반적으로 데이터를 create/update/delete를 수행하는 데 사용됩니다.

이를 위해 TanStack Query는 useMutation hook를 내보냅니다.

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

  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>
  )
}

❗️ mutate 함수는 비동기 함수이므로 React 16 및 이전 버전의 이벤트 콜백에서 직접 사용할 수 없습니다.

onSubmit에서 이벤트에 액세스해야 하는 경우 다른 함수에서 mutate를 래핑해야 합니다.

이는 React 이벤트 풀링 때문입니다.

const CreateTodo = () => {
  const mutation = useMutation({
    mutationFn: (event) => {
      event.preventDefault()
      return fetch('/api', new FormData(event.target))
    },
  })

  return <form onSubmit={mutation.mutate}>...</form>
}const CreateTodo = () => {

  const mutation = useMutation({
    mutationFn: (formData) => {
      return fetch('/api', formData)
    },
  })


  const onSubmit = (event) => {
    event.preventDefault()
    mutation.mutate(new FormData(event.target))
  }

  return <form onSubmit={onSubmit}>...</form>
}

mutateAsync

mutate 대신 mutateAsync를 사용하여 성공 시 resolve되거나 error가 발생하는 Promise를 가져옵니다.

const mutation = useMutation({ mutationFn: addTodo })

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

Important Default

기본적으로 TanStack Query는 기본값으로 구성됩니다.

때로 이러한 기본값은 개발자를 당황하게 만들거나 사용자가 모르는 경우 학습/디버깅을 어렵게 만들 수 있습니다.

다음 사항을 염두에 두십시오.

🌟 기본적으로 useQuery 또는 useInfiniteQuery를 통한 Query 인스턴스는 캐시된 데이터를 stale한 것으로 간주합니다.

stale한 query는 다음과 같은 경우 백그라운드에서 자동으로 다시 가져옵니다.

  • 쿼리 마운트의 새 인스턴스

  • window가 refocused 됬을 때

  • 네트워크가 재연결 되었을 때

예상하지 못한 refetch가 되는 경우 refetchOnWindowFocus를 수행하기 때문일 수 있습니다.

개발 중에는 특히 브라우저 DevTools와 앱 사이에 초점을 맞추면 가져오기가 발생하므로 이 작업이 더 자주 트리거될 수 있으므로 이 점에 유의하세요.


Profile picture

안녕하세요 🙌🏻 동수입니다.

주어진 상황에서 최고의 퍼포먼스를 내기위해 최선을 다하고 있습니다.

기억보단 기록을, 기록보단 공유하는 것을 좋아합니다.

현재는 캠핏에서 웹 프론트 개발을 하고 있습니다. 🎃

📬 : sonicce99@naver.com

githubnotionstorybook