[React] React Router

invisibleVoice·2025년 2월 4일

리액트

목록 보기
14/14
post-thumbnail

React Router

CSR Client Side Routing

기존 웹사이트는 새로운 페이지로 이동하기 위해서 웹 서버로 문서를 요청 후 받아와 렌더링하는 형식으로 작동했다. 그래서 많은 처리 시간이 필요했는데, CSR를 사용하는 React Router를 이용하면 한 페이지 내에서도 여러 페이지를 이동하듯 동작할 수 있다. 이 과정에서 웹 서버에 새로운 문서를 요청하지 않았음에도 URL이 업데이트되는데, 서버에 문서를 요청하는 대신 필요한 부분에 대한 데이터 요청을 할 것이다.

React Router 사용하기

// ./shared/Router.js
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
import Works from "../pages/Works";

const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
        {/* 
            Routes안에 라우팅할 페이지들을 넣는다.
            Route에는 react-router-dom에서 지원하는 props들이 있다
            path : 페이지로 접근할 경로
            element : 해당 주소로 이동했을 때 보여주고자 하는 컴포넌트
         */}
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="contact" element={<Contact />} />
        <Route path="works" element={<Works />} />
      </Routes>
    </BrowserRouter>
  );
};

export default Router;
// App.jsx
import React from "react";
import Router from "./shared/Router"; // 위에서 만든 라우터 불러오기

function App() {
  return <Router />;
}

export default App;

react-router-dom Hooks

useNavigate

이벤트 핸들러에 navigate를 사용하면 우리가 보내고자 하는 path로 페이지를 이동시킬 수 있다.

// src/pages/home.js
import { useNavigate } from "react-router-dom";

const Home = () => {
  // useNavigate 훅에서 제공하는 기능들을 navigate를 통해 사용가능
  const navigate = useNavigate();	

  return (
    <button
      onClick={() => {
        navigate("/works");
      }}
    >
      works로 이동
    </button>
  );
};

export default Home;

useLocation

현재 페이지의 여러 정보를 얻을 수 있는 Hook. 이 정보들을 이용하여 조건부 렌더링에 사용하는 등 여러 용도로 활용가능하다.

// src/pages/works.js
import { useLocation } from "react-router-dom";

const Works = () => {
  const location = useLocation();
  console.log("location :>> ", location);
  // { hash: "", key: "~", pathname: "/works", search: "", state: null }
  return (
    <div>
      <div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div>
    </div>
  );
};

export default Works;

Link는 Hook은 아니다. 그러나 알고 있어야 하는 API다.
Linka태그를 대체하는 API로, JSX에서는 무조건 a 대신 Link를 사용해야 한다. a태그는 페이지를 이동하면서 브라우저가 새로고침이 되는데, 그 과정에서 모든 컴포넌트가 리렌더링 되어 모든 상태값들이 초기화되기 때문이다.

import { Link, useLocation } from 'react-router-dom';

const Works = () => {
  const location = useLocation();
  return (
    <div>
      <div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div>
      <Link to="/contact">contact 페이지로 이동하기</Link>
    </div>
  );
};

export default Works;

props.children을 활용한 레이아웃 구성

모든 페이지들이 공통으로 사용하는 레이아웃이 있는 경우 children을 활용하여 공통 레이아웃을 만들 수 있다.

// Router.jsx
...

const Router = () => {
  return (
    <BrowserRouter> 
      <Layout>	// Layout 태그 내부가 props.children으로 Layout 컴포넌트로 전달된다.
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="contact" element={<Contact />} />
          <Route path="works" element={<Works />} />
        </Routes>
      </Layout>
    </BrowserRouter>
  );
};

// Layout.jsx
...

function Layout({ children }) {
  return (
    <div>
      <Header />
      <div style={{...layoutStyles}}>
        {children}
      </div>
      <Footer />
    </div>
  );
}
...

동적 라우팅 Dynamic Route

path에 유동적인 값을 넣어서 특정 페이지로 이동하도록 만드는 방법을 동적 라우팅이라 한다.

