React Router Upgrading

ν•™μŠ΅ ν‚€μ›Œλ“œ

  • React Router 6.4

    • createBrowserRouter

    • createMemoryRouter

    • RouterProvider

    • Outlet

React Router 6.4

React Router 버전 6.4λΆ€ν„° μ§€μ›ν•˜λŠ” λΌμš°ν„° 객체λ₯Ό λ§Œλ“€μ–΄μ„œ μ‚¬μš©ν•΄λ³΄μž.

μ•„λž˜μ™€ 같은 λ°©μ‹μœΌλ‘œ React Routerλ₯Ό μ΄μš©ν•΄μ„œ λΌμš°ν„° 객체λ₯Ό λ§Œλ“€μ–΄μ„œ μ‚¬μš©ν•΄λ³΄μž.

import Header from './components/Header';
import Footer from './components/Footer';

import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';

// ⬇️ React Routerλ₯Ό μ΄μš©ν•΄μ„œ λΌμš°ν„° 객체λ₯Ό λ§Œλ“€μ–΄ 보자!
const pages = {
  '/': HomePage,
  '/about': AboutPage,
};

export default function App() {
  const path = window.location.pathname;

  const Page = Reflect.get(pages, path) || HomePage;

  return (
    <div>
      <Header />
      <main>
        <Page />
      </main>
      <Footer />
    </div>
  );
}

React Router v6.4 의 μ‚¬μš©λ²•

πŸ€” App μ»΄ν¬λ„ŒνŠΈμ˜ 2가지 μ—­ν™œ

ν˜„μž¬ App μ»΄ν¬λ„ŒνŠΈλŠ” 2가지 역할을 ν•˜κ³  μžˆλ‹€. μ–΄λ–€ λ ˆμ΄μ•„μ›ƒμ„ μ·¨ν•˜κ³  μžˆλŠ”μ§€ 와 μ–΄λ–»κ²Œ λΌμš°νŒ…μ΄ λ˜λŠ”μ§€ 이닀.

// App.tsx 

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

import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';

import Header from './components/Header';
import Footer from './components/Footer';

export default function App() {
  return (
    <div>
      <Header />
      <main>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/about" element={<AboutPage />} />
        </Routes>
      </main>
      <Footer />
    </div>
  );
}

πŸ“‘ App μ»΄ν¬λ„ŒνŠΈμ—μ„œ 2가지 μ—­ν™œμ„ λΆ„λ¦¬ν•΄λ³΄μž

RoutingPage μ»΄ν¬λ„ŒνŠΈλ₯Ό μƒμ„±ν•΄μ„œ λΌμš΄νŒ…κ³Ό κ΄€λ ¨λœκ±΄ λ„˜κ²¨μ£Όκ³ , Appμ»΄ν¬λ„ŒνŠΈλŠ” λ ˆμ΄μ•„μ›ƒ κ΄€λ¦¬ν•˜λ„λ‘ λΆ„λ¦¬λ˜μ—ˆλ‹€. κ·Έλ ‡λ‹€λ©΄ RoutingPage λ₯Ό κ°μ²΄λ°©μ‹μœΌλ‘œ λ§Œλ“€μ–΄λ„ λ˜μ§€ μ•Šμ„κΉŒ?

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

import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';

import Header from './components/Header';
import Footer from './components/Footer';

function RoutingPage() {
  return (
    <Routes>
      <Route path="/" element={<HomePage />} />
      <Route path="/about" element={<AboutPage />} />
    </Routes>
  );
}

export default function App() {
  return (
    <div>
      <Header />
      <main>
        <RoutingPage />
      </main>
      <Footer />
    </div>
  );
}

πŸ“Œ λΌμš°νŒ… 처리λ₯Ό λ…λ¦½μ‹œν‚€μž

Route μ»΄ν¬λ„ŒνŠΈμ˜ path와 elementλ₯Ό λ§΅ν•‘ν•œ 객체λ₯Ό 가지도둝 ν•œλ‹€.

const routes = [
  {path: '/', element: <HomePage />},
  {path: '/about', element: <AboutPage />},
];

