Today Sangmin Learned
article thumbnail
728x90

어제 급하게 달렸던 소프트웨어공학 프로젝트가 마무리되었다. 계속 까먹고 있었는데 제한 접근 라우팅은 로그인 기능이 있는 대부분의 서비스에서 필수로 요구되는 기능이다. 나는 localStorage를 사용해서 구현했다.

 

App.js

import React from "react";
import { Route } from "react-router-dom";
import Manager from "../src/pages/Manager";
import Main from "../src/pages/Main";
import Start from "./pages/Start";
import Login from "./pages/Login";
import Complete from "./pages/Complete";
import Order from "./pages/Order";
import Staff from "./pages/Staff";
import AuthRoute from "./components/Auth/AuthRoute";
import StaffLogin from "./pages/StaffLogin";

export default function App() {
  return (
    <div>
      <Route exact path="/main" component={Main} />
      <AuthRoute version={1} exact path="/manager" component={Manager} />
      <Route exact path="/start" component={Start} />
      <Route path="/login" render={(props) => <Login {...props} />} />
      <Route path="/complete" component={Complete} />
      <Route path="/order" component={Order} />
      <Route exact path="/StaffLogin" component={StaffLogin} />
      <AuthRoute version={2} exact path="/Staff" component={Staff} />
    </div>
  );
}

다른 페이지는 일반 Route로, 주문 목록과 메뉴 추가/수정 등을 담당하는 관리자/직원 페이지는 AuthRoute로 구현했다. version이라는 파라미터는 왜  쓰인 건지 아래 AuthRoute.js를 보면서 확인해보자.

AuthRoute.js

import React from "react";
import { Route, Redirect } from "react-router-dom";
import isLogin1 from "../utils/isLogin1";
import isLogin2 from "../utils/isLogin2";

export default function AuthRoute({ version, component: Component, ...rest }) {
  if (version === 1) {
    return (
      <Route
        {...rest}
        render={(props) =>
          isLogin1() ? <Component {...props} /> : <Redirect to="/login" />
        }
      />
    );
  } else if (version === 2) {
    return (
      <Route
        {...rest}
        render={(props) =>
          isLogin2() ? <Component {...props} /> : <Redirect to="/stafflogin" />
        }
      />
    );
  }
}

코드가 좀 쓸데없이 길어진 경향이 있다. version이라는 파라미터를 하나 더 넣어서 1과 2에 따라 각각 /manager(관리자 페이지), /staff(직원 페이지)를 나눴다. Redirect하는 곳이 달랐기 때문이다. 충분히 하나로 줄일 수 있었던 것 같은데 어제는 그냥 마감이 다가오니 급해서 저렇게 했다.

 

간단하게 얘기하면 isLogin1 & isLogin2 함수가 실행이 된다면 이동시키고 그렇지 않다면 /login & /stafflogin 페이지로 리다이렉트를 시키는 것이다.

 

isLogin1.js

const isLogin1 = () => !!localStorage.getItem("token1");
export default isLogin1;

isLogin함수는 localStorage에 token1이라는 값을 저장하고 있는지를 !!localStorage.getItem으로 검사하는 함수이다. !!는 Boolean 타입으로 변경하는 방법이다. 이제 token1을 어디서 저장했는지만 보면 된다.

 

pages/Login.js

// ..
  const joinHandler = (e) => {
    e.preventDefault();
    try {
      const data = { id: Id, pw: Password, actor: "admin" };
      axios
        .post("/auth/", JSON.stringify(data), {
          headers: {
            "Content-Type": "application/json",
          },
        })
        .then((res) => {
          console.log(res);
          console.log("res.data.accessToken : " + res.data.jwt_token);
          localStorage.setItem("token1", res.data.jwt_token);
          axios.defaults.headers.common["Authorization"] =
            "Bearer " + res.data.jwt_token;
          history.push("/manager");
        })
        .catch((ex) => {
          console.log("login request fail: " + ex);
        })
        .finally(() => console.log("login request end"));
    } catch (e) {
      console.log(e);
    }
  };

  useEffect(() => {
    console.log("LoginPage render ...");
    localStorage.setItem("token1", "");
    localStorage.setItem("token2", "");
  }, []);

// (후략)

로그인 페이지인 pages/Login.js의 코드 일부를 가져왔다. 이 코드는 이미 서버와의 API통신(/auth/로 POST)이 완료된 코드이므로 로컬에서는 테스트할 수 없다는 점을 먼저 적어둔다. /auth/ API에서 요구하는 id, pw, actor 데이터(id: Id로 적은 이유는 Id값을 useState를 통해 input값에 적은 값을 그대로 setState를 했기 때문이다. 중요한 내용은 아니니 생략)를 POST로 보낸 뒤 성공했을 때(.then()) res.data.jwt_token을 localStorage에 저장한다(setItem 메소드를 이용).

 

그리고 axios.defalts.headers.common['Authorization']에 이 값을 넣어줌으로써 헤더값에 JWT 토큰이 들어갈 수 있게 하였다. 이렇게, 서버에서 미리 갖고 있던 관리자 계정 데이터(예를 들면 id: admin, 비번: 1234)와 일치하는 경우 일련의 과정을 거친 뒤 /manager로 푸시하는 것이다.

 

아래 useEffect문을 보면 비어있는 의존성 배열을 통해 첫 렌더링시에만 실행되도록 한 것을 확인할 수 있다. 처음에 들어갈 때 token1과 token2(직원 페이지: 코드가 거의 비슷하므로 생략)를 전부 비워줌으로써 로그아웃을 간접적으로 구현했다. 결과적으로 페이지에서 서버가 갖고 있는 관리자 아이디/비밀번호 데이터와 정확히 일치할 때만 localStorage.setItem을 한 뒤 /manager로 history.push를 하는 것이다. 따라서 그냥 주소창에 쳐서 접근할 경우 아래와 같이 강제로 로그인 페이지로 리다이렉트 된다.

로그인하지 않은 상태에서 JWT토큰을 필요로 하는 페이지로 접근할 경우 로그인 페이지로 리다이렉트됨

 

그리고 제대로된 로그인을 했을 때는 아래와 같이 콘솔에 jwt 토큰을 띄우면서 원하는 페이지로 리다이렉트한다. 주문 현황 페이지에 아무것도 없는 이유는 임시 데이터가 아무것도 없고 직접 주문을 하고 결제완료창까지 뜬 후에야 이쪽에서 보일 API로 POST가 이뤄지기 때문이다.

제대로 입력할 경우 JWT 토큰이 필요한 위치로 리다이렉트됨

오늘은 이렇게 localStorage.getItem의 여부를 확인하는 방식으로 제한 접근 라우팅을 구현해봤다.

전체 코드가 궁금한 분들을 위해 해당 프로젝트의 레포지토리를 남기며 글을 마친다.

 

https://github.com/collinahn/cafe_kiosk_solution/tree/release/v1.1

 

GitHub - collinahn/cafe_kiosk_solution

Contribute to collinahn/cafe_kiosk_solution development by creating an account on GitHub.

github.com

 

profile

Today Sangmin Learned

@steadily-worked

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!