๋ฐ˜์‘ํ˜•

React Router๊ฐ€ v6์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋˜๋ฉด์„œ ๊ฝค ๋งŽ์€ ๋ถ€๋ถ„์ด ๋ฐ”๋€Œ์—ˆ๋‹ค. ๊ณต์‹ ๋ฌธ์„œ์—์„  ์ƒ์„ธํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ๋„ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. v6์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด React 16.8 ๋ฒ„์ „ ์ด์ƒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. v6์€ ์ด์ „ ๋ฒ„์ „ ๋Œ€๋น„ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๋„ 70% ์ด์ƒ ์ค„์—ˆ๋‹ค๊ณ  ํ•œ๋‹ค(ํŒจํ‚ค์ง€ ์—…๋ฐ์ดํŠธ๋งŒ์œผ๋กœ ๋ฒˆ๋“ค ํฌ๊ธฐ ์ตœ์ ํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๋œป).

 

Switch → Routes


<Switch> ์ปดํฌ๋„ŒํŠธ๊ฐ€ <Routes>๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝ๋๋‹ค. ๋ฐ”๋€ ์ด๋ฆ„์ด ๋” ์ง๊ด€์ ์ธ ๊ฒƒ ๊ฐ™๋‹ค. <Route>๋งŒ ๋‹จ๋…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์—†๊ฒŒ ๋๋‹ค. <Route>๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•ญ์ƒ <Routes>๋กœ ๊ฐ์‹ธ์ค˜์•ผ ํ•œ๋‹ค.

// v5
<Switch>
  <Route ... />
</Switch>

// v6
<Routes>
  <Route ... />
</Route>

 

exact ์˜ต์…˜ ์‚ญ์ œ

v6๋ถ€ํ„ฐ ๊ฒฝ๋กœ๊ฐ€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋„๋ก ๋งค์นญ ๊ทœ์น™์ด ๋ฐ”๊ผˆ๋‹ค. ๋”์ด์ƒ exact ์˜ต์…˜์„ ๋ช…์‹œํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๋ชจ๋“  ๋ผ์šฐํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ exact ์˜ต์…˜์ด ๋ถ™๋Š”๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. ํ•˜์œ„ ๊ฒฝ๋กœ์— ๋‹ค๋ฅธ ๋ผ์šฐํŠธ๋ฅผ ๋งค์นญ์‹œํ‚ค๊ณ  ์‹ถ์„ ๋•Œ(์„œ๋ธŒ ๊ฒฝ๋กœ๊ฐ€ ํ•„์š”ํ•  ๋•Œ) URL ๋’ค์— * ๊ธฐํ˜ธ๋ฅผ ๋ถ™์ด๋ฉด ๋œ๋‹ค.

// v5 (exact ์˜ต์…˜์„ ๋ถ™์ด๊ธฐ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— /profiles/... ํ•˜์œ„ ๊ฒฝ๋กœ๊ฐ€ ๋งค์นญ๋  ์ˆ˜ ์žˆ๋‹ค)
<Route path="/profiles" />

// v6
<Route path="/profiles/*" />

 

element ์†์„ฑ

v5์—์„œ Route ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”ํ•  ๋•Œ component render children ์†์„ฑ์„ ์‚ฌ์šฉํ–ˆ๋‹ค. v6 ๋ถ€ํ„ด ์ด ์†์„ฑ๋“ค์ด ๋ชจ๋‘ ์—†์–ด์ง€๊ณ  element๋กœ ํ†ต์ผ๋๋‹ค. ๋”์ด์ƒ inline function์„ ์ด์šฉํ•ด routeProps๋ฅผ ๋„˜๊ธธ ์ˆ˜๋„ ์—†๋‹ค. ๋Œ€์‹  Router์—์„œ ์ œ๊ณตํ•˜๋Š” Hooks(useLocation ๋“ฑ)๋ฅผ ํ†ตํ•ด ๋ผ์šฐํŠธ ๊ฐ์ฒด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

// v5
<Route path="profiles" component={Profiles} />
<Route path="profiles" render={(routeProps) => <Profiles {...routeProps} />} />

// v6
<Route path="/profiles/*" element={<Profiles />} />

 

<Route> index ์†์„ฑ

