-
[React Router/lib.] React Router v6Front-End(Web)/React - 프레임워크(React, Next) 2021. 12. 14. 22:55반응형
오랜만에 React 학습을 진행하는데, React Router가 6버전이 출시되었다고 한다.
기존에 사용하던 문법들이 일부 수정된 것을 감안하여, 이를 한번 훑고자 짧게나마 포스팅을 적는다.
💙 개요
React Router 6버전은, 기존의 5버전에 비해 React 최신문법에 걸맞도록 업데이트 되었다고 공식문서는 소개한다.
특히, React Hooks가 적용되었기에, 이를 사용하려면 React v16.8 이상을 우선 설치해야하며, 5 -> 6버전으로 마이그레이션을 진행하면 된다.
React Router 6버전은 번들 사이즈가 5버전에 비해 약 70%가 감소하였으며, 이는 App 빌드시 큰 이점이 될 것이다.
💙 주요 변경사항
1. <Switch> ➡️ <Routes> 로 변경
- <Route> 요소들을 포함하는 <Routes>로 Wrapper 명칭변경
- <Route>는 component 및 render에 화살표 함수를 사용하지 않고, element 속성에 JSX 컴포넌트를 전달해준다.
- exact 옵션이 삭제되었다. 기본적으로 exact가 적용되며, 모든 하위 path에서 라우팅을 하고 싶다면 path 끝에 *을 붙인다.
- path에 ":id"와 같은 하위경로, "." 및 ".." 와 같은 상대경로도 지정이 가능하다.
// 기존(v5) import { BrowserRouter, Route, Switch } from "react-router-dom"; import Home from "./pages/Home"; import Write from "./pages/Write"; function App() { return ( <BrowserRouter> <Switch> <Route path="/" component={() => <Home />} /> <Route exact path="/write" component={() => <Write />} /> <Route component={() => <div>Page Not Found</div>} /> </Switch> </BrowserRouter> ); } export default App;
// 변경(v6) import React from "react"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import { Main, Page1, Page2, NotFound } from "../pages"; import { Header } from "."; const Router = () => { return ( <BrowserRouter> <Header /> <Routes> <Route path="/" element={<Main />} /> <Route path="/page1/*" element={<Page1 />} /> <Route path="/page2/*" element={<Page2 />} /> <Route path="/*" element={<NotFound />} /> </Routes> </BrowserRouter> ); }; export default Router;
그래서, 기존에 App.tsx를 Routes.tsx로 명명하던 관례를 원복하면 된다.
2. 간편해진 중첩 라우팅
기존에는 중첩 라우팅을 위해서는 페이지, 컨테이너 컴포넌트 각각을 <Switch>로 랩핑하고 내부에 <Route> 컴포넌트를 돌려야 했다.
특히, 컨테이너 컴포넌트는 useRouteMatch() Hooks를 통해 path를 받고 여기에 하위path를 연결시켜야만 했다.
6버전에서는 페이지 단에서 모든 경로를 중첩하고, 중첩영역 렌더링 요소는 <Outlet> 컴포넌트로 그려서 이중작업을 방지할 수 있다.
(이렇게 하면, 통상 App.tsx 최상단의 <Routes> 내에서 모든 라우팅 구조를 중첩할 수 있어 직관적이게 된다.)
// 기존(v5) import { BrowserRouter, Switch, Route, Link, useRouteMatch } from 'react-router-dom'; // App(루트) Component function App() { return ( <BrowserRouter> <Switch> <Route path="/user" component={User} /> </Switch> </BrowserRouter> ); } // Page Component function User() { const { path } = useRouteMatch(); return ( <div> <Switch> <Route path={`${path}/detail`}> <UserDetail /> </Route> </Switch> </div> ); }
import { BrowserRouter, Routes, Route, Outlet } from 'react-router-dom'; // App(루트) Component : Route 집약 function App() { return ( <BrowserRouter> <Routes> <Route path='user' element={<User />} > <Route path='detail' element={<UserDetail />} /> </Route> </Routes> </BrowserRouter> ); } // Page Component : <Outlet> 활용 function User() { return ( <> <Outlet /> </> ) }
3. useLocation, useParams - Router Parameter 사용
Routing을 통해 진입한 페이지의 파라미터를 참조하기 위한 Hooks로 useLocation과 useParams를 제공한다.
useLocation은 pathname(정적 경로), useParams는 :id(동적 경로 혹은 파라미터)를 참조하는데 사용된다.
// useLocaiton import React from "react"; import { Link, useLocation } from "react-router-dom"; import styled from "styled-components"; const HeaderWrapper = styled("header")` margin-bottom: 30px; `; const List = styled("ul")` display: flex; `; const Item = styled("li")` margin-right: 20px; text-transform: uppercase; font-weight: 600; color: ${(props) => (props.selected ? "white" : "black")}; background-color: ${(props) => (props.selected ? "#f1c40f" : "white")}; `; const Header = () => { const { pathname } = useLocation(); return ( <HeaderWrapper> {/* header 태그 */} <List> {/* ul 태그 */} <Item selected={pathname.startsWith("/web")}> {/* li 태그 */} <Link to="/web">Go to Web</Link> </Item> <Item selected={pathname === "/design"}> <Link to="/design">Go to Design</Link> </Item> <Item selected={pathname === "/server"}> <Link to="/server">Go to Server</Link> </Item> </List> </HeaderWrapper> ); }; export default Header;
// useParams import React from "react"; import { useParams } from "react-router"; const WebPost = () => { const { id } = useParams(); return <div>#{id}번째 포스트</div>; }; export default WebPost;
4. useRoutes
기존의 react-router-config가 useRoutes Hooks 으로 대체되었다. (패키지 설치가 불필요해짐)
기존엔, routes 배열을 정의하여 여기에 컴포넌트를 배치하고, renderRoutes 메서드로 Root(App) 컴포넌트에 반영할 수 있다.
// 기존(v5) 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)} </div> ); const GrandChild = ({ someProp }) => ( <div> <h3>Grand Child</h3> <div>{someProp}</div> </div> ); ReactDOM.render( <BrowserRouter> {/* renderRoutes에 가장 처음 정의했던 routes 자체를 뿌려줌으로써 차례로 렌더링될 수 있도록 함 */} {renderRoutes(routes)} </BrowserRouter>, document.getElementById("root") );
이 패턴을, 버전6부터는 useRoutes Hooks를 통해 직접 Root(App)에 라우터를 배치할 수 있다.
<Route>의 중첩 라우팅, useRoutes 둘 다 공식문서에서 권장하는 방법으로 기호에 따라 사용하면 된다.
function App() { let element = useRoutes([ // Route에서 사용하는 props의 요소들과 동일 { path: "/", element: <Home /> }, { path: "dashboard", element: <Dashboard /> }, { path: "invoices", element: <Invoices />, // 중첩 라우트의 경우도 Route에서와 같이 children이라는 property를 사용 children: [ { path: ":id", element: <Invoice /> }, { path: "sent", element: <SentInvoices /> } ] }, // NotFound 페이지는 다음과 같이 구현할 수 있음 { path: "*", element: <NotFound /> } ]); // element를 return함으로써 적절한 계층으로 구성된 element가 렌더링 될 수 있도록 함 return element; }
5. useHistory ➡️ useNavigate
버전5까지 history.push, history.replace 를 했던 useHistory Hooks가 useNavigate로 대체되었다.
// 기존(v5) import { useHistory } from "react-router-dom"; function App() { let history = useHistory(); function handleClick() { history.push("/home"); } return ( <div> <button onClick={handleClick}>go home</button> </div> ); }
// 변경(v6) import { useNavigate } from "react-router-dom"; function App() { let navigate = useNavigate(); function handleClick() { navigate("/home"); } return ( <div> <button onClick={handleClick}>go home</button> </div> ); }
6. 이외 변경사항
- <NavLink exact> 가 <NavLink end> 로 변경 (현재 활성화된 라우터)
- props 명칭들의 renaming
- activeClassNAme, activeStyle props 제거 => style, className에 함수 사용 가능
- StaticRouter가 react-router-dom/server 번들로 이동 (SSR 라우팅)
- <Link>의 component prop 제거
React를 오랜만에 사용하면서, Router 설정에서 오류 메세지가 많이 발생하였고 그 원인이 버전 변경에 걸맞는 문법이 아니어서였다.
기능들보다는 컴포넌트나 props 명칭들 위주로 변경되었으며, 중첩 라우팅과 useRoutes() 가 직관적으로 개선되어 Router 구조를 가시화하는데에는 큰 진전이 있었다고 느껴진다!
[출처]
- [React Router] 공식문서 : https://reactrouter.com/docs/en/v6/upgrading/v5
- [v6 변경사항] soryeongk 님의 블로그 : https://velog.io/@soryeongk/ReactRouterDomV6
- [v6 변경사항] woolta 님의 블로그 : https://blog.woolta.com/categories/1/posts/211
반응형'Front-End(Web) > React - 프레임워크(React, Next)' 카테고리의 다른 글
[Craco] Craco로 설정하기 (절대경로, Less) (0) 2022.01.23 [Craco] Craco 란? (0) 2022.01.18 [Next.js + TS] 초기세팅 - (4) Babel 설정 (4) 2021.11.02 [Next.js + TS] 초기세팅 - (3) Babel 개념 (0) 2021.10.22 [Next.js + TS] 초기세팅 - (2) ESLint, Prettier (0) 2021.10.14