Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

heyday2024 님의 블로그

[React 숙련 1주차(6)] React Router DOM : hooks, Dynamic Route, useParams, 중첩된 라우트 본문

프론트엔드 부트캠프

[React 숙련 1주차(6)] React Router DOM : hooks, Dynamic Route, useParams, 중첩된 라우트

heyday2024 2024. 11. 7. 23:20

react-router-dom이란?

리액트 프로젝트에서 라우팅을 가능하게 하는 라이브러리. 싱글 페이지 애플리케이션(SPA)에서도 여러 페이지 간 이동하는 것처럼 보이게 해줌.

 

yarn add react-router-dom

설치 위한 코드 ><

 

<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;

// children을 사용할 떄 범용적인 부분을 다룰떄...
// 자식 엘리먼트가 어떤게 들어올지를 모르는 상황에서 범용적인 박스역할을 하는 사이드 바, 헤더 등을 위함
// jsx에서는 Layout 컴포넌트로 children 활용

- 브라우저에 우리가 URL을 입력하고 이동했을 때 우리가 원하는 페이지 컴포넌트로 이동하게끔 만드는 부분

 

<App.js>

import React from "react";
import Router from "./shared/Router";

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

export default App;

- 해당 Router.js를 App 컴포넌트에 import해줌.

- App 컴포넌트에 넣어주는 이유는 우리가 만든 프로젝트에서 가장 최상위에 존재하는 컴포넌트가 App.js 이기 때문

 

==> "우리가 어떤 컴포넌트를 화면에 띄우던, 항상 App.js를 거쳐야만 함. 그래서 path 별로 분기가 되는 Router.js 를 App.js에 위치시키고 우리의 서비스를 이용하는 모든 사용자가 항상 App.js → Router.js 거치도록 코드를 구현해주는 것"


주요 기능 및 개념

  1. 싱글 페이지 애플리케이션(SPA) 지원
    • react-router-dom을 사용하면 하나의 HTML 파일로 구성된 SPA에서 여러 경로(route)를 설정하고, 경로에 따라 컴포넌트를 동적으로 렌더링할 수 있음.
  2. 브라우저 라우팅과 해시 라우팅
    • BrowserRouter: HTML5의 히스토리 API를 사용해 URL을 관리하며, 주로 사용되는 라우터임.
    • HashRouter: URL에 #을 포함해 경로를 관리함으로, 서버 설정이 불가능할 때 사용함.
  3. 경로 설정과 컴포넌트 매칭
    • <Route> 컴포넌트를 사용해 URL 경로와 해당 경로에서 렌더링할 컴포넌트를 지정함.
    • <Switch>는 Route 목록을 순서대로 검사하여 가장 먼저 일치하는 하나의 경로만 렌더링함.
  4. Link와 NavLink
    • <Link>: 페이지 간 이동을 위한 컴포넌트로, HTML의 <a> 태그와 유사하게 동작하지만, 새로고침 없이 경로를 변경함.
    • <NavLink>: <Link>와 유사하지만, 현재 활성화된 경로를 표시하는 추가 스타일을 제공함.
  5. useNavigate와 useParams
    • useNavigate: 프로그래밍적으로 경로를 변경할 때 사용됨. 함수 호출로 특정 경로로 이동 가능함.
    • useParams: URL 파라미터를 받아올 수 있음으로, 동적 라우팅을 구현할 때 유용함.

react-router-dom hooks

react-router-dom에서도 우리가 유용하게 사용할 수 있는 hook을 제공

(1) useNavigate

 

보통 어떤 버튼을 누르면 페이지로 이동하거나 또는 어떤 컴포넌트를 눌렀을 때 페이지를 이동하게 만드는데, 그때 사용하는 훅!!

 

아래 코드는 버튼을 클릭했을 때 우리가 보내고자 하는 path로 페이지를 이동시킬 수 있게함. 

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

