React Native에서 JWT 토큰 자동 갱신 구현하기

oversleep·2025년 2월 23일
0

app-development

목록 보기
21/38
post-thumbnail

들어가며

React Native 앱에서 로그인 상태를 유지하기 위해 JWT(JSON Web Token)를 사용하는 것은 일반적인 방법입니다.
하지만 보안을 위해 액세스 토큰의 유효기간이 짧게 설정되어 있어, 사용자 경험을 해치지 않으면서도, 보안성을 유지하는 것이 중요합니다.
오늘은 Axios interceptor를 사용하여 토큰 자동 갱신을 구현하는 방법을 알아보겠습니다.

JWT 인증 방식의 이해

JWT 인증은 두 가지 토큰을 사용합니다:
1. Access Token: 실제 API 요청에 사용되는 짧은 수명의 토큰
2. Refresh Token: Access Token을 갱신하기 위한 긴 수명의 토큰

토큰 갱신 프로세스

  1. 클라이언트가 만료된 Access Token으로 요청
  2. 서버가 401 Unauthorized 응답
  3. 클라이언트가 Refresh Token으로 새 토큰 요청
  4. 서버가 새로운 Access Token과 Refresh Token 발급
  5. 클라이언트가 새 Access Token으로 원래 요청 재시도

구현 코드

import axios from "axios";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { CommonActions } from '@react-navigation/native';

const axiosInstance = axios.create({
  baseURL: "http://your-api-server.com",
  timeout: 5000,
  headers: {
    "Content-Type": "application/json",
  },
});

// Request Interceptor
axiosInstance.interceptors.request.use(
  async (config) => {
    const token = await AsyncStorage.getItem("accessToken");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// Response Interceptor
axiosInstance.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    // 401 에러이고 재시도하지 않은 요청일 경우
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      try {
        // 새 토큰 요청
        const refreshToken = await AsyncStorage.getItem("refreshToken");
        const response = await axios.post("/auth/refresh", { refreshToken });

        // 새 토큰 저장
        const { accessToken, refreshToken: newRefreshToken } = response.data;
        await AsyncStorage.multiSet([
          ["accessToken", accessToken],
          ["refreshToken", newRefreshToken],
        ]);

        // 원래 요청 재시도
        originalRequest.headers.Authorization = `Bearer ${accessToken}`;
        return axiosInstance(originalRequest);
      } catch (refreshError) {
        // refresh token도 만료된 경우 로그아웃
        await AsyncStorage.multiRemove([
          "accessToken",
          "refreshToken",
          "isLoggedIn",
        ]);
        
        // 로그인 화면으로 이동
        navigationRef.current?.dispatch(
          CommonActions.reset({
            index: 0,
            routes: [{ name: "Login" }],
          })
        );
        
        Alert.alert("세션 만료", "로그인이 만료되었습니다. 다시 로그인해주세요.");
        return Promise.reject(refreshError);
      }
    }

    return Promise.reject(error);
  }
);

export default axiosInstance;

주요 기능 설명

1. Request Interceptor

  • 모든 API 요청 전에 실행
  • AsyncStorage에서 액세스 토큰을 가져와 헤더에 추가

2. Response Interceptor

  • API 응답 처리
  • 401 에러 발생 시 토큰 갱신 프로세스 시작
  • 갱신 성공 시 원래 요청 재시도
  • 갱신 실패 시 로그아웃 처리

3. 에러 처리

  • 토큰 갱신 실패 시 모든 인증 정보 삭제
  • 사용자를 로그인 화면으로 리다이렉트
  • 적절한 에러 메시지 표시

사용자 경험 개선 포인트

  1. 자동 갱신

    • 사용자는 토큰 갱신 과정을 인식하지 못함
    • 백그라운드에서 자동으로 처리
  2. 세션 만료 처리

    • Refresh Token까지 만료된 경우에만 로그아웃
    • 명확한 안내 메시지 제공
  3. 안전한 로그아웃

    • 모든 인증 관련 데이터 제거
    • 로그인 화면으로 깔끔하게 리셋

주의사항

  1. 보안

    • Refresh Token은 안전하게 저장
    • 모든 토큰은 암호화하여 저장 권장
  2. 에러 처리

    • 네트워크 오류 대비
    • 서버 응답 타임아웃 설정
  3. 사용자 경험

    • 불필요한 로그아웃 방지
    • 적절한 에러 메시지 제공

결론

JWT 토큰 자동 갱신을 구현함으로써 보안성을 유지하면서도 좋은 사용자 경험을 제공할 수 있습니다. Axios interceptor를 활용하면 이러한 복잡한 인증 로직을 깔끔하게 모듈화할 수 있으며, 앱의 다른 부분에 영향을 주지 않고 인증 로직을 관리할 수 있습니다.

profile
궁금한 것, 했던 것, 시행착오 그리고 기억하고 싶은 것들을 기록합니다.

0개의 댓글