jineecode

React-query의 에러 핸들링 본문

React-Query

React-query의 에러 핸들링

지니코딩 2022. 8. 3. 15:12

네트워크 요청이 실패하면 이상적으로는 쿼리가 오류 상태가 되기를 원할 것입니다. 그렇게 하지 않고 대신 성공한 쿼리가 표시 된다면 이는 queryFn 이 실패한 Promise를 반환하지 않았음을 의미합니다.

 

React Query는 상태 코드나 네트워크 요청에 대해 전혀 알지 못합니다(또는 신경 쓰지 않습니다). queryFn 이 제공 해야 하는 해결되거나 거부된 Promise가 필요합니다.

 

React Query가 거부된 Promise를 발견하면 잠재적으로 재시도를 시작하고 오프라인인 경우 쿼리를 일시 중지하고 결국 쿼리를 오류 상태로 만들 수 있으므로 올바르게 수행하는 것이 매우 중요합니다.

 

오류를 올바르게 처리하려면 React Query에서 거부(rejected)된 Promise가 필요합니다. 운 좋게도, 이것은 여러분이 axios와 같은 라이브러리를 다룰 때 얻을 수 있죠.

 

만약 당신이 fetch API 또는 4xx 5xx 같은 잘못된 상태 코드에 대해 거부된 Promise 제공하지 않는 다른 라이브러리를 사용하고 있다면, 당신은 직접 queryFn 통해 변환해야 합니다.  

 

 

1. fetch API 를 쓸 경우

 

https://tanstack.com/query/v4/docs/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default

 

Query Functions | TanStack Query Docs

A query function can be literally any function that returns a promise. The promise that is returned should either resolve the data or throw an error. All of the following are valid query function configurations:

tanstack.com

- 잘못된 코드

useQuery(['todos', todoId], async () => {
  const response = await fetch('/todos/' + todoId)
  // 🚨 4xx or 5xx are not treated as errors
  return response.json()
})

- 아래처럼 예외처리를 반드시 해주세요!!!

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

 

2. 로깅

두 번째로, 로깅 목적으로 queryFn 내부에서 오류가 포착된다는 것입니다. 

catch에서 오류를 다시 발생시키지 않으면 암시적으로 성공한 Promise를 다시 반환합니다.

 

- 잘못된 코드

useQuery(['todos', todoId], async () => {
  try {
    const { data } = await axios.get('/todos/' + todoId)
    return data
  } catch (error) {
    console.error(error)
    // 🚨 here, an "empty" Promise<void> is returned
  }
})

- 아래처럼 failed Promise를 반환해주세요!

useQuery(['todos', todoId], async () => {
  try {
    const { data } = await axios.get('/todos/' + todoId)
    return data
  } catch (error) {
    console.error(error)
    // ✅ here, a failed Promise is returned
    throw error
  }
})

- 물론 useQuery의 onError 콜백을 사용해도 됩니다.

useQuery(
  ['todos', todoId],
  async () => {
    const { data } = await axios.get('/todos/' + todoId)
    return data
  },
  { onError: (error) => console.error(error) }
)

 

3. ErrorBoundary

오류 경계(Error Boundaries)는 렌더링 중에 발생하는 런타임 오류를 포착하기 위한 리액트의 개념으로, 이를 통해 적절하게 반응하여 폴백 UI로 표시할 수 있습니다.

컴포넌트를 원하는 세분화로 ErrorBoundary를 래핑할 수 있으므로 나머지 UI는 해당 오류의 영향을 받지 않습니다.

 

ErrorBoundary는 렌더링 중에 발생하지 않기 때문에 비동기 오류를 포착할 수 없습니다.

ErrorBoundary가 React Query에서 동작하기 위해

1. 내부적으로 오류를 잡아

2. 다음 렌더링 주기에 다시 던져(throw)

3. 오류 경계가 오류를 선택할 수 있도록 합니다.

이것은 매우 천재적이면서도 간단한 오류 처리 방식이며, 작업을 수행하기 위해 필요한 것은 useErrorBoundary 플래그를 쿼리에 전달(또는 기본 구성을 통해 제공)하는 것입니다.

 

useErrorBoundary라는 함수는 오류 경계로 이동할 오류와 지역적으로 처리할 오류를 지정할 수도 있습니다.

useQuery(['todos'], fetchTodos, {
  // 🚀 only server errors will go to the Error Boundary
  useErrorBoundary: (error) => error.response?.status >= 500,
})

이것은 mutations에도 효과가 있으며, form 제출 매우 유용합니다. 4xx 범위의 오류는 지역적으로 처리할 있는 반면(: 백엔드 유효성 검사에 실패한 경우), 모든 5xx 서버 오류는 오류 경계로 전파될 있습니다.

 

4. 토스트 에러

전역에 등록하면, 각 쿼리에 대해 한 번만 오류 토스트가 표시됩니다.

import toast from 'react-hot-toast'

const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error) =>
      toast.error(`Something went wrong: ${error.message}`),
  }),
})

 

5. 적절하게 섞어 쓰기

React Query에서 오류를 처리하는 세 가지 주요 방법은 다음과 같습니다.

  • useQuery에서 반환된 오류 속성
  • onError 콜백(쿼리 자체 또는 글로벌 QueryCache/MutionCache)
  • ErrorBoundary 사용
const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error, query) => {
      // 🎉 only show error toasts if we already have data in the cache
      // which indicates a failed background update
      if (query.state.data !== undefined) {
        toast.error(`Something went wrong: ${error.message}`)
      }
    },
  }),
})

 

'React-Query' 카테고리의 다른 글

Error: Missing queryFn  (0) 2022.08.02
Comments