티스토리 뷰
메인 프로젝트를 진행하던 중 멘토 분께서 조언을 몇 가지 해주셨다. 여러 조언이 있었지만 오늘은 비동기 요청의 오류를 한 곳에서 관리하는 방법을 학습해 볼 것이다.
axios.interceptors
axios의 내장 메서드인 interceptors는 axios의 중앙 제어를 가능하게 한다. 이 메서드는 최상위 경로에서 주로 선언되며, 하위 경로에서 발생하는 axios 요청의 응답을 모두 가로챈다. 그리고 응답에 따라서 반환값을 달리 할 수 있다. 이를 통해, 오류 발생시, axios 요청부분에서 catch문으로 처리 하지않고 interceptors에서 처리할 수 있다.
대략적인 흐름은 다음과 같다.
- 먼저 axios.create로 axios의 인스턴스를 하나 만든다.
- 그 후 응답에 대한 interceptors를 설정하고, 요청 발생시 응답을 가로챈다.
- 응답이 성공한 경우에는 그대로 반환한다.
- 오류가 발생한 경우에는 Promise.reject(error) 반환한다.
- 이후, 원래 요청부분으로 돌아가 성공한 경우 정상적으로 실행되고, 오류가 발생했다면 종료된다.
코드로는 다음과 같이 만들 수 있다.
import axios from 'axios';
const setupAxiosInterceptors = (client) => {
const axiosInstance = axios.create();
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
// 서버에서 에러 응답을 받은 경우
client.setError(error.config.url, error.response.data);
} else if (error.request) {
// 요청을 보낸 후 응답을 받지 못한 경우
client.setError(error.config.url, 'Network Error');
} else {
// 요청을 보내기 전에 발생한 에러
client.setError(error.config.url, error.message);
}
return Promise.reject(error);
}
);
};
export default setupAxiosInterceptors;
react-query
코드를 만들었지만 제대로 활용하기 위해서는 setupAxiosInterceptors를 호출시 react-query의 QueryClient, QueryClientProvide를 사용해야한다.
react-query는 react에서 비동기 통신을 원할하게 하기 위한 기능을 제공하는 라이브러리로, 다음시간에 집중적으로 다루어 볼 것이다. 오늘 사용할 메서드는 QueryClient, QueryClientProvide으로 각각에 대해서 설명은 다음과 같다.
QueryClientProvider는 React Query의 컨텍스트를 제공하는 컴포넌트로, React Query의 모든 기능을 사용하기 위해서는 QueryClientProvider 컴포넌트를 App의 최상단에 위치시켜야 한다. 한마디로 redux에 provider과 같다.
QueryClient는 React Query에서 데이터 상태를 관리하고 서버와의 상호작용을 담당한다. 또 캐시 데이터의 저장, 쿼리 실행, 뮤테이션 실행 등을 관리하며, 캐시된 데이터의 유효성을 유지하고 업데이트하는 역할을 한다. 이를 통해 애플리케이션에서 데이터의 효율적인 관리와 최신 상태의 유지를 도와준다.
추가로 ReactQueryDevtools를 사용할 수 있는데, ReactQueryDevtools는 React Query의 개발 도구이다. 개발자 도구를 통해 React Query의 캐시, 쿼리 상태, 뮤테이션 등의 정보를 시각적으로 확인할 수 있어, 성능 모니터링과 디버깅에 유용합니다. 일반적으로 개발 중에만 사용하며, 프로덕션 환경에서는 비활성화하는 것이 좋다.
사실 React Query 데이터 상태 관리와 서버와의 상호작용을 간편하게 처리할 수 있도록 도와주는 역할이기에 없어도 setupAxiosInterceptors을 실행할 수 있지만 성능적 부분에서 비효율성이 발생하기에 axios에서 자주 같이 쓰인다. 자세한 이야기는 다음시간에 서술한다.
코드로는 다음과 같다
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import setupAxiosInterceptors from './axiosInterceptors';
const queryClient = new QueryClient();
function App() {
setupAxiosInterceptors(queryClient);
return (
<QueryClientProvider client={queryClient}>
{/*다른 컴포넌트들*/}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
export default App;
P.S 실제 사용 후기
글을 처음 작성할 때는 아직 css작업부터 하느라 실제 구현은 한참 뒤에 하였다. 단순히 메모장에서 짠 코드라 제대로 작동이 안되었기에 몇가지 수정을 가하였다.
우선 에러 핸들러 쪽에서 client.setError라는 것이 존재 하지 않았고, 추가적으로 react-query에서 위에 것보다 더 쉽게 구현할 수 있어서 변경하였다.
import { QueryClient } from 'react-query';
import apiErrorHandler from '../utill/apiErrorHandler.ts';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
keepPreviousData: true,
refetchOnWindowFocus: false,
onError: apiErrorHandler,
cacheTime: 500,
},
mutations: {
onError: apiErrorHandler,
},
},
});
이 코드는 모든 react-query 메서드의 기본적인 설정을 구현하고 있다. 여기서 onError에 미리 만든 에러 핸들러를 넣으면 간단히 전역 에러 관리가 가능해졌다.
추가로 에러 핸들러 axios로 만드는 게 아닌, 일반 함수로 구현할 수 있어고, 멘토분의 조언으로 상태코드별로 다른 내용이 출력되도록 만들었다.
import { AxiosError } from 'axios';
const apiErrorHandler = (error: unknown): void => {
if (error instanceof Error) {
const axiosError = error as AxiosError;
if (axiosError.response) {
const { status, data } = axiosError.response;
if (status === 400) {
console.error('상태 코드 400: 잘못된 요청 오류');
console.error('응답 데이터:', data);
// 상태 코드 400에 대한 처리 로직
} else if (status === 401) {
console.error('상태 코드 401: 인증 오류');
console.error('응답 데이터:', data);
// 상태 코드 401에 대한 처리 로직
} else if (status === 403) {
console.error('상태 코드 403: 접근 금지 오류');
console.error('응답 데이터:', data);
// 상태 코드 403에 대한 처리 로직
} else if (status === 404) {
console.error('상태 코드 404: 페이지 찾을 수 없음 오류');
console.error('응답 데이터:', data);
// 상태 코드 404에 대한 처리 로직
} else {
console.error(`HTTP 오류 - 상태 코드 ${status}`);
console.error('응답 데이터:', data);
// 기타 상태 코드에 대한 처리 로직
}
} else if (axiosError.request) {
console.error('응답 없음 - 상태 코드 401');
// 상태 코드 401에 대한 처리 로직
} else {
console.error('요청 설정 오류 - 상태 코드 400');
// 상태 코드 400에 대한 처리 로직
}
} else {
console.error('알 수 없는 오류:', error);
// 기타 오류에 대한 처리 로직
}
};
export default apiErrorHandler;
실제로 개발하면서 수 많은 에러가 나왔는데, 이때, 어떤 에러인지를 한눈에 알기 쉬웠다. 개발단계에서 주로 사용해서 콘솔창에 출력하게 하였지만, 이용자 측면에서 생각했을 땐 에러 페이지를 따로 만드는 것이 좋을 것 같다.
'프로젝트' 카테고리의 다른 글
메인 프로젝트: useEffect (1) | 2023.07.31 |
---|---|
메인 프로젝트: recoil (1) | 2023.07.28 |
메인 프로젝트: react-query (0) | 2023.07.26 |