안녕하세요. 대중적으로 데이터를 요청해서 받아올때 axios를 많이 사용합니다.
해당 라이브러리를 커스텀해서 사용할 수 있게 문서화가 되어있는데요.
기본적인 api설정할 수 있고 토큰 만료시 api retry하는 커스텀 axios를 만들어 활용하는 코드를 소개하겠습니다.
import axios from 'axios';
import { getAccessToken, haveAccessToken, setAccessToken } from '../service/TokenService';
const myAxios = axios.create({
withCredentials: true,
baseURL: process.env.NEXT_PUBLIC_LOCAL_API_HOST + '/api/v1',
timeout: 90000,
});
if (haveAccessToken()) {
myAxios.defaults.headers.common['Authorization'] = 'Bearer ' + getAccessToken();
}
myAxios.interceptors.request.use(
function (config) {
return Promise.resolve(config);
},
error => Promise.reject(error),
);
myAxios.interceptors.response.use(
// 응답 데이터를 가공하여 내보낸다
response => {
return response;
},
// 응답 데이터 에러 발생시
function (error) {
const originalRequest = error.config;
if (error.response) {
if (error.response.status === 401 && !originalRequest._retry) {
return axios
.get('/get_token?jwt_refresh=1')
.then(res => {
if (res.data && res.data.data.jwt_access) {
const access_token = res.data.data.jwt_access;
console.log('[axios]success new token receive : ' + access_token);
setAccessToken(access_token);
originalRequest.headers['Authorization'] = 'Bearer ' + access_token;
return myAxios(originalRequest);
} else {
return Promise.reject('재발급에러');
}
})
.catch(err => {
console.log('토큰 재발급 중 에러가 발생했습니다.');
console.log(err);
return Promise.reject(error);
});
} else {
return Promise.reject(error);
}
}
return Promise.reject(error);
},
);
export default myAxios;
import axios from 'axios';
import { getAccessToken, haveAccessToken, setAccessToken } from '../service/TokenService';
const myAxios = axios.create({
withCredentials: true,
baseURL: process.env.NEXT_PUBLIC_LOCAL_API_HOST + '/api/v1',
timeout: 90000,
});
이부분은 기본적으로 axios를 사용할 때 기본 설정을 해주고 myAxios 변수 선언 해줍니다. 간단히 쿠키, baseURL를 설정했네요!
if (haveAccessToken()) {
myAxios.defaults.headers.common['Authorization'] = 'Bearer ' + getAccessToken();
}
토큰이 존재한다면 위에서 선언한 myAxios 헤더 설정을 하는 코드 입니다.
myAxios.interceptors.request.use(
function (config) {
return Promise.resolve(config);
},
error => Promise.reject(error),
);
요청 받은 api의 config정보 입니다. 해당 정보를 성공적으로 넘겨주어야 다음 로직에서 정상적으로 해당 config를 사용하여 조건의 맞게 api처리를 할 수 있습니다.
return을 하기 전에 특정 api건의 대하여 조건을 걸고 config설정도 할 수 있겠네요!
myAxios.interceptors.response.use(
// 응답 데이터를 가공하여 내보낸다
response => {
return response;
},
// 응답 데이터 에러 발생시
function (error) {
const originalRequest = error.config;
if (error.response) {
if (error.response.status === 401 && !originalRequest._retry) {
return axios
.get('/get_token?jwt_refresh=1')
.then(res => {
if (res.data && res.data.data.jwt_access) {
const access_token = res.data.data.jwt_access;
console.log('[axios]success new token receive : ' + access_token);
setAccessToken(access_token);
originalRequest.headers['Authorization'] = 'Bearer ' + access_token;
return myAxios(originalRequest);
} else {
return Promise.reject('재발급에러');
}
})
.catch(err => {
console.log('토큰 재발급 중 에러가 발생했습니다.');
console.log(err);
return Promise.reject(error);
});
} else {
return Promise.reject(error);
}
}
return Promise.reject(error);
},
);
최종적으로 내보내는 로직이다. 오류없이 요청을 잘 받았다면 바로 response를 내보낸다.
하지만 에러가 발생 시 error 함수가 실행된다.
에러 함수에는 토큰이 만료되어 api 서버에서 401에러를 받았다면 토큰을 다시 받는 api를 요청해
새로운 토큰을 발급 받고 해당 토큰으로 api를 재요청해 데이터를 통신하는 로직이다.
이런식으로 retry하는 로직을 설정하지 않는다면 고객이 웹/앱 사용 시 토큰이 만료되는 시점에 데이터를 받지 못하게 된다.
export const fetcher = async <T>(url: string, config?: AxiosRequestConfig): Promise<T | null> => {
try {
const { data, status } = await myAxios.get<T>(url, config);
return data;
} catch (error) {
sendErrorToSynology({
message: `${synologyErrorBaseMsg(url)}GET\n${error}\nURL: ${url}\ntoken: ${getAccessToken()}`,
});
}
};
사용방법은 진짜 간단하다. 기본axios를 사용하듯이 myAxios를 import하여 사용하면 된다.
저희 회사의 경우 php -> react & node 전환 작업중 입니다.
php에서 받아야하는 api가 존재하기 때문에 php api 전용 커스텀 axios하나 더 만들어 사용중 입니다.
PhpAxios.ts의 코드는 MyAxios와 크게 다르지 않고 기본 설정 정도만 바뀌는 정도네요!