19 router
정보
🐨 목차
https://qwerewqwerew.github.io/retype-react/레시피앱/8/#1-시작파일
https://qwerewqwerew.github.io/retype-react/레시피앱/8/#1-시작파일
1. Router 이해 (Part1)
1.1. 시작파일
정보
💡 만약 이전 파일이 없을 경우 아래의 파일을 다운로드 하여 진행한다 파일다운로드
- 다운로드 받은 start.zip 파일의 압축을 푼다
- 새 리액트 앱을 설치한다. 리액트 앱 설치를 모를경우 아래 링크를 참고한다. 🔗리액트 설치안내
- 리액트 앱의 설치가 왼료 되었으면 1번의 파일압축을 푼다
- 압축을 풀게 되면 start/src 폴더가 있다.
- 2번의 src폴더에 4번의 src 폴더를 덮어 씌운다.

- 2번의 폴더를 루트로 선택하여 vscode를 연다.
- vscode 의 터미널에 npm start 를 입력하여 앱을 실행한다.
1.2. 연관링크
| 종류 | 🔗링크 |
|---|---|
| 리액트라우터 공식문서 | ReactRouter |
1.3. 라우터의 기초개념
1.3.1. 라우터란
데이터를 연결하는 장치
1.3.2 라우팅이란
데이터의 최단/최적경로 찾기

1.3.3. MPA란
레거시(Classic) 웹페이지의 링크(라우팅)방식. 여러개 페이지를 제작하여 연결하는 방식으로 데이터 변경시 페이지 전체가 업데이트 된다.
1.3.4. SPA란
한개의 페이지에서 변경된 데이터 컴포넌트만 업데이트 되는 방식으로 index 페이지 하나에 컴포넌트를 교체하는 방식으로 제작한다. 데이터 변경시 변경된 데이터만 업데이트 된다.

