React

Auth with JWT

토큰 기반 인증 사용에 대한 내용입니다.

Overview

해당 기능은 공통업무 기능입니다. 다음과 같이 미리 준비된 Boilerplate confy-app 형태로 제공됩니다.

주요 설정

axios 설정 사항

/src/shared/utils/apiClient.ts

import { GLOBAL_ENDPOINTS } from '@/api/endpoints'
import { API_RESULT_MESSAGE, APP_AUTH_TOKEN } from '@/shared/i18n/constant'
import { useUserinfoStore } from '@/shared/stores/userinfo.store'
import axios, { AxiosInstance, AxiosResponse } from 'axios'
import createAuthRefreshInterceptor from 'axios-auth-refresh'
/**
* 로컬 스토리지에서 토큰을 가져오는 함수
*/
const getToken = (): string | null => localStorage.getItem(APP_AUTH_TOKEN)
/**
* API 응답에서 토큰을 추출하여 로컬 스토리지에 저장하는 함수
* @param response - API 응답 객체
*/
const setToken = (response: AxiosResponse) => {
const { result, data } = response.data
if (result?.message === API_RESULT_MESSAGE.SUCCESS && data?.token) {
localStorage.setItem(APP_AUTH_TOKEN, data.token)
}
}
/**
* Axios 인스턴스를 생성하고 인터셉터를 설정하는 함수
* @returns 설정된 Axios 인스턴스
*/
const createInstance = (): AxiosInstance => {
const instance = axios.create({
timeout: Number(import.meta.env.VITE_APP_AXIOS_TIMEOUT) || 10000
})
// 요청 인터셉터: 인증 토큰 헤더 추가
instance.interceptors.request.use((config) => {
const token = getToken()
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 응답 인터셉터: 토큰 저장
instance.interceptors.response.use(
(response) => {
setToken(response)
return response
},
(error) => {
return Promise.reject(error)
}
)
// 401 응답 시 토큰 재발급 + 실패 요청 재시도
createAuthRefreshInterceptor(instance, async (failedRequest) => {
try {
const res = await instance.post(GLOBAL_ENDPOINTS.AUTH.REFRESH)
setToken(res)
const newToken = getToken()
if (newToken) {
failedRequest.response.config.headers.Authorization = `Bearer ${newToken}`
}
} catch (error) {
localStorage.removeItem(APP_AUTH_TOKEN)
useUserinfoStore.getState().reset()
window.location.href = '/member/login'
return Promise.reject(
error instanceof Error ? error : new Error(String(error))
)
}
})
return instance
}
/**
* 구성된 Axios 인스턴스
* 토큰 기반 인증 및 인터셉터가 설정됨
*/
export const clientInstance = createInstance()
  • axios 라이브러리를 이용하여 HTTP 외부 통신이 이루어짐.
  • response 행위가 이루어질때마다 인터셉터 기능을 통해 token 을 저장 합니다.
  • request 행위가 이루어질때마다 인터셉터 기능을 통행 요청 헤더에 token 을 지정 합니다.
  • axios-auth-refresh 라이브러리의 createAuthRefreshInterceptor를 통하여 401 Error 발생시에 token을 재발급 요청을 합니다.
  • token 재발급 실패시 logout 진행 후 login 페이지로 이동합니다.
  • 위와 같이 구성된 axios 인스턴스를 통해서 HTTP 외부 통신이 이루어짐

로그인을 위한 구성 파일

─── src
├── api
│ ├── endpoints
│ │ └── global.endpoint.ts # API 엔드포인트 URL 관리
│ └── services
│ └── global.service.ts # 백엔드 API와의 통신 처리
├── shared
│ ├── hooks
│ │ ├── useGlobalData.ts # 앱 초기화 시 필요한 전역 데이터 로드
│ │ └── useAuth.ts # 로그인 및 로그아웃 기능 제공
│ └── store
│ └── stores
│ ├── siteinfo.store.ts #
│ └── userinfo.store.ts #
└── pages/member/pages/login.tsx # 사용자 로그인 UI 제공 및 폼 상태 관리

로그인 프로세스

  • 인증 토큰은 localStorage에 APP_AUTH_TOKEN 키로 저장됩니다.
  • 모든 API 요청에 자동으로 Authorization 헤더가 추가됩니다.

요약

1.사용자 입력: 사용자가 로그인 페이지에서 이메일과 비밀번호를 입력합니다.
2.유효성 검증: Zod 스키마를 통해 폼 데이터의 유효성을 검증합니다.
3.인증 요청: 유효한 데이터로 API 서버에 인증 요청을 보냅니다.
4.토큰 저장: 인증 성공 시 응답으로 받은 토큰을 로컬 스토리지에 저장합니다.
5.초기 데이터 로드: 토큰을 사용하여 사용자 정보와 사이트 정보를 로드합니다.
6.상태 저장: 로드된 데이터를 Zustand 스토어에 저장하여 전역 상태로 관리합니다.
7.UI 업데이트: 로그인 상태에 따라 UI가 업데이트됩니다.

상세 내용

1.사용자가 로그인 페이지에 접근:

  • login.tsx 컴포넌트 로드
  • React Hook Form + Zod를 통해 폼 초기화

2.사용자가 이메일과 비밀번호 입력 후 제출:

  • onSubmit 함수 실행
  • Zod 스키마를 통해 이메일 형식 및 비밀번호 길이 검증
  • useAuth 훅의 login.mutate 호출

3.백엔드 API에 인증 요청:

  • authService.signIn 함수가 API 요청 수행
  • GLOBAL_ENDPOINTS.AUTH.SIGN_IN 엔드포인트로 POST 요청 전송

4.인증 성공 시:

  • API 응답에서 토큰 추출 (apiClient.ts의 setToken 함수)
  • 토큰을 localStorage에 APP_AUTH_TOKEN 키로 저장
  • useInitialData 훅의 refetch 함수 호출하여 전역 데이터 로드

5.전역 데이터 로드:

  • 저장된 토큰을 기반으로 사용자 정보와 사이트 정보 병렬 요청
  • authService.getMe와 siteInfoService.getOne 함수 호출
  • 각 API 응답 데이터를 해당 Zustand 스토어에 저장

6.UI 업데이트:

  • 사용자 정보가 로드되면 username 상태 변경
  • 로그인 상태에 따라 UI 표시 변경 (로그인 폼 vs 로그아웃 버튼)

7.로그아웃 처리:

  • handleLogout 함수 호출 시 useAuth 훅의 logout 함수 실행
  • localStorage에서 토큰 삭제
  • 사용자 및 사이트 정보 스토어 초기화

토큰 재발급 프로세스

1.API 호출 시 토큰 만료로 401 에러 발생
2.토큰 재발급 API 호출
2-1.재발급 성공 시 실패한 API 재호출
2-2.재발급 실패 시 logout 후 login 페이지로 이동

참고

axios-auth-refresh