๐Ÿ’ป ์‹ค์ „ ํ”„๋กœ๊ทธ๋ž˜๋ฐ | React ๋ผ์šฐํŒ…(Routing) ์™„๋ฒฝ ์ •๋ฆฌ ๋ฐ ํ™œ์šฉ๋ฒ• ๊ฐ€์ด๋“œ

๐ŸŒŸ ๋“ค์–ด๊ฐ€๋ฉฐ - ๋ผ์šฐํŒ…(Routing)์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • URL์„ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ํด๋ฆญํ•  ๋•Œ, ํ•ด๋‹น URL์„ ๋ถ„์„ํ•˜๊ณ  ์˜ฌ๋ฐ”๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š” ๊ณผ์ •์„ ๋ผ์šฐํŒ…(Routing)์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ๋ฆฌ์•กํŠธ(React)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ตœ์‹  ์›น ์•ฑ์€ Single Page Application(SPA) ๋ฐฉ์‹์„ ์ฑ„ํƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ผ์šฐํŒ…์˜ ์ค‘์š”์„ฑ์ด ๋งค์šฐ ํฝ๋‹ˆ๋‹ค.

๐Ÿ“Œ React ๋ผ์šฐํŒ…์˜ ํ•ต์‹ฌ ํŠน์ง• ์ •๋ฆฌ

  • ๋™์  ํŽ˜์ด์ง€ ์ „ํ™˜: ํŽ˜์ด์ง€ ์ „์ฒด๋ฅผ ์ƒˆ๋กœ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š๊ณ ๋„ ๋น ๋ฅด๊ฒŒ ํ™”๋ฉด ์ „ํ™˜ ๊ฐ€๋Šฅ
  • URL ํŒŒ๋ผ๋ฏธํ„ฐ๋‚˜ ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง: ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ URL ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง์„ ํ™œ์šฉ
  • ์ค‘์ฒฉ๋œ ๋ผ์šฐํŒ…(Nested Routing): ๋ณต์žกํ•œ UI๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ณ  ๊ตฌ์กฐํ™” ๊ฐ€๋Šฅ
  • ๋ธŒ๋ผ์šฐ์ € ํžˆ์Šคํ† ๋ฆฌ ์ œ์–ด: ๋ธŒ๋ผ์šฐ์ € ๋’ค๋กœ ๊ฐ€๊ธฐ/์•ž์œผ๋กœ ๊ฐ€๊ธฐ ๋“ฑ ๊ธฐ๋ณธ์ ์ธ ์›น ํ™˜๊ฒฝ๊ณผ์˜ ํ˜ธํ™˜์„ฑ ์œ ์ง€

๐Ÿšง ๋ฆฌ์•กํŠธ ๋ผ์šฐํ„ฐ(react-router)์˜ ์žฅ๋‹จ์  ๋ถ„์„

  • ์žฅ์ :
    • SPA ๊ฐœ๋ฐœ์—์„œ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ธ ํŽ˜์ด์ง€ ์ „ํ™˜
    • ๋‹ค์–‘ํ•œ Hook ๋ฐ ์ปดํฌ๋„ŒํŠธ ์ œ๊ณต์œผ๋กœ ๋”์šฑ ์‰ฌ์šด ์‚ฌ์šฉ๋ฒ•
    • ๊ธฐ์กด ์›น ํ‘œ์ค€ ๋ธŒ๋ผ์šฐ์ € API์™€์˜ ๋›ฐ์–ด๋‚œ ํ˜ธํ™˜์„ฑ
  • ๋‹จ์ :
    • ์ดˆ๊ธฐ ์„ค์ •๊ณผ ์ดํ•ด ํ•„์š”์„ฑ ๋†’์Œ
    • ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๊ผผ๊ผผํ•œ ์„ค๊ณ„๊ฐ€ ์ค‘์š”

๐Ÿ› ๏ธ ์‹ค์ œ ์ ์šฉ ์˜ˆ์‹œ ์ฝ”๋“œ ๋ฐ ํ™œ์šฉ๋ฒ•

๊ธฐ๋ณธ ์„ค์น˜ ๋ฐ ์„ค์ •ํ•˜๊ธฐ

// ๋ผ์šฐํ„ฐ ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
npm install react-router-dom @types/react-router-dom

// index.tsx์— BrowserRouter ์ ์šฉ
import { BrowserRouter } from 'react-router-dom';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

Route๋กœ ์ปดํฌ๋„ŒํŠธ ์—ฐ๋™ํ•˜๊ธฐ

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

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/profiles/:username" element={<Profile />} />
    </Routes>
  );
}

์ค‘์ฒฉ ๋ผ์šฐํŒ… ์˜ˆ์ œ

// ์ค‘์ฒฉ ๋ผ์šฐํŒ… ์„ค์ •ํ•˜๊ธฐ
<Routes>
  <Route path="articles" element={<Articles />}>
    <Route path=":id" element={<Article />} />
  </Route>
</Routes>

๐Ÿ”— Link์™€ NavLink ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€ ์ „ํ™˜ํ•˜๊ธฐ

// Link ํ™œ์šฉ ์˜ˆ์‹œ
<Link to="/about">์†Œ๊ฐœ ํŽ˜์ด์ง€</Link>

