Files
2026-04-08 02:27:27 +09:00

67 lines
2.0 KiB
TypeScript

/**
* JWT 토큰 단일 관리 모듈
*
* - localStorage 와 쿠키에 동시 저장 (미들웨어가 쿠키만 읽기 때문)
* - 만료/만료임박 판단 유틸 제공
* - 이 파일이 토큰 저장/조회의 단일 진실 공급원 (Single Source of Truth)
*
* 기존에 client.ts 와 useAuth.ts 양쪽에 중복 정의되어 있던 TokenManager 를
* 이 파일로 통합. 양쪽에서 import 해서 사용한다.
*/
const TOKEN_KEY = "authToken";
const COOKIE_MAX_AGE = 86400; // 24h
const REFRESH_THRESHOLD_MS = 30 * 60 * 1000; // 만료 30분 전부터 갱신 대상
export const TokenManager = {
getToken: (): string | null => {
if (typeof window !== "undefined") {
return localStorage.getItem(TOKEN_KEY);
}
return null;
},
setToken: (token: string): void => {
if (typeof window === "undefined") return;
localStorage.setItem(TOKEN_KEY, token);
// 미들웨어에서 사용
document.cookie = `${TOKEN_KEY}=${token}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax`;
},
removeToken: (): void => {
if (typeof window === "undefined") return;
localStorage.removeItem(TOKEN_KEY);
document.cookie = `${TOKEN_KEY}=; path=/; max-age=0; SameSite=Lax`;
},
isTokenExpired: (token: string): boolean => {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
return payload.exp * 1000 < Date.now();
} catch {
return true;
}
},
/** 만료 30분 전부터 true */
isTokenExpiringSoon: (token: string): boolean => {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
const expiryTime = payload.exp * 1000;
const currentTime = Date.now();
return expiryTime - currentTime < REFRESH_THRESHOLD_MS && expiryTime > currentTime;
} catch {
return false;
}
},
getTimeUntilExpiry: (token: string): number => {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
return payload.exp * 1000 - Date.now();
} catch {
return 0;
}
},
};