yja
[Offnal] interceptor pattern을 통한 토큰 재발급 본문
interceptor pattern을 통한 토큰 재발급은,
API 요청이 실패했을 때 그 요청이나 응답을 가로채서 자동으로 새 토큰을 발급하고 재요청하는 것을 말한다.
Axios 에서 interceptor 설정하기
성공한 응답(response)은 그대로 반환하고, 실패한 응답(error)에 대해 토큰 재발급 로직을 실행한다.
axios.interceptors.response.use(
response => response,
error => {
// 응답 에러가 발생했을 때 여기서 처리
}
)
토큰 재발급 로직
사용자가 로그인을 하면 Access Token과 Refresh Token을 동시에 받는다.
그리고 다음 API 요청을 할 때 헤더에 Access Token을 넣어 보내서 사용자를 인증하고 해당 API를 처리한다.
만료 시간이 짧은 Access Token 으로, 만료되면 401 unanthorized 오류가 발생한다.
이 오류가 발생하면 interceptor가 가로채서 Refresh Token으로 새로운 Access Token을 자동 발급할 수 있다.
Refresh Token 은 만료 기간이 길지만 언젠가는 만료된다. 이때는 서버가 새로운 Refresh Token을 함께 응답해준다.
또는 너무 오래되었다면 다시 로그인이 필요할 수 있다.
요청 인터셉터
어떤 API 요청을 보내기전에 요청 Authorization 헤더에 accessToken을 붙여서 벡엔드로 요청을 보낸다.
그러므로 로그인이 되어있는 상태에서만 다른 API 요청이 가능하도록 하는 것이다.
api.interceptors.request.use(
config => {
const token = useAuthStore.getState().accessToken
if (token) {
config.headers.Authorization = token.startsWith('Bearer ')
? token
: `Bearer ${token}`
}
return config
},
error => Promise.reject(error)
)
응답 인터셉터
API 응답으로 401이나 403 에러나 나왔을 때 자동으로 토큰을 재발급하고 요청을 재시도한다.
주요 변수
let isRefreshing = false // 토큰 갱신 중인지 여부
let failedQueue: any[] = [] // 갱신 중에 들어온 요청들을 저장하는 큐
새 토큰 전달 함수
토큰 재발급이 완료되면, 큐에 있던 요청들에게 새 토큰을 전달하고
실패 시 모든 요청을 에러로 종료시킨다.
const processQueue = (error: any, token: string | null = null) => {
failedQueue.forEach(prom => {
if (token) {
prom.resolve(token)
} else {
prom.reject(error)
}
})
failedQueue = []
}
응답 인터셉터
1. 토큰 만료 시
- _retry 플래그는 무한 루프 방지로, 같은 요청을 재시도하지 않도록 한다.
if (
(error.response?.status === 401 || error.response?.status === 403) &&
!originalRequest._retry
)
2. 이미 누군가 토큰을 갱신 중이면 나머지는 큐에 대기
if (isRefreshing) {
return new Promise((resolve, reject) => {
failedQueue.push({
resolve: (token: string) => {
originalRequest.headers.Authorization = `Bearer ${token}`
resolve(api(originalRequest))
},
reject: (err: any) => reject(err),
})
})
}
3. 토큰 재발급 API 요청
zustand store에 저장된 refreshToken을 가져와서, 그 값으로 새로운 accessToken 값을 받는다.
`tokenReissue()`가 api 요청을 하는 함수이다.
이 함수의 응답으로 받은 accessToken 과 refreshToken을 store에 저장시킨다.
대기 중이던 요청들(failedQueue)을 새 토큰으로 모두 재시도 한다.
원래 실패했던 요청(originalRequest)도 새 토큰으로 다시 보낸다.
try {
const refreshToken = useAuthStore.getState().refreshToken
const data = await authService.tokenReissue(refreshToken!)
console.log('/tokens/reissue 응답:', data)
const { accessToken: newAccessToken, refreshToken: newRefreshToken } =
data
// 새 토큰 저장
useAuthStore.getState().setAccessToken(newAccessToken)
useAuthStore.getState().setRefreshToken(newRefreshToken)
// 큐 처리
processQueue(null, newAccessToken)
// 원래 요청 재시도
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`
return api(originalRequest)
} catch (err) {
// ....
}
토큰 재발급이 잘 이루어지는 지 테스트하기
토큰 재발급은 accessToken이 만료되었을 때에만 이루어지기 때문에, 직접 만료된 토큰을 넣어서 테스트해봐야한다.
로그인 코드에서 로그인 후에 상태를 저장하는 코드는 이렇다.
// Zustand 상태에 로그인 정보 저장
const { login } = useAuthStore.getState()
login(
{
memberName: memberName,
email: email,
phoneNumber: '',
profileImageUrl: profileImageUrl,
},
accessToken,
refreshToken
)
여기에서 테스트를 위해 실제 `accessToken` 값 대신 `'expired-token'`이라는 문자열을 저장한다.
// Zustand 상태에 로그인 정보 저장
const { login } = useAuthStore.getState()
login(
{
memberName: memberName,
email: email,
phoneNumber: '',
profileImageUrl: profileImageUrl,
},
'expired_token',
refreshToken
)
이렇게 만료된 accessToken을 저장시킨 후, 다른 API 요청을 시도하면
응답 인터셉터가 401 또는 403 응답을 가로채서 토큰 재발급 API 를 호출하게 된다.
따라서 새로운 accessToken과 refreshToken 을 받게 되며 이 값을 다시 zustand 상태로 저장시키면 된다.

'[App] React Native > Study' 카테고리의 다른 글
| [Offnal] 교대근무 앱을 만들며.. 개발 회고 (1) | 2025.12.21 |
|---|---|
| [Offnal] 헬스 케어 어플리케이션 연동 (Apple Health Kit, Google Fit) (1) | 2025.10.21 |
| [Offnal] tailwind 커스텀 유틸리티 클래스, 커스텀 폰트 적용 (0) | 2025.10.06 |
| [Offnal] Nesting Navigators 구성하고 접근하기 (3) | 2025.08.04 |
| 캘린더 구현하기: react-native-calenders (5) | 2025.07.04 |