728x90
프로젝트에 로그인 기능을 넣던 도중에, 매번 페이지마다 useEffect로 로그인 관리하기엔 반복되는 코드가 많고 귀찮아서 chat GPT랑 어떻게 하는게 더 효율적인지 이야기하다가 AuthContext를 사용하여 하는 방법으로 리팩토링 하였다
- 로그인 관련 로직 고민 ⇒ AuthContext를 사용해서 로그인하는걸로 리팩토링
- useEffect를 사용하는 방법:
- 장점:
- 간단하고 직관적인 방법
- 컴포넌트가 마운트될 때, 혹은 로그인 상태가 변경될 때마다 감지하여 적절한 동작을 수행
- 단점:
- 여러 컴포넌트에서 동일한 로직을 반복해서 사용
- 컴포넌트 간의 의존성이 높아지고, 유지보수가 어려움
- 장점:
- AuthContext를 사용하는 방법:
- 장점:
- 전역적으로 인증 정보를 관리할 수 있으므로 여러 컴포넌트에서 쉽게 접근
- 코드 중복을 줄이고, 유지보수성을 향상
- 단점:
- 복잡한 구조를 가질 수 있으며, 처음에는 설정이 조금 복잡
- 컴포넌트의 리렌더링이 필요할 때마다 인증 상태를 확인하여야 하므로 약간의 오버헤드가 발생
- 장점:
- useEffect를 사용하는 방법:
AuthContext 적용 방법
1️⃣ AuthContext.tsx 생성 (src > contexts폴더 안에 생성)
먼저, AuthContext를 생성합니다. createContext 함수를 사용하여 createContext를 생성합니다.
그런 다음, AuthProvider를 구현하여 인증 상태 및 로그인 및 로그아웃 함수를 제공합니다. useState를 사용하여 인증 상태를 관리합니다.
useAuth 훅을 사용하여 컴포넌트에서 AuthContext에 액세스할 수 있도록 합니다.
import React, { createContext, useContext, useState } from 'react';
import { loginAPI, meAPI } from '../apis/auth/authAPI';
import { removeTokensFromCookies, setTokensInCookies } from '../lib/utils/setTokensInCookies';
import useUserStore from '../apis/user/useUserStore';
import { TAuthContextType, TAuthProviderProps } from '../types/auth';
const AuthContext = createContext<TAuthContextType | undefined>(undefined);
export default function AuthProvider({ children }: TAuthProviderProps) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const { setUser } = useUserStore();
const login = async (username: string, password: string): Promise<void> => {
try {
const res = await loginAPI({ username, password });
const accessToken = res.data.accessToken;
const refreshToken = res.data.refreshToken;
// accessToken, refreshToken을 Cookies에 저장
setTokensInCookies({ accessToken, refreshToken });
// API호출하여 유저 정보 가져오기
const userResponse = await meAPI();
const userData = userResponse.data;
console.log('meAPI userResponse - userData : ', userData);
// zustand에 사용자 데이터 저장
if (userData) setUser(userData);
setIsLoggedIn(true);
// return userData;
} catch (error) {
// 로그인 오류날 경우, 에러모달을 띄움
console.error('loginAPI - error: ', error);
throw error;
}
};
const logout = () => {
setIsLoggedIn(false);
// 로그아웃 로직
// 쿠키에 저장된 accessToken과 refreshToken 삭제
removeTokensFromCookies();
};
return <AuthContext.Provider value={{ isLoggedIn, login, logout }}>{children}</AuthContext.Provider>;
}
export const useAuth = (): TAuthContextType => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
export interface TAuthContextType {
isLoggedIn: boolean;
login: (username: string, password: string) => Promise<void>;
logout: () => void;
}
export interface TAuthProviderProps {
children: React.ReactNode;
}
2️⃣ index 혹은 App.tsx에 감싸기
App또는 index 컴포넌트에서 AuthProvider를 사용하여 앱 전역에서 인증 상태를 관리합니다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import reportWebVitals from './reportWebVitals';
import { CookiesProvider } from 'react-cookie';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ConfigProvider } from 'antd';
import AuthProvider from './contexts/AuthContext';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
const queryClient = new QueryClient();
root.render(
<React.StrictMode>
<AuthProvider>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<CookiesProvider>
<ConfigProvider
theme={{
components: {
Tabs: {
/* 상단 탭 커스텀 하는 방법 */
itemSelectedColor: 'white', // 상단탭 : 선택했을때 폰트 색상
itemColor: 'black', // 상단탭 : 기본적 폰트 색상
},
},
}}
>
<App />
</ConfigProvider>
</CookiesProvider>
</BrowserRouter>
</QueryClientProvider>
</AuthProvider>
</React.StrictMode>
);
reportWebVitals();
AuthContext를 사용하여 앱에서 인증 정보를 전역적으로 관리할 수 있습니다.
3️⃣ 사용 예시
const onFinished = async (data: TLoginData) => {
const username = data.email;
const password = data.password;
// "이메일 기억하기"가 체크된 경우, 입력한 이메일을 쿠키에 저장 (2,000초 = 약 33분)
if (isRemember)
setCookie('rememberUserEmail', username, { maxAge: 2000000 });
// 입력된 사용자 이름과 비밀번호를 사용하여 로그인함수를 호출
if (username && password) {
try {
await login(username, password);
} catch (error) {
console.error('로그인 실패: ', error);
// 로그인 오류날 경우, 에러모달을 띄움
setPostState(true);
}
}
};
위와같이 login함수를 호출하여 사용하는 예시이고
import React from 'react';
import { Route, Routes, Navigate } from 'react-router-dom';
import CommonLayout from './components/Layouts/CommonLayout';
import AuthLayout from './components/Layouts/AuthLayout';
import { LOGIN_ROUTES, ROUTES } from './constants/url';
import { useAuth } from './contexts/AuthContext';
export default function App() {
// 로그인 후 로그인페이지 혹은 메인으로 이동하는 로직
const { isLoggedIn } = useAuth();
return (
<div className='App'>
<Routes>
{/* 왼쪽 메뉴들이 있는 컴포넌트들 */}
<Route element={isLoggedIn ? <CommonLayout /> : <Navigate to='/login' replace />}>
{Object.values(ROUTES).map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
))}
</Route>
{/* 로그인하는 페이지로, 왼쪽 메뉴가 없는 AuthLayout */}
<Route element={!isLoggedIn ? <AuthLayout /> : <Navigate to='/' replace />}>
{Object.values(LOGIN_ROUTES).map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
))}
</Route>
</Routes>
</div>
);
}
위는 로그인이 되었는지 알수있는 isLoggedIn을 가져와서 확인하고 안되었으면 로그인페이지로 아니면 메인페이지로 이동하는 로직입니다
이렇게 되어 useEffect를 매 페이지마다 작성하기보다 Route에서 감지하도록 할수있습니다
'2. FrontEnd > React' 카테고리의 다른 글
[ React ] 회원약관 두개이상의 토글이 true일때, 다음 버튼 활성화 (1) | 2024.09.23 |
---|---|
[ React ] 이메일 유효성 검사 후 에러메세지 띄우는 로직 (onBlur 이벤트) (0) | 2024.09.20 |
[React] CRA(create-react-app) + TypeScript 세팅 +.eslintrc +.prettierrc (0) | 2024.06.10 |
[React] JSX(JavaScriptXML)은 JavaScript의 일종일까? (1) | 2024.03.26 |
[React] react-i18next로 다국어 처리 적용하기 (1) | 2023.11.20 |