https://tkdodo.eu/blog/react-query-error-handling
React Query Error Handling
After covering the sunshine cases of data fetching, it's time to look at situations where things don't go as planned and "Something went wrong..."
tkdodo.eu
서론
리액트 쿼리는 비동기 상태 관리 라이브러리입니다.
사전 지식
표준 예제
Copythe-standard-example: copy code to clipboard
function TodoList() {
const todos = useQuery(['todos'], fetchTodos)
if (todos.isLoading) {
return 'Loading...'
}
// ✅ standard error handling
// could also check for: todos.status === 'error'
if (todos.isError) {
return 'An error occurred'
}
return (
<div>
{todos.data.map((todo) => (
<Todo key={todo.id} {...todo} />
))}
</div>
)
}
1. 백그라운드 오류를 잘 처리하지 못합니다.
2. 쿼리를 사용하는 컴포넌트마다 보일러플레이트가 쌓입니다.
Error Boundaries
에러 바운더리(Error Boundaries)라는, 렌더링 중에 발생하는 런타임 오류를 포착하기 위한 React의 개념을 사용하면
에러 발생 시 대체 UI를 표시할 수 있습니다.
우리가 원하는 크기의 컴포넌트 단위를 래핑할 수 있으므로 나머지 UI가 해당 오류의 영향을 받지 않기 때문에 좋습니다.
Error Boundaries가 할 수 없는 일이 있습니다.
렌더링 중에 발생하지 않는 비동기 오류를 잡는 것입니다.
따라서 React Query에서 Error Boundaries가 작동하도록 하기 위해 라이브러리는 내부적으로 오류를 포착하고,
다음 렌더링 주기에서 에러 바운더리가 이를 캐치할 수 있도록 다시 던집니다.
나는 이것이 오류 처리에 대한 매우 천재적이며 간단한 접근 방식이라고 생각합니다.
이 작업을 수행하려면 useErrorBoundary 플래그를 쿼리에 전달하거나 기본 설정을 통해 제공하기만 하면 됩니다.
function TodoList() {
// ✅ will propagate all fetching errors to the nearest Error Boundary
// ✅ 모든 fetching 오류를 가장 가까운 에러바운더리로 전파합니다.
const todos = useQuery(['todos'], fetchTodos, { useErrorBoundary: true })
if (todos.data) {
return (
<div>
{todos.data.map((todo) => (
<Todo key={todo.id} {...todo} />
))}
</div>
)
}
return 'Loading...'
}
v3.23.0부터는 useErrorBoundary에 함수를 제공하여
- 어떤 오류는 Error Boundary로 보내고
- 어떤 오류는 로컬에서 처리할지
선택할 수 있습니다.
useQuery(['todos'], fetchTodos, {
// 🚀 only server errors will go to the Error Boundary
useErrorBoundary: (error) => error.response?.status >= 500,
})
이것은 mutations에도 적용되며 양식 제출을 수행할 때 매우 유용합니다.
4xx 범위의 오류는 로컬에서 처리할 수 있으며(예: 일부 백엔드 유효성 검사가 실패한 경우),
모든 5xx 서버 오류는 에러바운더리로 전파될 수 있습니다.
오류 알림 표시
import toast from 'react-hot-toast'
toast.error('Something went wrong')
The onError callback
const useTodos = () =>
useQuery(['todos'], fetchTodos, {
// ⚠️ 좋아보이지만 당신이 원하는 것이 아닐 수 있어요.
onError: (error) =>
toast.error(`Something went wrong: ${error.message}`),
})
CopyuseEffect-error-toast: copy code to clipboard
const useTodos = () => {
const todos = useQuery(['todos'], fetchTodos)
// 🚨 이펙트는 이 커스텀 훅을 사용하는
// 모든 컴포넌트에서 각각 실행됩니다.
React.useEffect(() => {
if (todos.error) {
toast.error(`Something went wrong: ${todos.error.message}`)
}
}, [todos.error])
return todos
}
글로벌 콜백
새 QueryClient를 만들 때 암시적으로 발생하는 QueryCache 필드에 전역 콜백을 제공합니다.
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error) =>
toast.error(`Something went wrong: ${error.message}`),
}),
})
- 이제 각 쿼리에 대해 한 번만 오류 토스트를 표시하며
- 해당 설정을 덮어쓸 수 없습니다.
요약
1. useQuery에서 반환된 error 프로퍼티
별로 추천하지 않습니다
2. onError 콜백(쿼리 자체 또는 전역 QueryCache / MutationCache에서)
주로 4xx 오류는 로컬 콜백으로 처리합니다(5xx 에러는 useErrorBoundary 사용).
오류를 컴포넌트 각각에서 처리하지 않고 한번에 처리하거나, 사용자에게 알릴 목적이면
QueryCache / MutationCache 의 필드를 사용합니다.
3. ErrorBoundary 사용
- 오류를 캐치하여, 렌더링을 발생시켜 다음 렌더링 주기에서 에러 바운더리를 렌더링 합니다.
- 대체 UI를 표시할 수 있을 때 사용합니다.
- 저는 500번대 에러처리에 GlobalErrorBoundary를 활용한 적이 있습니다.
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error, query) => {
// 🎉 이미 캐시에 데이터가 있는 경우에만 오류 알림을 표시합니다.
// 이는 백그라운드 업데이트가 실패했음을 의미합니다.
if (query.state.data !== undefined) {
toast.error(`Something went wrong: ${error.message}`)
}
},
}),
})
'FrontEnd' 카테고리의 다른 글
리액트 쿼리 : 뮤테이션 (0) | 2022.06.20 |
---|---|
리액트 쿼리 : 네트워크 오프라인 시 동작 (2) | 2022.06.20 |
리액트 쿼리 : 상태 관리 라이브러리 (0) | 2022.06.19 |
리액트 쿼리 : 초기값과 플레이스홀더 (0) | 2022.06.19 |
리액트 쿼리 : Query Function Context로 queryFn 타이핑 (0) | 2022.06.19 |