반응형
해당 글에서는 Axios의 Interceptor 기능에 대해 알아봅니다.
1) Axios
💡 Axios
- Node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트로 비동기 요청을 쉽게 처리할 수 있게 해 줍니다.
- 이는 비동기 처리를 통해 네트워크 요청이 완료될 때까지 다른 코드 실행을 차단하지 않고, 효율적인 리소스 사용이 가능합니다.
- then()과 catch() 메서드를 사용하거나 async/await 구문과 함께 사용하여 비동기 코드를 더 읽기 쉽고 관리하기 쉽게 작성할 수 있습니다.
💡 간단 예시
1. axios.get()
- axios만을 사용하여서 API 통신을 수행할 때 '비동기 통신'을 통해서 수행이 됩니다.
2. async/await를 사용한 비동기 처리
- 함수 내의 async를 선언하고 axios 내에 await를 사용하여 비동기 처리가 수행이 됩니다.
// 비동기 처리 예시
axios.get('api/v1/user/user')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('Error:', error);
});
// async/await를 사용한 비동기 처리
async function fetchData() {
try {
const response = await axios.get('api/v1/user/user');
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
}
1. Axios 주요 기능
💡 Axios 주요 기능
기능 | 설명 |
XMLHttpRequests 생성 기능 | 브라우저에서 서버로 데이터를 보낼 때 사용되는 객체를 생성하는 기능을 제공합니다 |
http 요청 생성 기능 | node.js에서 서버로 데이터를 보낼 때 사용되는 요청 객체를 생성하는 기능을 제공합니다 |
Promise API 기능 | 비동기 작업을 처리할 때 사용되는 Promise 객체를 지원하는 기능을 제공합니다 |
요청 및 응답 인터셉트 기능 | 요청이나 응답이 발생할 때, 그에 대한 처리를 중간에서 가로채서 추가적인 작업을 할 수 있는 기능을 제공합니다 |
요청 및 응답 데이터 변환 기능 | 요청이나 응답 데이터를 다른 형식으로 변환할 수 있는 기능을 제공합니다 |
요청 취소 기능 | 요청을 취소하는 기능을 제공합니다 |
JSON 데이터 자동 변환 기능 | JSON 형식으로 요청이나 응답 데이터를 자동으로 변환하는 기능을 제공합니다 |
XSRF를 막기 위한 클라이언트 사이드 지원 기능 | 클라이언트 측에서 XSRF(Cross-Site Request Forgery) 공격을 막기 위한 처리를 지원하는 기능을 제공합니다 |
2) Axios Interceptor
💡 Axios Interceptor
- HTTP 요청이나 응답을 가로채서 처리할 수 있는 강력한 기능입니다.
- 이를 통해 요청이 서버로 전송되기 전의 요청이나 응답으로 then/catch 핸들러로 전달되기 전에 코드를 실행할 수 있습니다.
1. Axios Interceptor 주요 특징
특징 | 설명 |
요청 전 처리 | 서버로 요청이 전송되기 전에 코드를 실행할 수 있습니다. |
응답 후 처리 | 서버로부터 응답을 받은 후, then/catch 핸들러로 전달되기 전에 코드를 실행할 수 있습니다. |
전역적 설정 | 모든 요청이나 응답에 대해 일괄적으로 처리할 수 있습니다. |
에러 처리 | 요청이나 응답 과정에서 발생하는 에러를 중앙에서 처리할 수 있습니다. |
헤더 수정 | 요청이나 응답의 헤더를 동적으로 수정할 수 있습니다. |
로깅 | 모든 요청과 응답을 로깅하여 디버깅에 활용할 수 있습니다. |
인증 처리 | 토큰 갱신이나 인증 관련 로직을 중앙에서 처리할 수 있습니다. |
3) Axois Interceptor 사용예시
1. 요청 인터셉터 : axios.interceptors.request
💡 요청 인터셉터 : axios.interceptors.request
- 클라이언트에서 API Server로 ‘요청이 전송되기 이전’에 실행될 함수나 ‘요청 과정에서 발생한 오류’에 대해 인터셉트를 하는 것을 의미합니다.
💡axios.interceptors.request.use() 메서드의 파라미터
1. onFulfiled : 요청이 전송되기 전에 실행될 함수를 정의합니다. 이 함수는 요청 설정(config) 객체를 인자로 받고, 수정된 설정 객체를 반환해야 합니다.
2. onRejected : 요청 과정에서 오류가 발생했을 때 실행될 함수입니다.
3. options : AxiosInterceptorOptions 타입의 추가 옵션을 의미합니다.
AxiosInterceptorManager<InternalAxiosRequestConfig<any>>
.use(
onFulfilled?: ((value: InternalAxiosRequestConfig<any>) => InternalAxiosRequestConfig<any> | Promise<InternalAxiosRequestConfig<any>>) | null | undefined,
onRejected?: ((error: any) => any) | null,
options?: AxiosInterceptorOptions
): number
💡axios.interceptors.request.use() 메서드 사용예시
- API Server로 요청을 수행하기 전에 요청에 대한 인터셉터 기능을 수행합니다.
- 첫 번째 파라미터의 함수의 경우는 API Server로 요청을 전달하기 이전에 수행이 됩니다.
- 두 번째 파라미터의 함수의 경우는 API Server로 요청을 보내는 중에 발생하는 오류가 발생할 때 수행이 됩니다.
import axios from 'axios';
/**
* 요청 이전 처리 방법 : 요청 인터셉터
*/
axios.interceptors.request.use(
// 요청이 전달되기 전에 작업 수행
(config) => {
console.log("[+] 요청 전달 이전에 수행이 됩니다. ", config)
return config;
},
// 요청 오류가 있는 작업 수행
(error) => {
console.log("[-] 요청 중 오류가 발생되었을때 수행이 됩니다. ", error)
return Promise.reject(error);
}
);
💡사용 결과
- 로그인 과정에서 요청 시도 시 아래와 같이 인터셉터가 됩니다.
속성 | 설명 |
adapter | 사용 가능한 HTTP 요청 방식 |
data | 로그인 요청에 포함된 사용자 데이터 |
env | 요청 환경 관련 객체 |
headers | 요청 헤더 정보 |
maxBodyLength | 요청 본문의 최대 길이 (-1은 제한 없음을 의미) |
maxContentLength | 응답 콘텐츠의 최대 길이 (-1은 제한 없음을 의미) |
method | HTTP 요청 메소드 |
timeout | 요청 타임아웃 시간 (0은 타임아웃 없음을 의미) |
url | 로그인 요청을 보내는 API 엔드포인트 |
2. 응답 인터셉터 : axios.interceptors.response
💡 응답 인터셉터 : axios.interceptors.response
- API Server로부터 응답을 받은 후, then/catch 핸들러로 결과가 전달되기 이전에 인터셉트를 하는 것을 의미합니다.
💡 axios.interceptors.response.use() 메서드의 파라미터
1. onFulfilled (성공 콜백)
- 응답이 성공적으로 수신되었을 때 실행되는 함수입니다. 이 함수는 응답 객체를 인자로 받으며, 필요에 따라 응답을 수정하거나 추가 작업을 수행할 수 있습니다. 수정된 응답 객체를 반환하거나 새로운 응답을 생성할 수 있습니다.
2. onRejected (실패 콜백)
- 응답 수신 중 오류가 발생했을 때 실행되는 함수입니다. 이 함수는 오류 객체를 인자로 받으며, 오류를 처리하거나 추가적인 에러 핸들링 로직을 구현할 수 있습니다. Promise.reject(error)를 반환하여 오류를 다시 던지거나, 새로운 응답을 생성하여 오류를 복구할 수 있습니다.
3. options
- AxiosInterceptorOptions 타입의 추가 옵션을 의미합니다.
AxiosInterceptorManager<AxiosResponse<any, any>>
.use(
onFulfilled?: ((value: AxiosResponse<any, any>) => AxiosResponse<any, any> | Promise<AxiosResponse<any, any>>) | null | undefined,
onRejected?: ((error: any) => any) | null,
options?: AxiosInterceptorOptions
): number
💡 axios.interceptors.response.use() 메서드 사용예시
- API Server로부터 응답이 들어오는 경우 try/catch가 수행되기 이전에 수행이 되며 이에 대한 인터셉터 기능을 수행합니다.
- 첫 번째 파라미터의 함수의 경우는 API Server로부터 응답이 성공적으로 수신이 되었을 경우 수행이 됩니다.
- 두 번째 파라미터의 함수의 경우는 API Server로부터 응답이 실패하였을 경우 수행이 됩니다.
axios.interceptors.response.use(
(response) => {
console.log("[+] 응답이 정상적으로 수행된 경우 수행이 됩니다. ", response)
// 2xx 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
// 응답 데이터가 있는 작업 수행
return response;
},
(error) => {
console.log("[-] 응답이 실패한 경우 수행이 됩니다. ", error)
// 2xx 외의 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
// 응답 오류가 있는 작업 수행
return Promise.reject(error);
}
);
💡실행 결과
- 로그인 과정에서 API Server로 요청 후 응답 값으로 아래와 같이 인터셉터가 됩니다.
속성 | 설명 |
config | 요청 설정 정보를 포함하는 객체 |
data | 서버로부터 받은 응답 데이터 |
headers | 응답 헤더 정보 |
request | 요청에 사용된 XMLHttpRequest 객체 |
status | HTTP 상태 코드 |
statusText | HTTP 상태 메시지 |
3. 인터셉터 제거 : axios.interceptors.request.eject(myInterceptor)
💡 인터셉터 제거 : axios.interceptors.request.eject(myInterceptor)
- 사전에 Axios에서 등록한 인터셉터를 제거하기 위해서 eject()를 사용합니다.
- 이 메서드는 인터셉터를 추가할 때 반환된 ID 인자로 받아 해당 인터셉터를 제거합니다.
💡 axios.interceptors.request.eject(myInterceptor) 메서드의 파라미터
- id : AxiosInterceptor로 발급된 아이디를 기반으로 해당 인터셉터를 제거합니다.
AxiosInterceptorManager<AxiosResponse<any, any>>.eject(
id: number
): void
💡axios.interceptors.request.eject(myInterceptor) 메서드 사용예시
- 요청 인터셉터와 응답 인터셉터가 함께 존재하고 있습니다.
- 이때에 axios.interceptors.response.eject(responseInterceptor)를 통해서 진행 중인 응답 인터셉터를 수행하지 않도록 구성하였습니다.
- 해당 예시에서는 응답에 대한 인터셉터를 수행하지 않도록 지정하였습니다.
// 요청 인터셉터 추가
const requestInterceptor = axios.interceptors.request.use(
config => {
// 요청 전 수행할 작업
return config;
},
error => {
// 요청 오류 처리
return Promise.reject(error);
}
);
// 응답 인터셉터 추가
const responseInterceptor = axios.interceptors.response.use(
response => {
// 응답 데이터 처리
return response;
},
error => {
// 응답 오류 처리
return Promise.reject(error);
}
);
// 인터셉터 제거
// axios.interceptors.request.eject(requestInterceptor);
axios.interceptors.response.eject(responseInterceptor);
💡사용 결과
- 아래와 같이 요청에 대한 인터셉터를 통해 로그가 출력이 되었고, 응답에 대해서는 출력이 안됨을 확인하였습니다.
4. 커스텀 인스턴스로 인터셉터 구성 : axios.create()
💡 커스텀 인스턴스로 인터셉터 구성 : axios.create()
- Axios를 사용할 때 커스텀 인스턴스를 생성하여 인터셉터를 적용할 수 있습니다. 이 방법은 서로 다른 설정이 필요한 여러 API 엔드포인트를 다룰 때 유용합니다
💡사용예시
- 아래의 예시와 같이 AxiosCustomInstance라는 컴포넌트를 구성하였습니다. 또한 외부에서 호출이 가능하게 끔 export default AxiosCustomInstance로 지정하였습니다.
- 이를 통해서, HTTP 요청과 응답을 중앙에서 관리하고 수정할 수 있으며, 로깅이나 인증 토큰 추가 등의 공통 작업을 쉽게 구현할 수 있습니다.
1. AxiosCustomInstance : Axios 인스턴스를 생성합니다. 이는 기본 URL, 타임아웃, 헤더 등의 설정을 포함하고 있습니다.
2. AxiosCustomInstance.interceptors.request : 요청 인터셉터 추가, 요청이 서버로 전송되기 전에 실행이 됩니다.
3. AxiosCustomInstance.interceptors.response : 응답 인터셉터 추가, 서버로부터 응답을 받은 후 그 응답이 then/catch 핸들러로 전달되기 전에 실행이 됩니다.
import axios from 'axios';
/**
* Axios 인스턴스를 생성합니다.
* - 이 인스턴스는 기본 URL, 타임아웃, 헤더 등의 설정을 포함합니다.
*/
const AxiosCustomInstance = axios.create({
baseURL: '<http://localhost:8080>',
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials': true,
},
});
/**
* 요청 인터셉터 추가
* - 요청이 서버로 전송되기 전에 실행이 됩니다.
*/
AxiosCustomInstance.interceptors.request.use(
(config) => {
console.log('[+] 요청 전달 이전에 수행이 됩니다. ', config);
return config;
},
(error) => {
console.log('[-] 요청 중 오류가 발생되었을때 수행이 됩니다. ', error);
return Promise.reject(error);
},
);
/**
* 응답 인터셉터 추가
* - 서버로부터 응답을 받은 후, 그 응답이 then/catch 핸들러로 전달되기 전에 실행됩니다.
*/
AxiosCustomInstance.interceptors.response.use(
(response) => {
console.log('[+] 응답이 정상적으로 수행된 경우 수행이 됩니다. ', response);
return response;
},
(error) => {
console.log('[-] 응답이 실패한 경우 수행이 됩니다. ', error);
return Promise.reject(error);
},
);
export default AxiosCustomInstance;
💡 로그인을 하는 서비스를 구성하였습니다.
- 해당 서비스에서는 ‘로그인’을 수행하는 API 통신을 위한 서비스입니다.
- 여기서 위에서 구성한 AxiosCustomInstance를 통해서 API 호출을 수행합니다. 사전에 BaseURL로 http://localhost:8080으로 지정을 했기에, 엔드포인트만 추가하여서 전송합니다.
import axios, { AxiosResponse } from 'axios';
import AxiosCustomInstance from '../common/interceptor/AxiosInterceptor';
import { LoginType } from '../types/LoginType';
class LoginService {
/**
* 사용자 아이디, 비밀번호를 기반으로 API 호출
* @param loginInfo
*/
login = async (
loginInfo: LoginType.LoginType,
): Promise<AxiosResponse<Boolean & LoginType.loginApiResponseType, any>> => {
try {
return await AxiosCustomInstance.post('/api/v1/user/login', loginInfo);
} catch (error) {
throw new Error(`Axios Login Error :${error}`);
}
};
}
export default new LoginService();
💡 웹 상 호출 예시
- 아래와 같이 실제 호출을 하였을 때, request 이전에 로깅이 수행이 되어서 해당 내용이 출력되었고, response 이후에 로깅이 수행이 됨을 확인할 수 있습니다.
5. 커스텀 인스턴스로 인터셉트 구성(특정 URL은 제외) : 블랙리스트 구성
💡 커스텀 인스턴스에서 특정 API는 인터셉터를 사용하지 않음 : 블랙리스트를 활용
- 블랙리스트 방식은 특정 URL에 대해서만 인터셉터를 수행하지 않도록 설정하는 방법입니다.
- 기본적으로 모든 요청에 인터셉터를 적용하되, 블랙리스트에 포함된 URL에 대해서는 인터셉터를 건너뛰게 됩니다.
- 이 방식은 대부분의 API 요청에 공통된 처리를 적용하면서, 특정 엔드포인트에 대해서만 예외 처리를 하고 싶을 때 유용합니다.
💡[참고] 화이트리스트와 블랙리스트
특성 | 화이트리스트 | 블랙리스트 |
정의 | 명시적으로 허용된 항목만 포함 | 명시적으로 금지된 항목을 제외한 모든 것을 포함 |
기본 접근 | 모든 것을 차단하고 특정 항목만 허용 | 모든 것을 허용하고 특정 항목만 차단 |
보안성 | 일반적으로 더 안전함 | 새로운 위협에 취약할 수 있음 |
유지보수 | 허용 목록 관리 필요 | 차단 목록 관리 필요 |
유연성 | 제한적이지만 안전함 | 더 유연하지만 위험 가능성 있음 |
인터셉터 적용 | 리스트에 있는 URL에만 적용 | 리스트에 없는 모든 URL에 적용 |
💡 사용예시 -1
- 아래와 같이 AxiosBlackListIntance라는 Axios 인스턴스를 구성하였습니다.
1. BLACK_LIST_URL : 인터셉터 수행을 제외하려는 블랙리스트를 문자열 배열로 구성하였습니다.
2. isNotBlackListed : 블랙리스트에 포함되지 않는 URL인지 확인하는 함수로 구성하였습니다.
3. 이외에는 위에 내용과 동일합니다.
4. if (isNotBlackListed(config.url)): 해당 과정에서 BLACK_LIST 내에 요청 URL이 포함되지 않은 경우만 수행이 됩니다.
import axios from 'axios';
// 인터셉터를 이용하지 않을 블랙 리스트를 문자열 배열로 구성합니다.
const BLACK_LIST_URL = [
'/api/v1/user/login'
];
// 블랙리스트에 포함되지 않는 URL인지 확인하는 함수
const isNotBlackListed = (url?: string) => !BLACK_LIST_URL.some((blackUrl) => url?.includes(blackUrl));
/**
* Axios 인스턴스를 생성합니다.
* - 이 인스턴스는 기본 URL, 타임아웃, 헤더 등의 설정을 포함합니다.
*/
const AxiosBlackListIntance = axios.create({
baseURL: 'http://localhost:8080',
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials': true,
},
});
/**
* 요청 인터셉터 추가
* - 요청이 서버로 전송되기 전에 실행이 됩니다.
*/
AxiosBlackListIntance.interceptors.request.use(
(config) => {
// 해당 과정에서 BLACK_LIST 내에 요청 URL이 포함되지 않은 경우만 수행이 됩니다.
if (isNotBlackListed(config.url)) {
console.log('[+] 요청 전달 이전에 수행이 됩니다. ', config);
}
return config;
},
(error) => {
if (isNotBlackListed(error.config.url)) {
console.log('[-] 요청 전달 실패한 경우 수행이 됩니다. ', error);
}
return Promise.reject(error);
},
);
/**
* 응답 인터셉터 추가
* - 서버로부터 응답을 받은 후, 그 응답이 then/catch 핸들러로 전달되기 전에 실행됩니다.
*/
AxiosBlackListIntance.interceptors.response.use(
(response) => {
if (isNotBlackListed(response.config.url)) {
console.log('[+] 응답이 정상적으로 수행된 경우 수행이 됩니다. ', response);
}
return response;
},
(error) => {
if (isNotBlackListed(error.config.url)) {
console.log('[-] 응답이 실패한 경우 수행이 됩니다. ', error);
}
return Promise.reject(error);
},
);
export default AxiosBlackListIntance;
💡사용예시 -2
- LoginService.login() 함수는 AxiosBlackListIntance를 불러와서 사용합니다.
- 해당 서비스는 BLACK_LIST_URL에 포함하는 URL입니다.
import axios, { AxiosResponse } from 'axios';
import AxiosBlackListIntance from '../common/instance/AxiosBlackListIntance';
import AxiosCustomInstance from '../common/instance/AxiosCustomInstance';
import { LoginType } from '../types/LoginType';
class LoginService {
/**
* 로그인을 수행합니다.
* @param loginInfo
*/
login = async (
loginInfo: LoginType.LoginType,
): Promise<AxiosResponse<Boolean & LoginType.loginApiResponseType, any>> => {
try {
return await AxiosBlackListIntance.post('/api/v1/user/login', loginInfo);
} catch (error) {
throw new Error(`Axios Login Error :${error}`);
}
};
}
export default new LoginService();
💡사용예시 -3
- UserService.selectUserList() 함수는 AxiosBlackListIntance를 불러와서 사용합니다.
- 해당 서비스는 BLACK_LIST_URL에 포함되지 않는 URL입니다.
import axios, { AxiosResponse } from 'axios';
import { UserType } from '../types/UserTypes';
import { CommonType } from '../types/common/CommonType';
import AxiosBlackListIntance from '../common/instance/AxiosBlackListIntance';
class UserService {
/**
* 사용자 리스트를 조회합니다.
* @param userInfo
* @param accessToken
* @param refreshToken
* @returns
*/
selectUserList = async (
userInfo: UserType.UserInfoType,
accessToken: string,
refreshToken: string,
): Promise<AxiosResponse<Boolean & CommonType.loginApiResponseType[], any>> => {
return await AxiosBlackListIntance.post(`/api/v1/user/user`, userInfo, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
'x-refresh-token': `Bearer ${refreshToken}`,
},
});
};
}
export default new UserService();
💡사용 결과 -1
- 아래와 같이 로그인 프로세스 처리 시 블랙리스트가 수행이 되어서 해당 로그가 출력이 안됨을 확인하였습니다.
💡사용 결과 -2
- 블랙리스트의 내용이 아닌 경우에는 아래와 같이 로그가 출력됨을 확인하였습니다.
6. Axios Interceptor가 여러 번 반복되는 경우
💡Axios Interceptor가 여러번 반복되는 경우
- 아래와 같이 App.tsx 파일 내에서 useEffect() 내에 Interceptor를 등록하였습니다.
- 그러나 이를 수행할 때 여러 번 수행됨이 확인되었습니다.
💡 해결 방법
- 이에 따라서 아래와 같이 useRef로 구분을 짓는 속성을 생성한 뒤, 한 번만 수행되도록 구성합니다.
import React, { useEffect, useRef } from 'react';
import { BrowserRouter } from 'react-router-dom';
import Layout from './layout/Layout';
import axios from 'axios';
const App = (props: any) => {
const interceptorsRegistered = useRef(false); // Interceptor 등록 속성
useEffect(() => {
// Interceptor 등록 속성 값이 false 인 경우
if (!interceptorsRegistered.current) {
axiosInterceptorHandler.regist(); // 인터셉터 등록
interceptorsRegistered.current = true; // 인터셉터 속성 변경
}
}, []);
/**
* Axios의 Interceptor를 구성합니다.
*/
const axiosInterceptorHandler = (() => {
return {
regist: () => {
/**
* - 요청 이전 처리 방법 : 요청 인터셉터
*/
axios.interceptors.request.use(
// 요청이 전달되기 전에 작업 수행
(config) => {
console.log("[+] 요청 전달 이전에 수행이 됩니다. ", config)
return config;
},
// 요청 오류가 있는 작업 수행
(error) => {
console.log("[-] 요청 중 오류가 발생되었을때 수행이 됩니다. ", error)
return Promise.reject(error);
}
);
/**
* - 응답 후 처리 방법 : 응답 인터셉터
*/
axios.interceptors.response.use(
(response) => {
console.log("[+] 응답이 정상적으로 수행된 경우 수행이 됩니다. ", response)
// 2xx 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
// 응답 데이터가 있는 작업 수행
return response;
},
(error) => {
console.log("[-] 응답이 실패한 경우 수행이 됩니다. ", error)
// 2xx 외의 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
// 응답 오류가 있는 작업 수행
return Promise.reject(error);
}
);
}
}
})();
return (
<BrowserRouter basename={props.basename}>
<Layout {...props} />
</BrowserRouter>
);
};
export default App;
💡 아래와 같이 정상적으로 수행됨을 확인하였습니다.
4) Axios Interceptor 사용예시 -2 : jwt 접근/리프레시 토큰
💡Axios Interceptor 사용예시 -2 : jwt 접근/리프레시 토큰
- 해당 예시는 API Server로부터 발급받은 접근 토큰(Access Token), 리프레시 토큰(Refresh Token)을 Axios 서비스 통신 시마다 Header에 추가하는 작업을 일괄적으로 Axios Interceptor를 통해서 관리하기 위함입니다.
1. JWT Interceptor를 적용하기 이전 코드
💡JWT Interceptor를 적용하기 이전 코드
- 서비스를 이용할 때마다 접근 토큰과 리프레시 토큰을 서비스 내에 넣어서 수행을 했습니다.
import axios, { AxiosResponse } from 'axios';
import { UserType } from '../types/UserTypes';
import { CommonType } from '../types/common/CommonType';
import AxiosJwtInstance from '../common/instance/AxiosJwtInstance';
/**
* 사용자 관리 서비스
*/
class UserService {
/**
* 사용자 정보 리스트 조회
* @param userInfo
* @returns
*/
selectUserList = async (
userInfo: UserType.UserInfoType,
): Promise<AxiosResponse<Boolean & CommonType.loginApiResponseType[], any>> => {
const accessToken = localStorage.getItem('accessToken'); // localstorage 내에 접근 토큰을 가져옵니다.
const refreshToken = localStorage.getItem('refreshToken'); // localstorage 내에 리프레시 토큰을 가져옵니다.
return await AxiosJwtInstance.post(`/api/v1/user/user`, userInfo, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
'x-refresh-token': `Bearer ${refreshToken}`,
},
});
};
}
export default new UserService();
2. JWT Interceptor를 적용한 코드
💡JWT Interceptor를 적용한 코드
- JWT Interceptor를 활용하여서 매번 서비스에서 Header를 추가하여 토큰을 전송하는 것이 아닌 Interceptor를 통한 중앙 관리 처리를 수행합니다.
- 또한, BLACK_LIST를 통해서 , 토큰 처리 하지 않을 서비스는 제외하였습니다.
import axios from 'axios';
const BLACK_LIST = ['/api/v1/user/login']; // 토큰 처리를 제외할 목록
/**
* Axios 인스턴스를 생성합니다.
* - 이 인스턴스는 기본 URL, 타임아웃, 헤더 등의 설정을 포함합니다.
*/
const AxiosJwtInstance = axios.create({
baseURL: '<http://localhost:8080>',
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials': true,
},
});
/**
* 요청 인터셉터 추가
* - 요청이 서버로 전송되기 전에 실행이 됩니다.
*/
AxiosJwtInstance.interceptors.request.use(
(config) => {
// BLACK_LIST 내에 요청 URL이 포함되는지 확인합니다.
if (!BLACK_LIST.some((url) => config.url?.includes(url))) {
const accessToken = localStorage.getItem('accessToken'); // 접근 토큰을 로컬스토리지 내에서 가져옵니다.
const refreshToken = localStorage.getItem('refreshToken'); // 리프레시 토큰을 로컬스토리지 내에서 가져옵니다.
if (accessToken && refreshToken) {
config.headers['Authorization'] = `Bearer ${accessToken}`;
config.headers['x-refresh-token'] = `Bearer ${refreshToken}`;
}
}
return config;
},
(error) => {
console.log('[-] 요청 중 오류가 발생되었을때 수행이 됩니다. ', error);
return Promise.reject(error);
},
);
/**
* 응답 인터셉터 추가
* - 서버로부터 응답을 받은 후, 그 응답이 then/catch 핸들러로 전달되기 전에 실행됩니다.
*/
AxiosJwtInstance.interceptors.response.use(
(response) => {
console.log('[+] 응답이 정상적으로 수행된 경우 수행이 됩니다. ', response);
return response;
},
(error) => {
console.log('[-] 응답이 실패한 경우 수행이 됩니다. ', error);
return Promise.reject(error);
},
);
export default AxiosJwtInstance;
💡 아래와 같이 Header 설정 정보를 제외하였습니다.
import axios, { AxiosResponse } from 'axios';
import { UserType } from '../types/UserTypes';
import { CommonType } from '../types/common/CommonType';
import AxiosJwtInstance from '../common/instance/AxiosJwtInstance';
/**
* 사용자 관리 서비스
*/
class UserService {
/**
* 사용자 정보 리스트 조회
* @param userInfo
* @returns
*/
selectUserList = async (
userInfo: UserType.UserInfoType,
): Promise<AxiosResponse<Boolean & CommonType.loginApiResponseType[], any>> => {
return await AxiosJwtInstance.post(`/api/v1/user/user`, userInfo);
};
}
export default new UserService();
3. 결과 확인
💡결과 확인 - 1
- 최초 로그인을 성공한 뒤 두 개의 키를 발급받아서 localstorage 내에 저장하였습니다.
💡결과 확인 -2
- 아래와 같이 API Server로 호출을 하였을 때, Header 내에 Authorization, x-refresh-token 값이 추가되어 호출이 되었음을 확인하였습니다.
💡 위에 소스코드는 아래에서 확인이 가능합니다.
오늘도 감사합니다. 😀
반응형