'use client'; /** * API 에러 컨텍스트 * * Server Action 결과에서 인증 에러를 감지하고 * 자동으로 로그인 페이지로 리다이렉트 */ import { createContext, useContext, useCallback, useRef, type ReactNode } from 'react'; import { useRouter } from 'next/navigation'; import { isAuthError, isApiError, type ApiErrorResponse } from '@/lib/api/errors'; import { callLogoutAPI } from '@/lib/auth/logout'; interface ApiErrorContextType { /** * API 응답을 체크하고 인증 에러면 로그인 페이지로 이동 * @returns true면 에러가 있음 (호출자는 early return 해야 함) */ checkAuthError: (response: unknown) => response is ApiErrorResponse; /** * 수동으로 인증 에러 처리 (로그아웃 후 로그인 페이지 이동) */ handleAuthError: () => Promise; } const ApiErrorContext = createContext(null); /** * API 에러 Provider * * Protected Layout에 추가하여 모든 하위 페이지에서 사용 */ export function ApiErrorProvider({ children }: { children: ReactNode }) { const router = useRouter(); const isRedirecting = useRef(false); /** * 인증 에러 처리 - 로그아웃 후 로그인 페이지 이동 */ const handleAuthError = useCallback(async () => { if (typeof window === 'undefined') return; // 중복 리다이렉트 방지 if (isRedirecting.current) return; isRedirecting.current = true; console.warn('[ApiErrorContext] 인증 에러 감지 - 로그인 페이지로 이동'); try { // 서버 로그아웃 API 호출 (HttpOnly 쿠키 삭제) await callLogoutAPI(); } catch (error) { console.error('[ApiErrorContext] 로그아웃 API 호출 실패:', error); } // 로그인 페이지로 이동 window.location.href = '/login'; }, []); /** * API 응답에서 인증 에러 체크 * 인증 에러면 자동으로 로그인 페이지 이동 */ const checkAuthError = useCallback((response: unknown): response is ApiErrorResponse => { if (isAuthError(response)) { handleAuthError(); return true; } if (isApiError(response)) { console.warn('[ApiErrorContext] API 에러:', response.message); return true; } return false; }, [handleAuthError]); return ( {children} ); } /** * API 에러 컨텍스트 훅 * * @example * ```typescript * const { checkAuthError } = useApiError(); * * const result = await getEmployees(); * if (checkAuthError(result)) return; // 인증 에러면 자동 리다이렉트 * * // 정상 데이터 처리... * ``` */ export function useApiError() { const context = useContext(ApiErrorContext); if (!context) { throw new Error('useApiError must be used within ApiErrorProvider'); } return context; } /** * Server Action 결과 래퍼 * * Server Action 호출 결과를 자동으로 체크하고 * 인증 에러면 null 반환 + 리다이렉트 * * @example * ```typescript * const { withAuthCheck } = useApiError(); * * const employees = await withAuthCheck(getEmployees()); * if (!employees) return; // 에러 발생 시 null * * // 정상 데이터 사용... * ``` */ export function useWithAuthCheck() { const { checkAuthError } = useApiError(); const withAuthCheck = useCallback(async ( promise: Promise ): Promise => { const result = await promise; if (checkAuthError(result)) { return null; } return result; }, [checkAuthError]); return { withAuthCheck }; }