[Project] Axios Interceptor - Token 설정하기

SEONDY·2024년 11월 30일

Project

목록 보기
8/9
post-thumbnail

[Project] Axios Interceptor - Token 설정하기

  • 프로젝트에서 백엔드에 API 요청을 보낼 때, 토큰을 함께 보내야 했다. (로그인이 필요한 요청)
headers: {
  Authorization: `Bearer ${token}`,
},
  • 로그인 시에 저장해 둔 Access Token을 보내야 하는데, 토큰이 만료됐을 때 Refresh Token을 통해 Access Token을 재발급 받는 과정이 필요했다.
    만약, Refresh Token 또한 만료가 된 상황 : 로그인 페이지로 이동

1. Axios Interceptor

  • Axios Interceptor는, then 또는 catch로 처리되기 전, 요청과 응답을 가로챌 수 있다.

요청 인터셉터

// 요청 인터셉터 추가
axios.interceptors.request.use(function (config) {
  // 요청이 전달되기 전에 작업 수행
  return config;
}, function (error) {
  // 요청 오류가 있는 작업 수행
  return Promise.reject(error);
});
  • 요청 인터셉터는, Axios로 서버에 요청을 보내기 전에 실행되는 코드이다.
    요청의 헤더 등 공통적으로 필요한 작업을 추가할 때 사용한다.
  • 사용자 인증을 위한 Access Token 추가
axios.interceptors.request.use(
  (config) => {
    // 요청 보내기 전 여기에서 처리! 토큰을 localStorage에서 가져온다고 가정
    const token = localStorage.getItem("accessToken");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config; // 수정한 요청을 서버로 보냄
  },
  (error) => {
    // 요청 오류가 발생했을 때,
    return Promise.reject(error); // 처리
  }
);

응답 인터셉터

// 응답 인터셉터 추가
axios.interceptors.response.use(function (response) {
  // 2xx 범위에 있는 상태 코드는 이 함수를 트리거
  // 응답 데이터가 있는 작업 수행
  return response;
}, function (error) {
  // 2xx 외의 범위에 있는 상태 코드는 이 함수를 트리거
  // 응답 오류가 있는 작업 수행
  return Promise.reject(error);
});
  • 응답 인터셉터는, 서버에서 받은 응답 데이터를 처리하기 전에 실행되는 코드이다.
    응답 데이터를 가공하거나, 에러 처리를 공통화할 때 사용한다.
axios.interceptors.response.use(
  (response) => {
    // 응답 데이터 처리
    return response.data; // 필요한 데이터 반환
  },
  (error) => {
    // 응답 오류 발생 시 => status 코드는 백엔드와 소통
    if (error.response.status === 401) {
      // 인증 오류, 재로그인이 필요한 상황
      navigate(); // 필요한 페이지로 이동
    }
    return Promise.reject(error);
  }
);

인터셉터 제거

필요 시 인터셉터를 제거할 수 있다.

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
  • 특정 상황에서 인터셉터가 더 이상 필요하지 않을 때 사용한다.
    인터셉터를 추가하면 해당 인터셉터의 ID가 반환되는데, eject() 메서드를 호출해 특정 인터셉터를 제거할 수 있다.
    특정 컴포넌트에서만 인터셉터가 작동하지 않도록 할 때 사용

커스텀 인스턴스에 인터셉터 추가

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
  • 이를 활용하면 인터셉터를 독립적으로 관리할 수 있다.
    API 서버가 여러 개인 경우, 인스턴스마다 다른 baseURL 설정이 가능

2. API 요청 구현

  • 로그인이 필요한 API 요청과 로그인이 필요 없는 API 요청을 분리해서 Axios 커스텀 인스턴스를 설정해 구현하려고 했다.

Axios 인스턴스 생성

  • 로그인이 필요하지 않은 요청용 인스턴스

    const publicApi = axios.create({
      baseURL: "요청 URL",
      timeout: 10000, // 10초가 넘었을 때 요청 중단 : 수정 필요
    });
  • 로그인이 필요한 요청용 인스턴스

    const privateApi = axios.create({
      baseURL: "요청 URL",
      timeout: 10000, // 10초가 넘었을 때 요청 중단 : 수정 필요
    });
  • 로그인이 필요한 요청용 인스턴스 - 요청 인터셉터

    // 요청 인터셉터 : 토큰 추가
    privateApi.interceptors.request.use(
      (config) => {
        const token = localStorage.getItem("accessToken");
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );
  • 로그인이 필요한 요청용 인스턴스 - 응답 인터셉터

    // 응답 인터셉터 : 토큰 만료 시 처리
    privateApi.interceptors.response.use(
      (response) => response,
      async (error) => {
        const originalRequest = error.config;
        
        // Access Token 만료 시
        if (error.response?.status === 401) {
          try {
            const refreshToken = localStorage.getItem("refreshToken");
            if (refreshToken) {
              // 백엔드 api로 변경 필요
              const { data } = await publicApi.post("/refresh", { refreshToken }); // Refresh Token으로 Access Token 재발급
    				// 재발급 받은 Access Token 저장
              localStorage.setItem("accessToken", data.accessToken);
    
              // 요청 다시 시도
              originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
              return privateApi(originalRequest);
            }
          } catch (refreshError) {
            // Refresh Token 만료 시
            localStorage.clear(); // 토큰 제거 (로컬)
            navigate(); // 필요한 페이지로 이동
          }
        }
    
        return Promise.reject(error);
      }
    );
  • 해당 인스턴스를 export해서 API 요청에 사용하면 된다.

사용 예시

로그인이 필요한 API 요청

async function tempApiData() {
  try {
    const response = await privateApi.get("/temp");
    // 헤더는 요청 인터셉터에서 처리됨
    console.log('데이터:', response.data);
  } catch (error) {
    console.error('오류:', error.message);
  }
}


참고 사이트

0개의 댓글