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:20react-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 거치도록 코드를 구현해주는 것"
주요 기능 및 개념
- 싱글 페이지 애플리케이션(SPA) 지원
- react-router-dom을 사용하면 하나의 HTML 파일로 구성된 SPA에서 여러 경로(route)를 설정하고, 경로에 따라 컴포넌트를 동적으로 렌더링할 수 있음.
- 브라우저 라우팅과 해시 라우팅
- BrowserRouter: HTML5의 히스토리 API를 사용해 URL을 관리하며, 주로 사용되는 라우터임.
- HashRouter: URL에 #을 포함해 경로를 관리함으로, 서버 설정이 불가능할 때 사용함.
- 경로 설정과 컴포넌트 매칭
- <Route> 컴포넌트를 사용해 URL 경로와 해당 경로에서 렌더링할 컴포넌트를 지정함.
- <Switch>는 Route 목록을 순서대로 검사하여 가장 먼저 일치하는 하나의 경로만 렌더링함.
- Link와 NavLink
- <Link>: 페이지 간 이동을 위한 컴포넌트로, HTML의 <a> 태그와 유사하게 동작하지만, 새로고침 없이 경로를 변경함.
- <NavLink>: <Link>와 유사하지만, 현재 활성화된 경로를 표시하는 추가 스타일을 제공함.
- 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)
==> 웹 애플리케이션에서 여러 계층의 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 컴포넌트를 이용하는 것을 권장함. ''
'프론트엔드 부트캠프' 카테고리의 다른 글
JavaScript의 History API (0) | 2024.11.11 |
---|---|
[React 숙련 1주차(7)] Supabase (3) | 2024.11.11 |
[React 숙련 1주차(5)] Redux: todolist, RTK (1) | 2024.11.07 |
[React 숙련 1주차(4)] Redux: useSelector, dispatch action 객체, payload, ducks (0) | 2024.11.06 |
[React 숙련 1주차(3)] Memoization - (React.memo, useCallback, useMemo), Custom Hooks (0) | 2024.11.05 |