
Axios의 미친 기능인 interceptor에 로직을 추가하여 api요청 전/후처리를 해보자.
기존에 만들어진 axios Interceptors
import axios from "axios";
import * as Sentry from "@sentry/react-native";
import * as SecureStore from "expo-secure-store";
const getValueFor = async (key: string) => {
return await SecureStore.getItemAsync(key);
};
const instance = axios.create({
baseURL: "ㅇㅇ",
timeout: 1000,
});
instance.interceptors.request.use(
async (config) => {
config.headers["Content-Type"] = "application/json; charset=utf-8";
config.headers["Authorization"] = `Bearer ${await getValueFor(
"accessToken",
)}`;
return config;
},
(error) => {
console.error(error);
return Promise.reject(error);
},
);
instance.interceptors.response.use(
(response) => {
console.log("response", response);
return response;
},
async (error) => {
if (error.response.status === 400) {
alert(
"세션이 만료되었습니다. 해당 서비스는 재 로그인 이후 이용 가능합니다.",
);
return "session expire";
}
if (error.response.status === 500) {
Sentry.captureMessage("서버 에러");
alert("시스템 에러, 관리자에게 문의 바랍니다.");
}
console.error(error);
},
);
중간에서 request, response를 가로채 커스텀한다.
import axios from "axios";
import * as Sentry from "@sentry/react-native";
import * as SecureStore from "expo-secure-store";
const getValueFor = async (key: string) => {
return await SecureStore.getItemAsync(key);
};
const setRequestHeaders = async (config) => {
config.headers["Content-Type"] = "application/json; charset=utf-8";
config.headers["Authorization"] = `Bearer ${await getValueFor(
"accessToken",
)}`;
return config;
};
const handleResponseSuccess = (response) => {
console.log("Success response", response);
return response;
};
const handleRequestError = (error) => {
console.error("handleRequestError :", error);
return Promise.reject(error);
};
const handleResponseError = async (error) => {
console.log("handleResponseError :", error);
if (!error.response) return;
const { status } = error.response;
if (status === 400) {
alert(
"세션이 만료되었습니다. 해당 서비스는 재 로그인 이후 이용 가능합니다.",
);
return "session expire";
} else if (status === 500) {
Sentry.captureMessage("서버 에러");
alert("시스템 에러, 관리자에게 문의 바랍니다.");
}
console.error(error);
};
const instance = axios.create({
baseURL: "ㅇㅇ",
timeout: 1000,
});
instance.interceptors.request.use(setRequestHeaders, handleRequestError);
instance.interceptors.response.use(handleResponseSuccess, handleResponseError);
export default instance;
access token이 만료되었을 때, 서버에 refreshToken → response access받는 과정, secure에 업데이트하는 그 로직들을 한번에 처리해버리자.

import axios, { AxiosError, AxiosRequestConfig } from "axios";
import * as Sentry from "@sentry/react-native";
import * as SecureStore from "expo-secure-store";
import { BASE_URL, CONTENT_TYPE, TIMEOUT } from "../constants/constants";
import { logout } from "./logout";
const instance = axios.create({
baseURL: BASE_URL,
timeout: TIMEOUT,
});
const getValueFor = async (key: string) => {
return await SecureStore.getItemAsync(key);
};
const getAuthorizationHeader = async (tokenKey: string) => {
return `Bearer ${await getValueFor(tokenKey)}`;
};
const setCommonHeaders = async (config: any) => {
// default header 설정
config.headers["Content-Type"] = CONTENT_TYPE;
config.headers["Authorization"] = await getAuthorizationHeader("accessToken");
return config;
};
const refreshAccessTokenAndRetry = async (config: AxiosRequestConfig) => {
// accessToken 만료시 refreshToken으로 재발급
try {
const response = await axios.post(
`${BASE_URL}/user/token`,
{},
{
headers: {
"Content-Type": CONTENT_TYPE,
Authorization: await getAuthorizationHeader("refreshToken"),
},
},
);
if (response.status === 201) {
const newAccessToken = response.data.data.accessToken;
await SecureStore.setItemAsync("accessToken", newAccessToken);
if (!config.headers) {
config.headers = {};
}
config.headers["Authorization"] = `Bearer ${newAccessToken}`;
return axios(config);
}
console.error("refreshAccessTokenAndRetry error :", response);
return Promise.reject(response);
} catch (error: any) {
console.error(error.response.status);
if (error.response.status === 401) {
await logout();
SecureStore.setItemAsync("session_expire", 'expired');
alert("토큰 갱신에 실패했습니다. 다시 로그인 해주세요.");
return Promise.reject(error);
}
}
};
const handleResponseError = async (error: AxiosError) => {
if (!error.response) return Promise.reject(error);
const { status, config } = error.response;
console.log("status :", status);
switch (status) {
case 400:
alert(
"올바르지 않은 내용을 입력하셨습니다. 다시 확인해주세요.",
);
break;
case 401:
return await refreshAccessTokenAndRetry(config);
case 409:
alert("동일한 이름의 반려견이 이미 등록되어 있습니다.");
case 500:
Sentry.captureMessage("서버 에러");
alert("시스템 에러, 관리자에게 문의 바랍니다.");
break;
default:
console.error(error);
return Promise.reject(error);
}
};
const handleResponseSuccess = (response) => {
console.log("Success response");
return response;
};
const handleRequestError = (error: AxiosError) => {
console.error("handleRequestError :", error);
return Promise.reject(error);
};
instance.interceptors.request.use(setCommonHeaders, handleRequestError);
instance.interceptors.response.use(handleResponseSuccess, handleResponseError);
export default instance;
api 요청 전후처리를 진행해버리면, 실제 api 요청 시에 굉장히 간결한 코드작성이 가능하다.