중첩 라우팅 하는 법에 대해 소개한다.
이전 문서에서 App.jsx 있는 컴포넌트들을 .jsx로 분리한다.
src/App.jsx
Navigation => src/navigation/index.jsx
Home => src/pages/Home/index.jsx
Category1 => src/pages/Category1/index.jsx
Category2 => src/pages/Category2/index.jsx
NotFound => src/NotFound.jsx
// App.jsx
import React from "react";
import {BrowserRouter as Router, Routes, Route} from "react-router-dom";
import Home from "@/pages/Home";
import Category1 from "@/pages/Category1";
import Category2 from "@/pages/Category2";
import NotFound from "@/pages/NotFound";
import Navigation from "@/navigation";
const App = () => {
return (
<div className="flex flex-row justify-center min-h-screen">
<div className="w-[1080px]">
<Router>
<Navigation className="bg-blue-200 h-32 flex items-center" />
<div className="bg-blue-50">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/category1" element={<Category1 />} />
<Route path="/category2" element={<Category2 />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
</div>
</div>
);
};
export default App;
중첩 라우팅은 쉽게 말해 route page 안에 navigation 메뉴가 있는 경우이다.
GNB
|– Category Link 1 – Sub Navigation Bar (Menu)
|– Page Link 1
|– Page Link 2
|– Category Link 2 – Sub Navigation Bar (Menu)
|– Page Link 1
|– Page Link 2
Category1 page에서 하위 페이지를 이동 하는 경우 Outlet component를 이용해 Page가 랜더링된다.
// App.jsx
import React from "react";
import {BrowserRouter as Router, Routes, Route} from "react-router-dom";
import Home from "@/pages/Home";
import Category1 from "@/pages/Category1";
import Category2 from "@/pages/Category2";
import NotFound from "@/pages/NotFound";
import Navigation from "@/navigation";
const Page1 = () => {
return <div>Page name is page1</div>;
};
const Page2 = () => {
return <div>Page name is page2</div>;
};
const App = () => {
return (
<div className="flex flex-row justify-center min-h-screen">
<div className="w-[1080px]">
<Router>
<Navigation className="bg-blue-200 h-32 flex items-center" />
<div className="bg-blue-50">
<Routes>
<Route path="/" element={<Home />} />
- <Route path="/category1" element={<Category1 />} />
+ <Route path="/category1" element={<Category1 />}>
+ <Route index element={<Page1 />} />
+ <Route path="/category1/page2" element={<Page2 />} />
+ </Route>
<Route path="/category2" element={<Category2 />} />
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
</div>
</div>
);
};
export default App;
...
// src/pages/Cetegory1/index.jsx
import {Outlet} from "react-router-dom";
const Category1 = () => {
return (
<div>
Category 1 {">>"} <Outlet /> => Outlet 추가
</div>
);
};
export default Category1;
컴포넌트에 하위 route들이 랜더링 된다.
localhost:3000/category1 =>Category1과 하위 Page1 출력
localhost:3000/category1/page2 => Category1과 하위 Page2 출력
// error code
<Route path="/page2" element={<Page2 />} />
// good
<Route path="/category1/page2" element={<Page2 />} />
* 주의사항: 하위 route의 path는 반드시 상위 path를 같이 기재 해줘야 한다.
Uncaught runtime errors:
ERROR
Absolute route path "/page2" nested under path "/category1" is not valid. An absolute child route path must start with the combined path of all its parent routes.
import React from "react";
import {Outlet, Link} from "react-router-dom";
const Category1 = () => {
return (
<React.Fragment>
<div className="flex flex-row px-10 py-2 border-b font-bold space-x-2">
<Link to={`/category1`}>Page1</Link>
<Link to={`/category1/page2`}>Page2</Link>
</div>
<div className="flex flex-row">
Category1 {">>"}
<Outlet />
</div>
</React.Fragment>
);
};
export default Category1;
<Route path="/category1/page2" element={<Page2 />} />
path="/category1/page2" 와 같이 정적 경로 구현외에
<Route path=":slug" element={<Page2 />} />
":slug" 와 같이 동적 파라미터 경로를 설정 하는 방법에 대해 알아 보자.
// App.jsx
...
import {BrowserRouter as Router, Routes, Route, useParams} from "react-router-dom";
...
const Page3 = () => {
const {slug} = useParams(); // hook을 이용하여 slug값을 받는다.
return (
<React.Fragment>
<div>this is dynamic {slug} </div> {/* 슬러그 값을 출력한다. */}
</React.Fragment>
);
};
...
- <Route path="/category2" element={<Category2 />} />
+ <Route path="/category2" element={<Category2 />}>
+ <Route path=":slug" element={<Page3 />} /> // dynamic parameter
+ </Route>
...
위와 같이 dinamic parameter path를 잡아 준다.
// src/pages/Category2/index.jsx
import React from "react";
import {Outlet, Link} from "react-router-dom";
const Category2 = () => {
return (
<React.Fragment>
<div className="flex flex-row px-10 py-2 border-b font-bold space-x-2">
<Link to={`/category2/page3`}>Page3</Link>
<Link to={`/category2/page4`}>Page4</Link>
</div>
<div className="flex flex-row">
Category2 {">>"}
<Outlet />
</div>
</React.Fragment>
);
};
export default Category2;
Category2에 page3, 4에대한 링크 및 출력용 Outlet 을 입력.
page3, Page4 메뉴 이동 값을 확인한다.
다음 문서에서는 useRoute hook을 이용하여 소스를 깔끔하게 관리 하는 법에 대해 작성한다.
// App.jsx
import React from "react";
import {BrowserRouter as Router, Routes, Route, useParams} from "react-router-dom";
import Home from "@/pages/Home";
import Category1 from "@/pages/Category1";
import Category2 from "@/pages/Category2";
import NotFound from "@/pages/NotFound";
import Navigation from "@/navigation";
const Page1 = () => {
return <div>Page name is page1</div>;
};
const Page2 = () => {
return <div>Page name is page2</div>;
};
const Page3 = () => {
const {slug} = useParams(); // hook을 이용하여 slug값을 받는다.
return (
<React.Fragment>
<div>this is dynamic {slug} </div> {/* 슬러그 값을 출력한다. */}
</React.Fragment>
);
};
const App = () => {
return (
<div className="flex flex-row justify-center min-h-screen">
<div className="w-[1080px]">
<Router>
<Navigation className="bg-blue-200 h-32 flex items-center" />
<div className="bg-blue-50">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/category1" element={<Category1 />} />
<Route path="/category1" element={<Category1 />}>
<Route index element={<Page1 />} />
<Route path="/category1/page2" element={<Page2 />} />
</Route>
<Route path="/category2" element={<Category2 />}>
<Route path=":slug" element={<Page3 />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
</div>
</div>
);
};
export default App;
// Catogory1/index.jsx
import React from "react";
import {Outlet, Link} from "react-router-dom";
const Category1 = () => {
return (
<React.Fragment>
<div className="flex flex-row px-5 py-2 border-b font-bold space-x-2">
<Link to={`/category1`}>Page1</Link>
<Link to={`/category1/page2`}>Page2</Link>
</div>
<div className="flex flex-row">
Category1 {">>"}
<Outlet />
</div>
</React.Fragment>
);
};
export default Category1;
// Catogory2/index.jsx
import React from "react";
import {Outlet, Link} from "react-router-dom";
const Category2 = () => {
return (
<React.Fragment>
<div className="flex flex-row px-10 py-2 border-b font-bold space-x-2">
<Link to={`/category2/page3`}>Page3</Link>
<Link to={`/category2/page4`}>Page4</Link>
</div>
<div className="flex flex-row">
Category2 {">>"}
<Outlet />
</div>
</React.Fragment>
);
};
export default Category2;