본문 바로가기

개발/Front-end

react-router-dom v6 무엇이 바뀌었는가?

반응형

React-router-dom에 많은 변경사항이 생겼다 해서 찾아보았다. 차이점을 알아보고 마지막에는 hashRouter와 browserRouter의 차이점까지 알아보자.

그전에 react-router에 대해 간단히 알아보면 React에서 브라우저 라우팅을 관리하기 위해 사용하는 라이브러리로 spa의 약점을 보완하기 위해 나왔다. 말 그대로 라우팅을 다루며 다른 주소에 다른 화면을 보여주는 역할을 한다. React 프로젝트를 하면 필수적으로 사용하는 라이브러리이다.

일단 이 포스팅은 react-router에 대해 알아보는 것이 아닌 v5 → v6로 업데이트로 변경된 점을 알아보기 위한 글이므로 여기까지만 정리하고 넘어가자.

Switch대신 Routes

기존에는 Switch를 사용하여 복수개의 route를 가져 component를 매칭하였다. 좀 더 직관적인 코딩이 가능하다.

// v5 에선 Switch
<Switch>
	<Route path='이동할 주소' component={출력할 컴포넌트} />
	<Route path='이동할 주소' component={출력할 컴포넌트} />
	<Route path='이동할 주소' component={출력할 컴포넌트} />
	<Route path='이동할 주소' component={출력할 컴포넌트} />
	<Route path='이동할 주소/하위 주소' component={출력할 컴포넌트} exact/>
</Switch>

// v6 에선 Routes

<Routes>
	<Route path="이동할 주소" element={출력할 컴포넌트}/>
	<Route path="이동할 주소/하위 주소" element={출력할 컴포넌트} />
</Routes>

번들 사이즈 작아짐

이전 버전 대비 약 70% 정도의 크기가 줄어들었다고 한다. 이는 버전 업데이트를 하면 번들 크기가 작아져 네트워크 속도가 빨라져 최적화가 가능하다는 것을 알 수 있다.

exact 옵션 삭제

기존의 / 라우트의 경우 앞부분만 일치해도 전부 매칭 되었다. 이를 위해 exact를 사용해 정확한 라우트에 일치하고자 했는데 v6부터는 더 이상 사용하지 않고 default를 exact를 포함시키고 하위 라우팅을 매칭 시키는 방법을 * 등으로 바꾸었다.

// v5
<Switch>
	<Route path='이동할 주소' component={출력할 컴포넌트} />
	<Route path='이동할 주소/하위 주소' component={출력할 컴포넌트} exact/>
</Switch>

// v6

<Routes>
	<Route path="이동할 주소" element={출력할 컴포넌트}/>
	<Route path="이동할 주소/하위 주소" element={출력할 컴포넌트} />
</Routes>

최상단에 사용할 때랑 최하단에 사용할 때랑 잘 구분해서 사용해야 한다. exact가 없는 상황에서 path="/"에는 웬만한 Route들이 다 매칭이 되서 원하지 않는 컴포넌트 매칭이 될 수 있다. 예를 들어 path="/"와 path="/product"가 있는 상황에서 '/'가 최상단에 있다면 product 컴포넌트를 매칭 할 수 없다.

route에서 컴포넌트 렌더링

// v5

<Route path="user" render={routeProps => (<UserInfo routePorps={routeProps} isLoin={true}/>)}/>

// v6

<Route path="user" element={<UserInfo isLogin={true} />} />

v5 에서는 컴포넌트를 렌더링 하기 위해서 render 속성에 화살표 함수를 사용하여 컴포넌트를 렌더링 하는 방식으로 사용했다. 기본적으로 component 속성에 사용했지만 컴포넌트를 렌더링 해야 하는 경우 render 속성을 사용했다. v6에서는 element에 바로 props를 넣는 방식으로 개선되어 좀 더 직관적인 코드를 사용할 수 있다.

중첩 라우팅

개인적으로 이 중첩 라우팅 기능이 가장 개발 스럽다고 생각하는 기능이다. 처음 보았을 때 children 함수가 떠올랐다. 간단하게 설명하면 라우팅 한 컴포넌트에서 다시 라우팅을 하는 것을 뜻하는데 v5 코드와 v6 코드를 살펴보면

// v5
// app.js 

function App(){
	return (
		<BrowserRouter>
			<Switch>
				<Route path="/user" component={User} />
			</Switch>	
	)
}

// user.js

