Programming/React & TypeScript

Axios에서 Interceptor: 인터셉터 뭔지 아니..?

감귤밭호지차 2024. 3. 27. 00:51

세상세상 놀라온 정말 간단한 Axios GET/PUT/DELTE/PATCH 이런 기능만 쓰다가 면접에서 딱 "Interceptor" 설명해주세요. 하는 순간 

아..........

 

그래요.. 나는 길에 굴러다니는 말도 못하는 감자입니다. 읍.ㅇ.ㅡㅂ....

 

인터셉터

공식 문서 : then 또는 catch 로 처리되기 전에 요청과 응답을 가로챌 수 있다.   

 

 

공식 문서에서는 단어 뜻 그대로 서버로부터 데이터를 받아오거나 에러로 분기 되기 전에 중간에서 훽! 가로채는 녀석이란다. 

서버랑 통신하기 전에 잠깐 기다렸다가 이것 좀 하고 가!!!! 이런 녀석이랄까...?

 

//요청
axios.interceptors.request.use(function () {
	// 요청이 전달 되기 전에 작업 수행 
    return config;
}, function (Err) {
	// 요청 오류가 있는 작업 수행 
	return Promise.reject(error);
})

 

 

//응답
axios.interceptios.response.use((function (response) {
	// 응답 데이터가 있는 작업 수행 
    return response;
}, function (error){
	// 응답 오류가 있는 작업 수행 
	return Promise.reject(error);
})

 

 

 

아니 세상에나 그래서 이걸 어케 사용하는 겨...? 실제 코드에 적용해볼까.. 

 

Axios  A.K.A : 인터셉터 - Interceptors 실제 코드에 적용해보기 

 

가장 많이 사용하는 예시가 서버와 통신을 하던 중 "토큰" 만료로 서버한테  401 에러 뽝! 반환 받아버린 경우! 

이때 토큰을 다시 갱신해주고 원래 하려던 요청을 다시 요청해야 하는 과정을 요 Axios 인터셉터를 적용해서  " 중복 코드를 제거 " 하고 " 유지보수성을 향상 " 할 수 있다는 장점을 얻을 수 있다. 

 

 

 

//util/axios_interceptor.ts

import axios from "axios";
import { rotateRefresh } from "./axiosCall";


const instance = axios.create({
    baseURL : process.env.NEXT_PUBLIC_BASE_URL,
});


instance.interceptors.request.use((config) => {
    config.withCredentials = true;

    return config;
}, (error) => {
    console.log(error);

    return Promise.reject(error);
}); 

instance.interceptors.response.use((response) => {
    return response;
}, async (error) => {
    if(error.response?.status === 401 && error.response.data.message === "기한이 만료된 AccessToken입니다."){
        //token문제 - 기한이 만료된 accesstoken
        await rotateRefresh();

        //중단된 요청 새로운 토큰으로 재전송
        const originalResponse = await axios.request(error.config);
        return originalResponse;
    }if(error.response?.status === 401 && error.response.data.message === "기한이 만료된 RefreshToken입니다."){
        // recoil 별도의 설정을 위해 코드를 어떻게 수정해야 할까요. 
        throw new Error("RefreshTokenExpired")
    }if(error.response?.status === 403 && error.response.data.message === "로그인 후 이용할 수 있습니다."){
        throw new Error("RefreshTokenExpired")
    }



    return Promise.reject(error);
});

export default instance;

 

추가 설명  :: 

나는 서버에서 바로 쿠키에 토큰을 넣어버리고 클라이언트 쪽에서는 건들지 못하는 " httpOnly" 설정이라서... 요청할 때 별도의 설정은 필요하지 않았다. 그리고 서버에서 토큰이 필요하지 않는 곳에는 유효하지 않은 토큰이어도 반응하지 않도록 해놔서 모든 요청에 쿠키를 넣어주고 있다. 

 

참고로.. 기획 단계에서 에러 헨들러 정의가 명확하지 않아서 각 요청마다 조금.. 엣헴.. RefrshToken 만료에 대한 코드가 다르르.ㅁ....쿨러ㅓ쿠루러ㅓㄱ...ㅜㅠ

 

 

 

 

 

rotateRefresh() 함수는 accessToken을 새롭게 업데이트 요청하는 함수.

refreshToken 마저 만료되었을 경우에는 RefreshTokenExpired 메세지를 캐치해서 로그인 안내 모달을 뛰우고 있다. 

 

import instance from "@/util/axios_interceptor";

const getList = async () => {
    try { 
      const listData = await instance.get("/api/chat-rooms/me");
      if(listData.status === 200){
        setChatList(listData.data.result);
      }
    } catch (err) {
      const error = err as AxiosError; // 타입 단언 사용
      if(error.message === "RefreshTokenExpired"){
        setOpenTokenModal({ tokenExpired: true });  //전역에서 로그인 안내 모달 관련 recoil 상태 관리
      } else {
        console.log(`목록 함수 에러 ${error}`)
      }
    }
  }

 

 

 

 

 

 

refreshToken이 완전 만료되서 axios 요청 후 403 응답을 받아서 로그인으로 보내버리는 로직이 구현됨. 

 

 

 

 

 

 

 

나는 기껏 rotateRefresh 함수를 만들어 놓고 죄다 에러 헨들러로 요청 마다 추가하고 붙여넣고 했다. 이 무슨 창조 경제란 말인가.. 

이걸 알았다면 아주 똑똑하게 초롱초롱하게 대답했을 텐데 ... 흑흑흑.

쭈~욱 늘어진 이전 코드

 

 

 

총 정리 

Axios의 Interceptors를 사용하는 이유 

  • 서버와의 통신 도중에 어떠한 작업을 해야 할 경우, (Ex. 데이터 조회하다가 토큰 만료 시 재발급 후 데이터를 재 요청)

 

Axios의 Interceptors를 사용하면 얻게 되는 장점은?

  • 중복 코드 제거!
  • 한 곳에서 관리하기 때문에 유지보수성 향상 가능!

 

[ 참고 ] 
- axios로 interceptor로 401 처리하기
- Axios Interceptor : 공식 문서
- Axios 인터셉터 적용하기