diff --git a/src/axios/authService.ts b/src/axios/authService.ts index 9a4c6e3..eab3e62 100644 --- a/src/axios/authService.ts +++ b/src/axios/authService.ts @@ -55,11 +55,28 @@ export const register = async (data: RegisterData): Promise<{ message: string }> // 로그아웃 API export const logout = async (): Promise => { - await axios.post('/auth/logout'); + try { + // HttpOnly 쿠키 삭제를 위해 서버의 로그아웃 API를 GET 방식으로 호출 + await axios.get('/auth/logout'); + } catch (error) { + // 서버 로그아웃 실패하더라도 클라이언트는 정리 진행 + } + + // 로컬 스토리지 정리 localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); - document.cookie = 'access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/'; - document.cookie = 'refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/'; + + // 쿠키 정리 + const cookieNames = ['access_token', 'refresh_token']; + const domain = window.location.hostname; + const paths = ['/', '/api']; // 가능한 경로들 + + cookieNames.forEach(name => { + paths.forEach(path => { + document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}`; + document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}; domain=${domain}`; + }); + }); }; // 토큰이 유효한지 확인 diff --git a/src/components/header/AppHeaderDropdown.tsx b/src/components/header/AppHeaderDropdown.tsx index aac5acd..e79124c 100644 --- a/src/components/header/AppHeaderDropdown.tsx +++ b/src/components/header/AppHeaderDropdown.tsx @@ -19,12 +19,16 @@ import { cilSettings, cilTask, cilUser, + cilAccountLogout, } from '@coreui/icons' +import { useAuth } from 'src/hooks/useAuth' import CIcon from '@coreui/icons-react' import avatar8 from 'src/assets/images/avatars/8.jpg' const AppHeaderDropdown = () => { + const { logout } = useAuth() + return ( @@ -84,9 +88,9 @@ const AppHeaderDropdown = () => { - - - Lock Account + + + Logout diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx index 967e451..40ebdd2 100644 --- a/src/context/AuthContext.tsx +++ b/src/context/AuthContext.tsx @@ -1,6 +1,6 @@ import React, { createContext, useReducer, useEffect } from 'react'; -import { isTokenValid, getAccessTokenFromCookie, getUserFromToken, getRefreshTokenFromCookie, renewAccessToken } from 'src/axios/authService'; +import { isTokenValid, getAccessTokenFromCookie, getUserFromToken, getRefreshTokenFromCookie, renewAccessToken, logout as apiLogout } from 'src/axios/authService'; // 사용자 타입 정의 export interface Member { @@ -63,6 +63,9 @@ const authReducer = (state: AuthState, action: AuthAction): AuthState => { }; case 'LOGOUT': localStorage.removeItem('accessToken'); + localStorage.removeItem('access_token'); + localStorage.removeItem('refreshToken'); + localStorage.removeItem('refresh_token'); return { ...state, isAuthenticated: false, @@ -72,6 +75,9 @@ const authReducer = (state: AuthState, action: AuthAction): AuthState => { }; case 'AUTH_ERROR': localStorage.removeItem('accessToken'); + localStorage.removeItem('access_token'); + localStorage.removeItem('refreshToken'); + localStorage.removeItem('refresh_token'); return { ...state, isAuthenticated: false, @@ -114,8 +120,14 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }; // 로그아웃 함수 - const logout = () => { - dispatch({ type: 'LOGOUT' }); + const logout = async () => { + try { + await apiLogout(); + } catch (error) { + console.error('Logout failed:', error); + } finally { + dispatch({ type: 'LOGOUT' }); + } }; // 초기 인증 상태 확인