Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

likeornament 님의 블로그

Axios에서 302 리디렉션이 인터셉터에서 안 잡힐 때 해결 방법 본문

오류

Axios에서 302 리디렉션이 인터셉터에서 안 잡힐 때 해결 방법

likeornament 2025. 4. 15. 16:54

토큰 만료 대응 작업을 하던 중 문제가 발생했다.

// axios.ts
import axios from 'axios';

// 인증이 필요한 요청을 위한 인스턴스
const authApi = axios.create({
  baseURL: process.env.NEXT_PUBLIC_BACKEND_URL
});

// 인증 인스턴스 추가
authApi.interceptors.request.use((config) => {
  const token = localStorage.getItem('accessToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

export { authApi };

// 응답 인터셉터 추가 (토큰 만료 처리)
authApi.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    if (error.response) {
      // 토큰 만료 오류 처리
      if (error.response.status === 401 || 
          error.response.status === 403
        ) {
        alert("토큰이 만료되었습니다. 다시 로그인해주세요.");
        localStorage.removeItem('accessToken');
        
        // 인터셉터 내부에서는 router 사용이 불가능하므로 window.location 사용
        window.location.replace('/');
      }
    }
    
    return Promise.reject(error);
  }
);

위는 프로젝트에서 사용 중인 커스텀 Axios 인스턴스인 authApi 코드다.

 

응답 인터셉터 내부에서는 오류 코드가 401이나 403인 경우, 토큰이 만료된 것으로 간주하고

alert를 띄운 후 localStorage에서 accessToken을 삭제하며, / 경로로 리디렉션시킨다.

 

 

그런데 실제로 토큰이 만료된 상태에서 api 요청을 보내면

alert도 뜨지 않고, / 경로로의 이동도 일어나지 않았다.

 

개발자 도구의 네트워크 탭을 열어 확인해보니,

만료된 토큰으로 요청을 보낼 경우 401이나 403이 아닌 302 Found 응답이 오는 것을 발견했다.

 

이는 백엔드에서 로그인되지 않은 사용자가 특정 api에 접근할 경우

401 대신 302 리디렉션으로 로그인 페이지로 보내는 정책이기 때문에 발생한 것으로 보인다.

 

(error) => {
    if (error.response) {
      // 토큰 만료 오류 처리
      if (error.response.status === 401 || 
          error.response.status === 403 ||
          error.response.status === 302
        ) {
        alert("토큰이 만료되었습니다. 다시 로그인해주세요.");
        localStorage.removeItem('accessToken');
        
        // 인터셉터 내부에서는 router 사용이 불가능하므로 window.location 사용
        window.location.replace('/');
      }
    }
    
    return Promise.reject(error);
  }

그래서 302 코드도 인터셉터에서 처리하도록 위와 같이 추가했다.

 

하지만 여전히 아무런 동작도 하지 않았다.

alert도 뜨지 않고, 토큰도 삭제되지 않으며, 리디렉션도 발생하지 않았다.

 

{
    "message": "Network Error",
    "name": "AxiosError",
    "stack": ...,
    "config": ...,
    "code": "ERR_NETWORK"
}

원인을 찾기 위해 console.log(error)로 오류 객체를 출력해봤다.

출력 결과는 위와 같다.

 

여기서 중요한 포인트는 error 객체에 response 속성이 없었다는 것이다.

본인은 302도 다른 오류처럼 error.response.status로 접근 가능한 줄 알았는데, 그렇지 않았다.

 

Axios는 기본적으로 HTTP 3xx 응답을 브라우저에서 직접 처리하게 되고

백엔드에서 리디렉션 url을 디폴트 값인 http로 했기 때문에 배포된 사이트에서는 mixed content 오류가 발생해 ERR_NETWORK로 처리해버리는 것이다.

(error) => {
    // 네트워크 오류 처리 (ERR_NETWORK)
    if (error.code === "ERR_NETWORK" || !error.response) {
      console.log("네트워크 오류 또는 응답 없음");
      alert("서버 연결에 문제가 있거나 토큰이 만료되었습니다. 다시 로그인해주세요.");
      localStorage.removeItem('accessToken');
      window.location.replace('/');
      return Promise.reject(error);
    }
    
    if (error.response) {
      // 토큰 만료 오류 처리
      if (error.response.status === 401 || 
          error.response.status === 403 ||
          error.response.status === 302
        ) {
        alert("토큰이 만료되었습니다. 다시 로그인해주세요.");
        localStorage.removeItem('accessToken');
        
        // 인터셉터 내부에서는 router 사용이 불가능하므로 window.location 사용
        window.location.replace('/');
      }
    }
    
    return Promise.reject(error);
  }

따라서 위와 같이 error.code"ERR_NETWORK" 이거나, error.response가 없는 경우로 3xx 에러를 대응해 주었다.

 

이제는 302 응답도 정상적으로 감지되어 토큰 만료 대응이 잘 작동하는 것을 확인할 수 있었다.