interface JwtPayload { exp?: number; [key: string]: unknown; } /** * JWT 토큰의 exp 클레임(Unix timestamp, 초 단위)을 반환합니다. * 파싱 실패 시 null을 반환합니다. */ export function getJwtExpiry(token: string): number | null { try { const base64Payload = token.split(".")[1]; if (!base64Payload) return null; const payload = JSON.parse(atob(base64Payload)) as JwtPayload; return typeof payload.exp === "number" ? payload.exp : null; } catch { return null; } } export const REFRESH_BUFFER_MS = 60_000; export const SESSION_EXTEND_CONFIRM_BUFFER_MS = 5 * 60_000; /** * 토큰 만료 bufferMs 전까지 남은 시간(ms)을 반환합니다. * 파싱 실패 시 0. bufferMs 기본값은 silent refresh용 1분. */ export function getRemainingMs( token: string, bufferMs: number = REFRESH_BUFFER_MS, ): number { const exp = getJwtExpiry(token); if (!exp) return 0; const remainingMs = exp * 1000 - Date.now() - bufferMs; return Math.max(0, remainingMs); }