/** * 커스텀 로그아웃 함수 * NextAuth의 signOut이 NEXTAUTH_URL로 강제 리다이렉트하는 문제를 해결하기 위해 직접 구현 */ interface CustomSignOutOptions { callbackUrl?: string; redirect?: boolean; } /** * 커스텀 로그아웃 함수 * * @param options - callbackUrl: 로그아웃 후 이동할 URL (상대 경로 권장: "/ko/partners") * @param options - redirect: 자동 리다이렉트 여부 (기본: true) */ export async function customSignOut(options?: CustomSignOutOptions): Promise { const { callbackUrl, redirect = true } = options || {}; console.log('[customSignOut] 시작:', { currentOrigin: window.location.origin, currentHref: window.location.href, callbackUrl, redirect, }); try { // 1. CSRF 토큰 가져오기 const csrfResponse = await fetch('/api/auth/csrf'); const { csrfToken } = await csrfResponse.json(); console.log('[customSignOut] CSRF 토큰 획득'); // 2. 서버에 로그아웃 요청 (리다이렉트 방지) const signoutResponse = await fetch('/api/auth/signout', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ csrfToken, json: 'true', }), redirect: 'manual', // ⭐ 서버의 리다이렉트를 자동으로 따라가지 않음 }); console.log('[customSignOut] 서버 응답:', { status: signoutResponse.status, statusText: signoutResponse.statusText, redirected: signoutResponse.redirected, url: signoutResponse.url, }); // 3. NextAuth 세션 쿠키 즉시 삭제 (middleware가 감지하도록) document.cookie = 'next-auth.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; document.cookie = '__Secure-next-auth.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Secure;'; console.log('[customSignOut] 세션 쿠키 삭제 완료'); // 4. 리다이렉트 if (redirect) { // ⭐ URL 객체로 변환하여 상대 경로인지 확인 let finalUrl: string; if (callbackUrl) { try { // callbackUrl이 절대 URL인 경우 (http:// 또는 https://로 시작) if (callbackUrl.startsWith('http://') || callbackUrl.startsWith('https://')) { const urlObj = new URL(callbackUrl); // 같은 origin인 경우 pathname만 사용 (상대 경로로 변환) if (urlObj.origin === window.location.origin) { finalUrl = urlObj.pathname + urlObj.search + urlObj.hash; } else { finalUrl = callbackUrl; } } else { // 이미 상대 경로인 경우 (/, /ko/partners 등) finalUrl = callbackUrl; } } catch (error) { console.error('[customSignOut] callbackUrl 파싱 오류:', error); finalUrl = callbackUrl; // 오류 시 원본 사용 } } else { // callbackUrl이 없는 경우 루트 경로로 finalUrl = '/'; } console.log('[customSignOut] 리다이렉트 실행:', finalUrl); window.location.href = finalUrl; } } catch (error) { console.error('Custom sign out error:', error); // 에러 발생 시에도 리다이렉트 (세션이 이미 만료되었을 수 있음) if (redirect) { let finalUrl = '/'; if (callbackUrl) { try { if (callbackUrl.startsWith('http://') || callbackUrl.startsWith('https://')) { const urlObj = new URL(callbackUrl); if (urlObj.origin === window.location.origin) { finalUrl = urlObj.pathname + urlObj.search + urlObj.hash; } else { finalUrl = callbackUrl; } } else { finalUrl = callbackUrl; } } catch { finalUrl = callbackUrl; } } window.location.href = finalUrl; } } }