React & React Native/이해하기

[React/RN] Axios Interceptor 동작 방법 이해하고 활용하기: JWT, 특정 URL 제외

adjh54 2024. 10. 19. 23:37
728x170
해당 글에서는 Axios의 Interceptor 기능에 대해 알아봅니다.

 

1) Axios


 

💡 Axios

- Node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트로 비동기 요청을 쉽게 처리할 수 있게 해 줍니다.
- 이는 비동기 처리를 통해 네트워크 요청이 완료될 때까지 다른 코드 실행을 차단하지 않고, 효율적인 리소스 사용이 가능합니다.

- then()과 catch() 메서드를 사용하거나 async/await 구문과 함께 사용하여 비동기 코드를 더 읽기 쉽고 관리하기 쉽게 작성할 수 있습니다.
 

시작하기 | Axios Docs

시작하기 브라우저와 node.js에서 사용할 수 있는 Promise 기반 HTTP 클라이언트 라이브러리 Axios란? Axios는 node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트 입니다. 그것은 동형 입니다(동일한 코

axios-http.com

 

💡 간단 예시

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 핸들러로 전달되기 전에 코드를 실행할 수 있습니다.
전역적 설정 모든 요청이나 응답에 대해 일괄적으로 처리할 수 있습니다.
에러 처리 요청이나 응답 과정에서 발생하는 에러를 중앙에서 처리할 수 있습니다.
헤더 수정 요청이나 응답의 헤더를 동적으로 수정할 수 있습니다.
로깅 모든 요청과 응답을 로깅하여 디버깅에 활용할 수 있습니다.
인증 처리 토큰 갱신이나 인증 관련 로직을 중앙에서 처리할 수 있습니다.

 

 

인터셉터 | Axios Docs

인터셉터 then 또는 catch로 처리되기 전에 요청과 응답을 가로챌수 있습니다. axios.interceptors.request.use(function (config) { return config; }, function (error) { return Promise.reject(error); }); axios.interceptors.response.use(f

axios-http.com

 

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 값이 추가되어 호출이 되었음을 확인하였습니다.

 

 

 

 

💡 위에 소스코드는 아래에서 확인이 가능합니다.
 

blog-codes/react-login at spring-boot3-security · adjh54ir/blog-codes

Contributor9 티스토리 블로그 내에서 활용한 내용들을 담은 레포지토리입니다. Contribute to adjh54ir/blog-codes development by creating an account on GitHub.

github.com

 

 

 

 

오늘도 감사합니다. 😀

 

 

 

 

그리드형