TIL - React Router Config

김수지·2020년 4월 18일
1

TILs

목록 보기
32/39

Today What I Learned

Javascript를 배우고 있습니다. 매일 배운 것을 이해한만큼 정리해봅니다.
오늘은 React Router 패키지 중 Config에 대해 적어봅니다.


지난 2주는 회사에 적응하느라 정신이 없었다. 프로젝트를 진행해 봤다고는 하나 역시나 현업에서 코드를 보니 @.@ 맙소사 아직 모르는 것이 참 많고, 업무를 위해 급히 배워야 할 것들도 많았다. 심지어 알던 것도 버벅거리는 나 자신을 보면서... 아? ㅋㅋㅋ
그래서 일단 이번 달 안으로 React 일부 기능(hoc, ref 등), Hooks, React Router Config, Styled Component, Emotion.js 등을 학습 or 재학습하려고 한다.

오늘은 React Router Config이다.

1. What is React Router Config?

  • 리액트를 사용하면서 SPA 구성을 위해서 대부분 리액트 라우터 돔 패키지를 사용할 것이다. (지난 프로젝트처럼 리액트 네이티브인 경우에는 리액트 네비게이션을 사용했지만 웹에서는 아무래도 리액트 라우터일 것이다.)
  • 리액트 라우터에서는 <Router>, <Link>, <Redirect> 등을 이용해 라우팅을 하는데 보통 커스텀 컴포넌트 안에 attributes로 어느 주소로 이동해 어느 컴포넌트를 보여줄 것인지를 정한다.
  • 회사 코드를 보다 보니 리액트 라우터 패키지 중 config라는 기능을 사용하고 있어서 검색을 해보았다.

    "Static route configuration helpers for React Router."

  • "리액트 라우터를 사용할 때 정적으로 라우트 환경 설정을 할 수 있는 헬퍼 기능" 이라고 나 나름대로 번역해 보았다. 위에서 말한 것처럼 컴포넌트 형식으로 라우팅을 정의하는 방식 말고, file config 형식으로 라우팅에 필요한 컴포넌트와 주소 그리고 기타 참조값들을 정리해둘 수 있는 기능이다.

2. Why React Router Config?

  • 패키지 Readme를 보니 react router config를 만든 동기를 알 수 있었다.
  • React Router v4가 소개된 이래로 중앙화 된 라우트 환경 설정은 더 이상 사용할 필요가 없어지게 되었지만 그럼에도 1)화면 이동 시 라이프사이클을 통해서 서버사이드 렌더링을 해야할 때나 2)이름 기준으로 라우트를 link해야 할 때 혹은 3)정적 분석이 필요할 때는 여전히 어플리케이션이 가진 모든 라우팅을 나열하고 파악하는 것이 필요하다. 바로 react router config는 이런 경우들을 지원하는 패키지라고 한다.
  • 확실히 어플리케이션 파일들을 모두 열어서 이동 가능한 라우트를 파악하는 것에 비해 통합된 한 파일에서 이동 지점을 파악할 수 있다는 것에서는 매력적이라고 생각했다. 회사 코드도 서버사이드 렌더링을 하고 있기 때문에 위에서 언급한 1)의 상황인 것 같았다.

3. How to use React Router Config

1. 설치하기

  • react router config를 사용하기 위해서는 기본적인 설치가 필요하다.
    $ npm install --save react-router-config 혹은 yarn add react-router-config
  • 그런 다음 리액트의 경우에는 비구조화 할당이 가능하므로 아래와 같이 import해서 사용한다. 비구조화가 어려운 상황이라면 require를 사용할 수도 있다.
    // using an ES6 transpiler, like babel
    import { matchRoutes, renderRoutes } from "react-router-config";
    // not using an ES6 transpiler
    var matchRoutes = require("react-router-config").matchRoutes;

2. 기본 구조

  • 아래 기본 구조를 보면 routes라는 배열을 선언/할당해서 사용한다. 최상단에는 루트 격의 컴포넌트를 배치하고 사용하게 될 라우트에 주소와 절대주소여부 그리고 사용될 컴포넌트를 지정하면된다.
  • 만약 현재 위치에서 한 뎁스를 더 들어가야 하는 라우트가 있다면 routes에 꼬리를 물리면서 해당 config를 늘려가면 된다. 아래 예시에서 Root> Home, Root> Child 혹은 Child> GrandChild 의 경우가 그렇다.
  • 만약 현재 라우트에서 한 뎁스 깊이 이동할 수 있는 라우트가 여러개라면 routes의 값인 배열에 계속 추가해주면 된다. 아래 예시에서 Root> Home, Child 케이스를 참고하면 된다.
    const routes = [
     {
       component: Root,
       routes: [
         {
           path: "/",
           exact: true,
           component: Home
         },
         {
           path: "/child/:id",
           component: Child,
           routes: [
             {
               path: "/child/:id/grand-child",
               component: GrandChild
             }
           ]
         }
       ]
     }
    ];

3. 2가지 API

1. matchRoutes

  • react route config의 api 중 하나는 matchRoutes이다.