Route์—์„  index ๋ผ๋Š” props๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. index๋Š” path=’/’ ์™€ ๋™์ผํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„๋‹ค. ์ฆ‰, ํ•ด๋‹น ๋ผ์šฐํŠธ๋‚ด์—์„œ ๊ฐ€์žฅ ์ƒ์œ„ ๊ฒฝ๋กœ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. ์˜ˆ๋ฅผ๋“ค๋ฉด...

 

  • ‘/’ ๊ฒฝ๋กœ์˜ ์„œ๋ธŒ ๋ผ์šฐํŠธ index๋Š” ‘/’
  • ‘/courses’ ๊ฒฝ๋กœ์˜ ์„œ๋ธŒ ๋ผ์šฐํŠธ index๋Š” ‘/courses’
  • ...

 

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ /courses ๊ฒฝ๋กœ์— ์ง„์ž…ํ•˜๋ฉด ์ฒซ๋ฒˆ์งธ ์„œ๋ธŒ ๋ผ์šฐํŠธ์˜ index๋Š” /courses ๊ฒฝ๋กœ์™€ ๋™์ผํ•˜๋ฏ€๋กœ CoursesIndex ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”ํ•œ๋‹ค.

 

๊ณต์‹ ๋ฌธ์„œ ์˜ˆ์ œ โ–ผ

function Courses() {
  return (
    <div>
      <h2>Courses</h2>
      <Outlet /> {/* ๊ฒฝ๋กœ์— ๋”ฐ๋ผ CoursesIndex ํ˜น์€ Course ์ปดํฌ๋„ŒํŠธ ๋ Œ๋” */}
    </div>
  );
}
// CoursesIndex / Course ์ปดํฌ๋„ŒํŠธ ์ƒ๋žต

const App = () => {
  return (
    <Routes>
      <Route path="/courses" element={<Courses />}>
        <Route index={true} element={<CoursesIndex />} />
        <Route path="/courses/:id" element={<Course />} />
      </Route>
    </Routes>
  );
};

export default App;

 

useRouteMatch → ์ƒ๋Œ€๊ฒฝ๋กœ


v6๋ถ€ํ„ฐ ์ƒ๋Œ€๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. .์€ ํ˜„์žฌ ๊ฒฝ๋กœ(๋ผ์šฐํŠธ) .. ์ƒ์œ„ ๊ฒฝ๋กœ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์˜จ์  .์ด ์•„๋‹Œ ์Šฌ๋ž˜์‹œ /๋ฅผ ์ด์šฉํ•ด์„œ ์ž…๋ ฅํ•˜๋ฉด(/profiles) ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ํ•ด์„ํ•˜๋Š” ์  ์ฃผ์˜

// v5 (์ ˆ๋Œ€๊ฒฝ๋กœ๋งŒ ๊ฐ€๋Šฅ)
<Link to="/profiles/smith">smith</Link>

// v6 (์ƒ๋Œ€๊ฒฝ๋กœ ๋ฐ ์ ˆ๋Œ€๊ฒฝ๋กœ ๋ชจ๋‘ ๊ฐ€๋Šฅ)
// ํ˜„์žฌ ๊ฒฝ๋กœ๊ฐ€ /profiles๋ผ๊ณ  ๊ฐ€์ •
<Link to="smith">smith</Link> // "/profiles/smith"
<Link to="./smith">smith</Link> // "/profiles/smith"
<Link to=".">smith</Link> // "/profiles"
<Link to="..">smith</Link> // "/"
<Link to="../smith">smith</Link> // "/smith"

 

match ๊ฐ์ฒด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” useRouteMatch Hook๋„ v6์—์„œ ์‚ฌ๋ผ์กŒ๋‹ค. ๋Œ€์‹  ์ƒ๋Œ€๊ฒฝ๋กœ๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค.

// ํ˜„์žฌ ๊ฒฝ๋กœ๊ฐ€ /home ์ด๋ผ๊ณ  ๊ฐ€์ •
// v5 
const match = useRouteMatch(); // {path: '/home', url: '/home', isExact: true, params: {…}}
<Link to={match.url} /> // ํ˜„์žฌ url('/home')๋กœ ์ด๋™ํ•˜๋Š” ๋งํฌ
<Route exact path={`${match.path}/about`} /> // '/home/about'์— ๋Œ€ํ•œ ๋ผ์šฐํŠธ

// v6
<Link to="."/> // '/home'
<Route path="about"/> // '/home/about'

 

ํ˜น์€ useRouteMatch์™€ ์œ ์‚ฌํ•œ useMatch๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ˜„์žฌ ๊ฒฝ๋กœ์™€ useMatch ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธด ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•œ๋‹ค๋ฉด ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ผ์น˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด null์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

