Frontend/Package
React Router 핵심 개념과 버전 | react-router-dom
신림쥐
2024. 8. 22. 13:43
728x90
React Router
- React Router는 React를 위한 다중 전략 라우터입니다.
- react-router-dom 라이브러리를 통해 React 애플리케이션에서 주로 사용합니다.
- React 애플리케이션에서 여러 페이지를 쉽게 관리하고 내비게이션을 구현할 수 있습니다.
<Routes>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route element={<AuthLayout />}>
<Route path="login" element={<Login />} />
<Route path="register" element={<Register />} />
</Route>
<Route path="concerts">
<Route index element={<ConcertsHome />} />
<Route path=":city" element={<City />} />
<Route path="trending" element={<Trending />} />
</Route>
</Routes>
React Router 버전별 특징
- v5 : v4의 안정성을 높힌 버전, v5이상 부터 모던 프론트엔드에 적합한 버전이라고 한다.
- v6 : 구조적 개선이 진행, v7을 데이터 패칭 강화를 위한 마련한 버전이다.
- v7 : v6을 유지하면서 성능 및 데이터 패칭을 강화한 버전이다.
React Router v4 이하 | React Router v5 | React Router v6 | React Router v7 | |
배포이력 |
|
|
|
|
리액트 최소 버전 |
|
|
|
|
하위 호완성 |
|
|
|
|
주요 변경 사항 |
|
|
|
|
지원 모드
- React Router에서 얼마나 많은 제어나 도움을 원하는지에 따라 모드를 선택할 수 있습니다.
- Declarative에서 Data로, 그리고 Framework로 이동하면 아키텍처 제어를 희생하고 더 많은 기능이 추가됩니다.
- 선언적 모드
- 데이터 모드
- 프레임워크 모드
선언적 모드
- 구성요소 매칭, 탐색, 활성화 상태 등 기본 라우터 기능만 간단하게 React Router를 사용하고 싶습니다
- 보류 중인 상태(로컬 우선, 백그라운드 데이터 복제/동기화 등)를 건너뛰거나 해당 상태에 대한 자체 추상화가 있는 데이터 계층이 있습니다.
- Create React App에서 나옵니다(프레임워크 모드도 고려할 수 있음)
- v6에서 왔고 만족합니다.<BrowserRouter>
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router";
import App from "./app";
ReactDOM.createRoot(root).render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
</Routes>
</BrowserRouter>
);
데이터 모드
- React 렌더링 외부로 경로 구성을 이동한 아키텍처입니다.
- action와 같은 데이터 API를 사용하여 데이터 로딩, 작업, 보류 중인 상태 등을 추가할 수 있습니다.
- 데이터와 관련된 기능 외 번들링, 데이터 및 서버 추상화에 대한 제어 기능도 지원합니다.
- v6.4에서 데이터 라우터를 시작했고 만족합니다.
import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router";
import App from "./app";
let router = createBrowserRouter([
{
path: "/",
Component: Root,
loader: loadRootData,
},
]);
ReactDOM.createRoot(root).render(
<RouterProvider router={router} />
);
프레임워크 모드
- Framework Mode는 Vite 플러그인으로 Data Mode를 래핑하여 다음과 같은 React Router 전체 환경을 추가합니다.
- Remix, Next.js, Solid Start, SvelteKit, Astro, TanStack Start, typesafe 등을 사용중
- SPA, SSR 및 정적 렌더링 전략
- 지능형 코드 분할
import { index, route } from "@react-router/dev/routes";
export default [
index("./home.tsx"),
route("about", "./about.tsx"),
];
데이터 모드로 언제 이동하나요?
- 원래 Remix Data API를 React Router로 가져오기 시작했을 때 , 우리는 그것이 경로를 구성하는 꽤 다른 방식을 가져온다는 것을 깨달았습니다.
- React가 구성 요소 트리를 렌더링할 때<Routes> 구성 요소 를 통해 경로를 발견하는 대신, 우리는 페칭을 렌더링에서 분리할 수 있도록 경로 정의를 들어올려야 했습니다 . 이러하면서 BrowserRouter이로 인해 흥미로운 난제가 발생했습니다.
- 데이터 라우터를 통해 정의된 경로에서만 작동하는 소수의 새로운 데이터 API를 사용할 수 있다.
- 예) loader, action, shouldRevalidate, handle, 및 와 같은 경로 수준 데이터 APIlazy
- useLoaderData, useActionData, useFetcher, useMatches, useNavigation, 등과 같은 구성 요소 내 데이터 후크
- route.errorElement, route.ErrorBoundary, 및 와 같은 오류 처리 APIuseRouteError
<BrowserRouter>에서 <RouterProvider>로 마이그레이션 방법
1. <BrowerRouter> 구성 요소를 사용하는 프로젝트 준비
import {
BrowserRouter,
Link,
Route,
Routes,
} from "react-router-dom";
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/blog/*" element={<BlogApp />} />
<Route path="/users/*" element={<UserApp />} />
</Routes>
</BrowserRouter>
);
}
2. 루트 스플랫 경로로 RouterProvider 추가
import {
createBrowserRouter,
Link,
Route,
RouterProvider,
Routes,
} from "react-router-dom";
// 3️⃣ Router singleton created
const router = createBrowserRouter([
{ path: "*", element: <Root /> },
]);
// 4️⃣ RouterProvider added
export default function App() {
return <RouterProvider router={router} />;
}
// 1️⃣ Changed from App to Root
function Root() {
// 2️⃣ `BrowserRouter` component removed, but the <Routes>/<Route>
// component below are unchanged
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/blog/*" element={<BlogApp />} />
<Route path="/users/*" element={<UserApp />} />
</Routes>
);
}
3. <Route>정의를 데이터 라우터까지 올리는 것
const router = createBrowserRouter([
{ path: "/", element: <Home /> },
{
path: "/blog/*",
children: [
{ index: true, element: <h1>Blog Index</h1> },
{ path: "*", element: <BlogApp /> },
],
},
{ path: "*", element: <Root /> },
]);
export default function App() {
return <RouterProvider router={router} />;
}
function Root() {
return (
<Routes>
{/* ⬆️ Home route lifted up to the data router */}
<Route path="/blog/*" element={<BlogApp />} />
<Route path="/users/*" element={<UserApp />} />
</Routes>
);
}
React Router 주요 구성요소
- BrowserRouter: HTML5의 History API를 사용하여 URL을 관리하는 라우터입니다.
- Routes: Route들을 래핑하여 경로와 해당 경로에 매핑되는 컴포넌트를 정의합니다.
- Route: 특정 경로에 대해 렌더링할 컴포넌트를 정의합니다.
- Link: 사용자가 클릭할 수 있는 링크를 제공합니다. 이 링크를 클릭하면 페이지가 새로 고침 없이 변경됩니다.
React Router 후크
useNavigate
useLocation
- useLocation는 현재 location객체를 반환합니다. 현재 위치가 변경될 때마다 어떤 부작용을 수행하고 싶을 때 유용할 수 있습니다.
속성 | 설명 |
location.hash | 현재 URL의 해시(# 이후의 부분). |
location.key | 이 위치의 고유 키 (주로 React Router에서 사용). |
location.pathname | 현재 URL의 경로 (도메인 이후부터 쿼리 문자열 이전까지의 부분). |
location.search | 현재 URL의 쿼리 문자열 (? 포함, 키-값 쌍의 리스트). |
location.state | 또는 navigate에 의해 전달된 상태 값. (React Router에서 상태 전송 시 사용). |
declare function useLocation(): Location;
interface Location<State = any> extends Path {
state: State;
key: string;
}
interface Path {
pathname: string;
search: string;
hash: string;
}
지원 기능
라우팅 - 중첩된 경로
- 기본 라우터 설정
<Routes>
<Route path="dashboard" element={<Dashboard />}>
<Route index element={<Home />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
- 페이지 설정
// dashboard.tsx
import { Outlet } from "react-router";
export default function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
{/* will either be <Home/> or <Settings/> */}
<Outlet />
</div>
);
}
라우팅 - 레이아웃 경로
- 기본 라우터 설정
// 라우터
<Routes>
<Route element={<MarketingLayout />}>
<Route index element={<MarketingHome />} />
<Route path="contact" element={<Contact />} />
</Route>
<Route path="projects">
<Route index element={<ProjectsHome />} />
<Route element={<ProjectsLayout />}>
<Route path=":pid" element={<Project />} />
<Route path=":pid/edit" element={<EditProject />} />
</Route>
</Route>
</Routes>
라우팅 - 동적 세그먼트
- prop 이 <Route path> 없는 A는 element부모 레이아웃을 도입하지 않고 자식 경로에 경로 접두사를 추가합니다.
<Route path="projects">
<Route index element={<ProjectsHome />} />
<Route element={<ProjectsLayout />}>
<Route path=":pid" element={<Project />} />
<Route path=":pid/edit" element={<EditProject />} />
</Route>
</Route>
- 경로 세그먼트가 :다음으로 시작하면 "동적 세그먼트"가 됩니다. 경로가 URL과 일치하면 동적 세그먼트가 URL에서 구문 분석되어 . params와 같은 다른 라우터 API에 제공됩니다 useParams.
<Route
path="/c/:categoryId/p/:productId"
element={<Product />}
/>
- 하나의 경로 경로에 여러 개의 동적 세그먼트를 포함할 수 있습니다.
import { useParams } from "react-router";
export default function CategoryProduct() {
let { categoryId, productId } = useParams();
// ...
}
페이지 탐색
- <NavLink>
- <Link>
- <Form>
- redirect
- useNavigate
찾을 수 없는 페이지
- 존재하지 않는 경로에 대한 처리를 위해 찾을 수 없는 페이지를 설정
├─src/
│ ├─components/
│ ├─src/
│ │ ├─layouts/
│ │ │ └─Default.tsx
│ │ ├─pages/
│ │ │ ├─main.tsx
│ │ │ └─NotFound.tsx
│ │ └─index.tsx
import NotFound from './NotFound'
<Routes>
<Route path="dashboard" element={<Dashboard />}>
<Route index element={<Main/>} />
<Route path="*" element={<NotFound/>} />
</Route>
</Routes>
인증된 페이지
선언적 모드 문서 : https://reactrouter.com/6.30.0/route/route
- 승인된 상태에서만 접근 할 수 있는 페이지 설정
├─src/
│ ├─components/
│ ├─src/
│ │ ├─loaders/
│ │ │ └─requiresAuth.ts
│ │ ├─pages/
│ │ │ ├─SignIn.tsx
│ │ │ └─Mypage.tsx
│ │ └─index.tsx
- 사용자가 페이지에 접근하면, 페이지를 렌더링하기 전에 Loader 함수를 통해 승인 여부를 확인
- Loader 함수는 페이지 접근 시 호출되는 로더(Loader) 함수로 접근 페이지의 요청 정보(request)를 받아서 사용한다.
async function getUser() {
const token = localStorage.getItem('access_token')
return token ? true : false'
}
export async function requiresAuth({ request }: { request: Request }) {
const user = await getUser() // 인증 여부를 확인하는 함수
if (!user) {
return redirect(`/signin`) // 돌아갈 페이지로 이동
}
return user // 원하는 반환값 처리...
}
import SignIn from './pages/SignIn'
import { requiresAuth } from './loaders/requiresAuth'
const router = createBrowserRouter([
{
element: <DefaultLayout />,
children: [
// ...
{
path: '/signin',
element: <SignIn />
},
{
path: '/mypage',
element: <Mypage />,
loader: requiresAuth,
children: [
{
path: ':info',
element: <Myinfo />
}
]
}
// ...
]
}
])
export default function Router() {
return <RouterProvider router={router} />
}
스크롤 복원
- 사용자가 뒤로 혹은 앞으로 가기를 할 때 페이지의 스크롤 위치를 복원하거나, 새로운 페이지의 스크롤 위치를 취상단으로 자동으로 처리
- <ScrollRestoration> 컴포넌트 사용는 최상위 레이아웃에 한 번만 추가하면 됩니다.
import { Outlet, ScrollRestoration } from 'react-router-dom'
import TheHeader from '@/components/TheHeader'
export default function DefaultLayout() {
return (
<>
<TheHeader />
<Outlet />
<ScrollRestoration />
</>
)
}
react-router-dom v5 -> v6 마이그레이션
- Node.js 12.2.0 이상과 호환, React 16.8 이상 버전에서 사용이 가능
- 가장 최적화된 성능은 Node.js 14 이상, React 18과 함께 사용하는 것
npm install react-router-dom@6
1. Switch가 Routes로 대체
Routes는 경로에 맞는 첫 번째 Route만 렌더링
import { Route, Switch } from 'react-router-dom';
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
</Switch>
import { Route, Routes } from 'react-router-dom';
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
2. withRouter 지원 안함
useNavigate, useLocation, useParams, useMatch 같은 훅으로 대체
import { withRouter } from 'react-router-dom';
class MyComponent extends React.Component {
componentDidMount() {
const { history, location, match } = this.props;
console.log(history, location, match);
}
render() {
return <div>My Component</div>;
}
}
export default withRouter(MyComponent);
import { useNavigate, useLocation, useParams } from 'react-router-dom';
const MyComponent = () => {
const navigate = useNavigate();
const location = useLocation();
const params = useParams();
console.log(navigate, location, params);
return <div>My Component</div>;
};
export default MyComponent;
3. useHistory가 useNavigate로 대체
import { useHistory } from 'react-router-dom';
const MyComponent = () => {
const history = useHistory();
const goToHome = () => {
history.push('/home'); // 페이지 이동
history.push('/home', { userId: 123 }); // 페이지 이동 및 상태 전달
history.replace(RouteLink.ROOT); // 페이지 대체
history.goBack(); // 이전 페이지로 돌아가기
};
return <button onClick={goToHome}>Go Home</button>;
};
import { useNavigate } from 'react-router-dom';
const MyComponent = () => {
const navigate = useNavigate();
const goToHome = () => {
navigate('/home'); // 페이지 이동
navigate('/home', { state: { userId: 123 } }); // 페이지 이동 및 상태 전달
navigate(RouteLink.ROOT, { replace: true }); // 페이지 대체
navigate(-1); // 이전 페이지로 돌아가기
};
return <button onClick={goToHome}>Go Home</button>;
};
상태읽기(React Router v5, v6동일)
import { useLocation } from 'react-router-dom';
const Home = () => {
const location = useLocation();
const state = location.state as { userId: number };
return <div>User ID: {state?.userId}</div>;
};
export default Home;
4. RouteComponentProps 지원 안함
useParams 같은 훅으로 대체
import { RouteComponentProps } from 'react-router-dom';
interface MyProps extends RouteComponentProps<{ id: string }> {}
const MyComponent: React.FC<MyProps> = ({ match }) => {
const { id } = match.params;
return <div>ID: {id}</div>;
};
import { useParams } from 'react-router-dom';
const MyComponent: React.FC = () => {
const { id } = useParams<{ id: string }>();
return <div>ID: {id}</div>;
};
5. 중첩 라우트(Nested Routes)
- Route 내부에서 설정
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/dashboard/settings" component={Settings} />
</Switch>
<Routes>
<Route path="/dashboard" element={<Dashboard />}>
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
6. Route 속성값 변경 및 삭제
- render -> element
- render 대신 element 속성을 사용하여 JSX를 직접 전달합니다.
- <>로 Fragment 래핑
- 여러 요소를 반환할 때 React.Fragment(<>)를 사용합니다.
- NotFound 컴포넌트
- <Route path={RouteLink.DEFAULT} render={() => <NotFound />} />에서 element={<NotFound />}로 수정했습니다.
- exac 명시 안함
- useParams path 표시는 ':id'로 v5버전과 동일
<Route path="/user/:id" component={User} />
<Route path="/user/:id" element={<User />} />
728x90