본 글의 순서는 다음과 같다.
현재 Project의 페이지에는 Main, Redirect, LectureStream 등등 다양한 페이지가 있습니다. 이러한 페이지 중 메인과 리다이렉트를 제외하고서는 로그인된 사용자를 대상으로만 제공 가능한 페이지입니다.
이렇듯이 사용자에게 상황에 맞게 페이지 접근을 제한해야할 때가 생깁니다. 관리자에 제공할 목적인 대시보드를 일반 사용자가 접근하면 안 되듯이 말입니다. 이를 위해서 라우터에서 Private Route를 제공해야 합니다. 사용자의 상황에 맞게 페이지를 변경하거나 접근을 막아야 합니다. 이러한 Private Route를 React의 기능을 활용해 구현해보겠습니다.
React의 Outlet를 사용하면 손쉽게 Private Route를 구현할 수 있습니다. 이에 우선하여 Outlet이 뭔지에 대해서 살펴보도록 하겠습니다.
Type으로 살펴보는 Outlet
interface OutletProps {
context?: unknown;
}
declare function Outlet(
props: OutletProps
): React.ReactElement | null;
대강 context라는 것을 받아서 이 결과로 ReactElement나 null을 반환하는 것을 살펴보실 수 있습니다. 해석해보자면 context에 맞게 react 요소를 보여줄 지 안 보여줄 지를 결정한다는 것입니다.
실 사용으로 살펴보는 Outlet
import styled from 'styled-components';
import { Outlet } from 'react-router-dom';
import Header from '@/component/Header';
const Layout: React.FC = () => {
return (
<LayoutContainer>
<Header />
<MainContainer>
<Outlet />
</MainContainer>
</LayoutContainer>
);
};
const Router: React.FC = () => {
const { isAuthenticated } = useAuthenticationContext();
return (
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Main />} />;
</Route>
</Routes>
</BrowserRouter>
);
};
React를 경험해보신 분이라면 느낌이 오시리라 생각합니다. 위의 경우 헤더의 중첩 라우팅 구성이 되면서 Outlet을 통해서 상위의 컴포넌트를 레이아웃화 할 수 있게 됩니다.
이전에는 {children}을 사용해서 layout을 구성한다음에 라우트에 항상 레이아웃과 연결짓도록 했는데 을 사용하면 {children} 을 사용하는 것과 같은 효과가 내면서 가독성을 향상시킬 수 있습니다.
그렇다면 Outlet를 통해서 레이아웃을 중첩 컴포넨트를 통해 손쉽게 구성하는 것은 알겠는데 이게 Private Route와 무슨 상관이 있나요?
위의 layout과 같은 코드에서 props로 state 값을 받아서 이를 통해 분기를 나눠 route 값을 반환하면 해당 기능을 구현할 수 있습니다.
interface ProtectedRouteProps {
loginState: boolean;
redirectPath?: string;
}
const ProtectedRoute = ({ loginState, redirectPath = '/' }: ProtectedRouteProps) => {
if (!loginState) {
return <Navigate to={redirectPath} replace />;
}
return <Outlet />;
};
Layout의 코드에서 loginState, redirectPath라는 props가 추가되었습니다. 코드는 간단합니다. loginState(로그인 여부)가 거짓이라면 redirectPath(기본값 = "/")로 사용자를 라우트 시켜주고 아닌 경우에는 아래의 요소(로그인 사용자만 접근할 수 있는 라우트)로 접근할 수 있도록 Outlet를 반환해줍니다.
아래는 위에서 말씀드린 컴포넌트의 레이아웃화와 Private Route가 섞여 있는 실제 Router입니다.
const Router: React.FC = () => {
const { isAuthenticated } = useAuthenticationContext();
return (
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Main />} />;
<Route element={<ProtectedRoute loginState={isAuthenticated} />}>
<Route path="/space" element={<LectureSpace />} />;
<Route path="/upload" element={<LectureUpload />} />;
<Route path="/setting" element={<LectureSetting />} />;
<Route path="/mypage" element={<Mypage />} />;
</Route>
<Route path="*" element={<Main />} />
</Route>
<Route element={<ProtectedRoute loginState={isAuthenticated} />}>
<Route path="/stream" element={<LectureStream />} />;
</Route>
<Route path="/redirect" element={<Redirect />} />;
</Routes>
</BrowserRouter>
);
};
export default Router;
공통적인 Header가 필요한 페이지의 경우에 Layout으로 둘러싸인 Route 안에 있으며 이 안에 로그인 사용자만 접근 가능한 페이지에 대해서 ProtectedRoute(PrivateRoute)와 login 여부가 props로 제공됨을 살펴볼 수 있습니다.