// ํ˜„์žฌ ๊ฒฝ๋กœ๊ฐ€ "/profiles" ๋ผ๊ณ  ๊ฐ€์ •
const match = useMatch('/profiles')
console.log(match) // {params: {…}, pathname: '/profiles', pathnameBase: '/profiles', pattern: {…}}

 

์ค‘์ฒฉ ๋ผ์šฐํŠธ (์„œ๋ธŒ ๋ผ์šฐํŠธ)


๋”๋ณด๊ธฐ
import React from 'react';
import { Route, Link, Routes } from 'react-router-dom';
import About from './components/About';
import Home from './components/Home';
import Profiles from './components/Profiles';
import Profile from './components/Profile';
import './App.css';

const App = function () {
  return (
    <div className="container">
      <h2>๋งํฌ ๋ชฉ๋ก</h2>
      <ul>
        <li>
          <Link to="/">ํ™ˆ์œผ๋กœ ์ด๋™</Link>
        </li>
        <li>
          <Link to="/about">์†Œ๊ฐœ๋กœ ์ด๋™</Link>
        </li>
        <li>
          <Link to="/profiles">ํ”„๋กœํ•„ ๋ชฉ๋ก</Link>
        </li>
      </ul>
      <hr />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />

        {/* ๋ฐฉ๋ฒ• 1 : ๋ถ€๋ชจ ๋ผ์šฐํŠธ ๊ฒฝ๋กœ์— "/profiles/*" ์ž…๋ ฅ์‹œ(ํ•˜์œ„ Route ์ˆœ์„œ์— ์ฃผ์˜) */}
        <Route path="/profiles/*" element={<Profiles />}>
          <Route path=":username" element={<Profile />} />
          <Route path="*" element={<div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š” </div>} />
        </Route>

        {/* ๋ฐฉ๋ฒ• 2 : ๋ถ€๋ชจ ๋ผ์šฐํŠธ ๊ฒฝ๋กœ์— "/profiles" ์ž…๋ ฅ์‹œ(์ถ”์ฒœ) */}
        <Route path="/profiles" element={<Profiles />}>
          <Route index={true} element={<div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š” </div>} />
          <Route path=":username" element={<Profile />} />
        </Route>
      </Routes>
    </div>
  );
};

export default App;

/profiles ๋ผ์šฐํŒ… ํ™”๋ฉด

 

์„œ๋ธŒ ๋ผ์šฐํŠธ(์ค‘์ฒฉ ๋ผ์šฐํŠธ)๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๋ผ์šฐํŠธ(/profiles) ๋‚ด๋ถ€์— ๋ผ์šฐํŠธ(/profiles/:username)๋ฅผ ๋˜ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

 

  • /profiles ๊ฒฝ๋กœ ๋ผ์šฐํŠธ : <div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”</div> ์—˜๋ฆฌ๋จผํŠธ ๋ Œ๋”
  • (/profiles ๊ฒฝ๋กœ์—์„œ)์œ ์ € ์„ ํƒ ํ›„ /profiles/:username ๊ฒฝ๋กœ๋กœ ๋ผ์šฐํŠธ
    • ๊ธฐ์กด <div>...</div> ์—˜๋ฆฌ๋จผํŠธ๋Š” ๋งˆ์šดํŠธ ํ•ด์ œ๋˜๊ณ 
    • <Profile> ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”

 

React Router v5์—์„  exact ์†์„ฑ์„ ํ™œ์šฉํ•ด ์„œ๋ธŒ ๋ผ์šฐํŠธ๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์•„๋ž˜๋Š” v5 ์ฝ”๋“œ โ–ผ

// App.js
<Route path="/profiles" component={Profiles} />

// Profiles.js
<Route path="/profiles" exact render={() => <div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š” </div>} />
<Route path="/profiles/:username" component={Profile} />

 

๋ฐฉ๋ฒ• 1

v6์—์„  exact ์†์„ฑ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ ์šฉ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ *๋ฅผ ์ด์šฉํ•ด ์„œ๋ธŒ ๋ผ์šฐํŠธ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. v6๋ถ€ํ„ฐ ์ƒ๋Œ€๊ฒฝ๋กœ๋ฅผ ์ง€์›ํ•˜๋ฏ€๋กœ /profiles/:username ์ฝ”๋“œ๋Š” :username๋งŒ ์ž…๋ ฅํ•˜๋ฉด ๋œ๋‹ค.

 

v6 ์ฝ”๋“œ - ๋ฐฉ๋ฒ• 1 โ–ผ

