큰 꿈은 파편이 크다!!⚡️

react router dom6 적용하기 본문

Web FE

react router dom6 적용하기

wood.forest 2022. 2. 27. 14:35

React를 사용한다면 아주 익숙할 React Router가 2020년 12월에 v6을 릴리스했다. 메이저 버전넘버가 바뀐 만큼 breaking-changes가 있었고, 그동안 무지성으로 이 패키지를 사용했고 공식문서에 소홀했던 것을 반성하며 v5에서 v6으로 넘어가면서 알게 된 몇가지 내용을 정리해보려 한다.

 

🐓 react-router-dom을 뭣모르고 사용했더니 일어난 일

react-router-dom vs react-router-native

$ npm install react-router-dom
# or, for a React Native app
$ npm install react-router-native

웹 개발을 위해 react를 사용한다면 react-router-dom을 설치한다. 위 코드에서 알 수 있듯 react-router-native는 react native를 위한 패키지이다.

 

react-router vs react-router-dom

import { Link } from "react-router"
import { Link } from "react-router-dom"

위 두가지 import방법의 결과는 동일하다. 그렇다고 아무거나 쓰면 나같은 닭대가리가 된다.
react-router = dom + native라고 볼 수 있다. 즉, 우리는 웹 개발에 필요한 게 다 들어있는 dom만 사용하면 되지 굳이 native의 내용까지 포함하고 있는 react-router를 import할 필요가 없다.

📈 v5 -> v6 가즈아

이제 기존에 사용하던 v5를 v6으로 올리는 작업에 착수한다.
모든 내용은 공식문서에서 더 자세히, 더 많이 확인할 수 있다. 이 글을 작성하면서 공식문서를 보는 연습을 할 수 있었다.

 

Switch 바로 아래에 있는 Redirect 삭제

현재(v5) 기준으로 약간의 밑작업을 한다. Switch 바로 아래에 있는 Redirect는 삭제한다.
Switch 아래 아래에 있는 Redirect는 둬도 상관없다. 어차피 나중에 Redirect 자체가 없어질 예정이다^^

// Change this:
<Switch>
  <Redirect from="about" to="about-us" />
</Switch>

// to this:
<Switch>
  <Route path="about" render={() => <Redirect to="about-us" />} />
</Switch>

PrivateRoute 리팩토링

로그인된 사용자만 접근 가능한 페이지, 등을 만들 때 custom하게 만드는 RouteProtected Route, Private Route 라고 한다. 이런 Plain Route가 아닌 것들을 Switch 아래에서 일단 제거한다.

드디어 v6으로 패키지 업데이트

를 하기 전에 선행되어야 할 작업이 있다. 사용중인 react의 버전이 v16.8이상인지 확인하고, 미만이라면 react버전을 먼저 맞게 업데이트하기!

yarn upgrade react-router-dom --latest

나는 yarn을 사용하기 때문에 최신 패키지로 업데이트하는 명령어를 사용했다.

Switch -> Routers

Switch가 Routers로 바뀌었고, exact가 삭제되었다. (이름이 바뀐 것이 꽤 많은데, 흐름, 가독성 등을 고려해서 변경한 것이라고 함!) Routers안에 Router가 있으니 편-안하다.

function AppV5() {
  return (
    <Switch>
      <Route exact path="/">
        <Home />
      </Route>
      <Route path="/about">
        <About />
      </Route>
    </Switch>
  );
}

function AppV6() {
  return (
    <Routers>  // 😮
      <Route path="/">
        <Home />
      </Route>
      <Route path="/about">
        <About />
      </Route>
    </Routers>
  );
}

Route의 변경사항

Route에 children을 넣어주는 방식이 element를 넣어주도록 바뀌었다.
· Element: DOM 노드를 나타내는 객체
· Component: Input을 받고 element를 반환하는 함수/클래스
결과적으로 해당 컴포넌트에 props를 전달하기 편리해졌고,
기존에 있던 <Route children>은 v3에서 제공했던 nesting routes를 사용할 수 있는 예약어가 되었다.

function AppV5() {
  return (
    <Switch>
      <Route exact path="/" component={ Home } />
      <Route path=":userId" component={Profile} passProps={{ animate: true }} />
      <Route
        path=":schoolId"
        render={routeProps => (
          <School routeProps={routeProps} animate={true} />
        )}
      />
    </Switch>
  );
}