2. 라우터 연습
정보
💡 리액트스캐폴딩
https://github.com/qwerewqwerew/react-scaffolding
2.1. react-router 설치
react-router 란 리액트 개발환경에서 리액트의 컴포넌트를 연결하는 모듈이다. v3 까지는 하나의 모듈 이었으나 v4부터 네이티브 앱 용 react-router-native와 웹 개발용 react-router-dom 으로 분리되었다.
React-router-dom v5 VS v6
React-rounter-dom v5 와 v6의 명령어가 크게 변경 되었으므로 프로젝트 유지보수 전 사용된 버전을 확인 후 그에 맞는 문법을 사용해야 한다. 또한 v6도 v6.4이후 추가된 문법이 있다. 예제에서는 v6.4이후를 중점적으로 다룬다.
- 모듈설치
- 열려있는 개발서버를 닫고 vscode 터미널 창에 아래의 명령어를 입력후 설치한다.
1npm i react-router- 설치가 완료되면 서버를 실행한다.
1npm run dev2.2. 컴포넌트 작성
연습할 컴포넌트 환경을 구성한다.
- 아래 구조대로 컴포넌트를 생성한다
/src─┬─ └──/pages ─┬ ├──Home.jsx └──Products.jsx- Home 과 Projects 컴포넌트를 생성한다.
1import React from "react";2const Home = () => {3 return <h1>Home</h1>;4};5export default Home;1import React from "react";2const Products = () => {3 return <h1>Products</h1>;4};5export default Products;jsx 확장자는 뭔가요? 리액트 프로젝트 코드를 분석하다 보면 jsx 라는 확장자를 볼수 있는데 js와 jsx는 같다. 사용이유는 개발자가 컴포넌트 파일의 구별을 쉽게 하기 위해 확장자를 jsx 로 표기하는것 뿐이다. 컴포넌트용 파일일 경우 jsx 확장자를 사용해도 된다.
2.3. 라우터 연결하기
2.3.1. 라우터 설치
라우터(전화기)를 사용하여 라우팅(통화)을 하는 과정을 3단계로 나누어보자.
- 👉 전화기를 산다 = 라우터 설치
- 통신서비스를 신청한다. = 통신서비스를 중개하는 객체에 서비스 신청
- 친구한테 전화건다 = 라우팅 연결
정보
💡 `실생활에서 사용하는 전화기의 종류가 다양하듯 리액트 라우터의 종류도 다양하다.
2024년 6월 현재 reactrouter v6.23.1 기준으로 RouterComponents는 아래 6종류가 있다.
- BroswerRouter
- HashRouter
- MemoryRouter
- NativeRouter
- Router
- StaticRouter`
지금 우리는 여기서 👉 1단계 를 하는 것 이다.
이 단계를 맡고 있는 🔗createBrowserRouter 이라는 객체를 사용해보자.
createBrowserRouter 객체로 특정 컴포넌트를 래핑하게 되면 하위의 컴포넌트는 라우터를 설정 할수있는 다양한 객체들을 상속받아 사용 할 수 있다.
2.3.2. createBrowserRouter 설치
이 API 는 react-router v6.4 에 새로 추가되었다. 🔗Official
라우팅 서비스를 이용할 컴포넌트들은 이 객체로 래핑되어 있어야 한다. 또한 새로 추가된 loaders,actions,fetchers 등의 API를 이용하려면 이 객체로 래핑 되어야 한다. 어차피 앱내의 컴포넌트들은 모두 라우팅 될것이므로 최상위 컴포넌트에 작성하자.
- 객체 구성확인
1import React from "react";2import ReactDOM from "react-dom/client";3import { createBrowserRouter } from "react-router-dom";4import "./index.css";5import App from "./App";6import Home from "./pages/Home";7console.log(createBrowserRouter([{}]));8const root = ReactDOM.createRoot(document.querySelector("#root"));9root.render(10 <>11 <Home />12 <App />13 </>14);
콘솔창에서 객체가 가지고 있는 값을 확인해보면 url 정보가 많이 보인다. 우리는 이제 필요할때마다 이 안의 객체들을 사용해서 리액트의 컴포넌트 연결을 하는 것이다.
- 객체를 변수에 할당후 라우터 연결
👉 1. 전화기를 산다 = 라우터 설치
- 통신서비스를 신청한다. = 통신서비스를 중개하는 객체에 서비스 신청
- 친구한테 전화건다 = 라우팅 연결
- path : 경로
- element: 경로접근시 열릴 컴포넌트
1const router = createBrowserRouter([2 {3 path: "/",4 element: <Home />,5 },6]);2.3.3. RouterProvider 서비스신청
이 API 는 react-router v6.4 에 새로 추가되었다. 🔗Official
- 전화기를 산다 = 라우터 설치 👉 2. 통신서비스를 신청한다. = 통신서비스를 중개하는 객체에 서비스 신청
- 친구한테 전화건다 = 라우팅 연결 설정한 라우터와 컴포넌트를 연결할때는 provider 컴포넌트를 사용한다
- RouterProvider 컴포넌트를 이용한다
1import React from "react";2import ReactDOM from "react-dom/client";3import { createBrowserRouter, RouterProvider } from "react-router-dom";4import "./index.css";5import App from "./App";6import Home from "./pages/Home";7const router = createBrowserRouter([8 {9 path: "/",10 element: <Home />,11 },12]);13const root = ReactDOM.createRoot(document.querySelector("#root"));14root.render(15 <RouterProvider router={router}>16
17 <Home /> <App />18 </RouterProvider>19);RouterProvider 객체는 router속성을 포함하고 있다. 이 속성에 연결할 컴포넌트의 정보를 전달하면 라우팅이 완료 된다.
- 아래와 같이 Home 컴포넌트가 라우팅 된것을 볼수있다.

- 라우터 추가 작성
1import Products from "./pages/Products";2//...중략3const router = createBrowserRouter([4 {5 path: "/",6 element: <Home />,7 },8 {9 path: "/app",10 element: <App />,11 },12 {13 path: "/products",14 element: <Products />,15 },16]);- 브라우저 주소창에 설정한 경로를 입력하면 페이지 새로고침 없이 컴포넌트가 이동한다.

2.4. Link
Link 컴포넌트는 html의 a 태그로 변환된다.
href 속성은 to 라는 props로 작성한다.
- Home 컴포넌트에 a태그로 링크생성
1//Home .js2import React from "react";3const Home = () => {4 return (5 <div>6 <h1>Home</h1>7 <a href="/products">Products</a>8 </div>9 );10};11export default Home;- 클릭하면 화면이 한번 깜빡이고 이동한다.