// App.js
<Routes>
  <Route path="/profiles/*" element={<Profiles />} />
</Routes>

// Profiles.js
// ํ˜„์žฌ ๊ฒฝ๋กœ(/profiles)์—์„œ ๋งค์นญํ•˜๋Š” ์„œ๋ธŒ ๊ฒฝ๋กœ๊ฐ€ ์—†์„๋•Œ๋ฅผ ๋งค์นญํ•˜๊ณ  ์‹ถ์„ ๋•,
// path="*" ํ˜น์€ path="" ํ˜•ํƒœ๋กœ ์ž…๋ ฅํ•˜๋ฉด ๋œ๋‹ค.
<Routes>
  <Route path="*" element={<div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š” </div>} /> {/* "/profiles" */}
  <Route path=":username" element={<Profile />} /> {/* "/profiles/:username" */}
</Routes>

 

๋ฐฉ๋ฒ• 2-1 — Outlet ํ™œ์šฉ

๐Ÿ’ก Outlet์€ ์ค‘์ฒฉ ๋ผ์šฐํŠธ, ๊ณตํ†ต์ ์œผ๋กœ ๋ณด์—ฌ์•ผํ•  ๋ ˆ์ด์•„์›ƒ์ด ์žˆ์„ ๋•Œ๋„ ์œ ์šฉํ•˜๋‹ค

 

React Router v6์— ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€๋œ <Outlet>์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. 1๊ฐœ ํŒŒ์ผ(์˜ˆ์‹œ์—์„  App.js)์— ๋ชจ๋“  ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜๊ณ , ์ค‘์ฒฉ ๋ผ์šฐํŠธ(์„œ๋ธŒ ๋ผ์šฐํŠธ) ๋˜๋Š” ๊ณณ์— <Outlet /> ์ฝ”๋“œ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. ํ•œ ๊ณณ์—์„œ ์ค‘์ฒฉ ๋ผ์šฐํŠธ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ๋” ํŽธํ•˜๋‹ค. (ํ•˜์œ„ Route ์ˆœ์„œ ์ฃผ์˜. :username ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ๋ผ์šฐํŠธ๊ฐ€ ์œ„์— ์™€์•ผํ•จ)

 

v6 ์ฝ”๋“œ - ๋ฐฉ๋ฒ• 2 (Outlet ํ™œ์šฉ) โ–ผ

// App.js
import { Routes, Route, Outlet } from 'react-router-dom';

<Routes>
  <Route path="/profiles/*" element={<Profiles />}>
    <Route path=":username" element={<Profile />} />{' '}
    {/* "/profiles/:username" */}
    <Route path="*" element={<div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”</div>} />{' '}
    {/* "/profiles/*" */}
  </Route>
</Routes>;

// Profiles.js
<Outlet />
/*
    ์•„๋ž˜ 2๊ฐœ์ค‘ 1๊ฐœ๊ฐ€ ๋ผ์šฐํŠธ๋จ
    "/profiles" ํ˜น์€ "/profiles/*" ๊ฒฝ๋กœ → <div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”</div>
    "/profiles/:username" ๊ฒฝ๋กœ → <Profile>
*/

 

๋ฐฉ๋ฒ• 2-2 — Outlet + Route Index ํ™œ์šฉ (์ถ”์ฒœ)

Outlet์„ ํ™œ์šฉํ•ด ์„œ๋ธŒ ๋ผ์šฐํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ƒํ™ฉ์—์„œ, /profiles/:username ์•„๋ž˜ ๋”์ด์ƒ ํ•˜์œ„ ๊ฒฝ๋กœ๊ฐ€ ์—†๋‹ค๋ฉด ๋ถ€๋ชจ ๋ผ์šฐํŠธ ๊ฒฝ๋กœ์— ๊ตณ์ด *(asterisk)๋ฅผ ๋ถ™์ธ /profiles/* ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

 

๋ถ€๋ชจ ๋ผ์šฐํŠธ์—” * ๊ธฐํ˜ธ ์—†์ด /profiles ๊ฒฝ๋กœ๋งŒ ์ž…๋ ฅํ•˜๊ณ , ์ƒ์œ„ ๋ผ์šฐํŠธ(/profiles)์™€ ์ผ์น˜ํ•˜์ง€๋งŒ ๊ทธ ์ดํ›„ ๊ฒฝ๋กœ๊ฐ€ ์ฃผ์–ด์ง€์ง€ ์•Š์€ ์ƒํ™ฉ์— ๋งค์นญ๋  ์„œ๋ธŒ ๋ผ์šฐํŠธ์— index props๋ฅผ ๋„˜๊ธฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

`Route`์˜ `index` ์†์„ฑ์€ ํ•ด๋‹น ๋ผ์šฐํŠธ์˜ ๊ฐ€์žฅ ์ƒ์œ„ ๊ฒฝ๋กœ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค. ์ด ์˜ˆ์ œ์—์„  `/profiles` ๊ฒฝ๋กœ์™€ ๊ฐ™๋‹ค.

// App.js
<Routes>
  <Route path="/profiles" element={<Profiles />}>
    <Route index element={<div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”</div>} /> {/* "/profiles" */}
    <Route path=":username" element={<Profile />} />{' '}
    {/* "/profiles/:username" */}
  </Route>
