웹 애플리케이션을 구현하다 보면 사용자의 권한 등에 따라 접근 제한을 두어야 하는 페이지가 있습니다.
우리는 여러 서비스를 비회원으로 사용하면서 로그인이 필요한 기능 페이지에 진입 시에는 로그인, 회원가입 페이지로 이동한 경험을 모두 가지고 있을 것입니다.
그렇다면 이러한 페이지 별 접근 제한을 React에서는 어떻게 구현할 수 있을까요?
오늘은 제가 이전에 useEffect hook을 이용하여 페이지 별 접근 제한을 구현한 방법 그리고 이를 개선한 1가지 방법을 더 알아보려고 합니다.
1. useEffect를 사용한 페이지 접근 제한 구현
// login 상태가 아닌 경우에 Keyword 페이지로 접근하면 Login으로 redirect
useEffect(() => {
if (!isLoggined) { // 사용자가 로그인하지 않은 경우
navigate("/login"); // 로그인을 유도하기 위하여 '/login' 페이지로 redirect
}
}, [navigate, isLoggined]); // navigate, isLoggined의 value가 변경될 때마다 실행
위와 같이 코드를 작성한 경우에는 useEffect를 사용하여 로그인 여부를 확인한 뒤, 로그인된 경우가 아니라면 useNavigate를 이용하여 로그인 페이지로 redirect 합니다.
언뜻 보면 별 문제 없어보이는 코드지만 useEffect hook의 동작 방식, 그리고 사용되는 경우를 고려하지 않아 몇 가지 문제점이 있습니다.
1.1. useEffect를 통한 페이지 접근 제한의 문제점
useEffect는 리액트 컴포넌트가 리렌더링될 때마다 특정 작업을 수행할 수 있도록 하는 React Hook이며 전달하는 인자에 따라 각각 다르게 사용할 수 있습니다.
간단하게 useEffect에 대하여 알아보면 다음과 같습니다.
1️⃣ useEffect(callBackFunc);
2️⃣ useEffect(callBackFunc, []);
3️⃣ useEffect(callBackFunc, [a, b]);
4️⃣ useEffect(()=>{ return(() => func()) });
첫 번째의 경우에는 렌더링이 될 때(컴포넌트 마운트, 컴포넌트 업데이트, 컴포넌트 언마운트)마다.
두 번째의 경우에는 컴포넌트가 최초 렌더링 될 때.
세 번째의 경우에는 최초 렌더링 되었을 경우와 의존성 배열에 있는 상태값 a, b가 변경 될 때.
마지막 네 번째의 경우에는 컴포넌트가 언마운트되기 전에 수행됩니다(return을 사용하여 생명주기 메소드 componentWillUnmount의 역할을 수행할 수 있습니다).
아무튼 useEffect는 컴포넌트 렌더링 이후에 실행된다는 특징을 가지고 있기 때문에 useEffect를 통하여 페이지 접근을 구현하게 된다면
페이지 이동 -> 페이지 렌더링 -> useEffect 실행 -> 접근 권한 확인 -> 로그인 페이지로 이동 및 현재 페이지 유지
와 같은 순서로 동작하게 됩니다. 이 때문에 사용자 입장에서는 페이지가 깜빡이는 현상을 겪을 수 밖에 없으며 렌더링 단계에서 시간이 오래 걸린다고 하면 그 시간들을 온전히 기다려야 합니다.
useEffect는 기존 클래스 컴포넌트에서 사용하던 생명주기 메소드를 함수형 컴포넌트에서 사용할 수 있도록 분리한 것이기 때문에, 페이지 접근 제한보다 컴포넌트의 생명주기에 따른 처리에 사용하는 것이 더 좋다고 볼 수 있습니다.
2. Route element 내부에서 페이지 접근 권한 확인하기
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import MainPage from "./pages/main/main";
import HomePage from "./pages/home/home";
import Cookies from "universal-cookie";
interface ProtectedRouteProps {
isAuthenticated: boolean;
children: React.ReactNode;
}
const isAuthenticated = () => {
const cookies = new Cookies();
return (
cookies.get("accessToken") !== undefined &&
cookies.get("refreshToken") !== undefined
);
};
const ProtectedRoute = ({ isAuthenticated, children }: ProtectedRouteProps) => {
return isAuthenticated ? <>{children}</> : <Navigate to="/" />;
};
function App() {
return (
<>
<BrowserRouter>
<Routes>
<Route path="/" element={<MainPage />} />
<Route
path="/home"
element={
<ProtectedRoute isAuthenticated={isAuthenticated()}>
<HomePage />
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
</>
);
}
export default App;
useEffect를 사용하였을 때의 문제점을 개선하기 위하여 위와 같이 ProtectedRoute를 새로 정의하였습니다. 매개변수 isAuthenticated를 이용하여 접근 가능 여부를 판단한 뒤, 접근 가능한 경우에는 매개 변수 children(컴포넌트)을 렌더링하기 시작합니다.
접근 권한이 없는 경우에는 Navigate를 이용하여 '/' (해당 프로젝트에서는 로그인, 회원가입 페이지입니다)로 redirect하도록 하였습니다.
2.1. 무엇이 더 좋아졌을까?
먼저 접근 제한을 두기 위한 페이지를 ProtectedRoute로 한 번 감싸주기만 하면 되기에 보다 적용하기 편하고 유지보수에도 용이합니다(만일 useEffect를 사용한 경우에는 접근별 제한이 필요한 페이지마다 같은 코드를 추가해야 했을 것입니다).
또한, 페이지 접근을 막기 위한 기능인데 useEffect를 사용하면 페이지에 적어도 한 번은 접근하여 권한을 확인해야 한다는 다소 모순적인 상황을 피할 수 있습니다.
사실 가장 중요한 것은 사용자가 페이지 깜빡임 현상을 겪지 않으며 컴포넌트 렌더링 시간을 기다리지 않아도 되는 점에서 보다 좋은 사용자 경험을 제공할 수 있다는 점이라고 봅니다.
https://github.com/kookmin-sw/capstone-2024-04/blob/master/src/client/src/App.tsx
좀 더 자세한 코드를 보고 싶으신 분들은 위에서 적용 예시를 살펴보면 좋을 것 같습니다 :)
'Web > React' 카테고리의 다른 글
[React] React에서 API 호출을 효과적으로 막는 방법 - useRef 활용하기 (0) | 2024.11.22 |
---|---|
[React] Warning: React has detected a change in the order of Hooks 에러 해결하기 (0) | 2024.07.17 |
[React] Invalid Hook Call Warning 해결하기 (0) | 2024.04.15 |
[Recoil] React를 위한 상태 관리 라이브러리, Recoil에 대하여 알아보자 (2) | 2024.02.07 |
[React + Vite] "Failed to load resource: the server responded with a status of 404(not found)" 해결하기 (0) | 2023.11.16 |