function User(){
	return (
		<div>
			<Switch>
				<Route>
					<UserDetail />
				</Route>
			</Switch>
		</div>
	)
}
// v6
// app.js 

function App(){
	return (
		<BrowserRouter>
			<Routes>
				<Route path="/user" element={<User />}>
					<Route path='detail' element={<UserDetail />} />
				</Route>
			</Routes>	
	)
}

// user.js

function User(){
	return (
		<>
			<Outlet />
		</>
	)
}

이런 식으로 하위 컴포넌트에서 따로 명시할 필요없이 <Outlet />으로 중첩 라우팅을 해결할 수 있다. 직관적이지 않을 수 있지만 익숙해지면 훨씬 간단한 코딩이기 때문에 자주 사용할 것을 추천한다.

useHistory 가 useNavigate로 변경

useHistory는 SPA에서 뒤로가기나 앞으로 가기를 구현하기 위해 사용하던 훅인데 useNavigate로 변경되었다.

// v5
const history = useHistory();
const {go, goBack, goForward } = useHistory();

history.push('/home')
history.replace('/home')

<button onClick={goBack}>Go back</button>
// or <button onClick={()=> go(-1)}>Go back</button> 

// v6
const navigate = useNavigate();

navigate('/home')
navigate('home', {replace: true})

<button onClick={()=> navigate(-1)}>Go Back</button>

blog에 언급한 대로 replace를 사용할 때 뒤에 {replace: true}를 넘겨주는 것이 썩 깔끔해 보이진 않는다. 훅 메서드 네이밍은 확실히 직관적이긴 하다.

react-router-config ⇒ useRoutes

이전 회사에서 개발 시 이런 식으로 컴포넌트 라우팅을 정리하는 컴포넌트를 따로 정리해서 개발한 경험이 있다. 라우팅은 전부 한 컴포넌트에서 개발하기에 굉장히 유용하게 사용하였었다. 규모가 큰 프로젝트에서는 이처럼 개발하는 것을 추천한다.

이를 react-router-config에서 useRoute로 키워드를 변경한 것이다.

// v5

// react-router-config
// yarn add react-router-config로 설치 후 사용
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")
);
// v6

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;
}

이정도 까지 알아본 결과 여러 곳에서 기존에 사용하던 코드들에서 에러가 발생할 것으로 보인다. Routes 부분이 가장 많지 않을까 생각되는데 기존 서비스들은 react-router 업데이트를 유념하며 업데이트를 진행해야 할 것으로 보인다.

BrowserRouter vs HashRouter

교육시 나왔던 질문사항으로 검색을 해보았다. 사실 나는 HashRouter를 공부만 해보고 실제로 써본 적은 없기에 잘 모른다. 이번 기회에 다시 알아보았다.

HashRouter

  • 주소에 해쉬 #가 붙는다.
  • 검색엔진이 읽지 못한다. ⇒ ssr을 설정할 수 없다.
  • 히스토리를 지원하지 않는다.
  • 정적인 페이지에서 주로 사용한다.

BrwoserRouter

  • Link 컴포넌트로 to 속성에 이동할 경로를 써준다.
  • 새로 고침 하면 경로를 못 찾아 에러가 난다.
  • History API 사용 가능하다. ⇒ js로 url 변경 가능하다.
  • 동적인 페이지에서 사용한다.
// HashRouter
<HashRouter basename="/calender"/>
<Link to="/today"/> // render <a href="#/calender/today">

// BrowserRouter
<BrowserRouter basename=="calendar">
	<Link to="/today" /> // render <a href="/calendar/today">
</BrowserRouter>

둘의 가장 큰 차이점은 #이 붙어 새로고침 여부와 동적인 페이지와 정적인 페이지의 사용 여부, 서비스의 목적 여부에 따라 사용하는 Router가 달라질 것 같다.

하지만 react 는 spa이기에 동적이고 ssr을 nextjs로 극복할 수 있으며 js로 url 변경이 유용하기에 BrowserRouter가 잘 어울린다고 생각한다. 실제로 점유율도 BrowserRouter가 높다고 하니 참고해서 사용하면 좋을 것 같다.

참고 reference

반응형

'개발 > Front-end' 카테고리의 다른 글

React 톺아보기를 보고 정리하기_1  (0) 2021.08.11
TypeScript 하기 전에 이정도는 알아야지!  (0) 2021.07.05
Event Loop에 대하여  (0) 2021.06.19
Npm vs Yarn  (0) 2021.06.11