</Routes>;

 

  • /profiles ๊ฒฝ๋กœ ๋ Œ๋” ์ปดํฌ๋„ŒํŠธ : ๋ฐฉ๋ฒ• 2-1 / ๋ฐฉ๋ฒ• 2-2 ๋ชจ๋‘ <div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š” </div>
  • /profiles/:username ๊ฒฝ๋กœ ๋ Œ๋” ์ปดํฌ๋„ŒํŠธ : ๋ฐฉ๋ฒ• 2-1 / ๋ฐฉ๋ฒ• 2-2 ๋ชจ๋‘ <Profile />
  • /profiles/:username/123 ๊ฒฝ๋กœ ๋ Œ๋” ์ปดํฌ๋„ŒํŠธ
    • ๋ฐฉ๋ฒ• 2-1 : <div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š” </div>
    • ๋ฐฉ๋ฒ• 2-2 : ๋งค์นญํ•˜๋Š” ๋ผ์šฐํŠธ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋ Œ๋”ํ•˜์ง€ ์•Š์Œ. ์ด๋• path=”:username”์— ๋Œ€ํ•œ ํ•˜์œ„ ๋ผ์šฐํŠธ(/profiles/:username/123)๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

useHistory → useNavigate


v6๋ถ€ํ„ฐ useHistory ์ด๋ฆ„์ด useNavigate๋กœ ๋ณ€๊ฒฝ๋๋‹ค. v5์˜ useHistory๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ด ๊ฐ์ฒด์— push replace ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ํฌํ•จ๋ผ์žˆ๋‹ค. ๋ฐ˜๋ฉด v6์˜ useNavigate๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋•Œ๋ฌธ์— navigate๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ์ฒ˜๋Ÿผ ์จ์•ผํ•œ๋‹ค. navigate์—๋„ ์ƒ๋Œ€๊ฒฝ๋กœ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

// ํ˜„์žฌ ๊ฒฝ๋กœ๊ฐ€ 'profiles'๋ผ๊ณ  ๊ฐ€์ •
// v5
const history = useHistory();
history.push('/profiles/smith');
history.replace('/profiles/smith');
history.go(1); // 1ํŽ˜์ด์ง€ forward
history.go(-2); // 2ํŽ˜์ด์ง€ back

// v6
const navigate = useNavigate();
navigate('smith'); // (์ƒ๋Œ€๊ฒฝ๋กœ)"profiles/smith"
navigate('/profiles/smith', { replace: true }); // ์ ˆ๋Œ€๊ฒฝ๋กœ
navigate(1);
navigate(-2);

 

push, replace ์ฐจ์ด์  ๋น„๊ต

๐Ÿ’ก React Router์˜ ๋ฅผ ํ†ตํ•ด ์ด๋™ํ•  ๋•Œ๋งˆ๋‹ค history stack ์Œ“์ธ๋‹ค. replace๋Š” ๋กœ๊ทธ์ธ์„ ๋งˆ์น˜๊ณ  ์ด๋™ํ•œ ๋‹ค์Œ ํŽ˜์ด์ง€์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ ๋‹ค์‹œ ๋กœ๊ทธ์ธ ํ™”๋ฉด์ด ๋ณด์ด์ง€ ์•Š๋„๋กํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค.

 

