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