ky HTTP 클라이언트의 afterResponse 훅에서 401 응답을 처리할 때, 에러 코드가 **AUTH_401_04(토큰 만료)**인 경우에만 RefreshToken으로 재발급을 시도하고 그 외 401 응답은 아무 처리 없이 무시했습니다.
그 결과 토큰이 완전히 유효하지 않은 상태에서는 인증 오류가 발생해도 사용자에게 아무런 피드백 없이 요청만 조용히 실패했습니다. 또한 RefreshToken 재발급 자체가 실패하는 경우에도 쿠키 삭제나 리다이렉트 없이 인메모리 토큰만 초기화되어, 다음 요청에서 또다시 동일한 401이 반복될 수 있었습니다.
// lib/apis/api.ts (수정 전)
afterResponse: [
async ({ request, response, retryCount }) => {
if (response.status !== 401 || retryCount > 0) return;
const body = (await response.clone().json()) as { code?: string };
// AUTH_401_04가 아닌 401(잘못된 토큰, 권한 없음 등)은 아무 처리 없이 return
// → 사용자에게 피드백 없이 요청만 조용히 실패
if (body.code !== ERROR_CODE.AUTH_401_04) return;
try {
const newToken = await postRefreshToken();
// 원래 요청 재시도
} catch {
// 인메모리 토큰만 초기화 → 쿠키는 그대로 남아있고 리다이렉트도 없음
// 다음 요청에서 쿠키 토큰으로 Authorization 헤더가 채워져 동일한 401이 반복됨
setAccessToken(null);
}
},
],
두 가지 문제가 있었습니다.
AUTH_401_04 외 401 무시 : 토큰 자체가 유효하지 않거나 권한이 없는 경우에도 그냥 return하기 때문에, 사용자는 아무 피드백 없이 요청이 실패하는 상황을 마주하게 됩니다.null로 바꿔도 beforeRequest 훅이 쿠키 토큰을 폴백으로 읽기 때문에, 다음 요청에서 만료된 토큰이 다시 헤더에 실려 같은 401이 계속 반복됩니다.redirectToLogin 함수를 별도로 추출해 쿠키 삭제 + 인메모리 초기화 + 로그인 리다이렉트를 원자적으로 처리하도록 했습니다.
에러 코드에 따라 분기하되, 두 케이스 모두 인증 상태를 완전히 초기화하고 로그인 페이지로 이동하도록 통일했습니다.
// lib/apis/api.ts (수정 후)
const redirectToLogin = async () => {
await deleteAuthCookie(); // Server Action으로 HttpOnly 쿠키 삭제
setAccessToken(null); // 인메모리 초기화
window.location.href = "/login";
};
afterResponse: [
async ({ request, response, retryCount }) => {
if (response.status !== 401 || retryCount > 0) return;
const body = (await response.clone().json()) as { code?: string };
if (body.code === ERROR_CODE.AUTH_401_04) {
// 토큰 만료 → 재발급 시도 후 원래 요청 재시도
try {
const newToken = await postRefreshToken();
const headers = new Headers(request.headers);
headers.set("Authorization", `Bearer ${newToken}`);
return ky.retry({ request: new Request(request, { headers }) });
} catch {
await redirectToLogin(); // 재발급 실패 → 강제 로그아웃
}
} else {
await redirectToLogin(); // 그 외 401 → 강제 로그아웃
}
},
],
인터셉터에서 에러를 처리할 때 "특정 케이스만 처리하고 나머지는 무시"하는 패턴은 위험합니다. 예외 케이스까지 명시적으로 처리해 사용자가 의도치 않게 인증 오류 상태에 갇히지 않도록 해야 한다는 것을 배웠습니다.