// api 형태
matchRoutes(routes, pathname)
  • matchRoutes는 어떤 라우트 주소에 부합하는 라우트 정보를 리턴한다.
    • 전달되는 파라미터 2가지: routes, pathname
      • routes: 어플리케이션의 routes, 배열 형식
      • pathname: 세부 url, 문자열 형식
    • 리턴값: 매칭된 라우트 정보를 배열로 리턴
      import { matchRoutes } from "react-router-config";
      const branch = matchRoutes(routes, "/child/23");
      /* 리턴값 -> [ routes[0], routes[0].routes[1] ]
      routes[0] -> 전체 라우트 객체
      routes[0].routes[1] -> "/child/23"를 기준으로 찾은 라우트 "/child/:id"의 정보들 즉,
      {path: "/child/:id",
       component: Child,
         routes: [
           {path: "/child/:id/grand-child",
            component: GrandChild}
        ]
      }
      */
  • 이렇게 matchRoutes의 리턴값을 할당 받은 branch는 routesmatch라는 속성이 있다.
    • routes: 전체 라우트 객체, 참고로 주어짐
    • match: 어떤 라우트에 매칭이 되었는지에 대한 정보 (기존 react router에서 제공되는 match 객체와 동일)
      branch[0].match.url;
      branch[0].match.isExact;
      // match에는 url, isExact, params 등 라우트에 관련된 정보가 담겨있다.

2. renderRoutes

  • matchRoutes를 올바르게 사용하기 위해서는 <Route> 대신에 renderRoutes를 통해서 routes 구조를 뿌려(?)줘야 한다.
  • 전달되는 파라미터 3가지: routes, extraProps, switchProps
    • routes: 렌더할 라우트
    • extraProps: 라우트에 추가할 props가 있을 때 객체로 전달, 디폴트는 빈 객체
    • switchProps: 라우트에서 변경할 props가 있을 때 객체로 전달, 디폴트는 빈 객체
  • 예시 구조
import { renderRoutes } from "react-router-config";

const routes = [
  {
    component: Root,
    routes: [
      {
        path: "/",
        exact: true,
        component: Home
      },
      {
        path: "/child/:id",
        component: Child,
        routes: [
          {
            path: "/child/:id/grand-child",
            component: GrandChild
          }
        ]
      }
    ]
  }
];

const Root = ({ route }) => (
  <div>
    <h1>Root</h1>
    {/* 자식 라우트들이 렌더할 수 있도록  renderRoutes 실행 */}
    {renderRoutes(route.routes)}
  </div>
);

const Home = ({ route }) => (
  <div>
    <h2>Home</h2>
  </div>
);

const Child = ({ route }) => (
  <div>
    <h2>Child</h2>
    {/*  자식 라우트들이 렌더할 수 있도록  renderRoutes 실행  */}
    {renderRoutes(route.routes, { someProp: "these extra props are optional" })}
  </div>
);

const GrandChild = ({ someProp }) => (
  <div>
    <h3>Grand Child</h3>
    <div>{someProp}</div>
  </div>
);

ReactDOM.render(
  <BrowserRouter>
    {/* renderRoutes에 루트 라우트를 뿌려주면서 앱 구조 나열 */}
    {renderRoutes(routes)}
  </BrowserRouter>,
  document.getElementById("root")
);

4. 프로젝트에 적용하기

  • 예시가 있긴 하지만 Root, Home, Child, GrandChild 뭐 이런 식이라 다른 예시는 없나 찾다보니 react router training 홈에서 좋은 예시가 있어서 가져와 봤다.
  • 구조를 조금 더 이해해 보려면 최하위 컴포넌트인 Bus나 Cart 아래 자식 라우트를 하나 더 추가하는 연습을 해보면 좋다.
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

const routes = [
  {
    path: "/sandwiches",
    component: Sandwiches
  },
  {
    path: "/tacos",
    component: Tacos,
    routes: [
      {
        path: "/tacos/bus",
        component: Bus
      },
      {
        path: "/tacos/cart",
        component: Cart
      }
    ]
  }
];

export default function RouteConfigExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/tacos">Tacos</Link>
          </li>
          <li>
            <Link to="/sandwiches">Sandwiches</Link>
          </li>
        </ul>

        <Switch>
          {routes.map((route, i) => (
            <RouteWithSubRoutes key={i} {...route} />
          ))}
        </Switch>
      </div>
    </Router>
  );
}

function RouteWithSubRoutes(route) {
  return (
    <Route
      path={route.path}
      render={props => (
        // pass the sub-routes down to keep nesting
        <route.component {...props} routes={route.routes} />
      )}
    />
  );
}

function Sandwiches() {
  return <h2>Sandwiches</h2>;
}

function Tacos({ routes }) {
  return (
    <div>
      <h2>Tacos</h2>
      <ul>
        <li>
          <Link to="/tacos/bus">Bus</Link>
        </li>
        <li>
          <Link to="/tacos/cart">Cart</Link>
        </li>
      </ul>

      <Switch>
        {routes.map((route, i) => (
          <RouteWithSubRoutes key={i} {...route} />
        ))}
      </Switch>
    </div>
  );
}

function Bus() {
  return <h3>Bus</h3>;
}

function Cart() {
  return <h3>Cart</h3>;
}
profile
선한 변화와 사회적 가치를 만들고 싶은 체인지 메이커+개발자입니다.

3개의 댓글

comment-user-thumbnail
2020년 4월 21일

수지님 화이팅~~~ 나중에 저도 알려주세요!

답글 달기
comment-user-thumbnail
2020년 8월 27일
  1. How to use React Router Config
  2. 설치하기
    에서 yarn 으로 설치하는 방식에서 add가 빠진거 같아요 ~
1개의 답글