이 방법은 페이지 내의 모든 컴포넌트를 새로고침하게 되며 SPA 내의 모든 컴포넌트를 리렌더 하게 된다. 리액트 앱은 작은 컴포넌트가 모여 큰 페이지를 만든다. 위의 방법은 변경내역이 없는 컴포넌트 까지 불필요하게 리렌더 되므로 성능저하를 유발한다.
- Link 컴포넌트로 변경하자
1**//Home.js**2import { Link } from "react-router-dom";3const Home = (props) => {4 return (5 <>6 <h1>Home</h1>7 <Link to="/products">LINK-Product </Link>8 <a href="/products">Products</a>9 </>10 );11};12export default Home;- 화면의 새로고침 없이 이동이 되는지 확인한다

a태그로 변환 되었는지도 확인한다.

2.5. children
라우터 설정을 구조화 할수 있는 방법을 알아보자
컴포넌트 간의 관계에 따라 라우터도 계층화 한다면 좋을것이다.
- pages 폴더에 모든 컴포넌트를 제어할 RootLayout 컴포넌트를 만든다. 이 컴포넌트는 mpa의 index.html 과 같은 역할을 한다.
1import Navi from "./Navi";2const RootLayout = () => {3 return (4 <>5 <h1>Root</h1> <Navi />6 </>7 );8};9export default RootLayout;- Navi 컴포넌트를 작성한다.
1import { Link } from "react-router-dom";2import "./Navi.css";3const Navi = () => {4 return (5 <ul>6 <li>7 <Link to="/home">Home</Link>8 </li>9 <li>10 <Link to="/products">Products</Link>11 </li>12 </ul>13 );14};15export default Navi;- Navi.css 는 아래 코드를 복붙한다.
1ul {2 padding: 1rem;3 display: flex;4 position: fixed;5 bottom: 0;6 list-style: none;7 gap: 2rem;8 background-color: pink;9}- index.js 로 이동하여 router 의 속성을 추가한다. 이때 헷갈리므로 root.render 함수는 RouterProvider 만 남겨놓는다
1import RootLayout from './pages/RootLayout';2const router = createBrowserRouter([3 {4 path: '/',5 element: <RootLayout />,6 children: [7 {8 path: 'products',9 element: <Products />,10 },11 {12 path: 'home',13 element: <Home />,14 },15 ],16 },17]);18...19root.render(<RouterProvider router={router} />);브라우저에서 테스트 해보면 경로의 이동은 되는데 컴포넌트의 렌더링이 되지 않는다. 라우터 설정은 잘 되었으나 RootLayout 에서 Home과 Products 컴포넌트를 임포트 하지 않았기 때문이다. 리액트의 컴포넌트는 이점이 굉장히 불편하다. GNB나 모달팝업, 패널메뉴 같이 모든 컴포넌트에 표시돼야 하는 UI의 경우 매번 임포트해야 한다. 하지만 최상위 컴포넌트를 만들어서 제어한다면 최상위에서 Outlet 이라는 컴포넌트만 작성하면 해결할수 있다.
2.6. Outlet
중첩된 라우트를 렌더링하는 데 사용되는 컴포넌트
React Router v6부터 도입된 Outlet은 부모 라우트 내에서 자식 라우트가 렌더링될 위치를 지정하는 역할을 한다.
- RootLayout 에 Outlet 을 임포트 한다.
1**//RootLayout.js2**3import { Outlet } from "react-router-dom";4import Navi from "./Navi";5const RootLayout = () => {6 return (7 <>8 <h1>Root</h1> <Navi />9 <Outlet />10 </>11 );12};13export default RootLayout;브라우저에서 테스트 해보면 Root와 Navi 는 모든 컴포넌트에서 표시되며 컴포넌트간 이동도 잘 되는것이 확인된다.
2.7. ** errorElement**
react-router 에서 자체적으로 제공하는 예외처리문은 여러개가 있다.
그중 에러상황 발생시 사용할수 있는 객체를 알아보자 🔗
- pages/Error.js 를 만들고 404 메시지를 작성한다.
1import "./Error.css";2import Navi from "./Navi";3const ErrorPage = (props) => {4 return (5 <>6 <Navi /> <div className="error">404Error</div>7 </>8 );9};10export default ErrorPage;1.error {2 width: 100vw;3 height: 100vh;4 font-size: 5vw;5 color: white;6 background-color: #ff0000;7}- 라우터 설정 추가
1import ErrorPage from './pages/Error';2 {3 path: '/',4 element: <RootLayout />,5 errorElement: <ErrorPage />,6 children: [7 {8 path: 'products',9 element: <Products />,10 },11 {12 path: 'home',13 element: <Home />,14 },15 ],16 },17]);- 주소창에 없는 경로를 입력하면 에러컴포넌트가 확인된다