replace๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด history stack์— ์Œ“์—ฌ ์žˆ๋Š” ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ์š”์†Œ('/profiles')๋ฅผ ์ด๋™ํ•  ๊ฒฝ๋กœ('/profiles/smith')๋กœ ๋Œ€์ฒดํ•œ๋‹ค. ์ด๋™ ํ›„ ์ด์ „ ๊ฒฝ๋กœ๋ฅผ ๋‚จ๊ธฐ์ง€ ์•Š์„ ๋•Œ replace๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

  • push ์ด์šฉ์‹œ
    • ์ด๋™ : '/''/profiles''/profiles/smith'
    • ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ ํ›„ : 'profiles'
    • history stack: ['/', 'profiles', 'profiles/smith']
  • replace ์ด์šฉ์‹œ
    • ์ด๋™ : '/' (push) → '/profiles' (replace) → '/profiles/smith'
    • ๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ ํ›„ : '/'
    • history stack: ['/', 'profiles/smith']

 

state ๋„˜๊ธฐ๊ธฐ

navigate ๋‘๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ(๊ฐ์ฒด)๋ฅผ ํ†ตํ•ด ์›ํ•˜๋Š” state๋ฅผ ๋„˜๊ธธ ์ˆ˜๋„ ์žˆ๋‹ค.

// Profiles.js
// v5
history.push({ pathname: '/profiles/smith', state: { name: 'smith' } });
// v6
navigate('smith', { state: { name: 'smith' } });

// Profile.js
import { useLocation } from 'react-router-dom';
const Profile = function () {
  const { state } = useLocation();
  console.log(state); // {name: 'smith'}
};

 

Redirect → Navigate


๐Ÿ’ก Navigate๋Š” useNavigate ํ•จ์ˆ˜์˜ ์ปดํฌ๋„ŒํŠธ ๋ฒ„์ „

 

v6๋ถ€ํ„ฐ <Redirect> ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—†์–ด์ง€๊ณ  <Navigate>๋กœ ๋Œ€์ฒด๋๋‹ค. ๊ธฐ์กด <Redirect>๋Š” replace ๋กœ์ง์œผ๋กœ ์ž‘๋™ํ–ˆ์ง€๋งŒ, <Navigate>๋Š” push ๋กœ์ง์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค. <Navigate>์— replace ๋กœ์ง์„ ์ ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ replace={true} props๋ฅผ ๋„˜๊ธฐ๋ฉด ๋œ๋‹ค.

// v5
<Redirect to="about " /> // replace ๋กœ์ง์œผ๋กœ ์ž‘๋™
<Redirect to="home" push /> // push ๋กœ์ง์œผ๋กœ ์ž‘๋™

// v6
<Navigate to="about" repalce /> // replace ๋กœ์ง์œผ๋กœ ์ž‘๋™
<Navigate to="home" /> // push ๋กœ์ง์œผ๋กœ ์ž‘๋™

 

Navigate๋Š” useNavigate๋ฅผ ์ปดํฌ๋„ŒํŠธ ํ˜•ํƒœ๋กœ ๊ฐ์‹ผ ๋ฒ„์ „์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๋‘˜ ๋ชจ๋‘ to replace state props๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. return() ๋ฌธ ์•ˆ์—์„œ ์ปดํฌ๋„ŒํŠธ๋กœ ์ž‘์„ฑํ•  ๋•Œ Navigate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์•„๋ž˜๊ฐ™์€ ์ƒํ™ฉ์—์„  ๋‘˜ ์ค‘ ์•„๋ฌด๊ฑฐ๋‚˜ ์จ๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

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

const Profile = function () {
  const { username } = useParams();
  const profile = profileData[username];

  if (!profile) {
    return <Navigate to="profiles" />;
    // useNavigate()('/profiles')
  }
};

 

useRoutes๋ฅผ ์ด์šฉํ•ด ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๋ผ์šฐํŠธ๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ๋„ Navigate๊ฐ€ ์œ ์šฉํ•˜๊ฒŒ ์“ฐ์ธ๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ฆฌ๋””๋ ‰์…˜ ์‹œํ‚ฌ ๋•Œ Navigate ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

์ฝ”๋“œ ์ฐธ๊ณ  StackOverFlow โ–ผ

// routes.js
import { Navigate } from 'react-router-dom';

const routes = (isLoggedIn) => [
  {
    path: '/app',
    element: isLoggedIn ? <DashboardLayout /> : <Navigate to="/login" />,
    children: [
      { path: '/dashboard', element: <Dashboard /> },
      { path: '/account', element: <Account /> },
      { path: '/', element: <Navigate to="/app/dashboard" /> },
      // ...
    ],
  },
];
export default routes;

// App.js
import routes from './routes';
import { useRoutes } from 'react-router-dom';

function App() {
  const { isLoggedIn } = useSelector((state) => state.auth);
  const routing = useRoutes(routes(isLoggedIn));

  return <>{routing}</>;
}

 