createBrowserRouterλ₯Ό μ΄μš©ν•΄μ„œ λΌμš°ν„° 객체λ₯Ό μƒμ„±ν•œλ‹€. μ΄λ ‡κ²Œ μƒμ„±λœ λΌμš°ν„° κ°μ²΄λŠ” λΌμš°νŒ…μ„ μ²˜λ¦¬ν•˜λŠ”λ° μ‚¬μš©ν•œλ‹€.

const router = createBrowserRouter(routes);

그리고 RouterProviderλ₯Ό μ΄μš©ν•΄μ„œ router 객체λ₯Ό μ“°κ² λ‹€κ³  ν•œλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ „μ²΄μ—μ„œ λΌμš°νŒ…μ„ μ²˜λ¦¬ν•  수 있게 λœλ‹€.

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';

const routes = [
  { path: '/', element: <HomePage /> },
  { path: '/about', element: <AboutPage /> },
];

const router = createBrowserRouter(routes);


export default function App() {
  return (
    <RouterProvider router={router}></RouterProvider>
  )
}

더 이상 BrowserRouterκ°€ λΌμš°νŒ…μ„ κ΄€λ¦¬ν•˜μ§€ μ•ŠλŠ”λ‹€. createBrowserRouter ν•¨μˆ˜μ™€ RouterProvider μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜μ—¬ λΌμš°νŒ…μ„ μ„€μ •ν•˜κ³  κ΄€λ¦¬ν•œλ‹€. λ•Œλ¬Έμ— main μ»΄ν¬λ„ŒνŠΈμ—μ„œ BrowserRouter μ»΄ν¬λ„ŒνŠΈλŠ” μ œκ±°ν•œλ‹€.

πŸ“Œ λ ˆμ΄μ•„μ›ƒ 독립 μ‹œν‚€κΈ°

λΆ„κΈ°μ‹œμ μ΄ μ—†κΈ° λ•Œλ¬Έμ— μœ„μ˜ λ°©μ‹μ²˜λŸΌ μ‚¬μš©ν•˜λŠ”κ±΄ λ„ˆλ¬΄ λΆˆνŽΈν•˜λ‹€. λ ˆμ΄μ•„μ›ƒ 자체λ₯Ό λ‹€μŒκ³Ό 같이 μ»΄ν¬λ„ŒνŠΈλ‘œ λ…λ¦½μ‹œν‚¨λ‹€.

function Layout() {
  return (
    <div>
      <Header />
      <main>
        {/* <RoutingPage /> */}
        <Outlet />
      </main>
      <Footer />
    </div>
  );
}

routesν•œν…Œ Layout μ»΄ν¬λ„ŒνŠΈμ— μ μš©ν•œλ‹€λŠ” 것을 μž‘μ•„μ€˜μ•Ό ν•œλ‹€. κ·Έλ €μ§€λŠ” 것은 λͺ¨λ‘ <Layout />으둜 그렀지도둝 ν•΄μ•Ό ν•œλ‹€.

이λ₯Ό μœ„ν•΄μ„œ routesλ₯Ό κ³„μΈ΅ν˜•μœΌλ‘œ λ°”κΎΈμ—ˆλ‹€.

const routes = [
  {
    element: <Layout />,
    children: [
      { path: '/', element: <HomePage /> },
      { path: '/about', element: <AboutPage /> },
    ],
  },
];

λ‹€λ§Œ Layout μ»΄ν¬λ„ŒνŠΈκ°€ HomePage μ»΄ν¬λ„ŒνŠΈμ™€ AboutPage μ»΄ν¬λ„ŒνŠΈλ₯Ό μΈμ§€ν•΄μ„œ μ˜¬λ°”λ₯Έ μœ„μΉ˜μ— λ„£μ–΄μ•Ό ν•œλ‹€. μ΄λŠ” React Routerκ°€ μ§€μ›ν•˜λŠ” μ»΄ν¬λ„ŒνŠΈμΈ Outlet 을 μ“°λ©΄λœλ‹€.

Outlet μ»΄ν¬λ„ŒνŠΈλŠ” Layout μ»΄ν¬λ„ŒνŠΈ μ•ˆμ— μ •μ˜λœ λ ˆμ΄μ•„μ›ƒ ꡬ쑰λ₯Ό κ·ΈλŒ€λ‘œ μœ μ§€ν•˜λ©΄μ„œ μ»΄ν¬λ„ŒνŠΈλ₯Ό λ™μ μœΌλ‘œ λ Œλ”λ§ ν•  수 μžˆλ‹€.

