사용자의 로그인 상태나 권한에 따라 접근할 수 있는 경로가 다르다. 물론 서버쪽에서 체크를 해주긴 하지만, 해당 URL로 넘어간다는 것 자체를 프론트측에서 막아주는 것이 바람직하다. 이를 그림으로 표현하면 다음과 같다.
1. 접근 제한 (Access Control)
PrivateRoute는 로그인한 사용자에게 제공되는 루트이고, 만약 로그인하지 않은 사용자가 이 루트로 접근하고자 한다면 로그인 페이지로 Redirection한다.
반대로, PubilcRoute는 로그인 여부와 상관없이 사용자에게 제공되는 루트이다. 아래 그림과 같이 이미 로그인한 사용자가 해당 루트로 접근하는 것을 막고자 한다면 restricted 옵션을 줘서 제어할 수 있다.
2. 구현
1) 로직
- App.jsx의 Router 컴포넌트 안에 PrivateRoute 컴포넌트와 PublicRoute 컴포넌트를 추가한다.
- 각각의 루트 컴포넌트 안에 로그인을 검증하는 isLogin()함수를 삽입하여 Rendering할 것인지, 혹은 Redirection할 것인지 판별한다.
- PrivateRoute 컴포넌트는 로그인 페이지로, PublicRoute 컴포넌트는 메인 페이지로 Redirection한다.
라우터 | 옵션 | 조건 | 리디렉션 경로 |
Private Route | - | 로그인한 상태에서만 접근 가능: 마이 페이지 | 로그인 페이지 |
Public Route | restricted X | 로그인 여부와 관계없이 접근 가능: 메인 페이지 | - |
restricted O | 로그인한 상태에선 접근 불가능: 로그인, 회원가입 | 메인 페이지 |
2) 코드
먼저 로그인 여부를 검증하는 함수를 작성하여 모듈화시킨다.
난 토큰을 쿠키에 셋팅했기 때문에 다음과 같이 작성했으며, 실질적인 인증(토큰 유효성 검사)는 서버에서 이루어진다.
만약 쿠키를 사용하고자 한다면 $ npm i js-cookie 명령어를 통해 모듈을 설치해줘야 한다.
로그아웃시 프론트에서 토큰을 지우는 게 LocalStorage에 비해 귀찮으므로 간단히 구현하고자 한다면 LocalStorage에 토큰을 넣는 것도 하나의 방법이다.
src/utils/isLogin.js
import Cookies from 'js-cookie';
const isLogin = () => !!Cookies.get('token')
export default isLogin;
src/components/PrivateRoute.jsx
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { isLogin } from '../utils/isLogin';
const PrivateRoute = ({component: Component, ...rest}) => {
return (
// Show the component only when the user is logged in
// Otherwise, redirect the user to /signin page
<Route {...rest} render={props => (
isLogin() ?
<Component {...props} />
: <Redirect to="/signin" />
)} />
);
};
export default PrivateRoute;
src/components/PublicRoute.jsx
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import isLogin from '../utils/isLogin';
const PublicRoute = ({component: Component, restricted, ...rest}) => {
return (
// restricted = false meaning public route
// restricted = true meaning restricted route
<Route {...rest} render={props => (
isLogin() && restricted ?
<Redirect to="/dashboard" />
: <Component {...props} />
)} />
);
};
export default PublicRoute;
이제 이 두 개의 컴포넌트를 APP.jsx 내부의 Router 컴포넌트 안에 집어넣는다.
src/App.jsx
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; // react-router-dom:
import Main from './containers/Main';
import Register from "./containers/Register";
import Login from "./containers/Login";
import MyPage from "./containers/MyPage";
import PublicRoute from "./components/PublicRoute";
import PrivateRoute from "./components/PrivateRoute";
import NotFound from "./containers/NotFound";
const App = () => {
return (
<Router>
<Switch>
<PublicRoute restricted={false} component={Main} path="/" exact />
<PublicRoute restricted={true} component={Register} path="/register" exact />
<PublicRoute restricted={true} component={Login} path="/login" exact />
<PrivateRoute component={MyPage} path="/mypage" exact />
<Route component={NotFound}/>
</Switch>
</Router>
);
}
export default App;
3. 참고
'웹 > React.js' 카테고리의 다른 글
[React.js] 좋아요 하트 아이콘 구현하기 (0) | 2021.05.18 |
---|---|
[React.js] API를 연동한 Pagination 구현 (4) | 2021.05.16 |
[React.js] 간략한 리덕스(Redux) 정리 (0) | 2021.04.20 |