Optional URL


ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๋ฐ›๋Š” ์ƒํ™ฉ์—์„œ ? ๋ฌผ์Œํ‘œ๋ฅผ ํ™œ์šฉํ•œ Optional URL๋„ ์—†์–ด์กŒ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ํ˜น์€ ์—†์„ ๋•Œ ๋ชจ๋‘ ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋” ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. v6์—์„  ํ•„์š”ํ•œ ๋งŒํผ Route๋ฅผ ์ •์˜ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ์—์„œ username ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†์„ ๋•Œ(/profiles) ํ˜น์€ ์žˆ์„ ๋•Œ(/profiles/smith) ๋ชจ๋‘ Profiles ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”ํ•œ๋‹ค.

// v5
<Route path="/profiles/:username?" component={Profiles} />

// v6
<Route path="/profiles/:username" element={<Profiles />} />
<Route path="/profiles/" element={<Profiles />} />

 

NavLink


NavLink๋Š” Link์™€ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™ํ•˜์ง€๋งŒ ํ™œ์„ฑํ™”(to="..."์— ๋ช…์‹œํ•œ ๊ฒฝ๋กœ์™€ ํ˜„์žฌ ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•  ๋•Œ) ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํŠน์ • ์Šคํƒ€์ผ์ด๋‚˜ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. v5 ๋Œ€๋น„ ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

  • <NavLink exact><NavLink end> ์ด๋ฆ„ ๋ณ€๊ฒฝ
  • activeClassName activeStyle props ์ œ๊ฑฐ. ๋Œ€์‹  style๊ณผ className์— ์ธ๋ผ์ธ ํ•จ์ˆ˜ ์ „๋‹ฌ ๊ฐ€๋Šฅ

 

active ๊ฐ์ฒด

style๊ณผ className ์†์„ฑ ์ธ๋ผ์ธ ํ•จ์ˆ˜์˜ ์ฒซ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ํ™œ์„ฑํ™” ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค. ํ˜„์žฌ ๊ฒฝ๋กœ๊ฐ€ to์— ๋ช…์‹œํ•œ ๊ฒฝ๋กœ์™€ ์ผ์น˜ํ•˜๋ฉด {isActive: true}, ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด {isActive: false}๋กœ ๋‚˜์˜จ๋‹ค

<NavLink to="smith" style={(active) => console.log(active)}>
  ๋งํฌ
</NavLink>;
// console.log -> ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•˜๋ฉด {isActive: true} ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด {isActive: false}

 

className

className ์กฐ๊ฑด์„ ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•  ๋•Œ(ํ™œ์„ฑํ™” ์ƒํƒœ) active ๋ผ๋Š” ์ด๋ฆ„์˜ ํด๋ž˜์Šค๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋œ๋‹ค. ์ผ์น˜ํ•˜์ง€ ์•Š์„ ๋• ๋นˆ ๋ฌธ์ž์—ด""๋กœ ๋‚˜์˜จ๋‹ค.

 

HTML ์—˜๋ฆฌ๋จผํŠธ โ–ผ

// ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•  ๋•Œ NavLink ์—˜๋ฆฌ๋จผํŠธ
<a href="/profiles/smith" class="active" aria-current="page">๋งํฌ</a>

// ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ๋•Œ NavLink ์—˜๋ฆฌ๋จผํŠธ
<a href="/profiles/smith" class="">๋งํฌ</a>

 

NavLink ์˜ˆ์‹œ ์ฝ”๋“œ

ํ™œ์„ฑํ™” ์ƒํƒœ๋ฉด(to="..."์— ๋ช…์‹œํ•œ ๊ฒฝ๋กœ์™€ ํ˜„์žฌ ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•  ๋•Œ) red ์Šคํƒ€์ผ ๋งํฌ์™€ 'activated' ๋ผ๋Š” ํด๋ž˜์Šค ์ด๋ฆ„์„ ๊ฐ–๊ณ , ํ™œ์„ฑํ™” ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ฉด 'deactivated' ๋ผ๋Š” ํด๋ž˜์Šค ์ด๋ฆ„์„ ๊ฐ–๋Š” ์˜ˆ์‹œ.

<NavLink
  to="smith"
  style={({ isActive }) => (isActive ? { color: 'red' } : null)}
  className={({ isActive }) => (isActive ? 'activated' : 'deactivated')}
>
  Smith(NavLink)
</NavLink>;

 

end ์†์„ฑ