import { createBrowserRouter, RouterProvider, Outlet } from 'react-router-dom';

import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';

import Header from './components/Header';
import Footer from './components/Footer';

function Layout() {
  return (
    <div>
      <Header />
      <main>
        <Outlet />
      </main>
      <Footer />
    </div>
  );
}

const routes = [
  {
    element: <Layout />,
    children: [
      { path: '/', element: <HomePage /> },
      { path: '/about', element: <AboutPage /> },
    ],
  },
];

const router = createBrowserRouter(routes);

export default function App() {
  return <RouterProvider router={router}></RouterProvider>;
}

πŸ“ 파일둜 λΆ„λ¦¬ν•˜μž

routesλŠ” ν…ŒμŠ€νŠΈ λ•Œ ν•„μš”ν•œ 정보이닀. κ·Έλž˜μ„œ App.tsx νŒŒμΌμ—μ„œ λΆ„λ¦¬ν•˜λŠ”κ²Œ 가져닀쓰기에 μ’‹λ‹€.

- src
  - components
    - Footer.tsx
    - Header.tsx
    - Layout.tsx βœ…
 - pages
   - AboutPage.tsx
   - HomePage.tsx
- App.tsx
- main.tsx
- routes.tsx βœ…

🧐 App μ»΄ν¬λ„ŒνŠΈλŠ” ν•  일이 μ—†λ‹€?

App μ»΄ν¬λ„ŒνŠΈκ°€ ν•˜λŠ” 일이 μ€„μ—ˆλ‹€. 이 일은 main μ»΄ν¬λ„ŒνŠΈμ— 보내도 λ˜λŠ” μΌμ΄λΌμ„œ App μ»΄ν¬λ„ŒνŠΈκ°€ ν•„μš” μ—†μ–΄μ‘Œλ‹€.

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

import routes from './routes';

const router = createBrowserRouter(routes);

export default function App() {
  return <RouterProvider router={router}></RouterProvider>;
}

πŸ› οΈ λΌμš°νŒ… ν…ŒμŠ€νŠΈ ν•˜κΈ°

μ΄μ „μ—λŠ” App.test.tsx둜 App μ»΄ν¬λ„ŒνŠΈλ₯Ό ν…ŒμŠ€νŠΈν–ˆλ‹€. μ΄μ œλŠ” routesκ°€ μ»΄ν¬λ„ŒνŠΈμ™€ λ ˆμ΄μ•„μ›ƒμ— λŒ€ν•œ 정보λ₯Ό λͺ¨λ‘ λ“€κ³  μžˆλ‹€. λ•Œλ¬Έμ— routes만 ν…ŒμŠ€νŠΈ ν•˜λ©΄ λœλ‹€.

MemoryRouterλŠ” μ£Όμ†Œλ₯Ό 가지고 μžˆμ§€ μ•Šμ•„, μ£Όμ†Œλ₯Ό λ”°λ‘œ μž‘μ•„ μ€€ κ²ƒμ²˜λŸΌ createMemoryRouterλ₯Ό μ‚¬μš©ν•΄μ„œ ν…ŒμŠ€νŠΈ ν•œλ‹€.

// App.test.tsx β†’ routes.test.tsx 

import { render, screen } from '@testing-library/react';

import { createMemoryRouter, RouterProvider } from 'react-router-dom';

import routes from './routes';

const context = describe;
describe('App', () => {
  
  function renderRouter(path: string) {
    const router = createMemoryRouter(routes, { initialEntries: [path] });
    render(<RouterProvider router={router} />);
  }

  context('when the current path is β€œ/”', () => {
    it('renders the home page', () => {
      renderRouter('/');

      screen.getByText(/ν™˜μ˜/);
    });
  });
  
  context('when the current path is β€œ/about”', () => {
    it('renders the about page', () => {
      renderRouter('/about');

      screen.getByText('about에 λŒ€ν•œ 정보λ₯Ό 가지고 μžˆμ–΄μš”!');
    });
  });
});

πŸ”— μ°Έκ³ 

Last updated