logout 수정정

This commit is contained in:
2026-01-20 15:00:08 +09:00
parent 7a231c541a
commit 599e8a95c7
3 changed files with 120 additions and 5 deletions
+23 -2
View File
@@ -9,10 +9,31 @@ import {
export async function POST() {
const cookieStore = await cookies();
// 백엔드에 로그아웃 알림 (토큰 블랙리스트 처리)
const refreshToken = cookieStore.get(REFRESH_TOKEN)?.value;
if (refreshToken) {
try {
// ⭐ Spring Boot 백엔드에 로그아웃 요청
await fetch(`${process.env.NEXT_PUBLIC_SERVER_API_URL}/api/auth/logout`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Cookie": `${REFRESH_TOKEN}=${refreshToken}`,
},
// credentials: "include", // fetch에서는 직접 쿠키 전달
});
} catch (error) {
console.error("Backend logout failed:", error);
}
}
// Next.js 쿠키 삭제
cookieStore.delete(ACCESS_TOKEN);
cookieStore.delete(REFRESH_TOKEN);
cookieStore.delete(AUTH_USER_ID);
cookieStore.delete(CLAIM_NAME);
return NextResponse.json({ message: "Logged out" }, { status: 200 });
}
return NextResponse.json({ message: "Logged out" }, { status: 200 });
}
@@ -1,4 +1,4 @@
import { AUTH_USER_ID, CLAIM_NAME } from "@/shared/config";
import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME, REFRESH_TOKEN } from "@/shared/config";
import { getCookie, setCookie, removeCookie } from "@/shared/lib";
export class TokenManager {
@@ -76,6 +76,8 @@ export class TokenManager {
this.removeToken(CLAIM_NAME);
this.removeToken(AUTH_USER_ID);
this.removeToken(REFRESH_TOKEN);
this.removeToken(ACCESS_TOKEN);
}
}
@@ -64,7 +64,99 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
queryFn: () => getSiteList(),
enabled: isLoggedIn && !viewOnly,
});
const logout = async () => {
try {
// 1. 백엔드 API 로그아웃 호출
const response = await fetch("/api/auth/logout", {
method: "POST",
credentials: "include", // ⭐ 쿠키 포함 중요!
});
if (!response.ok) {
console.error("Logout API failed");
// API 실패해도 계속 진행
}
} catch (error) {
console.error("Logout error:", error);
// 에러가 나도 계속 진행
} finally {
// 2. 클라이언트 쿠키 삭제 (백업)
clearClientCookies();
// 3. 로컬/세션 스토리지 정리
if (typeof window !== "undefined") {
localStorage.clear();
sessionStorage.clear();
}
// 4. 로그인 페이지로 리다이렉트
window.location.href = "/auth/login";
}
};
// 쿠키 삭제 헬퍼 함수
const clearClientCookies = () => {
const actualHost = window.location.hostname;
// 루트 도메인 추출
const getRootDomain = (hostname: string): string => {
if (hostname === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
return hostname;
}
if (hostname.includes("hasuon.or.kr")) {
return ".hasuon.or.kr";
}
const parts = hostname.split(".");
if (parts.length >= 2) {
return `.${parts.slice(-2).join(".")}`;
}
return hostname;
};
const domain = getRootDomain(actualHost);
// 삭제할 쿠키 목록
const cookiesToDelete = [
appConfig.AUTH_USER_ID,
appConfig.REFRESH_TOKEN,
appConfig.ACCESS_TOKEN,
appConfig.CLAIM_NAME,
"site-id-key",
"site-theme-key",
].filter(Boolean); // undefined 제거
// 여러 방법으로 쿠키 삭제 시도
cookiesToDelete.forEach((cookieName) => {
if (!cookieName) return;
// 방법 1: removeCookie 함수 사용
if (domain === "localhost") {
removeCookie(cookieName, {});
} else {
removeCookie(cookieName, { domain });
}
// 방법 2: 직접 document.cookie 설정 (도메인 포함)
if (domain !== "localhost") {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}`;
}
// 방법 3: 도메인 없이 삭제 (현재 도메인만)
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
// 방법 4: Secure 속성 포함 삭제
if (window.location.protocol === "https:") {
if (domain !== "localhost") {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}; secure`;
}
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; secure`;
}
});
};
/*
const logout = async () => {
try {
const response = await fetch("/api/auth/logout", {
@@ -123,7 +215,7 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
console.error(error);
}
};
*/
const openPopup = useCallback(async (popup: Popup) => {
try {
const cookieName = `cs-popup-hide-for-day-${popup.popupSn}`;