[에러 핸들링] 에러 처리 공통 로직 분리
❌ Query Cache를 사용할 수 없는 이유
처음에는 공통되는 에러 핸들링 로직을 QueryCache에서 처리하려고 했다.
먼저 에러 처리 로직을 보면 네트워크 에러일 때는 Toast, API 에러일 때는 Modal을 사용한다.
onError: (error) => {
if (error instanceof NetworkError) {
show(error.message);
return;
}
showModal(AlertModal, { title: '에러', message: error.message });
},
따라서 Context API로 내려주고 있는 Toast와 Modal을 QueryCache에서 사용하려면, ToastProvider와 ModalProvider가 QueryClientProvider보다 부모에 위치해야 한다.
QueryCache는 QueryClient를 선언할 때만 같이 선언할 수 있기 때문이다.
하지만 방 설정 모달에서 invalidateQueries
함수를 통해 QueryClient를 사용하기 때문에, QueryClientProvider도 Modal보다 부모에 위치해야 하는 모순이 발생한다.
Modal 내에서 query 관련 로직을 다루면 에러 처리 로직을 분리할 수 없는건가?
🔍 해결 방법
머릿속을 정리하기 위해 현재 Provider 구조를 그려보니 해결책을 찾을 수 있었다.
현재 구조 : Toast ➡️ Modal ➡️ QueryClient ➡️ Modal
하지만 이 중에서도 QueryClient ➡️ Modal 관계
는 순서가 바뀌면 안된다. 하나의 QueryClient를 공유해야 의도한대로 동작할 것이기 때문이다.
그렇다면 ModalProvider 하위에서 queryClient의 설정을 바꿀 수 있는 방법은 없을까?
QueryCache를 사용할 순 없었지만 다행히도 queryClient.setDefaultOptions
를 이용해 기본 동작을 덮어쓰는 기능이 있었다.
이를 통해 ModalProvider 하위에서 queryClient의 기본 동작에 에러 핸들링 로직을 추가하여 공통 에러 처리 로직을 분리할 수 있었다.
추가로 테스트 코드에서도 에러 UI 테스트가 존재하기 때문에 해당 컴포넌트를 사용하였다.
그런데 에러 폴백 테스트 코드가 계속 통과를 안해서 힘들었는데, 알고보니 테스트 코드에서 설정해둔 retry:false
옵션이 덮어씌워져서 문제가 생긴 것이였다. 이를 테스트 환경에서만 해당 옵션이 동작하도록 분기처리하였다.
// QueryClient는 모든 Provider에 공유되면서 공통 에러 핸들링 로직에 Toast와 Modal을 넣기 위해 setDefaultOptions 사용
// 테스트 환경에서 retry 값이 있을 경우 에러 폴백 테스트가 돌지 않아 분기 처리
const QueryClientDefaultOptionProvider = ({ children }: PropsWithChildren) => {
const queryClient = useQueryClient();
const { show } = useToast();
const { show: showModal } = useModal();
queryClient.setDefaultOptions({
queries: {
retry: process.env.NODE_ENV === "test" ? false : 3,
throwOnError: true,
},
mutations: {
onError: (error) => {
if (error instanceof NetworkError) {
show(error.message);
return;
}
showModal(AlertModal, { title: "에러", message: error.message });
},
throwOnError: (err) => {
const error = err as CustomError;
return isServerError(error.status);
},
networkMode: "always",
},
});
return <>{children}</>;
};
✅ 결론 (해결)
뿌듯해지는 코멘트 ㅎㅎㅎ
댓글남기기