[troubleshooting] JWT Token 관리

hobeom·2024년 5월 22일

troubleshooting

목록 보기
4/4

JWT Token 관리 트러블슈팅 회고

JWT (JSON Web Token)를 활용한 인증 시스템을 구축하면서 겪었던 여러 문제들을 해결한 과정을 기록해보겠다. 특히, 토큰 만료 및 갱신 문제와 그 해결 방법에 대해 집중적으로 다루겠다.

1. 문제 정의

로그인 시에 발급받은 토큰이 예상보다 빠르게 만료되는 문제가 발생했다. 테스트 시에 사용자들이 일정 시간이 지나지 않았음에도 불구하고 토큰이 만료되어 로그아웃되는 상황이 빈번하게 발생했다.

2. 사실 확인

  • 로그인 시 발급받은 access token을 요청 헤더에 포함하여 API 호출을 수행함.
  • 서버 응답으로 새로운 access token이 발급됨.
  • 그러나, 클라이언트에서 요청 헤더의 access token 값이 갱신되지 않음.
  • 개발자 도구를 통해 확인한 결과, 쿠키에 저장된 access token과 요청 헤더에 포함된 access token 값이 서로 다른 것을 확인.

3. 원인 추론

  • Axios 인스턴스를 초기화할 때, 쿠키에서 access token을 가져와 헤더의 Authorization에 추가하는 코드가 존재.
  • 그러나, 이 코드는 Axios 인스턴스 초기화 시 한 번만 실행되기 때문에, 이후에 쿠키에 저장된 access token이 갱신되어도 헤더의 token 값은 갱신되지 않음.

4. 조사 방법

  • Axios의 인터셉터(interceptor) 기능을 활용하여, 모든 요청 전에 쿠키에서 최신 access token을 가져와 헤더에 설정하는 방법을 검토.
  • GPT를 통해 Axios 인터셉터 기능에 대한 정보를 확인하고, 이를 구글링하여 더블 체크.

5. 구현

Axios 인터셉터를 사용하여 요청 전에 쿠키에서 최신 access token을 가져오는 코드를 작성.

코드 구현

1. Axios 인스턴스 초기화

import axios from 'axios';
import Cookies from 'js-cookie';

const apiClient = axios.create({
  baseURL: '/api',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
});

2. 요청 인터셉터 추가

// 요청 인터셉터 설정
apiClient.interceptors.request.use(config => {
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
}, error => {
  return Promise.reject(error);
});

6. 결과

  • 인터셉터를 통해 요청 시마다 최신 토큰을 헤더에 포함하도록 설정.
  • 그러나, 여전히 요청 헤더의 토큰 값이 갱신되지 않는 문제 발생.

추가 원인 추론 및 해결

추가 원인:

  • Axios 인스턴스 초기화 시, 쿠키에서 토큰을 가져오는 로직이 인터셉터 바깥에 위치하여 초기화 시 한 번만 실행되고 있었음.

구현:

  • Cookies.get('access_token') 호출을 인터셉터 내부로 이동하여, 요청 시마다 최신 토큰 값을 가져오도록 수정.
// 수정된 인터셉터 설정
apiClient.interceptors.request.use(config => {
  const token = Cookies.get('access_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
}, error => {
  return Promise.reject(error);
});

추가 문제

추가 문제:

  • 토큰 유효기간은 백엔드에서 관리해주고 있었기 때문에, 인증이 필요한 api마다 토큰 갱신을 해주고 있었는데, 이에 대한 유지보수에 불편함을 느낌.

최종 구현:

  • 응답 인터셉터 내부에 토큰 갱신, 만료 처리와 요청 재시도 로직을 추가하여 토큰이 만료되면 재요청 시도를 할 수 있도록 함. 또 refresh 토큰이 유효하지 않거나 없을 경우 로그아웃 처리가 되도록 설정.
// 응답 인터셉터 설정
apiClient.interceptors.response.use(response => {
  return response;
}, async (error) => {
  if (error.response && error.response.status === 401) {
    // access token 만료 처리
    const refreshToken = Cookies.get('refresh_token');
    if (refreshToken) {
      try {
        const response = await axios.post('/users/refresh-token', { token: refreshToken });
        const newToken = response.data.access_token;
        Cookies.set('access_token', newToken);

        // 요청 재시도
        error.config.headers.Authorization = `Bearer ${newToken}`;
        return axios.request(error.config);
      } catch (refreshError) {
        // 리프레시 토큰이 유효하지 않으면 사용자 로그아웃 처리
        Cookies.remove('access_token');
        Cookies.remove('refresh_token');
        window.location.href = '/';
        return Promise.reject(refreshError);
      }
    } else {
      // 리프레시 토큰이 없으면 사용자 로그아웃 처리
      Cookies.remove('access_token');
      Cookies.remove('refresh_token');
      window.location.href = '/';
    }
  }
  return Promise.reject(error);
});

결과 관찰:

  • 인터셉터 안에 요청 때마다 token의 갱신을 확인할 수 있도록 로직을 추가하여 유지보수성을 높임.

결론

JWT 토큰을 관리하는 과정에서 발생할 수 있는 여러 문제들 중 토큰 갱신 문제에 대해 살펴 보았다. Axios 인터셉터를 활용하여 요청 시마다 최신 토큰을 사용하도록 하고 응답 인터셉터에 재인증 요청 및 로그아웃 처리 로직을 추가함으로써 토큰 갱신 문제를 해결할 수 있었다.

profile
서두르지 마라, 멈추지도 마라

0개의 댓글