/** * #31 세션 자동 만료 훅 * 마지막 활동 시간을 기록하고, 15분 비활성 시 토큰을 삭제하고 로그인 화면으로 보낸다. * * 저장소: expo-secure-store (AsyncStorage 미설치 → EAS 빌드 안전을 위해 SecureStore 사용) * 키: grd_last_activity */ import { useCallback } from 'react' import { useRouter } from 'expo-router' import * as SecureStore from 'expo-secure-store' export const SESSION_TIMEOUT = 15 * 60 * 1000 // 15분 const LAST_ACTIVITY_KEY = 'grd_last_activity' const TOKEN_KEY = 'grd_token' const USER_KEY = 'grd_user' export async function recordActivity(): Promise { try { await SecureStore.setItemAsync(LAST_ACTIVITY_KEY, String(Date.now())) } catch {} } /** 만료 여부만 판정 (라우터 의존 없이 _layout 등에서 사용) */ export async function isSessionExpired(): Promise { try { const token = await SecureStore.getItemAsync(TOKEN_KEY) if (!token) return false // 로그인 전이면 만료 개념 없음 const last = await SecureStore.getItemAsync(LAST_ACTIVITY_KEY) if (!last) return false return Date.now() - parseInt(last, 10) > SESSION_TIMEOUT } catch { return false } } export async function clearSession(): Promise { try { await SecureStore.deleteItemAsync(TOKEN_KEY) await SecureStore.deleteItemAsync(USER_KEY) await SecureStore.deleteItemAsync(LAST_ACTIVITY_KEY) } catch {} } export function useSessionExpiry() { const router = useRouter() const updateActivity = useCallback(async () => { await recordActivity() }, []) const checkExpiry = useCallback(async (): Promise => { const expired = await isSessionExpired() if (expired) { await clearSession() router.replace('/(auth)/login') return true } return false }, [router]) return { updateActivity, checkExpiry, isSessionExpired, clearSession } }