Frontend/React

react-router-dom 버전 5 vs 6 비교 및 차이점 알아보기

둉이 2022. 4. 19. 23:33

2021년 11월에 드디어 react-router-dom v6 정식 버전이 출시되었다.

 

기존에 사용되던 버전 5와 비교하여 다양한 기능이 추가/삭제되면서 자잘한 문법 수정이 이루어졌으며, 패키지 번들 사이즈도 약 70% 이상 감소했다.

 

그리고 react-router-dom v6은 React 16.8 이상 버전에서 사용이 가능하다.

 

이 두 버전의 차이점에 대해 먼저 알아보고, react-router-dom 사용 꿀팁도 알아보자.

 


 

Redirect 컴포넌트 삭제

버전 5에서 유용하게 사용되던 Redirect 컴포넌트가 삭제됐다.

 

Redirect는 사용자가 유효하지 않은 path로 접근했을 때, 올바른 특정 path로 이동시킬 수 있도록 하는 컴포넌트이다.

 

따라서 버전 6에서는 서버 측에서 리다이렉트 처리를 하거나, 아래처럼 Navigate 컴포넌트를 사용해야 한다.

import { Route, Routes, Navigate } from 'react-router-dom';

<Route path="*" element={<Navigate to="/quotes" />} />

 

 

Switch, exact props 삭제

버전 5에서는 라우터의 중복 적용이 가능했다.

 

이게 무슨 소리냐면 부모 path가 같은 경우에는 2개 이상의 라우팅이 동시에 적용되는 문제가 있었다.

 

아래의 코드를 예시로 들면, /quotes/2 경로로 접근하는 경우 Quotes 컴포넌트와 QuoteDetail 컴포넌트가 동시에 표출된다.

 

이를 해결하기 위해, Switch 컴포넌트로 Route 목록을 감싸주거나 exact props를 추가했다.

(exact props를 추가하면 현재 URL의 path가 Route에 명시한 path와 정확히 일치할 때만 컴포넌트가 렌더링된다.)

<Route path="/quotes" exact render={() => <Quotes />} />
<Route path="/quotes/:quoteId" render={() => <QuoteDetail />} />
// 혹은
<Switch>
  <Route path="/quotes" render={() => <Quotes />} />
  <Route path="/quotes/:quoteId" render={() => <QuoteDetail />} />
</Switch>

 

버전 6에서는 Routes 컴포넌트가 추가되어 해당 컴포넌트를 사용하여 라우터 중복 적용 문제를 해결할 수 있다.

 

이에 따라 더이상 필요없게 된 Switch 컴포넌트와 exact props가 삭제되었다.

 