//V6
function AppV6() {
  return (
    <Routers> 
      <Route path="/" element={ <Home/> } />  // 😮
      <Route path=":userId" element={<Profile animate={true} />} />
      <Route path=":schoolId" element={<School animate={true} />} />

      <Route path="users" element={<Users />}>
        <Route path="me" element={<OwnUserProfile />} />  // nested😮
        <Route path=":id" element={<UserProfile />} />
      </Route>
    </Routers>
  );
}

nested route를 사용하는 경우에는 <Outlet />을 사용해서 child route를 렌더링해줄 수 있다.

function Users() {
  return (
    <div>
      <nav>
        <Link to="me">My Profile</Link>
      </nav>

      <Outlet />
    </div>
  );
}

<Route path>를 정하는 방법도 간소화되었다. 현재 v6에서는 다이나믹 라우팅 (:id) 스타일과, path의 마지막에만 사용할 수 있는 와일드카드 (*)만을 제공한다. 기존에 regex를 사용해서 id값을 숫자로 고정시킨다던가.. 하는 방식은 더이상 쓸 수 없다🥲 please know that we plan to add some more advanced param validation in v6 at some point 라고 하니 기다려봐야겠다.

//👍 All of the following are valid route paths in v6:
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*

//👎 The following RegExp-style route paths are not valid in v6:
/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*

useHistory 대신 useNavigate

이건 단순히 이름 뿐만 아니라 사용 방법도 조금 바뀌었다.

//from this:
let history = useHistory();
function handleClick() {
  history.push("/home");
}

//to this:
let navigate = useNavigate();
function handleClick() {
  navigate("/home");  //여기까진 👍
}

만약 state를 같이 전달한다면?

//from this:
let history = useHistory();
function handleClick() {
  history.push({
    path: "/home",
    state: { name: "John" }
  });
}

//to this:
let navigate = useNavigate();
function handleClick() {
  navigate("/home", {   //😮
    state: { name: "John" }
  }); 
}

go, goback, goforward등은 어떻게? navigate로 전부 해결한다.

//from this:
<button onClick={goBack}>Go back</button>
<button onClick={goForward}>Go forward</button>
<button onClick={() => go(2)}>Go 2 pages forward</button>

//to this: 😮
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>Go forward</button>
<button onClick={() => navigate(2)}>Go 2 pages forward</button>

Redirect -> Navigate

Redirect를 삭제할 차례다. Redirect의 default는 replace였고 Navigate의 default는 push이기 때문에 적절하게 교차해서 바꿔준다.
· replace: stack top의 페이지를 교체한다.
· push: stack top에 페이지를 쌓는다.

// Change this:
<Redirect to="about" />
<Redirect to="home" push />

// to this:
<Navigate to="about" replace />
<Navigate to="home" />

Link 개선

v5에서는 현재 URL이 /users일 때, <Link to="me"> -> <a href="/me"> 를 렌더링했고,
현재 URL이 /users/일 때(=trailing slash를 포함할 때)는, <Link to="me"> -> <a href="/users/me"> 를 렌더링해서 혼란스러운 경우가 있었다.
v6에서는 이 문제가 개선되어 현재 url의 trailing slash와 관계없이 항상 <Link to="me"> -> <a href="/me">를 렌더링한다.

또한 ".."와 같은 상대 URL을 지원한다. 단, <Route path>가 하나 이상의 segment와 매칭될 때에는 모든 segment를 제거하고 부모의 route path를 따를 것이다.

function App() {
  return (
    <Routes>
      <Route path="users">
        <Route
          path=":id/messages"
          element={
            // This links to /users
            <Link to=".." />
          }
        />
      </Route>
    </Routes>
  );
}

🙀 맺으며

· 너무 많기도 하고 내가 사용하지 않는 기능 (config)도 있어서 모든 변경사항을 담지는 않았다! 하지만 path match등 메인 페이지에 언급되지 않은 내용들도 변경된 것으로 보아 버전업을 진행하면서 맞추어나가야 할 것으로 보인다.
· 이번 기회를 통해 몰랐던 단어, 차이점 등의 내용도 정리할 수 있어 의미있었다.

· 근데 이거 코드블럭 언어가 왜이럴까.. 다른 방안을 찾아봐야겠다 ㅠㅠ;

반응형