private route 구현

jwt token의 유효성을 검증하는 유틸리티 클래스를추가한다.

src/utils/JwtUtils.ts

import jwtDecode from "jwt-decode";

export class jwtUtils {
  static isAuth(token: any) {
    if (!token) {
      return false;
    }
    const decoded: any = jwtDecode(token);
    // console.log(decoded);
    if (decoded.exp > new Date().getTime() / 1000) {
      return true;
    } else {
      return false;
    }
  }
}  

routes 폴더에 라우팅 패스를 명시한다.

src/routes/index.js

export const ROUTES_PATH = {
  Main: "/",
  BoardRegister: "/board-register",
  BoardView: "/board-view/:id",
  BoardEdit: "/board-edit/:id",
  Login: "/login",
  SignUp: "/signUp"
}

PrivateRoute 컴포넌트를 생성한다. 이 컴포넌트는 인증된 사용자만 통과시킨다.

src/routes/PrivateRoute.jsx

import React from 'react';
import {Redirect, Route} from "react-router-dom";
import {useSelector} from "react-redux";
import {ROUTES_PATH} from "./index";
import {jwtUtils} from "../utils/jwtUtils";

const PrivateRoute = (props) => {
  // BrowseRouter로 부터 넘어오는 props를 파악하는게 중요.
  // path, location ...
  console.log(props);

  const { component: RouteComponent, ...rest } = props;

  const token = useSelector(state => state.Auth.token);

  // 아래 view가 리턴되지 않도록 한다.
  // redirectUrl은 로그인이 성공후 돌아갈 화면이다.
  if (!jwtUtils.isAuth(token)) {
    return <Redirect to={`${ROUTES_PATH.Login}?redirectUrl=${props.path}`} />
  }

  return (
    <Route
      {...rest}
      render = {
        routeProps => <RouteComponent {...routeProps} />
      }
    />
  );
}

export default PrivateRoute

인증 로직 적용

App.jsx에 인증 로직을 적용한다.

useEffect 훅에서 token을 가져와서 유효성을 검증하고 isAuth 상태를 정의한다. 이 상태를 가지고 우측 상단 로그인 혹은 로그아웃 버튼을 구현한다.

라우팅에서 게시판 글등록과 게시판 수정에는 인증로직이 필요하므로, Route가 아니라 앞에서 만든 PrivateRoute를 적용하였다.

src/App.jsx

function App(props: any) {
  const [isAuth, setIsAuth] = useState(false);
  const dispatch = useDispatch();
  const token = useSelector((state: any) => state.Auth.token);

  useEffect(() => {
    if (jwtUtils.isAuth(token)) {
      setIsAuth(true);
    } else {
      setIsAuth(false);
    }
  }, [token]);

  const logout = () => {
    dispatch(setToken(''));
  }

  return (
    <>
      <BrowserRouter>
        <Container fluid className="p-0">
          <Navbar bg="dark" variant="dark" expand="lg">
            <Link to="/" className="navbar-brand">HOME</Link>
            <Navbar.Toggle aria-controls="basic-navbar-nav" />
            <Navbar.Collapse id="basic-navbar-nav">
              <Nav className="mr-auto flex-grow-1">
                <Link to="/board-list" className="nav-link">게시판</Link>
                <Link to="/board-register" className="nav-link">글등록</Link>
                <span className="flex-grow-1"></span>
                {
                  isAuth ? <Nav.Link onClick={logout}>로그아웃</Nav.Link> :
                    <Link to="/login" className="nav-link">로그인</Link>
                }

              </Nav>
            </Navbar.Collapse>
          </Navbar>
        </Container>
        <Container fluid className="px-3 py-2">
          <Switch>
            <Route exact path="/" component={Home}></Route>
            <Route path="/board-list" component={BoardList}></Route>
            <PrivateRoute path="/board-register" component={BoardRegister}></PrivateRoute>
            <Route path="/board-view/:id" component={BoardView}></Route>
            <PrivateRoute path="/board-edit/:id" component={BoardEdit}></PrivateRoute>
            <Route path="/login" component={Login}></Route>
            <Route path="/sign-up" component={SignUp}></Route>
          </Switch>
        </Container>
      </BrowserRouter>
      <ToastContainer />
    </>
  );
}