1. React Router가 뭐냐?
React 애플리케이션에서 URL에 따라 보여줄 컴포넌트를 결정하는 라우팅 라이브러리입니다.
<Route
path="/products"
element={<ProductList />}
/>
위 코드는 /products 주소가 매칭되면 <ProductList />를 렌더링하라는 뜻입니다.
설치
npm install react-router
기존 프로젝트에서는 react-router-dom에서 import하는 코드도 자주 볼 수 있습니다.
2. BrowserRouter / Routes / Route
BrowserRouter
BrowserRouter는 아래 컴포넌트 트리에서 Router 기능을 사용할 수 있도록 Router 환경을 제공합니다.
import {
BrowserRouter,
Routes,
Route
} from 'react-router'
function App() {
return (
<BrowserRouter>
<Routes>
<Route
path="/"
element={<Home />}
/>
<Route
path="/products"
element={<Products />}
/>
<Route
path="/mypage"
element={<MyPage />}
/>
</Routes>
</BrowserRouter>
)
}
path="/products"
element={<Products />}
element를 렌더링합니다.
3. Link와 NavLink
페이지 이동 링크는 일반적으로 Link를 사용합니다.
import { Link } from 'react-router'
function Header() {
return (
<nav>
<Link to="/">홈</Link>
<Link to="/products">상품</Link>
<Link to="/mypage">마이페이지</Link>
</nav>
)
}
to="/products"는 이동할 URL입니다.
현재 메뉴에 active 클래스 적용: NavLink
import { NavLink } from 'react-router'
<NavLink
to="/products"
className={({ isActive }) =>
isActive
? 'menu active'
: 'menu'
}
>
상품
</NavLink>
현재 URL이 /products와 매칭되면 isActive가 true가 되고,
menu active 클래스가 적용됩니다.
5. Dynamic Route와 useParams
상품 상세처럼 ID에 따라 같은 컴포넌트에서 서로 다른 데이터를 보여줄 때 사용합니다.
<Route
path="/products/:id"
element={<ProductDetail />}
/>
useParams로 URL 값 꺼내기
import { useParams } from 'react-router'
function ProductDetail() {
const { id } = useParams()
return (
<div>
상품 번호 : {id}
</div>
)
}
URL이 /products/100이면 id는 문자열 "100"입니다.
상품 목록 → 상세페이지 전체 흐름
const products = [
{ id: 1, name: '노트북' },
{ id: 2, name: '키보드' },
{ id: 3, name: '마우스' }
]
function ProductList() {
return (
<div>
{products.map(product => (
<Link
key={product.id}
to={`/products/${product.id}`}
>
{product.name}
</Link>
))}
</div>
)
}
function ProductDetail() {
const { id } = useParams()
const product =
products.find(
product =>
product.id === Number(id)
)
return (
<h1>
{product?.name}
</h1>
)
}
6. Query String — useSearchParams
페이지, 정렬, 검색조건, 필터처럼 화면의 조건을 URL에 표현할 때 많이 사용합니다.
import { useSearchParams } from 'react-router'
function Products() {
const [searchParams] = useSearchParams()
const page =
searchParams.get('page')
const category =
searchParams.get('category')
return (
<div>
<p>page: {page}</p>
<p>category: {category}</p>
</div>
)
}
| 종류 | 예 | 주 용도 |
|---|---|---|
| Params | /products/10 |
특정 자원 식별 |
| Query String | /products?page=2 |
검색, 정렬, 필터, 페이지 조건 |
useLocation
import { useLocation } from 'react-router'
function Page() {
const location = useLocation()
console.log(location.pathname)
console.log(location.search)
return null
}
7. Nested Route와 Outlet
마이페이지처럼 공통 레이아웃은 유지하고 내부 화면만 바뀌는 구조에서 중요합니다.
<Route
path="/mypage"
element={<MyPageLayout />}
>
<Route
index
element={<MyPageHome />}
/>
<Route
path="profile"
element={<Profile />}
/>
<Route
path="orders"
element={<Orders />}
/>
</Route>
자식 Route의 path="profile"은 부모 경로와 합쳐져
/mypage/profile이 됩니다.
Outlet
import {
Link,
Outlet
} from 'react-router'
function MyPageLayout() {
return (
<div className="layout">
<aside>
<Link to="profile">프로필</Link>
<Link to="orders">주문내역</Link>
</aside>
<main>
<Outlet />
</main>
</div>
)
}
<Outlet />은 현재 매칭된 자식 Route가 들어가는 자리입니다.
8. 404와 Navigate
404 Route
<Route
path="*"
element={<NotFound />}
/>
*는 앞에서 매칭되지 않은 나머지 경로를 처리할 때 사용할 수 있습니다.
로그인 보호 Route
import { Navigate } from 'react-router'
function ProtectedRoute({ children }) {
const isLogin = true
if (!isLogin) {
return (
<Navigate
to="/login"
replace
/>
)
}
return children
}
<Route
path="/mypage"
element={
<ProtectedRoute>
<MyPage />
</ProtectedRoute>
}
/>
navigate로 state 전달
navigate('/complete', {
state: {
name: '홍길동',
productId: 10
}
})
const location = useLocation()
console.log(location.state)
9. Data Router — createBrowserRouter + loader
React Router는 기본적인 JSX Route 방식 외에 Router 설정을 객체로 만들고 데이터 로딩 기능을 사용하는 방식도 제공합니다.
createBrowserRouter
import {
createBrowserRouter
} from 'react-router'
const router =
createBrowserRouter([
{
path: '/',
element: <Home />
},
{
path: '/products',
element: <Products />
}
])
import { RouterProvider } from 'react-router/dom'
<RouterProvider router={router} />
loader
async function productsLoader() {
const response =
await fetch('/api/products')
return response.json()
}
{
path: '/products',
element: <Products />,
loader: productsLoader
}
useLoaderData
import { useLoaderData } from 'react-router'
function Products() {
const products =
useLoaderData()
return (
<div>
{products.map(product => (
<div key={product.id}>
{product.name}
</div>
))}
</div>
)
}
Dynamic Route + loader
{
path: '/products/:id',
element: <ProductDetail />,
loader: productLoader
}
async function productLoader({ params }) {
const response =
await fetch(
`/api/products/${params.id}`
)
return response.json()
}
10. 실무형 전체 구조
Layout.jsx
import {
Link,
Outlet
} from 'react-router'
export default function Layout() {
return (
<>
<header>
<Link to="/">
쇼핑몰
</Link>
<nav>
<Link to="/products">상품</Link>
<Link to="/cart">장바구니</Link>
<Link to="/mypage">마이페이지</Link>
</nav>
</header>
<main>
<Outlet />
</main>
</>
)
}
router.jsx
import {
createBrowserRouter
} from 'react-router'
export const router =
createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{
index: true,
element: <Home />
},
{
path: 'products',
element: <ProductList />
},
{
path: 'products/:id',
element: <ProductDetail />
},
{
path: 'cart',
element: <Cart />
},
{
path: 'mypage',
element: <MyPage />
},
{
path: '*',
element: <NotFound />
}
]
}
])
main.jsx
import ReactDOM from 'react-dom/client'
import {
RouterProvider
} from 'react-router/dom'
import {
router
} from './router/router'
ReactDOM
.createRoot(
document.getElementById('root')
)
.render(
<RouterProvider
router={router}
/>
)
11. React Router를 보는 핵심 사고방식
학습 순서
Router 환경 만들기
URL과 컴포넌트 연결
사용자 페이지 이동
JavaScript로 이동
URL에서 데이터 읽기
공통 레이아웃 + 자식 화면
로그인 등의 접근 제어
Data Router로 확장