🔥 쿼리 비활성화 및 일시 중지

536자
7분

쿼리가 자동으로 실행되는 것을 막고 싶을 때가 있습니다. 이런 경우 enabled = false 옵션을 사용할 수 있습니다. enabled 옵션은 불리언 값을 반환하는 콜백 함수도 받을 수 있습니다.

enabledfalse일 때 다음과 같은 상황이 발생합니다:

  • 쿼리에 캐시된 데이터가 있다면, status === 'success' 또는 isSuccess 상태로 초기화됩니다.
  • 쿼리에 캐시된 데이터가 없다면, status === 'pending'fetchStatus === 'idle' 상태로 시작합니다.
  • 쿼리는 마운트 시 자동으로 데이터를 가져오지 않습니다.
  • 쿼리는 백그라운드에서 자동으로 데이터를 다시 가져오지 않습니다.
  • 쿼리는 쿼리 클라이언트의 invalidateQueriesrefetchQueries 호출을 무시합니다. 이 호출은 보통 쿼리가 데이터를 다시 가져오게 만듭니다.
  • useQuery가 반환하는 refetch 함수를 사용해 수동으로 쿼리 실행을 트리거할 수 있습니다. 하지만 skipToken과 함께 사용할 수는 없습니다.

TypeScript 사용자라면 enabled = false 대신 skipToken을 사용하는 것이 좋습니다.

function Todos() {
  const { isLoading, isError, data, error, refetch, isFetching } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
    enabled: false,
  })
 
  return (
    <div>
      <button onClick={() => refetch()}>  목록 가져오기</button>
 
      {data ? (
        <>
          <ul>
            {data.map((todo) => (
              <li key={todo.id}>{todo.title}</li>
            ))}
          </ul>
        </>
      ) : isError ? (
        <span>오류: {error.message}</span>
      ) : isLoading ? (
        <span>로딩 ...</span>
      ) : (
        <span>준비되지 않음 ...</span>
      )}
 
      <div>{isFetching ? '데이터 가져오는 중...' : null}</div>
    </div>
  )
}
 
typescript

쿼리를 영구적으로 비활성화하면 TanStack Query가 제공하는 많은 좋은 기능(예: 백그라운드 리페치)을 사용할 수 없게 됩니다. 또한 이는 TanStack Query의 선언적 접근 방식(쿼리가 실행되어야 할 때의 의존성 정의)에서 벗어나 명령적 모드(여기를 클릭할 때마다 가져오기)로 전환됩니다. refetch에 매개변수를 전달할 수도 없습니다. 대부분의 경우, 여러분이 원하는 것은 초기 데이터 가져오기를 지연시키는 지연 쿼리일 것입니다.

지연 쿼리

enabled 옵션은 쿼리를 영구적으로 비활성화하는 데만 사용되는 것이 아니라, 나중에 쿼리를 활성화하거나 비활성화하는 데도 사용할 수 있습니다. 좋은 예로 필터 폼이 있습니다. 사용자가 필터 값을 입력한 후에만 첫 번째 요청을 보내고 싶을 때 이 옵션을 사용할 수 있습니다:

function Todos() {
  const [filter, setFilter] = React.useState('')
 
  const { data } = useQuery({
    queryKey: ['todos', filter],
    queryFn: () => fetchTodos(filter),
    // ⬇️ 필터가 비어있는 동안 비활성화됨
    enabled: !!filter,
  })
 
  return (
    <div>
      {/* 🚀 필터를 적용하면 쿼리가 활성화되고 실행됨 */}
      <FiltersForm onApply={setFilter} />
      {data && <TodosTable data={data} />}
    </div>
  )
}
 
typescript

isLoading (이전의 isInitialLoading)

지연 쿼리는 처음부터 status: 'pending' 상태입니다. 왜냐하면 'pending'은 아직 데이터가 없다는 것을 의미하기 때문입니다. 이는 기술적으로 사실이지만, 쿼리가 활성화되지 않았기 때문에 현재 데이터를 가져오고 있지 않습니다. 따라서 이 플래그를 로딩 스피너를 표시하는 데 사용하기 어려울 수 있습니다.

비활성화되거나 지연된 쿼리를 사용하고 있다면, isLoading 플래그를 대신 사용할 수 있습니다. 이는 다음과 같이 계산된 플래그입니다:

isPending && isFetching

따라서 쿼리가 처음으로 데이터를 가져오고 있을 때만 true가 됩니다.

skipToken을 사용한 타입 안전한 쿼리 비활성화

TypeScript를 사용한다면 쿼리를 비활성화하기 위해 skipToken을 사용할 수 있습니다. 이는 조건에 따라 쿼리를 비활성화하면서도 타입 안전성을 유지하고 싶을 때 유용합니다.

중요: useQuery에서 반환된 refetch는 skipToken과 함께 작동하지 않습니다. 그 외에는 skipToken은 enabled: false와 동일하게 작동합니다.

function Todos() {
  const [filter, setFilter] = React.useState<string | undefined>()
 
  const { data } = useQuery({
    queryKey: ['todos', filter],
    // ⬇️ 필터가 undefined이거나 비어있는 동안 비활성화됨
    queryFn: filter ? () => fetchTodos(filter) : skipToken,
  })
 
  return (
    <div>
      {/* 🚀 필터를 적용하면 쿼리가 활성화되고 실행됨 */}
      <FiltersForm onApply={setFilter} />
      {data && <TodosTable data={data} />}
    </div>
  )
}
 
typescript

이 방식을 사용하면 쿼리의 활성화 여부를 타입 시스템을 통해 안전하게 제어할 수 있으며, 동시에 코드의 가독성과 유지보수성을 높일 수 있습니다.