에러 처리에 대한 의문점들이 생겼습니다. 제가 구현하려고 했던 것은 만약 서버에서 400 에러 혹은 403 에러가
나타났을 때 거기에 대응하는 에러 페이지를 보여주는 것인데, 현재 리액트 쿼리를 사용하고 있는 가운데,
어떻게 전역적으로 에러처리를 할 수 있을까? 라는 생각으로 시작하였습니다.
const AdminDashboard = () => {
const [currentPage, setCurrentPage] = useState(0);
const accessToken = localStorage.getItem("admin_accessToken");
const { data, isLoading, statusCode } = useUserData({
page: currentPage,
token: accessToken as string,
});
if (isLoading) return <div>loading중..</div>;
if (statusCode === 403) return <GuestPage />;
if (statusCode === 400) return <ExpireTokenPage />;
return ( //레이아웃 생략 );
}
저는 리액트 쿼리와 axios를 사용해서 서버에 요청하고 거기에 대한 상태를 관리하고 있습니다.
제가 느낀 문제는 무엇이냐면 바로 다른 코드를 봐볼까요?
const TimeLineComponent = () => {
const param = useParams();
const inspectionId = Number(param.inspectionId);
const accessToken = localStorage.getItem("accessToken") as string;
const { data, isLoading, progress, statusCode } = useGetTimelineData({
token: accessToken,
inspectionId,
});
const navigate = useNavigate();
if (isLoading) return <LoadingPage progress={progress} />;
if (statusCode === 403) return <GuestPage />;
if (statusCode === 400) return <ExpireTokenPage />;
일단 제가 인식한 문제는 세 가지 입니다.
저는 1,2 번은 일단 차치하고 오늘은 3번 문제에 집중을 한 번 해보겠습니다.
사용되고 있는 react - hook은 이러합니다.
import { getAdminUserData } from "@/api/admin/api";
import { useQuery } from "@tanstack/react-query";
import { AxiosError } from "axios";
const useUserData = ({ page, token }: { page: number; token: string }) => {
const { data, error, isLoading } = useQuery({
queryKey: ["adminUser"],
queryFn: () => getAdminUserData({ page, token }),
retry: 1,
});
let statusCode;
if (error instanceof AxiosError) {
statusCode = error.status;
}
return { data, statusCode, isLoading };
};
export default useUserData;
useQuery와 axios를 통해서 요청된 값을 관리하고, status code를 return 하는 함수입니다.
아주 간단합니다. status code를 통해서 분기를하고 해당하는 컴포넌트들을 보여주고자 했습니다.
결론적으로는 제게 필요한 것은 지금 에러처리 하는 방법에서 중복적인 코드를 발생시키기 때문에
전역적으로 에러를 처리할 수 있는 방법입니다.
ai와 동료들의 조언을 구했고, 거기에 따른 방법을 2가지로 정리해봤습니다.
둘 다 괜찮은 방법처럼 보입니다.
하지만 저에게 주어진 상황에서는 QueryCache를 사용하는게 조금 더 나아보입니다. 그 이유는
그러므로 queryCache를 사용해서 전역적으로 세팅을 한 번 해보겠습니다.
전역적으로 사용하기 위해서는 일단 Provider 단에서 설정을 해줘야겠다고 생각했습니다.
다음은 제 QueryProvider의 코드입니다.
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { PropsWithChildren } from "react";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000,
},
mutations: {
retry: 1,
},
},
});
function QueryProvider({ children }: PropsWithChildren) {
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
}
export default QueryProvider;
여기에다 추가적으로 query Cache를 이용해서 작성해보겠습니다.
// src/providers/QueryProvider.tsx
import ExpireTokenPage from "@/components/common/error/ExpireTokenPage";
import GuestPage from "@/components/common/error/GuestPage";
import {
QueryCache,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { AxiosError } from "axios";
import type { PropsWithChildren } from "react";
const handleError = (error: unknown) => {
if (error instanceof AxiosError) {
const status = error.response?.status;
switch (status) {
case 400:
return <ExpireTokenPage />;
break;
case 403:
return <GuestPage />;
}
}
};
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: handleError,
}),
defaultOptions: {
queries: {
retry: 1,
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000,
},
mutations: {
retry: 1,
},
},
});
function QueryProvider({ children }: PropsWithChildren) {
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
}
export default QueryProvider;
이런식으로해서 전역적인 에러처리를 컴포넌트를 리턴하는 것으로 수정하였습니다!
여기서 사용되는 에러처리는 모든 patch나 axios 요청에 해당되는 것이 아니라
react-query를 통한 요청만 처리한다는 사실?!
직접적인 요청은 axios interceptor에서 해야겠죠?!
하지만 제 프로젝트에는 직접 요청을 보내는 것은 없기 때문에 지금 일관성있게 처리할 수 있었던 것 같습니다~
감사합니다~!!
//변경전
const handleError = (error: unknown) => {
if (error instanceof AxiosError) {
const status = error.response?.status;
switch (status) {
case 400:
return <ExpireTokenPage />;
break;
case 403:
return <GuestPage />;
}
}
};
handleError에서 Jsx문을 리턴하고 있기 때문에 에러처리가 안됐습니다.
그래서 redirect 해주기로 했습니다!
onError 핸들러는 컴포넌를 렌더링하지 않고 , 단순히 에러처리 로직만 수행해야하며ㅡ
현재 코드에서 에러를 전역적으로 처리하지만 에러 상태를 화면에 표시하는 로직이 없기 때문에 아래와 같이 수정했습니다!
//변경 후
const handleError = (error: unknown) => {
if (error instanceof AxiosError) {
const status = error.response?.status;
switch (status) {
case 400:
window.location.href = "/error/token-expired";
break;
case 403:
window.location.href = "/error/guest";
break;
}
}
};
[tanstack query 공식문서] https://tanstack.com/query/latest/docs/reference/QueryCache#querycache