const Home = () => {
  const navigate = useNavigate();

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

export default Home;

==> navigate 를 생성하고, navigate(’보내고자 하는 url’) 을 통해 페이지를 이동 시킬 수 있음!

(2) useLocation

react-router-dom을 사용하면, 현재 위치하고 있는 페이지의 여러가지 정보를 추가적으로 얻을 수 있음. 이 정보들을 이용해서 페이지 안에서 조건부 렌더링에 사용하는 등, 여러가지 용도로 활용할 수 있음.

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

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

export default Works;

(3) Link

Link 는 훅이 아니지만, 꼭 알아야 할 API!

Link 는 html 태그중에 a 태그의 기능을 대체하는 API임( 페이지 간 이동을 위한 컴포넌트 )

 

==>  만약 JSX에서 a 태그를 사용해야 한다면, 반드시 Link 를 사용해서 구현해야 함. 왜냐하면 a 태그를 사용하면 페이지를 이동하면서 브라우저가 새로고침(refresh)되기 때문. 브라우저가 새로고침 되면 모든 컴포넌트가 다시 렌더링되야 하고, 또한 리덕스나 useState를 통해 메모리상에 구축해놓은 모든 상태값이 초기화됨(즉, SPA를 쓰는 이유가 없어짐....). 이것은 곧 성능에 악역향을 줄 수 있고, 불필요한 움직임이될 수도 있음.

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

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

export default Works;

children

props와 chilren을 활용한 공통 Layout

 

Q. Children을 왜 쓰나요?


A. "어떤 컴포넌트들은 어떤 자식 엘리먼트가 들어올지 미리 예상할 수 없는 경우가 있습니다. 범용적인 ‘박스’ 역할을 하는 Sidebar 혹은 Dialog와 같은 컴포넌트에서 특히 자주 볼 수 있습니다"

 

여기서 말하는 범용적인 “박스” 역할을 하는 컴포넌트란 크게 봤을 때 Layout 역할을 하는 컴포넌트.

 

==> children props를 가지고 페이지 레이아웃을 만들 수 있음. 이때, 개별적으로 존재하는 Header, Footer, Page를 합성(composition)하여 개발자가 의도하는 UI를 만들 수 있음.

 

// src/shared/Layout.js

import React from 'react';

const HeaderStyles = {
  width: '100%',
  background: 'black',
  height: '50px',
  display: 'flex',
  alignItems: 'center',
  paddingLeft: '20px',
  color: 'white',
  fontWeight: '600',
};
const FooterStyles = {
  width: '100%',
  height: '50px',
  display: 'flex',
  background: 'black',
  color: 'white',
  alignItems: 'center',
  justifyContent: 'center',
  fontSize: '12px',
};

const layoutStyles = {
  display: 'flex',
	flexDirection: 'column',
  justifyContent: 'center',
  alignItems: 'center',
  minHeight: '90vh',
}

function Header() {
  return (
    <div style={{ ...HeaderStyles }}>
      <span>Sparta Coding Club - Let's learn React</span>
    </div>
  );
}

function Footer() {
  return (
    <div style={{ ...FooterStyles }}>
      <span>copyright @SCC</span>
    </div>
  );
}


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

export default Layout;

- src/shared/Layout.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';
import Layout from './Layout';

const Router = () => {
  return (
    <BrowserRouter>
      <Layout>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="contact" element={<Contact />} />
          <Route path="works" element={<Works />} />
        </Routes>
      </Layout>
    </BrowserRouter>
  );
};

export default Router;

- src/shared/Router.js


Dynamic Route란?

동적 라우팅이라고도 말하는데 path에 유동적인 값을 넣어서 특정 페이지로 이동하게끔 구현하는 방법!

 

==> URL 경로의 일부를 변수로 처리하여, 동적으로 다른 콘텐츠나 페이지를 로드하는 방식

 

==> react-router-dom에서 지원하는 Dynamic Routes 기능을 이용해서 간결하게 동적으로 변하는 페이지를 처리 가능!

(예시)

  • /user/:id에서 :id 부분은 동적으로 변할 수 있는 값으로, 예를 들어 /user/1, /user/2와 같이 사용자의 ID에 따라 다른 페이지를 보여줍니다.
  • /product/:productId에서 :productId는 각 상품에 해당하는 ID로 바뀌어, 상품 상세 페이지를 동적으로 생성합니다.

 

Dynamic Route 설정하기

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>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="contact" element={<Contact />} />
        <Route path="works" element={<Works />} />
				{/* 아래 코드를 추가해주세요. 👇 */}
        <Route path="works/:id" element={<Works />} />
      </Routes>
    </BrowserRouter>
  );
};

