129 lines
4.0 KiB
TypeScript
129 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import React, { createContext, useContext, useEffect, useState } from "react";
|
|
import { useAuth } from "@/hooks/useAuth";
|
|
import { SessionManager, initSessionManager, cleanupSessionManager, formatTime } from "@/lib/sessionManager";
|
|
|
|
interface AuthContextType {
|
|
sessionManager: SessionManager | null;
|
|
showSessionWarning: boolean;
|
|
warningTime: number;
|
|
}
|
|
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
interface AuthProviderProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
/**
|
|
* 모바일 환경 감지 함수
|
|
* WebView, 모바일 브라우저 등을 감지
|
|
*/
|
|
function isMobileEnvironment(): boolean {
|
|
if (typeof window === "undefined") return false;
|
|
|
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
|
|
// 모바일 기기 감지
|
|
const isMobileDevice = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
|
|
|
// WebView 감지 (앱 내 브라우저)
|
|
const isWebView =
|
|
/wv/.test(userAgent) || // Android WebView
|
|
/webview/.test(userAgent) ||
|
|
(window as unknown as { webkit?: unknown }).webkit !== undefined || // iOS WKWebView
|
|
/fb_iab|fban|fbav/.test(userAgent) || // Facebook 앱 내 브라우저
|
|
/instagram/.test(userAgent) || // Instagram 앱 내 브라우저
|
|
/kakaotalk/.test(userAgent) || // 카카오톡 앱 내 브라우저
|
|
/naver/.test(userAgent); // 네이버 앱 내 브라우저
|
|
|
|
return isMobileDevice || isWebView;
|
|
}
|
|
|
|
/**
|
|
* 인증 컨텍스트 프로바이더
|
|
* 전역 세션 관리 및 세션 만료 경고 처리
|
|
*/
|
|
export function AuthProvider({ children }: AuthProviderProps) {
|
|
const { isLoggedIn, logout } = useAuth();
|
|
const [sessionManager, setSessionManager] = useState<SessionManager | null>(null);
|
|
const [showSessionWarning, setShowSessionWarning] = useState(false);
|
|
const [warningTime, setWarningTime] = useState(0);
|
|
|
|
// 세션 매니저 초기화 및 정리
|
|
useEffect(() => {
|
|
if (isLoggedIn) {
|
|
// 모바일 환경 여부에 따라 타임아웃 시간 조정
|
|
const isMobile = isMobileEnvironment();
|
|
|
|
// 모바일: 24시간 (JWT 토큰 만료 시간과 동일), 데스크톱: 30분
|
|
const maxInactiveTime = isMobile ? 86400000 : 1800000;
|
|
// 모바일: 1시간 전 경고, 데스크톱: 5분 전 경고
|
|
const warningTimeConfig = isMobile ? 3600000 : 300000;
|
|
|
|
// 세션 매니저 초기화
|
|
const manager = initSessionManager(
|
|
{
|
|
checkInterval: 60000, // 1분마다 체크
|
|
warningTime: warningTimeConfig,
|
|
maxInactiveTime: maxInactiveTime,
|
|
},
|
|
{
|
|
onWarning: (remainingTime: number) => {
|
|
console.log(`세션 만료 경고: ${formatTime(remainingTime)} 남음`);
|
|
setWarningTime(remainingTime);
|
|
setShowSessionWarning(true);
|
|
},
|
|
onExpiry: () => {
|
|
console.log("세션이 만료되어 자동 로그아웃됩니다");
|
|
setShowSessionWarning(false);
|
|
logout();
|
|
},
|
|
onActivity: () => {
|
|
// 활동 감지 시 경고 숨김
|
|
if (showSessionWarning) {
|
|
setShowSessionWarning(false);
|
|
}
|
|
},
|
|
},
|
|
);
|
|
|
|
setSessionManager(manager);
|
|
manager.start();
|
|
|
|
return () => {
|
|
manager.stop();
|
|
cleanupSessionManager();
|
|
setSessionManager(null);
|
|
};
|
|
} else {
|
|
// 로그인되지 않은 경우 세션 매니저 정리
|
|
cleanupSessionManager();
|
|
setSessionManager(null);
|
|
setShowSessionWarning(false);
|
|
}
|
|
}, [isLoggedIn, logout, showSessionWarning]);
|
|
|
|
const contextValue: AuthContextType = {
|
|
sessionManager,
|
|
showSessionWarning,
|
|
warningTime,
|
|
};
|
|
|
|
return <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>;
|
|
}
|
|
|
|
/**
|
|
* 인증 컨텍스트 사용 훅
|
|
*/
|
|
export function useAuthContext() {
|
|
const context = useContext(AuthContext);
|
|
if (context === undefined) {
|
|
throw new Error("useAuthContext must be used within an AuthProvider");
|
|
}
|
|
return context;
|
|
}
|
|
|
|
export default AuthProvider;
|