JWT 기반 인증에서 Access Token이 만료되었을 때 Refresh Token으로 새로운 토큰을 발급받는 과정을 구현하면서 여러 시행착오를 겪었습니다.
보통 Refresh Token은 Request Body에 담아 전송합니다:
const response = await axios.post(
"/auth/refresh",
refreshToken,
{
headers: {
"Content-Type": "text/plain"
}
}
);
이 방식이 선호되는 이유:
1. 토큰은 중요한 인증 정보이므로 body에 담아 전송
2. Request body는 암호화되어 전송 가능
3. 서버 로그에 헤더보다 body가 덜 노출됨
여러 방식을 시도했지만 계속 실패:
// 401 Unauthorized 에러 발생
// "Compact JWT strings may not contain whitespace" 에러
// "Cannot deserialize value of type 'java.lang.String'" 에러
백엔드 개발자와의 소통 결과, 헤더에 담아 전송하는 방식으로 결정:
const response = await axios.post("/auth/refresh", null, {
headers: { refreshToken }
});
일반적인 방식(Body)과 다른 방식(Header)을 선택한 이유:
서버 구현의 특성
API 설계의 RESTful 특성
구현의 단순화
토큰 갱신 과정에서 중요한 것은 무한 루프 방지:
if (
error.response?.status === 500 &&
error.response?.data?.message?.includes("JWT expired") &&
!originalRequest._retry
) {
originalRequest._retry = true;
// 토큰 갱신 로직
}
_retry
플래그로 재시도 여부를 체크하여 무한 루프를 방지합니다.
API 설계는 정답이 없음
문서화의 중요성
보안과 편의성의 균형
Refresh Token 구현은 "이렇게 해야 한다"는 절대적인 규칙보다는, 프로젝트의 요구사항과 팀의 합의에 따라 유연하게 결정해야 합니다.
중요한 것은 선택한 방식에 대한 명확한 이유와 이해, 그리고 팀원 간의 합의입니다.