React Router
는 React
기반의 Javascript
라이브러리로써, 웹 애플레케이션 간에 페이지 전환 및 라우팅을 관리하는 역할을 담당한다.React Router
를 사용하면 싱글 페이지 애플리케이션 내에서 여러 페이지 개념을 사용할 수 있게된다. 또한 동적 라우팅 기능도 포함하고 있어 URL 매개변수를 사용해 데이터를 전달 및 복잡한 중첩된 라우트들을 관리할 수 있게된다.$ pnpm i react-router-dom
App.jsx
나 main.jsx
에 구성하거나 routes
파일을 따로 관리하는 경우도 있고 각기 다르지만 나는 파일을 최소단위로 쪼개는걸 지향하기 때문에 routes
라는 파일을 생성 후 거기서 관리하는 방법을 선택했다.📝 routes.jsx
import { createBrowserRouter} from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <Home /> },
{ path: 'products', element: <Product /> },
{ path: 'contact', element: <Contact /> },
],
},
]);
export default router;
createBrowserRouter
메서드 내에 배열내의 객체로 설정하는 방법이 현재 최신방법이지만, createRoutesFromElements
내에서 컴포넌트로써 관리하는 방법도 가능하다.
import { createBrowserRouter, createRoutesFromElements } from 'react-router-dom';
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<RootLayout />}>
<Route index element={<Home />} />
<Route path="products" element={<Product />} />
<Route path="contact" element={<Contact />} />
</Route>
)
);
App.jsx
에서 구성했던 router
객체를 import
해서 RouterProvider
컴포넌트에 공급해준다.
import { RouterProvider } from 'react-router-dom';
import router from './routes';
function App() {
return (
<div className="App">
<RouterProvider router={router} />
</div>
);
}
export default App;
<Link />
컴포넌트로 페이지 이동기능 부여하기일단 RouterProvier
로 만든 router
를 공급하면, 기본적으로 해당 페이지별로 컴포넌트가 정의 되어있는데, 이제 특정 버튼/태그를 클릭하면 해당 페이지로 이동할 수 있도록 Link
컴포넌트로 감싸주면 된다.
아래는 Navigation
기능에 특화된 NavLink
라는 라우터 컴포넌트인데, 해당 NavLink
는 isActive
,isPending
이라는 상태를 className
에 콜백함수에서 인자로 가질 수 있으며 해당 상태를 기반으로 다양한 스타일링 또는 조건처리가 가능해진다.
<import { NavLink } from 'react-router-dom';
const Nav = () => (
<nav>
<ul className="flex gap-4 border-b-2 py-4">
<li>
<NavLink
to="/"
className={({ isActive }) => {
return isActive ? 'font-black text-rose-500' : '';
}}
>
Home
</NavLink>
</li>
<li>
<NavLink
to="/products"
className={({ isActive }) => {
return isActive ? 'font-black text-rose-500' : '';
}}
>
Products
</NavLink>
</li>
<li>
<NavLink
to="/contact"
className={({ isActive }) => {
return isActive ? 'font-black text-rose-500' : '';
}}
>
Contact
</NavLink>
</li>
</ul>
</nav>
);
export default Nav;
<Outlet />
컴포넌트Outlet
컴포넌트를 사용해야한다.React
에서의 props.children
과 같은 느낌으로 사용하면 문제없다
📝 routes.jsx
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <Home /> },
{ path: 'products', element: <Product /> },
{ path: 'contact', element: <Contact /> },
],
},
]);
export default router;
routes
가 RootLayout
컴포넌트일때, 해당 RootLayout
컴포넌트 사이에 <Outlet />
컴포넌트를 끼워넣으면 RootLayout
내부의 자식 컴포넌트들이 해당 path
를 따라 렌더링 될 수 있게된다.
📝 RootLayout.jsx
import { Outlet } from 'react-router-dom';
const RootLayout = () => {
return (
<>
<NavigationBar />
<main>
// props.children 대체!
<Outlet />
</main>
<SiteInfo />
</>
);
};
routes
의 path
를 동적으로 설정 가능한데const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<RootLayout />}>
<Route index element={<Home />} />
<Route path="products" element={<Product />} />
<Route path="product/edit/:productId" element={<ProductEdit />} />
<Route path="contact" element={<Contact />} />
</Route>
)
);
path
로 :productId
로 설정했을때 해당 path
로 이동하는 간단한 예시를 확인해보자.function Product() {
useDocumentTitle('제품 목록');
const { isLoading, data: products } = useProductList();
if (isLoading) {
return <Spinner size={160} />;
}
return (
<>
<div>
<h1>Products</h1>
<ul >
{products &&
products.items?.map((product) => {
return (
<li key={product.id} className="justify-self-center">
<Link to={`/product/edit/${product.id}`}>
<img
src={getPocketBaseImageURL(product,'photo')}
alt={product.title}
/>
<figcaption>
<span>{product.title}</span>
<span>
{numberWithComma(product.price)}
</span>
</figcaption>
</Link>
</li>
);
})}
</ul>
</div>
</>
);
}
fetch
를 활용한 useProductList
로 서버에서 데이터를 가져와 렌더링을 했으며, 해당 productItem
들의 id
를 기반으로 템플릿 리터럴을 사용해, /product/edit/${product.id}
로 이동할 수 있도록 구성하였다.