ํ•˜์œ„ ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•˜๋”๋ผ๋„ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” ํ™œ์„ฑํ™” ์ƒํƒœ๋กœ ๋งค์นญ๋˜์ง€ ์•Š๋„๋ก ํ•  ๋•Œ end ์†์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ์—์„  ์›น์‚ฌ์ดํŠธ ๋ฃจํŠธ('/')์—์„œ๋งŒ ํ™œ์„ฑํ™”๋˜๊ณ  ๋‹ค๋ฅธ URL์€ ํ™œ์„ฑํ™”๋˜์ง€ ์•Š๋Š”๋‹ค. v6 ๋ถ€ํ„ฐ ๊ธฐ๋ณธ์ ์œผ๋กœ exact ์˜ต์…˜์ด ์ ์šฉ๋˜๋ฏ€๋กœ end ์†์„ฑ์€ ๋”ฑํžˆ ์‚ฌ์šฉํ•  ์ผ์ด ์—†์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ•œ๋‹ค.

<NavLink to="/" end>
  Home
</NavLink>

 

useRoutes


v5์—์„œ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด ๊ณ„์ธต ๊ตฌ์กฐ๋กœ ๋ผ์šฐํŠธ๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ react-router-config๋ผ๋Š” ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ–ˆ๋‹ค. v6๋ถ€ํ„ด React Router์—์„œ ์ œ๊ณตํ•˜๋Š” useRoutes Hook์„ ์ด์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

useRoutes Hook์€ ๊ธฐ๋Šฅ์ ์œผ๋กœ <Routes>์™€ ๋™์ผํ•˜์ง€๋งŒ, JSX๋ฅผ ์“ฐ์ง€ ์•Š๊ณ  JS ๊ฐ์ฒด ํ˜•ํƒœ๋กœ <Routes> ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ useRoutes([{...}]) ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋„˜๊ธฐ๋Š” {...} ๊ฐ์ฒด๋Š” <Route> ์—˜๋ฆฌ๋จผํŠธ์™€ ๋™์ผํ•˜๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

 

useRoutes๋Š” ๋งค์นญํ•˜๋Š” ๋ผ์šฐํŠธ๊ฐ€ ์žˆ์œผ๋ฉด React ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์—†๋‹ค๋ฉด null์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. <Routes>์™€ useRoutes๋Š” ๋™์ผํ•˜๋ฏ€๋กœ ์„ ํ˜ธํ•˜๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์•„๋ž˜ ๋‘ ์ฝ”๋“œ๋Š” ์™„์ „ํžˆ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™ํ•œ๋‹ค.

๋”๋ณด๊ธฐ
import { useRoutes } from 'react-router-dom';

const App = function () {
  const elements = useRoutes([
    // Route์—์„œ ์‚ฌ์šฉํ•˜๋Š” props์™€ ๋™์ผ
    { path: '/', element: <Home /> },
    { path: '/about', element: <About /> },
    {
      path: '/profiles',
      element: <Profiles />,
      children: [
        // ์ค‘์ฒฉ ๋ผ์šฐํŠธ(์„œ๋ธŒ ๋ผ์šฐํŠธ)๋Š” children ํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉ
        { index: true, element: <div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”</div> },
        { path: ':username', element: <Profile /> },
      ],
    },
  ]);

  return (
    <div>
      <ul>{/* ์ƒ๋žต */}</ul>
      {elements}
    </div>
  );
};
๋”๋ณด๊ธฐ
import { Routes, Route } from 'react-router-dom';

const App = function () {
  return (
    <div>
      <ul>{/* ์ƒ๋žต */}</ul>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/profiles" element={<Profiles />}>
          <Route index element={<div>์œ ์ €๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š” </div>} />
          <Route path=":username" element={<Profile />} />
        </Route>
      </Routes>
    </div>
  );
};

 

์ฐธ๊ณ ๋กœ NotFound ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•  ๋• ์•„๋ž˜์ฒ˜๋Ÿผ '*' ๊ฒฝ๋กœ๋ฅผ ๊ฐ–๋Š” ๊ฐ์ฒด(๋ผ์šฐํŠธ)๋ฅผ ํ•˜๋‚˜ ๋” ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค

const element = useRoutes([
  { path: '/', element: <Home /> },
  // ์ƒ๋žต...
  { path: '*', element: <NotFound /> },
]);

 

๊ณต์‹ ๋ฌธ์„œ ์˜ˆ์ œ ์ฝ”๋“œ


 

๋ ˆํผ๋Ÿฐ์Šค


 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•