기본적으로 exact props가 추가된 것처럼 동작하기 때문에 만약 해당 path의 모든 하위 path를 허용하기 위해서는 뒤에 /*를 붙여주면 된다.

<Route path="new-quote/*" element={<NewQuote />} />

 

그리고 주의할 점으로는 Route 컴포넌트들은 반드시 Routes 컴포넌트의 자식 컴포넌트여야 한다.

(Routes 컴포넌트로 감싸지 않으면 오류가 발생)

 

아래와 같이 사용하도록 하자.

<Routes>
  <Route path="/quotes" element={<Quotes />} />
  <Route path="/quotes/:quoteId" element={<QuoteDetail />} />
  <Route path="/new-quote" element={<NewQuote />} />
  <Route path="*" element={<Navigate to="/quotes" />} />
</Routes>

 

 

optional path 파라미터 삭제

리액트에서는 :파라미터명 형태로 path 파라미터를 정의하여 사용할 수 있다.

 

여기서 뒤에 ? 기호를 붙여주면 optinal path 파라미터가 된다.

 

해당 파라미터를 넘겨줘도 되고 넘겨주지 않아도 된다는 뜻이다.

 

아래 코드는 productId를 optional하게 명시했으므로 /products, /products/[:productId] 두 경우 모두 동작한다.

<Route path="/products/:productId?" exact render={() => <Products />} />

 

버전 5에서는 이러한 optinal path 파라미터를 지원하지만 버전 6에서는 지원하지 않기 때문에 만약 optinal path 파라미터를 사용하는 것처럼 사용하고 싶다면 아래와 같이 Route를 두 번 선언하면 된다.

<Route path="/products" exact render={() => <Products />} />
<Route path="/products/:productId" exact render={() => <Products />} />

 

 

render/component 삭제 -> element로 대체

버전 5에서는 Router의 render 혹은 component props를 통해 해당 path에 맞는 리액트 컴포넌트를 매핑할 수 있었다.

 

버전 6에서는 이 두 props가 element로 통합되었다. 또한 사용법도 달라졌다.

 

render/component props의 경우에는 컴포넌트 포인터 혹은 생성자 함수를 넘겨주는 방식으로 사용할 수 있었는데, element의 경우에는 아래와 같이 생성된 컴포넌트를 넘겨줘야 한다.

<Route path="/quotes" element={<Quotes />} />

 

 

NavLink의 activeClassName 및 activeStyle props 삭제

버전 5에서 네비게이션 바에서 주로 사용되는 NavLink 컴포넌트의 activeClassName 및 activeStyle props를 더 이상 지원하지 않는다.

 

NavLink는 Link 컴포넌트와는 달리 activeClassName, activeStyle이라는 props를 지원한다.

 

activeClassName, activeStyle에 설정한 클래스 이름과 스타일 값은 현재 URL의 path가 해당 Link의 to props가 가리키는 path일 경우 활성화된다.

 

버전 6에서는 activeClassName, activeStyle 대신 className과 style props에서 활성화 함수를 넘길 수 있게 변경되었다.

 

아래와 같이 isActive를 props로 받는 활성화 함수 또는 일반 문자열을 넘겨서 사용하면 된다.

// 버전 5
<NavLink to="/home" activeClassName={styles.active} activeStyle={{ backgroundColor: '#111' }}>
  Home
</NavLink>

// 버전 6
<NavLink
  to="/new-quote"
  className={({ isActive }) => (isActive ? 'active' : '')}
  style={({ isActive }) => ({
    backgroundColor: isActive ? 'blue' : 'black',
  })}
>
  Add a Quote
</NavLink>

 

 

useRouteMatch 훅 삭제 + path에서의 상대 경로 가능

버전 5에서는 Route 컴포넌트의 path props 값은 무조건 절대 경로로만 사용이 가능했다.

 

Route 컴포넌트를 중첩하여 배치해도 무조건 절대 경로로만 작성해야 한다.

// 버전 5에서의 중첩 라우팅
<Route path="/quotes">
  <Route path="/quotes" exact render={() => <Quotes />} />
  <Route path="/quotes/:quoteId" exact render={() => <QuoteDetail />} />
</Route>

 

따라서 컴포넌트에서 현재 경로를 얻기 위해서는 useLocation 혹은 useRouteMatch 훅을 사용하여 현재 URL의 path를 가져온 후, 이를 이용하여 절대 경로로 작성해야 했다.

 

버전 6에서부터는 Route 컴포넌트에서 중첩 라우팅 방식일 때 일일이 절대 경로로 작성하지 않고 다음과 같이 상대 경로로 작성할 수 있게 되었다. (자동으로 부모 경로 + '/'이 붙음)

// 버전 6에서의 상대 경로
<Route path="quotes">
  <Route path="" element={<Quotes />} />
  <Route path=":quoteId" element={<QuoteDetail />} />
</Route>

 

Link 컴포넌트에서도 앞에 / 기호를 붙이지 않으면 자동으로 해당 컴포넌트에 매핑된 라우터 기준 상대 경로로 동작하도록 작성할 수 있다.

// App.js
<Route path="new-quote/*" element={<NewQuote />} />

// NewQuote.js, Link 클릭 시 /new-quote -> /new-quote/test로 이동
<Link to="test">test</Link>

 

참고로 버전 5에서 Link에 상대 경로로 작성할 경우에는 다음과 같이 마지막 path의 값이 변경된다.

// App.js
<Route path="/products/:productId" exact render={() => <ProductDetail />} />

// ProductDetail.js, Link 클릭 시 /products/id -> /products/test로 이동
<Link to="test">test</Link>

 

또한 버전 6에서는 useRouteMatch 훅이 삭제되었다.

 

 

Route 내에 컴포넌트 작성 불가

버전 6부터는 Route 컴포넌트에서 render/component(element) props를 사용하지 않고 Route 컴포넌트 내에 자식 컴포넌트를 작성하는 방식이 불가능하도록 변경되었다.

// 버전 5에서는 가능한 중첩 Route 문법
<Route path="/products" exact>
  <Products />
</Route>

 

 

Outlet 컴포넌트 추가 

버전 6에서는 Outlet 컴포넌트를 이용하여 서브 라우팅을 구현하도록 변경되었다.

 

먼저, 아래처럼 중첩 Route 문법을 사용하여 다음과 같이 부모 Route에 element를 지정하는 방식으로 코드를 작성하게 되면 부모 Route 컴포넌트의 element 컴포넌트(Welcome) 외에 자식 Route 컴포넌트에서 지정한 컴포넌트(Quotes, QuoteDetail)들은 보이지 않는다.

<Route path="quotes" element={<Welcome />} >
  <Route path="" element={<Quotes />} />
  <Route path=":quoteId" element={<QuoteDetail />} />
</Route>

 

자식 Route 컴포넌트에서 지정한 컴포넌트들도 화면에 렌더링하려면 반드시 다음과 같이 부모 Route의 element 내에서 자식 Route의 element가 렌더링될 위치에 Outlet 컴포넌트를 위치시켜야 한다.

// Welcome.js => 부모 element
const Welcome = () => {
  return (
    <>
      <p>Welcome! This is Parent Element.</p>
      <Outlet />
    </>
  );
};

export default Welcome;

 

Outlet 컴포넌트가 위치한 곳에 정상적으로 자식 element가 렌더링된 것을 확인할 수 있다.

 

 

하위 컴포넌트에서 Route 사용 방식 변경

버전 5에서는 모든 컴포넌트에서 Route 컴포넌트를 import하여 서브 라우팅을 구현할 수 있다.

 

버전 6에서도 모든 컴포넌트에서 서브 라우팅이 가능하지만, Route 컴포넌트를 사용하기 위해서는 무조건 Routes 컴포넌트로 감싸줘야 한다.

const NewQuote = () => {
  // ...
  return (
    <div>
      <Outlet />
      <QuoteForm isLoading={isLoading} onAddQuote={onAddQuote} />
      <Routes>
        <Route path="sub-path" element={<p>This is Sub Path.</p>} />
      </Routes>
    </div>
  );
};

 

 

useHistory -> useNavigate 변경

버전 5에서 사용했던 history 변경 훅인 useHistory가 사라지고 navigate 함수를 사용할 수 있는 useNavigate가 추가되었다.

useHistory의 경우에는 goBack, go 등 다양한 메소드를 제공했지만, useNavigate에서는 navigate 함수 하나만 제공한다.

-1(뒤로 가기), 1(앞으로 가기)처럼 숫자만 넘기거나 문자열 형태의 path를 넘겨서 사용할 수 있다.

// 버전 5에서의 useHistory
import { useHistory } from "react-router-dom";
const history = useHistory();  // 혹은 const { goBack, replace } = useHistory();로도 사용 가능

// history.push => history 목록에 새로운 기록 추가
history.push('/', { isLogin: true, id: 'sap03110' });

// history.replace => 최근 history을 해당 기록으로 덮어 씌우기
history.replace('/', { isLogin: true, id: 'sap03110' });

// history.go => n번 앞으로 가기
history.go(n);

// history.goBack => 1번 뒤로 가기
history.goBack();

// history.goForward => 1번 앞으로 가기
history.goForward();


// 버전 6에서의 useNavigate
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();

// /quotes로 이동(기본적으로 history.replace로 동작)
navigate('/quotes');

// history.push로 동작 && history.state.usr 값 변경
navigate('/', { state: { isLogin: true, id: 'sap03110' }, replace: false });

navigate(-1);  // 뒤로 가기
navigate(1);  // 앞으로 가기

 

 


 

 

그 외 알아두면 좋은 react-router 관련 꿀팁을 알아보자.

 

 

Link 컴포넌트에서의 상대 경로

Link 컴포넌트에서는 to=".."와 같이 이전 페이지로 이동할 수 있도록 상대 경로를 지정할 수 있다.

 

해당 방식은 react-router 5, 6버전 모두 사용 가능하다.

<Link to="..">뒤로 가기</Link>

 

useLocation 훅

react-router-dom에서는 useLocation 훅을 제공한다.

 

해당 훅을 이용하면 현재 URL에서 갖고 있는 hash, key, pathname, search, state 등의 정보를 담고 있는 object를 얻을 수 있다.

useLocation을 통해 얻은 location 정보

 

 

useParams 훅

현재 URL의 path 파라미터만 뽑아서 받아올 수 있는 훅이다. (쿼리스트링 X)

 

리액트에서는 :파라미터명 형태로 path 파라미터를 정의하여 사용할 수 있다.

 

아래와 같이 해당 path 파라미터로 정의한 라우터의 하위 컴포넌트에서 useParams를 사용하여 path 파라미터를 가져올 수 있다.

<Route path="/quotes/:quoteId" element={<QuoteDetail />} />

// path 파라미터를 정의한 라우터에 바인딩된 컴포넌트
const QuoteDetail = () => {
  const { quoteId } = useParams();
  // ...
};

 

 

useRouteMatch 훅(버전 6에서는 사용 불가)

useLocation + useParam 훅과 비슷하며, 현재 URL의 path 파라미터 객체(params) + path url(url) + path 명세(path) + exact 여부(isExact)를 얻을 수 있다.

 

isExact는 현재 URL의 url 값이 path 명세와 일치하면 true, 일치하지 않으면 false 값을 갖는다.

 

예를 들어 path: '/products/:productId'일 때 url: '/products/2'이면 true이고, url: '/products/2/comments'이면 false이다.

useRouteMatch를 통해 얻은 match 정보

 

 

Prompt 컴포넌트(버전 6에서는 사용 불가)

사용자가 현재 페이지를 벗어나려고 할 때를 감지하여 confirm 시스템 창을 띄워주는 컴포넌트이다.

 

props로 when, message 등을 넘길 수 있는데, when에는 confirm 창 표출 여부를 담당하는 state를 전달하면 된다.

 

when: true여야 페이지를 벗어날 때 confirm 창이 표출된다.

 

message에는 문자열 혹은 문자열을 반환하는 함수를 넘겨주면 된다.

const [isEntered, setEntered] = useState(false);

<Prompt when={isEntered} message='정말 페이지를 벗어나시겠습니까?' />