React Router 제한적 경로 접근

public_danuel·2020년 3월 26일
12
post-thumbnail

웹사이트를 작성하다 보면 '로그인 한' 혹은 '관리자만' 혹은 그 외의 제한적으로 접근 가능한 페이지를 구현할 필요가 생깁니다.
여러 시행착오를 겪으며 제가 현재 사용중인 방법을 소개하고자 합니다.

// RestrictRoute.tsx
import React, { FC } from "react";

interface Props {
  path: string | string[];
  component: FC;
  fallback: FC;
  exact?: boolean;
  isAllow: (user: User) => boolean;
}

export const RestrictRoute = ({
  component: Component,
  fallback: Fallback,
  isAllow
}: Props) => {
  const user = useUser();
  return isAllow(user) ? <Component /> : <Fallback />;
};

RestrictRoute 컴포넌트는 해당 페이지에 접근하면 인증 여부에 따라 컴포넌트이며, 기본 4개의 Props와 1개의 추가적인 Props를 받습니다.

path 는 경로를 의미하며 React Router 기반으로 사용하던 경로를 그대로 사용할 수 있습니다.
component 는 인증 성공시의 컴포넌트입니다.
fallback 은 인증 실패시의 컴포넌트입니다.
exact 는 정확히 일치하는 경로일 때에만 컴포넌트를 렌더링 할지 결정합니다.
isAllow 함수는 인증 여부를 판정하는 역할을 합니다.

useUser는 예시일 뿐이니, User 뿐만 아니라 더 많은 데이터를 가진 무엇인가를 isAllow 함수의 파라미터로 전달해도 괜찮습니다.

이 컴포넌트에는 path와 exact를 사용하는 부분이 없는데, 이 부분은 잠시 후 소개하겠습니다.

// App.tsx
import React from "react";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import { RestrictRoute } from "./RestrictRoute";

const GoToMainPage = () => <Redirect to="/" />;

const App = () => (
  <BrowserRouter>
    <Switch>
      <Route path="/" component={MainPage} />
      <RestrictRoute
        path="/sign-in"
        component={SignInPage}
        fallback={GoToMainPage}
        exact
        isAllow={user => user.isSignedIn}
      />
      <RestrictRoute
        path="/sign-up"
        component={SignUpPage}
        fallback={GoToMainPage}
        exact
        isAllow={user => user.isSignedIn}
      />
      {/* ... */}
      {/* ... */}
      {/* ... */}
      <Redirect to="/" />
    </Switch>
  </BrowserRouter>
);

실제 사용은 위처럼 할 수 있습니다.

이미 로그인을 한 유저가 로그인(/sign-in) 페이지 혹은 회원가입(/sign-up) 페이지에 접근할 필요는 없을테니, 로그인 여부를 확인한 후 로그인을 이미 했다면 메인(/) 페이지로 이동시킵니다.

React RouterSwitch 컴포넌트는 바로 하위 컴포넌트를 순회하면서 pathexact 조건을 만족하면 전달 받은 component를 바로 렌더링 합니다.
이것을 이용하기 위해 RestrictRoute 컴포넌트는 pathexact 를 받는 것입니다.

활용

// App.tsx
import React from "react";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import { RestrictRoute } from "./RestrictRoute";

const GoToMainPage = () => {
  alert("관리자만 접근 가능한 페이지입니다.");
  return <Redirect to="/" />;
};

const App = () => (
  <BrowserRouter>
    <Switch>
      {/* ... */}
      {/* ... */}
      {/* ... */}
      <RestrictRoute
        path="/admin"
        fallback={GoToMainPage}
        component={AdminPage}
        isAllow={user => user.isAdmin}
      />
      {/* ... */}
      {/* ... */}
      {/* ... */}
    </Switch>
  </BrowserRouter>
);

관리자만 접속 가능한 페이지인 경우도 간단하게 구현할 수 있습니다.
관리자가 아니면 alert(Toast 등도 가능!)로 관리자만 접근 가능한 페이지라고 알릴 수도 있습니다.

결론

interface 선언을 제외하면 몇 라인 되지 않는 이 간단한 컴포넌트는 다양한 요구사항을 만족시킬 수 있습니다.
응용하기에 따라 더 많은 유연성을 확보할 수 있기도 합니다.

이 아이디어가 여러분을 도울 수 있기를 바랍니다.

profile
다뉴하는 코딩

8개의 댓글

comment-user-thumbnail
2020년 3월 26일

좋아요 감사합니다!

답글 달기
comment-user-thumbnail
2020년 3월 26일

직접 Route 컴포넌트를 사용하지 않아도 Prop으로 명시만 잘 해둔다면 path 설정이 된다는 얘기인가요?

1개의 답글
comment-user-thumbnail
2020년 3월 27일

hoc로 withLogin 작성하고 인증 필요한 페이지마다 래핑하는건 어때요?

1개의 답글
comment-user-thumbnail
2020년 3월 29일

예전에 리액트 라우터로 인증 만들때 고민했던 경험이 새록새록 떠오르네요...
나중에 다시 리액트 개발을 하게 되면 참고해봐야겠네요! 감사합니다.

답글 달기
comment-user-thumbnail
2020년 4월 6일

설명이 친절하시네여 좋은 아이디어 얻어갑니당!

답글 달기
comment-user-thumbnail
2020년 4월 8일
답글 달기