// NavLink๋กœ ํ™œ์„ฑํ™”๋œ ๋งํฌ ์Šคํƒ€์ผ ์ ์šฉํ•˜๊ธฐ
<NavLink
  to="/articles/1"
  style={({ isActive }) => (isActive ? {color:'green', fontWeight:'bold'} : undefined)}
>
  ๊ฒŒ์‹œ๊ธ€ 1
</NavLink>

๐Ÿ’ก URL ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง ํŽธ๋ฆฌํ•˜๊ฒŒ ๋‹ค๋ฃจ๊ธฐ

// ํŒŒ๋ผ๋ฏธํ„ฐ ์ ‘๊ทผ
import { useParams } from 'react-router-dom';
const { username } = useParams();

// ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง ์ ‘๊ทผ
import { useSearchParams } from 'react-router-dom';
const [searchParams, setSearchParams] = useSearchParams();
let detail = searchParams.get("detail");

// ์ฟผ๋ฆฌ ๋ณ€๊ฒฝ ์˜ˆ์‹œ
setSearchParams({ detail: "true", page: "2" });

๐Ÿšฆ NotFound ํŽ˜์ด์ง€ ์„ค์ •ํ•˜์—ฌ ๊ณ ๊ฐ ๊ฒฝํ—˜ ๊ฐœ์„ ํ•˜๊ธฐ

// NotFound ๋ผ์šฐํŒ… ์ถ”๊ฐ€
<Route path="*" element={<NotFound />} />

๐Ÿ”ฎ ํŠธ๋ Œ๋“œ ๋ฐ ์ „๋ฌธ๊ฐ€ ์˜๊ฒฌ๊ณผ ๋ฏธ๋ž˜ ์ „๋ง

์ตœ๊ทผ Next.js์™€ ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ํŒŒ์ผ ๊ธฐ๋ฐ˜์˜ ๋ผ์šฐํŒ… ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR) ๋ฐ ์ •์  ์‚ฌ์ดํŠธ ์ƒ์„ฑ(SSG) ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ ๋ผ์šฐํ„ฐ์™€ ๊ฐ™์€ ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํŒ… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ SPA ๊ตฌ์ถ•์—์„œ ์œ ์šฉํ•˜๊ณ  ํญ๋„“๊ฒŒ ํ™œ์šฉ๋˜๊ณ  ์žˆ์ง€๋งŒ, SEO๋‚˜ ํผํฌ๋จผ์Šค ์ตœ์ ํ™”๋ฅผ ๊ณ ๋ คํ•œ SSR/SSG ํ™˜๊ฒฝ์—์„œ๋Š” Next.js์™€ ๊ฐ™์€ ๋„๊ตฌ๋กœ ์ ์  ๋” ์ „ํ™˜๋  ๊ฐ€๋Šฅ์„ฑ๋„ ๋†’์Šต๋‹ˆ๋‹ค.

์ „๋ฌธ๊ฐ€๋“ค์˜ ์˜๊ฒฌ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • React Router๋Š” ์‚ฌ์šฉ์„ฑ์ด ๋›ฐ์–ด๋‚˜ ๋น ๋ฅด๊ฒŒ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ SPA ๊ตฌ์ถ• ํ™˜๊ฒฝ์— ์—ฌ์ „ํžˆ ๋†’์€ ํ™œ์šฉ์„ฑ์„ ๋ณด์ด๊ณ  ์žˆ๋‹ค.
  • ์ ์  ๋” ๋งŽ์€ ๊ธฐ์—…๋“ค์ด SEO์™€ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด Next.js๋‚˜ Remix์™€ ๊ฐ™์€ ์„œ๋ฒ„ ๊ธฐ๋ฐ˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒฝํ–ฅ์ด ๋šœ๋ ทํ•ด์ง€๊ณ  ์žˆ๋‹ค.

โœ… ์š”์•ฝ ๋ฐ ๋งˆ๋ฌด๋ฆฌ

๋ฆฌ์•กํŠธ ๋ผ์šฐํŒ…์€ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ธ SPA ์›น์‚ฌ์ดํŠธ ๊ฐœ๋ฐœ์˜ ํ•„์ˆ˜ ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ ์‹ค์Šต ์˜ˆ์ œ๋ฅผ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•˜๊ณ  ํ™œ์šฉํ•˜๋ฉด ๊ฐœ๋ฐœ์ž๋Š” ๋”์šฑ ์‚ฌ์šฉ์„ฑ ์ข‹๊ณ  ๋งค๋„๋Ÿฌ์šด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ์›น ์•ฑ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ์˜ ์˜จ๋ผ์ธ ํ™˜๊ฒฝ์ด ์ ์  ๋” SPA ๋ฐฉ์‹๊ณผ ์„œ๋ฒ„ ๋ Œ๋”๋ง์˜ ํ˜ผํ•ฉ ํ˜•ํƒœ๋กœ ๋ฐœ์ „ํ•˜๋Š” ๋งŒํผ ์ตœ์‹  ์›น ๊ธฐ์ˆ ์˜ ํ๋ฆ„์„ ์ž˜ ํŒŒ์•…ํ•˜๊ณ  ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋Œ“๊ธ€

๊ฐ€์žฅ ๋งŽ์ด ๋ณธ ๊ธ€