728x90
728x90
리액트 라우터(React Router)
들어가며
- 리액트(React.js)에서 라우팅을 할 때 많이 사용하는 리액트 라우터(React Router) 라이브러리에 대해 정리해본다.
- v6을 기준으로 정리하였다.
리액트 라우터(React Router)
개념
- 리액트 애플리케이션에서 클라이언트 사이드 라우팅을 처리하기 위한 라이브러리
- 리액트 라우터를 사용하면, 사용자가 페이지를 전환할 때 전체 페이지를 새로고침하지 않고도 URL에 따라 서로 다른 컴포넌트를 렌더링할 수 있다.
구성 요소
BrowserRouter
- HTML5의 History API를 사용하여 URL을 관리한다.
- 주로 애플리케이션의 최상위 컴포넌트로 사용된다.
Routes
- 각 경로에 대한 라우트를 정의하는 컨테이너
- 하나 이상의 @Route@를 자식으로 가질 수 있다.
Route
- 특정 경로와 컴포넌트를 매핑한다.
- 사용자가 특정 URL로 접근했을 때 어떤 컴포넌트를 보여줄지를 정의한다.
Link
- 다른 경로로 이동하기 위한 컴포넌트
- @<a>@ 태그처럼 작동하지만, 페이지를 새로 고침하지 않고 클라이언트 사이드에서 라우팅한다.
Navigate
- 다른 경로로 리다이렉션(Redirection)하는 데 사용된다.
설치하기
$ npm install react-router-dom # yarn add react-router-dom
사용하기
기본 사용법
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
const Home = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;
const App = () => {
return (
<Router>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
};
export default App;
라우트 정의
- @Route@ 컴포넌트는 @element@ prop을 사용하여 렌더링할 컴포넌트를 지정한다.
<Route path="/about" element={<About />} />
동적 라우팅(Dynamic Routing)
- 리액트 라우터는 동적 라우팅(Dynamic Routing)을 지원하여 URL의 일부를 매개변수(Parameter)로 사용할 수 있다.
- 예를 들어, @/users/:id@와 같은 경로를 정의하여 특정 사용자 ID에 따라 다른 내용을 표시할 수 있다.
import { useParams } from 'react-router-dom';
const User = () => {
const { id } = useParams();
return <h2>User ID: {id}</h2>;
};
// 라우트에서 사용
<Route path="/users/:id" element={<User />} />
중첩 라우트
- 라우트를 계층적으로 구성할 수 있다.
const Dashboard = () => {
return (
<div>
<h2>Dashboard</h2>
<Routes>
<Route path="settings" element={<Settings />} />
<Route path="profile" element={<Profile />} />
</Routes>
</div>
);
};
// 라우트에서 사용
<Route path="/dashboard/*" element={<Dashboard />} />
리다이렉션(Redirection)
- 리다이렉션 기능을 사용하면 특정 경로에서 다른 경로로 자동으로 이동할 수 있다.
- @Navigate@ 컴포넌트를 사용하여 리다이렉션을 처리할 수 있다.
import { Navigate } from 'react-router-dom';
<Navigate to="/login" />
404 에러 페이지 처리
- 다음과 같이 @Routes@의 마지막에 @Route@를 추가하고, @path@를 @*@로 설정하여 404 에러 페이지를 처리할 수 있다.
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>
여러가지 훅
- @useNavigate@ : 프로그래밍적으로 페이지를 이동할 수 있도록 해준다.
- @useNavigation@ : 경로 변경 상태를 추적할 수 있도록 도와준다. (@idle@, @loading@, @submitting@)
- @useLocation@ : 현재 위치 정보를 가져올 수 있게 해준다.
- @useMatch@ : 특정 경로와 매칭되는지 여부를 확인해준다.
- @useParams@ : 동적 라우트 매개변수에 접근할 수 있게 해준다.
import { useNavigate } from 'react-router-dom';
const MyComponent = () => {
const navigate = useNavigate();
const handleClick = () => {
navigate('/about');
};
return <button onClick={handleClick}>Go to About</button>;
};
⇒ 자세한 내용은 아래 글을 참고한다.
데이터 로딩 및 처리
- 로더(@loader@)와 액션(@action@)을 통해 데이터를 로딩하거나 특정 작업을 처리할 수 있는 기능을 제공한다.
- 이 기능은 @createBrowserRouter@ 및 @RouterProvider@와 함께 사용된다.
/src/App.jsx
import { createBrowserRouter, RouterProvider, Route } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <Home />,
loader: () => fetch('/api/home'),
},
{
path: '/about',
element: <About />,
},
]);
const App = () => (
<RouterProvider router={router} />
);
createBrowserRouter와 RouterProvider
개념
- 리액트 라우터 v6에서 사용 가능하며, 애플리케이션의 라우트를 더 효과적으로 관리할 수 있도록 해준다.
- @createBrowserRouter@를 사용하면 라우트 정의와 로더, 액션을 한 곳에서 관리할 수 있다.
- 최상위 컴포넌트(@App.jsx@)에서 라우터 생성 및 설정을 해준다.
./src/App.jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import NotFound from './pages/NotFound';
// 라우터 생성
const router = createBrowserRouter([
{
path: '/',
element: <Home />,
},
{
path: '/about',
element: <About />,
},
{
path: '*',
element: <NotFound />,
},
]);
// 애플리케이션의 최상위 컴포넌트
export const App = () => {
return (
<div>
<RouterProvider router={router} />
</div>
);
};
export default App;
사용하기 : @children@
- 중첩 라우트(Nested Route)를 정의하는 데 사용된다.
- 라우트를 계층적으로 구성하고, 부모 라우트 아래에 여러 자식 라우트를 설정할 수 있다.
- 중첩 라우트를 사용하면, 특정 경로에 따라 다른 컴포넌트를 렌더링하고, 구조적으로 애플리케이션을 조직할 수 있다.
예제 코드
- 부모 컴포넌트 (@./src/App.jsx@)
import {
createBrowserRouter,
RouterProvider,
} from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';
import Team from './pages/Team';
// 라우트 정의
const router = createBrowserRouter([
{
path: '/',
element: <Home />, // 부모 라우트 정의
children: [ // 자식 라우트 정의
{
path: 'about',
element: <About />,
},
{
path: 'contact',
element: <Contact />,
},
{
path: 'team',
element: <Team />,
},
],
},
]);
const App = () => {
return (
<div>
<RouterProvider router={router} />
</div>
);
};
export default App;
- @./src/components/Nav.jsx@
import { Link } from "react-router-dom";
const Nav = () => {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
<Link to="/team">Team</Link>
</nav>
);
};
export default Nav;
- @./src/pages/Home.jsx@ (부모 컴포넌트)
- @<Outlet>@은 중첩 라우트를 사용할 때 자식 라우트를 렌더링하는 데 사용되는 컴포넌트이다.
- 자식 컴포넌트 : @<About>@, @<Contact>@, @<Team>@
- 부모 라우트가 렌더링되는 위치에 @Outlet@을 삽입하면, 해당 경로에 맞는 자식 라우트가 그 자리에서 렌더링된다.
- @<Outlet conext={{ value }} />@와 같이 @context@ prop을 이용하여 자식 컴포넌트에 전역 변수를 넘겨줄 수 있다.
- @<Outlet>@은 중첩 라우트를 사용할 때 자식 라우트를 렌더링하는 데 사용되는 컴포넌트이다.
import { Outlet } from "react-router-dom";
import Nav from '../components/Nav'
const Home = () => (
const value = "some value";
<div>
<Nav />
<Outlet /> {/* 자식 컴포넌트를 렌더링할 위치 */}
</div>
);
사용하기 : @errorElement@
- 특정 라우트에서 오류가 발생했을 때 렌더링할 컴포넌트를 지정하기 위해 사용된다.
- 예를 들어, 데이터 로딩 중 오류가 발생하거나 해당 컴포넌트가 예외를 던질 경우, 사용자에게 표시할 에러 페이지를 제공할 수 있다.
예제 코드
- @./src/App.jsx@
import {
createBrowserRouter,
RouterProvider,
} from 'react-router-dom';
import Posts, { loader as postsLoader } from './pages/Posts';
import ErrorPage from './pages/ErrorPage';
// 라우트 정의
const router = createBrowserRouter([
{
path: '/posts',
element: <Posts />,
loader: loadData,
errorElement: <ErrorPage />, // 에러 발생 시 표시할 컴포넌트
},
]);
const App = () => {
return (
<div>
<RouterProvider router={router} />
</div>
);
};
export default App;
- @./pages/Posts@
import { useLoaderData } from 'react-router-dom';
// 데이터 로딩 함수
export const loader = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Failed to fetch data');
}
return response.json();
};
const Posts = () => {
const posts = useLoaderData();
return (
<div>
<h2>Posts</h2>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
};
export default Posts;
- @./pages/ErrorPage.jsx@
const ErrorPage = () => <h2>Something went wrong!</h2>;
export default ErrorPage;
로더(Loader)와 액션(Action)
- 로더(@loader@)와 액션(@action@)은 클라이언트 사이드 라우팅에서 데이터를 로드하거나 작업을 수행하는 데 사용된다.
- 애플리케이션의 URL에 따라 필요한 데이터를 미리 로드하고, 폼 제출이나 특정 이벤트에 대한 처리를 가능하게 한다.
로더(Loader)
- 특정 경로에 대해 데이터를 비동기적으로 로드하는 데 사용된다.
- URL이 변경될 때마다 로더가 호출되어 필요한 데이터를 가져올 수 있다.
- @createBrowserRouter@를 통해 정의된 각 라우트에서 사용할 수 있다.
- 서버에서 데이터를 비동기적으로 가져오는 작업을 처리한다.
- URL 경로에 따라 로드할 데이터를 동적으로 결정할 수 있다.
- 로드된 데이터는 해당 경로의 컴포넌트에서 @useLoaderData@ 훅을 통해 접근할 수 있다.
@loader@는 반드시 @return@ 값을 가져야 한다.
- @loader@는 데이터를 미리 로드하고, 그 데이터를 라우트 컴포넌트로 전달하는 역할을 한다.
- return 값이 없으면 @useLoaderData()@를 사용해서 데이터를 접근할 때 문제가 발생한다.
- @loader@는 주로 비동기 함수로 정의되며, 보통 서버에서 데이터를 가져온 후 그 데이터를 반환한다.
예제 코드
- @./src/components/UserList.jsx@
import { useLoaderData } from 'react-router-dom';
// 데이터 로딩 함수
export const loader = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error('Failed to fetch data');
}
return response.json(); // 반드시 반환값이 있어야 한다.
};
export const UserList = () => {
const users = useLoaderData(); // 로더 함수에서 반환된 데이터를 사용하기
return (
<div>
<h2>User List</h2>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};
export default UserList;
- @./src/App.jsx@
- @loader@ 속성에 로더 함수를 넣어준다.
import {
createBrowserRouter,
RouterProvider,
} from 'react-router-dom';
import UserList, { loader as loadUserData } from './component/UserList';
// 라우터 생성
const router = createBrowserRouter([
{
path: '/users',
element: <UserList />,
loader: loadUserData, // 데이터 로딩 함수 연결
},
]);
const App = () => {
return <RouterProvider router={router} />;
};
export default App;
액션(Action)
- 폼 제출 및 특정 이벤트에 대해 작업을 수행하는 데 사용된다.
- 예를 들어, 데이터 생성, 업데이트, 삭제하는 등의 작업을 처리할 수 있다.
- @createBrowserRouter@를 통해 정의된 각 라우트에서 사용할 수 있다.
- 폼 데이터를 서버에 제출하거나 상태를 변경하는 비동기 작업을 처리한다.
- URL 경로에 따라 처리할 작업을 동적으로 결정할 수 있다.
- 작업이 완료된 후 특정 경로로 리다이렉션할 수 있다.
action은 return 값을 반드시 가질 필요는 없다.
예제 코드
- @./components/Contact.jsx@
import { Form, redirect } from 'react-router-dom';
// 액션 처리 함수
export const action = async ({ request }) => {
const formData = await request.formData();
const data = Object.fromEntries(formData); // 키-값 쌍의 배열을 객체로 변환
// data는 { name: "John", age: "30" } 형태의 객체가 된다.
// -> 키 값은 input 요소의 name 속성을 통해 가져온다.
try {
const response = await axios.post('api.example.com', data);
return redirect('/'); // 메인으로 이동
} catch (error) {
console.log(error);
return error;
}
};
export const Contact = () => {
return (
<div>
<h2>Contact</h2>
<Form>
<input type="text" name="name" placeholder="Your Name" required />
<input type="email" name="email" placeholder="Your Email" required />
<button type="submit">Submit</button>
</Form>
</div>
);
};
export default Contact;
- @./src/App.jsx@
- @action@ 속성에 액션 함수를 넣어준다.
import {
createBrowserRouter,
RouterProvider,
} from 'react-router-dom';
import Contact, { action as contactAction } from './components/Contact';
// 라우터 생성
const router = createBrowserRouter([
{
path: '/contact',
element: <Contact />,
action: contactAction, // 액션 연결
},
]);
const App = () => {
return <RouterProvider router={router} />;
};
export default App;
종합 예제 코드
- 리액트 라우터의 종합적인 기능들이 포함되어 있는 예제 코드이다.
- 실제 제작했었던 미니 프로젝트의 코드 중, 뼈대가 되는 일부 코드만 첨부하였다.
./src/App.jsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import {
About,
HomeLayout,
Landing,
Error,
Newsletter,
Cocktail,
SinglePageError,
} from './pages';
import { loader as landingLoader } from './pages/Landing';
import { loader as singleCocktailLoader } from './pages/Cocktail';
import { action as newsletterAction } from './pages/Newsletter';
const queryClient = new QueryClient({
defaultOptions: {
// 쿼리 지속 시간
queries: {
staleTime: 1000 * 60 * 5, // 5분
},
},
});
const router = createBrowserRouter([
{
// Nested Pages
path: '/',
element: <HomeLayout />,
errorElement: <Error />,
children: [
{
index: true,
element: <Landing />,
errorElement: <SinglePageError />, // 개별 페이지 에러
loader: landingLoader(queryClient), // 로더 설정
},
{
path: 'cocktail/:id',
loader: singleCocktailLoader(queryClient),
element: <Cocktail />,
errorElement: <SinglePageError />,
},
{
path: 'newsletter',
element: <Newsletter />,
action: newsletterAction,
errorElement: <SinglePageError />,
},
{
path: 'about',
element: <About />,
},
],
},
]);
const App = () => {
return (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
{/* <ReactQueryDevtools initialIsOpen={false} /> */}
</QueryClientProvider>
);
};
export default App;
./src/pages/index.js
export { default as Landing } from './Landing';
export { default as About } from './About';
export { default as Cocktail } from './Cocktail';
export { default as Newsletter } from './Newsletter';
export { default as HomeLayout } from './HomeLayout';
export { default as Error } from './Error';
export { default as SinglePageError } from './SinglePageError';
./src/pages/HomeLayout.jsx
import { useNavigation } from 'react-router-dom';
import { Outlet } from 'react-router-dom';
import Navbar from '../components/Navbar';
const HomeLayout = () => {
const navigation = useNavigation(); // 'idle', 'loading'
const isPageLoading = navigation.state === 'loading';
const value = 'some value';
return (
<>
<Navbar />
<section className="page">
{isPageLoading ? (
<div className="loading"></div>
) : (
<Outlet context={{ value }} /> // 전역 컨텍스트로 전달
)}
</section>
</>
);
};
export default HomeLayout;
참고 사이트
728x90
728x90
'Programming > React' 카테고리의 다른 글
[React.js] caseReducers 속성 (Redux Toolkit) (0) | 2024.10.03 |
---|---|
[React.js] URL의 파리미터(Parameter) 값 가져오기 (1) | 2024.10.02 |
[React.js] index.js로 컴포넌트(Component), 페이지(Page) 관리하기 (0) | 2024.10.01 |
[React.js] Thunk API (Redux Toolkit) (1) | 2024.09.28 |
[React.js] 라우팅 관련 기능들 정리 (React Router) : useNavigate, useNavigation, redirect, useLocation, useParams, useHistory, Navigate (1) | 2024.09.26 |
[React.js] 폼 데이터 처리하기 (React, React Router) (1) | 2024.09.26 |
[React.js] 무한 스크롤(Infinite Scroll), 스켈레톤(Skeleton) 효과 적용하기 (with React Query) (0) | 2024.09.23 |
[React.js] .env 파일 만들고 사용하기 (환경 변수 관리) (0) | 2024.09.23 |