const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        // 일반 라우팅
        <Route path="works" element={<Works />} />
        // 동적 라우팅
        <Route path="works/:id" element={<Works />} />
      </Routes>
    </BrowserRouter>
  );
};

일반 라우팅과 다르게 동적 라우팅은 :id라는 동적인 값을 받는다. 그래서 "works/1"로 이동하든 "works/42"로 이동하든 "works/pikachu"로 이동하든 모두 <Works />로 이동하게 된다.
:iduseParams Hook에서 조회할 수 있는 값이 되어 페이지마다 표시되는 화면을 다르게 만들 수 있다.

🪄동적 라우팅을 사용하더라도 여전히 문서는 하나다! SPA!

useParams

useParams는 상술했듯 path에 있는 id값을 조회할 수 있게 해주는 Hook이다.

// src/pages/Work.js

import React from 'react';
import { useParams } from 'react-router-dom';

const data = [
  { id: 1, todo: '리액트 배우기' },
  { id: 2, todo: '노드 배우기' },
  { id: 3, todo: '자바스크립트 배우기' },
  { id: 4, todo: '파이어 베이스 배우기' },
  { id: 5, todo: '넥스트 배우기' },
  { id: 6, todo: 'HTTP 프로토콜 배우기' },
];

function Work() {
  const param = useParams();
  // 현재 페이지에 따라 param이 결정된다. 만약 지금 works/6 페이지에 있다면
  // param.id = "6", param.todo = 'HTTP 프로토콜 배우기' 가 될 것이다.
  const work = data.find((work) => work.id === parseInt(param.id));

  return <div>{work.todo}</div>;
}

export default Work;

중첩된 라우트 (Outlet)

중첩 라우팅은 특정 라우트 내에서 추가적인 라우트를 정의하는 방식을 말한다. 여러 계층의 UI를 구성할 때 많이 쓰인다.

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import DashboardLayout from './DashboardLayout';
import Profile from './Profile';
import Settings from './Settings';
import Reports from './Reports';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/dashboard" element={<DashboardLayout />}>
          <Route index element={<Profile />} />
          <Route path="settings" element={<Settings />} />
          <Route path="reports" element={<Reports />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

/dashboard 경로는 DashboardLayout 컴포넌트를 사용한다. 이 레이아웃 내에서 /dashboard/settings, /dashboard/reports 등의 경로로 추가적인 페이지들을 중첩하여 설정할 수 있다! 이렇게 DashboardLayout 컴포넌트 안에서 각 섹션의 독립적인 라우트 관리가 가능하며, UX를 개선할 수 있다.

다만 사용하기 위해서는 부모 컴포넌트 DashboardLayout에서 Outlet을 설정해줘야 한다.

import { Outlet } from 'react-router-dom';

function DashboardLayout() {
  return (
    <div>
      <header>Header Section</header>
      <main>
        <Outlet />  {/* 여기에 자식 라우트의 컴포넌트가 렌더링된다! (Settings, Reports) */}
      </main>
      <footer>Footer Section</footer>
    </div>
  );
}

중첩 라우팅 vs. children을 사용한 공통 레이아웃

둘 모두 비슷한 동작을 하지만, 적용 범위에서 차이가 있다.
중첩 라우팅을 사용하면 각 라우트마다 고유의 Layout을 적용할 수 있는 반면, 공통 Layout 방식은 모든 라우트에 동일한 레이아웃을 적용한다. 그래서 후자는 Layout 컴포넌트가 Routes의 자식으로 직접 포함되지 않고, 라우트 구성 외부에서 사용된다.

따라서 중첩 라우팅은 더 동적이고 유연한 라우트 관리를 제공한다는 장점이, 공통 레이아웃 방식은 구조가 단순하고 일관되어 관리가 용이하다는 장점이 있다.

profile
내배캠 React 9기 수료 후 Wecommit 풀스택 개발자로 근무중

0개의 댓글