export default Router;

==> :id 라는 것이 바로 동적인 값을 받겠다라는 의미

==> 그래서 works/1 로 이동해도 <Work /> 로 이동하고, works/2, works/3 …. works/100 모두 <Work /> 로 이동

==> 그리고 :id 는 useParams 훅에서 조회할 수 있는 값이 됨.

 

path parameter 조회하기

path parameter를 조회하려면 react-router-dom의 useParams 훅을 사용할 수 있음. useParams는 URL에서 동적으로 지정된 파라미터 값을 쉽게 조회할 수 있도록 도와줌

 

Dynamic Routes를 사용하면 element에 설정된 같은 컴포넌트를 렌더링하게 됨.

<Route path="works/:id" element={<Work />} />

 

// src/pages/Works.js

import React from 'react';
import { Link } 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 Works() {
  return (
    <div>
      {data.map((work) => {
        return (
          <div key={work.id}>
            <div>할일: {work.id}</div>
            <Link to={`/works/${work.id}`}>
              <span style={{ cursor: 'pointer' }}>➡️ Go to: {work.todo}</span>
            </Link>
          </div>
        );
      })}
    </div>
  );
}

export default Works;
// 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();

  const work = data.find((work) => work.id === parseInt(param.id));

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

export default Work;

==> useParams을 이용하면 같은 컴포넌트를 렌더링하더라도 각각의 고유한 id 값을 조회 가능!

 


중첩된 라우트(Outlet)

중첩된 라우트 (Nested Routes)는 React에서 react-router-dom의 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 등의 경로로 추가적인 페이지들을 중첩하여 설정할 수 있음

 

==> 대시보드 내 각 섹션의 독립적인 라우트 관리가 가능하며, UX(사용자 경험)을 개선할 수 있음.

Outlet 컴포넌트

Outlet 컴포넌트는 중첩 라우팅 환경에서 부모 라우트 내에 렌더링되어야 할 자식 라우트를 표시할 위치를 정의함. 이는 부모 컴포넌트가 자식 경로의 렌더링 결과를 어디에 표시할지 결정할 때 사용됨.

==> 복잡한 애플리케이션의 라우트 구조를 더 명확하고 관리하기 쉽게 만들 수 있음.

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

function Layout() {
  return (
    <div>
      <header>Header Section</header>
      <main>
        <Outlet />  {/* 여기에 자식 라우트의 컴포넌트가 렌더링됩니다. */}
      </main>
      <footer>Footer Section</footer>
    </div>
  );
}

 

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

 

중첩 라우팅을 사용하면 각 라우트마다 고유의 Layout을 적용할 수 있는 반면, 공통 Layout을 자식으로 사용하는 방식은 모든 라우트에 동일한 레이아웃을 적용하고 싶을 때 유용함.

==> Layout 컴포넌트는 Routes에 포함될 수 없고, 항상 Routes를 감싸는 상위 레벨에 위치해야한다는 것.

 

## 중첩 라우팅

<Routes>
  <Route path="/" element={<Layout />}>
    <Route index element={<Home />} />
    <Route path="about" element={<About />} />
  </Route>
</Routes>

## 공통 레이아웃

<Layout>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="about" element={<About />} />
  </Routes>
</Layout>

''중첩 라우팅은 더 동적이고 유연한 라우트 관리를 제공하는 반면, 공통 레이아웃 방식은 구조가 단순하고 일관되어 관리가 용이해요.

각각의 특징을 잘 고려해서 사용하는 것이 중요.
따라서 react-router-dom을 사용하는 경우 Outlet 컴포넌트를 이용하는 것을 권장함. ''