로그아웃 기능 추가
This commit is contained in:
@@ -55,11 +55,28 @@ export const register = async (data: RegisterData): Promise<{ message: string }>
|
|||||||
|
|
||||||
// 로그아웃 API
|
// 로그아웃 API
|
||||||
export const logout = async (): Promise<void> => {
|
export const logout = async (): Promise<void> => {
|
||||||
await axios.post('/auth/logout');
|
try {
|
||||||
|
// HttpOnly 쿠키 삭제를 위해 서버의 로그아웃 API를 GET 방식으로 호출
|
||||||
|
await axios.get('/auth/logout');
|
||||||
|
} catch (error) {
|
||||||
|
// 서버 로그아웃 실패하더라도 클라이언트는 정리 진행
|
||||||
|
}
|
||||||
|
|
||||||
|
// 로컬 스토리지 정리
|
||||||
localStorage.removeItem('accessToken');
|
localStorage.removeItem('accessToken');
|
||||||
localStorage.removeItem('refreshToken');
|
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}`;
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 토큰이 유효한지 확인
|
// 토큰이 유효한지 확인
|
||||||
|
|||||||
@@ -19,12 +19,16 @@ import {
|
|||||||
cilSettings,
|
cilSettings,
|
||||||
cilTask,
|
cilTask,
|
||||||
cilUser,
|
cilUser,
|
||||||
|
cilAccountLogout,
|
||||||
} from '@coreui/icons'
|
} from '@coreui/icons'
|
||||||
|
import { useAuth } from 'src/hooks/useAuth'
|
||||||
import CIcon from '@coreui/icons-react'
|
import CIcon from '@coreui/icons-react'
|
||||||
|
|
||||||
import avatar8 from 'src/assets/images/avatars/8.jpg'
|
import avatar8 from 'src/assets/images/avatars/8.jpg'
|
||||||
|
|
||||||
const AppHeaderDropdown = () => {
|
const AppHeaderDropdown = () => {
|
||||||
|
const { logout } = useAuth()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CDropdown variant="nav-item">
|
<CDropdown variant="nav-item">
|
||||||
<CDropdownToggle className="py-0 pe-0" caret={false}>
|
<CDropdownToggle className="py-0 pe-0" caret={false}>
|
||||||
@@ -84,9 +88,9 @@ const AppHeaderDropdown = () => {
|
|||||||
</CBadge>
|
</CBadge>
|
||||||
</CDropdownItem>
|
</CDropdownItem>
|
||||||
<CDropdownDivider />
|
<CDropdownDivider />
|
||||||
<CDropdownItem href="#">
|
<CDropdownItem onClick={logout} style={{ cursor: 'pointer' }}>
|
||||||
<CIcon icon={cilLockLocked} className="me-2" />
|
<CIcon icon={cilAccountLogout} className="me-2" />
|
||||||
Lock Account
|
Logout
|
||||||
</CDropdownItem>
|
</CDropdownItem>
|
||||||
</CDropdownMenu>
|
</CDropdownMenu>
|
||||||
</CDropdown>
|
</CDropdown>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { createContext, useReducer, useEffect } from 'react';
|
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 {
|
export interface Member {
|
||||||
@@ -63,6 +63,9 @@ const authReducer = (state: AuthState, action: AuthAction): AuthState => {
|
|||||||
};
|
};
|
||||||
case 'LOGOUT':
|
case 'LOGOUT':
|
||||||
localStorage.removeItem('accessToken');
|
localStorage.removeItem('accessToken');
|
||||||
|
localStorage.removeItem('access_token');
|
||||||
|
localStorage.removeItem('refreshToken');
|
||||||
|
localStorage.removeItem('refresh_token');
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
@@ -72,6 +75,9 @@ const authReducer = (state: AuthState, action: AuthAction): AuthState => {
|
|||||||
};
|
};
|
||||||
case 'AUTH_ERROR':
|
case 'AUTH_ERROR':
|
||||||
localStorage.removeItem('accessToken');
|
localStorage.removeItem('accessToken');
|
||||||
|
localStorage.removeItem('access_token');
|
||||||
|
localStorage.removeItem('refreshToken');
|
||||||
|
localStorage.removeItem('refresh_token');
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
@@ -114,8 +120,14 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 로그아웃 함수
|
// 로그아웃 함수
|
||||||
const logout = () => {
|
const logout = async () => {
|
||||||
dispatch({ type: 'LOGOUT' });
|
try {
|
||||||
|
await apiLogout();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Logout failed:', error);
|
||||||
|
} finally {
|
||||||
|
dispatch({ type: 'LOGOUT' });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 초기 인증 상태 확인
|
// 초기 인증 상태 확인
|
||||||
|
|||||||
Reference in New Issue
Block a user