웹 개발을 할 때 사용자 인증을 위해 서버로부터 token을 발급받는 방식을 많이 사용하게 됩니다.
사용자가 로그인한 상태에서 무언가 요청을 보낼 때, 사용자 권한이 필요한 요청이라면 token을 header에 실어 api 호출을 하게 되죠.
이 때, 유효하지 않은 token을 보내거나, 서버에서 설정한 token 만료 시간이 지났을 때 에러가 납니다.
그 중, 필연적(?)으로 맞딱뜨릴 수 밖에 없는 에러가 ‘토큰 인증 만료’입니다.
보통 토큰을 사용하여 사용자 인증을 처리할 때는 서버에서 Access Token과 Refresh Token이 두 가지를 발급합니다.
보안의 목적을 위해 Access Token의 만료 기간은 짧게 설정하여 자주 재발급 받아야 하지만, 그 때마다 사용자를 로그아웃 시키고 사용자가 다시 아이디나 비밀번호를 입력하여 로그인을 하게 된다면 여간 불편한게 아닙니다.
그래서 클라이언트는 비교적 만료 기간이 긴 Refresh Token을 함께 받게 되는데, 이를 이용해서 Access Token을 갱신 받을 수 있습니다.
즉, Access Token의 만료 기간이 30분이고 Refresh Token 만료 기간이 7일이라면, 7일동안 만료된 Access Token을 새로운 Access Token으로 갱신받는 것이죠.
이 때, Access Token이 만료된 경우 서버가 에러 코드를 내려주고 프론트에서는 Refresh Token을 서버에 보내 새로운 Access Token을 요청합니다. 그리고 이를 구현하기 위해 몇가지 고려해야할 점이 있습니다.
1. 사용자가 요청한 작업을 수행하기 위해 header에 Access Token을 실어 API 요청을 보낼 때 토큰 만료라면, 작업이 끊기지 않게 토큰 갱신후 사용자가 요청한 작업이 다시 수행되게 할 것
2. Refresh Token도 만료되었거나 유효하지 않다면 그에 따른 처리(로그아웃 등)를 할 것
3. 토큰을 담아 보내는 API가 호출되는 모든 곳에서 각각 처리하지 않고 한곳에서 처리할 것
이를 고려하여 토큰 재발급을 구현하기 위해 좋은 것이 바로 axios interceptor를 사용하는 것입니다. 😁
이번 글에서는 axios interceptor에 대한 간단한 설명과, 이를 사용하여 어떻게 Access token 재발급 기능을 구현했는지 공유하고자 합니다.
1. axios Interceptor란?
axios interceptor는 API 요청(request)이나 응답(response)이 완료되기 전에, 이를 가로채 어떠한 작업을 수행할 수 있게 하는 axios가 제공하는 기능입니다. 아래와 같이 작성합니다.
//요청(request)시의 interceptor
axios.interceptors.request.use(function (config) {
// 요청을 보내기 전 어떠한 작업을 하고 싶다면 여기에 작성합니다.
return config;
}, function (error) {
// 요청 에러 시의 어떠한 처리를 해야한다면 여기에 작성합니다.
return Promise.reject(error);
});
//응답(response)시의 interceptor
axios.interceptors.response.use(function (response) {
// 2xx으로 시작하는 statusCode를 응답받게 되면 이곳의 코드가 트리거 됩니다.
// 응답받은 데이터에 대해 어떠한 작업을 하고 싶다면 여기에 작성합니다.
return response;
}, function (error) {
// 2xx이 아닌 statusCode를 응답받게 되면 이곳의 코드가 트리거 됩니다.
// 응답받은 에러에 대한 처리를 하고 싶다면 여기에 작성합니다.
return Promise.reject(error);
});
위와같이 바로 axios에 interceptor를 적용할 수 있지만 제가 이전 글에서 설명한 axios instance를 만들어서 해당 axios instance에만 적용되도록 할 수도 있습니다.
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
axios instance에 관한 설명은 이전 게시글을 참고해주세요.
api요청 시, header에 token을 담아 보냈을 때, token오류(인증기간 만료)로 Error를 응답받았다면 interceptor에서 처리할 수 있습니다.
2. Access Token 만료 시 구현할 로직
작성하기 전, Access Token이 만료인 경우 어떻게 처리할지 다시 정리해보겠습니다.
시퀀스 플로우는 다음과 같습니다.
access Token과 refresh Token을 로컬스토리지에 저장한다는 것을 전제로 작성하였습니다.
이제 위의 플로우를 axios interceptor에서 구현해보겠습니다.
3. interceptor에서 Error 처리
우선, Interceptor를 작성할 js또는 ts파일을 생성합니다. 저는 타입스크립트를 사용하였습니다.
api 요청 시 request interceptor는 생략하고 응답받은 경우의 사용되는 response interceptor만 살펴보겠습니다.
우선, token 인증에 실패하면 보통 Http status code는 401(Unauthorized)를 받게 됩니다. 401 statusCode 중에서 '토큰 만료'인 경우 우리 서버는 exceptionCode : 1004 를 응답으로 준다고 가정합시다.
그럼 interceptor response에서 다음과 같이 작성할 수 있습니다.
저는 LocalStorage 유틸 함수(localStorageUtil.ts)를 따로 만들어서 썼습니다.
axiosInstance.interceptors.response.use(
(response: AxiosResponse) => {
//응답 성공 시 서버에서 주는 data를 반환합니다.
return response.data;
},
async (error: AxiosError<BaseErrorResponse>) => {
if (error.response.status === 401) {
if (error.response.data.exceptionCode === 1004) {
try {
const newToken = await renewToken();
localStorageUtil().setToken(newToken.token);
error.config.headers.Authorization = `Bearer ${newToken.token}`;
return axiosInstance(error.config);
} catch (e) {
localStorageUtil().deleteWholeData();
}
}
return Promise.reject(error.response.data);
}
);
우선 http status가 401일때의 조건을 먼저 걸어서 그 중에 exceptionCode가 1004(만료)인 경우를 if문안에서 처리하였습니다.
renewToken()을 호출하여 refresh Token을 담아 서버에 새 토큰 발급을 요청합니다.
이 요청이 성공하면 localStorage에 token을 저장해줍니다. 그리고 1004 error 응답이 왔던 api 호출의 config에서 토큰을 새로 받은 토큰으로 다시 set 해줍니다.
그리고 Return axiosInstance를 하면, 기존에 맨 처음 호출해던 api를 재시도하게 됩니다.
renewToken()이 실패한 경우 localStorageUtil().deleteWholeData();를 호출하여 기존에 로컬스토리지에 저장되었던 유저의 토큰 값을 삭제합니다.
추가로 로그아웃 구현이 필요한 경우 이어서 작성해주시면 되겠습니다.
이렇게 axios interceptor로 token 관련 에러처리를 간단하게 구현할 수 있습니다.
token외에도 axios가 제공하는 여러가지 옵션이나 기능을 사용하여, api 호출 시 개발 기능에 따른 여러가지 설정이 가능합니다.
저는 가장 기본적이고 간단한 예제를 보여드렸지만, axios 공식문서를 살펴보시면 더 디테일하고 간편하게 api 호출관련 기능을 다룰 수 있습니다. 아래에 axios 공식문서 사이트를 첨부하였으니 한번쯤은 꼭 읽어보시는걸 추천합니다!
'땀내 품긴 개발 노-트 > Web | API' 카테고리의 다른 글
[API 비동기 통신! 이렇게 해보자] 01 - Axios instance 만들기 (1) | 2023.05.12 |
---|