[Groupware] React : axios (API 통신)

2025. 6. 4. 23:03·Project/Frontend

📝 개요

Backend 서버와 Frontend 서버와의 HTTP 통신을 위해 axios 라이브러리를 사용한다. axios 를 사용하는 이유는 fetch() 와 비교했을 때 비교적 코드가 간결하고 직관적이기 때문이다. 또한, Interceptor 기능을 통해서 JWT 가 적용되어 있는 Backend 에서 매 요청 마다 헤더에 토큰을 적용하는데 효율적이기 때문이다.

다음은 Groupware Frontend 서버에 적용되어 있는 axios 공통 설정에 대해서 설명한다.


🚀 axios

1️⃣ import 및 CustomAxiosRequestConfig type 설정

import axios, { type AxiosRequestConfig } from 'axios';

type CustomAxiosRequestConfig = AxiosRequestConfig & {
  _retry?: boolean;
};

import를 통해서 axios 라이브러리의 기본 인스턴스(기본 export 객체) 를 가져온다. (axios.get()처럼 함수처럼 사용 가능) { type AxiosRequestConfig } 를 통해 AxiosRequestConfig 타입만 타입 시스템에서 사용하겠다고 명시한다.

 

이는 TypeScript 에서 불필요한 타입 import가 JS 결과물에 포함되는 것을 방지하며, 번들 크기를 최소화한다. 결과적으로, AxiosRequestConfig 타입을 활용해 axios 설정 객체의 타입을 명확히 하고 타입 안전성을 높인다.

 

AxiosRequestConfig 타입을 기반으로 _retry 라는 커스텀 필드를 추가한 타입 정의이다. _retry 는 axios 요청이 실패했을 때 중복 재요청을 방지하기 위한 플래그로 사용된다.

 

예를 들어, accessToken 이 만료되어 401 오류가 발생했을 때, refreshToken 으로 재발급받고 같은 요청을 다시 시도하는 로직이 필요하다. 이때 _retry 가 true 면 이미 재시도한 요청이므로 중복으로 또 시도하지 않게 제어하는 데 사용된다.

 

AxiosRequestConfig 타입에는 원래 _retry 가 없기 때문에 as CustomAxiosRequestConfig 와 같이 타입 단언(type assertion) 을 통해 확장된 타입으로 인식시켜야 한다.

const BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api'; // http:localhost:8080

export const axiosInstance = axios.create({
  baseURL: BASE_URL,
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json',
  },
});

axios.create() 를 통해 axios 의 커스텀 인스턴스를 생성하고, 이를 axiosInstance 라는 이름으로 export 한다. 이렇게 하면 매번 axios.get() 이 아니라 axiosInstance.get() 처럼 재사용 가능한 객체로 API 요청을 관리할 수 있다.

설정 항목 설명
baseURL 모든 요청의 기본 URL로, .env에서 지정한 VITE_API_BASE_URL 또는 /api가 사용됨
timeout 요청 제한 시간 (ms 단위). 5000ms = 5초 넘으면 요청 실패로 간주
headers['Content-Type'] 모든 요청에 application/json 형식의 데이터를 전송하도록 기본 헤더 설정

2️⃣ Request Interceptor

// Request Interceptor : accessToken 포함
axiosInstance.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('accessToken');
    if (token) config.headers.Authorization = `Bearer ${token}`;
    return config;
  },

  (error) => Promise.reject(error),
);

이 코드는 axios 요청이 나가기 전에 실행되는 요청 인터셉터(Request Interceptor) 를 정의한 것이다.
로그인 이후 localStorage 에 저장된 accessToken 을 자동으로 요청 헤더에 포함시켜준다.

항목 설명
localStorage.getItem('accessToken') 브라우저에 저장된 토큰을 가져옴
config.headers.Authorization 요청 헤더에 Bearer {토큰} 형태로 Authorization 설정
return config 수정된 설정(config)을 그대로 다음 요청으로 넘김
Promise.reject(error) 요청 설정 중 오류 발생 시 거부 처리 (기본 패턴)

3️⃣ Response Interceptor

// Response Interceptor (401 → 토큰 재발급)
axiosInstance.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config as CustomAxiosRequestConfig;

    const isLoginRequest = originalRequest?.url?.includes('/auth/login');
    const isRefreshRequest = originalRequest?.url?.includes('/auth/refresh');

    if (error.response?.status === 401 && !isLoginRequest && !isRefreshRequest && !originalRequest._retry) {
      originalRequest._retry = true;

      try {
        const refreshToken = localStorage.getItem('refreshToken');
        const res = await axios.post(`${BASE_URL}/auth/refresh`, null, {
          headers: {
            'X-Refresh-Token': refreshToken,
          },
        });

        const { accessToken } = res.data;
        localStorage.setItem('accessToken', accessToken);

                // 갱신된 토큰 저장
        originalRequest.headers = originalRequest.headers || {};
        originalRequest.headers.Authorization = `Bearer ${accessToken}`;

                // 갱신된 토큰이 저장된 채로 원래 요청 재시도
        return axiosInstance(originalRequest);
      } catch (refreshError) {
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');

                // 실패 시 로그아웃
        window.location.href = '/login';

        return Promise.reject(refreshError);
      }
    }

    return Promise.reject(error);
  },
);
단계 설명
1️⃣ 응답 오류 감지 API 응답에서 401(Unauthorized)이 발생하면 실행됨
2️⃣ 예외 URL 확인 /auth/login, /auth/refresh 요청에는 적용되지 않도록 예외 처리
3️⃣ _retry 체크 동일 요청의 무한 재시도 방지를 위해 _retry 플래그 사용
4️⃣ refresh 요청 refreshToken을 사용해 새 accessToken을 발급 요청
5️⃣ 토큰 저장 및 재요청 새로운 accessToken을 저장 후 원래 요청(originalRequest)을 다시 시도
6️⃣ 실패 시 로그아웃 refresh 실패 → 로컬 토큰 삭제 후 /login 리디렉션

'Project > Frontend' 카테고리의 다른 글

[Groupware] React : Route (라우팅 설정)  (1) 2025.06.04
'Project/Frontend' 카테고리의 다른 글
  • [Groupware] React : Route (라우팅 설정)
arraysort
arraysort
arraysort 님의 블로그 입니다.
  • arraysort
    arraysort 님의 블로그
    arraysort
  • 전체
    오늘
    어제
    • 분류 전체보기 (14)
      • Study (5)
        • Java (3)
        • DataBase (1)
        • Spring-Boot (1)
        • React (0)
        • WEB (0)
      • Project (9)
        • Backend (5)
        • Frontend (2)
        • Database (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    DTO
    Database
    backend
    Spring Security
    java
    SQL
    react
    mabatis
    SQL Mapper
    oracle
    FilterChain
    lombok
    utilityclass
    TypeScript
    API
    spring boot
    Groupware
    CDB
    VO
    objects.eqauls()
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
arraysort
[Groupware] React : axios (API 통신)
상단으로

티스토리툴바