2.8. useNavigate
웹페이지 개발시 a태그를 사용하지 않고 자바스크립트를 사용하여 링크를 해야 하는 경우가 있다.
이런 상황을 리액트에서는 어떻게 해결할수 있을까? useNavigate 훅은 이펙트 등에서 프로그래밍 방식으로 탐색할 수 있는 함수를 반환한다. React 애플리케이션에서 페이지 간의 탐색(navigation)을 수행하는 훅이다.
- 아래와 같이 코딩한다
1import { Link, useNavigate } from "react-router-dom";2const Home = (props) => {3 const navi = useNavigate();4 const naviFn = () => {5 navi("/App");6 };7 return (8 <>9 <h1>Home</h1>10 <button onClick={naviFn}>click</button>11 <Link to="/products">LINK-Product</Link> <a href="/products">Products</a>12 </>13 );14};15export default Home;- index.js 로 이동하여 라우터를 설정한다.
1const router = createBrowserRouter([2{3 //...중략4},5{6 path:'/app',7 element: <App />,8}버튼을 클릭하면 App 컴포넌트가 렌더링된다.

이 코드는 링크 컴포넌트를 사용하여 이동하는 것이 아닌 버튼을 클릭시 함수를 호출하고 그 함수에서 다른 컴포넌트로 이동하기 위한 네비게이션 훅을 호출하는 방식이다.
2. useParams (Part2)
2.9. 동적라우팅(useParams)
📢 시나리오:
상품페이지에는 여러개의 상품이 있다. 각 상품별 데이터가 다르므로 상품상세페이지는 상품마다 각각 라우터를 작성해야 한다. 이럴 경우 상품의 고유 식별자와 useParams를 활용하여 라우팅 하면 편리하다.
정보
🐨
useParams 란?
**의미를 풀어보면 다음과 같다 (☞゚ヮ゚)☞ ****use(사용하다)+Params(매개변수) = useParams(매개변수를 사용하다) **
- src\pages\Products.js 에 구조를 추가하자
1import React from 'react';2
3const Products = () => {4 return (5 <>6 <h1>Products</h1>7 <ul>8 <li>상품1</li>9 <li>상품2</li>10 <li>상품3</li>11 </ul>12 </>13 );14};15export default Products;- 상품상세페이지 컴포넌트를 만든다
1const ProductDetail = () => {2 return <div>ProductDetail</div>;3};4export default ProductDetail;- 라우터를 작성한다
1import React from 'react';2 import ReactDOM from 'react-dom/client';3 import { createBrowserRouter, RouterProvider } from 'react-router-dom';4 import './index.css';5 import App from './App';6 import Home from './pages/Home';7 import Products from './pages/Products';8 import ProductDetail from './pages/ProductDetail';9 import RootLayout from './pages/RootLayout';10 import ErrorPage from './pages/Error';11 const router = createBrowserRouter([12 {13 path: '/',14 element: <RootLayout />,15 errorElement: <ErrorPage />,16 children: [17 {18 path: 'products',19 element: <Products />,20 },21 {22 path: 'products/detail',23 element: <ProductDetail />,24 },25 {26 path: 'home',27 element: <Home />,28 },29 ],30 },31 {32 path: '/app',33 element: <App />,34 },35 ]);36// ...상품의 아이디나 관리번호에 따라 path 는 변경되야 할것이다
- 동적경로 작성하기
:
1{ path: '/products/:id', element: <ProductDetail /> },**: **이후의 값은 동적으로 처리된다
- 확인 숫자 5를 넣어도 apple을 넣어도 상세 컴포넌트가 렌더링 된다

- 상세 페이지에서 parameter 을 받아보자
전달하는 parameter 을 받을때는
useParams가 필요하다
1import { useParams } from 'react-router-dom';2
3const ProductDetail = (props) => {4 console.log(useParams());5
6 return (7 <>8 <div>ProductDetail</div>9 </>10 );11};12export default ProductDetail;- 확인

- id 저장
1const {id}=useParams()2 // ...3 <div>{id}</div>2.10.동적링크
Link 컴포넌트의 to props를 동적으로 처리해보자
- src\pages\Products.js 로 이동후 Link 컴포넌트를 추가한다
- 상품들은 일반적으로 백엔드 서버에서 받아오겠지만 지금은 상수로 작성한후 map으로 불러온다
- Link to 에 템플릿리터럴을 사용하여 속성을 전달하면 동적으로 링크주소를 작성할수 있다
1import { Link } from 'react-router-dom';2const PRODUCTS = [3 { id: '1', title: '상품1' },4 { id: '2', title: '상품2' },5 { id: '3', title: '상품3' },6];7const Products = () => {8 return (9 <>10 <h1>Products</h1>11 <ul>12 {PRODUCTS.map((item) => (13 <li key={item.id}>14 <Link to={`/products/${item.id}`}>{item.title}</Link>15 </li>16 ))}17 </ul>18 </>19 );20};21export default Products;- 짜잔

2.11. Router Path
createBrowserRouter 함수가 반환하는 여러 객체중에는 경로 설정 키인 path의 속성에 다양한 옵션을 추가할수 있다
2.11.1. 상대경로
정보
💡 router 의 children 배열의 path 속성에서 / 를 삭제 한다는것은 해당 경로를 상대경로로 변경한다는 의미이다.
1import { createBrowserRouter, RouterProvider } from 'react-router-dom';2import Home from './pages/Home';3import Products from './pages/Products';4import ProductDetail from './pages/ProductDetail';5import RootLayout from './pages/Root';6import ErrorPage from './pages/Error';7
8const router = createBrowserRouter([9 {10 path: '/',11 element: <RootLayout />,12 errorElement: <ErrorPage />,13 children: [14 { path: '', element: <Home /> },15 { path: 'products', element: <Products /> },16 { path: 'products/:id', element: <ProductDetail /> },17 ],18 },19]);20
21const App = () => {22 return <RouterProvider router={router} />;23};24export default App;

product.js 의 Link 를 보자

상대경로로 수정한다


1//...2{PRODUCTS.map((item) => (3 <li key={item.id}>4 <Link to={item.id}>{item.title}</Link>5 </li>6//...정보
💡 src\pages\ProductDetail.js 에 뒤로가기 버튼을 상대 경로로 넣어보자
1import { useParams, Link } from 'react-router-dom';2
3const ProductDetail = () => {4 const params = useParams();5 return (6 <div>7 <h1>ProductDetail</h1>8 <div>{params.id}</div>9 <div>10 <Link to='..'>Back</Link>11 </div>12 </div>13 );14};15export default ProductDetail;

상품 상세 페이지에서 상품 페이지로 이동 하는 것이 아니라 바로 홈으로 이동한다. 이유는 상세페이지 와 상품 페이지는 형제 관계이기 때문이다 수정해보자
1//...2<div><Link to=".." relative="path">Back</Link></div>relative 속성에 path 를 사용하면 path 단위로 이동이 가능하다
2.11.2. index 라우트
메인 컴포넌트에 설정할수 있는 index 속성을 사용해보자
1import { createBrowserRouter, RouterProvider } from 'react-router-dom';2import Home from './pages/Home';3import Products from './pages/Products';4import ProductDetail from './pages/ProductDetail';5import RootLayout from './pages/Root';6import ErrorPage from './pages/Error';7
8const router = createBrowserRouter([9 {10 path: '/',11 element: <RootLayout />,12 errorElement: <ErrorPage />,13 children: [14 { index: true, element: <Home /> },15 { path: 'products', element: <Products /> },16 { path: 'products/:id', element: <ProductDetail /> },17 ],18 },19]);20
21const App = () => {22 return <RouterProvider router={router} />;23};24export default App;이 키는 빈 path 대신 메인 컴포넌트를 지정할수 있는 속성이다.
1import { createBrowserRouter, RouterProvider } from 'react-router-dom';2import Home from './pages/Home';3import Products from './pages/Products';4import ProductDetail from './pages/ProductDetail';5import RootLayout from './pages/Root';6import ErrorPage from './pages/Error';7
8const router = createBrowserRouter([9 {10 path: '/',11 element: <RootLayout />,12 errorElement: <ErrorPage />,13 children: [14 { index: true, element: <Home /> },15 { path: 'products', element: <Products /> },16 { path: 'products/:id', element: <ProductDetail /> },17 ],18 },19]);20
21const App = () => {22 return <RouterProvider router={router} />;23};24export default App;