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>;
};

 

⇒ 자세한 내용은 아래 글을 참고한다.

 

[React.js] 라우팅 관련 기능들 정리 (React Router) : useNavigate, useNavigation, redirect, useLocation, useParams, useH

라우팅 관련 기능들 정리 (React Router) : useNavigate, useNavigation, redirect, useLocation, useParams, useHistory, Navigate들어가며리액트(React.js)에서 라우팅을 위해 사용되는 관련 기능들에 대해 간단하게 정리해

dev-astra.tistory.com

 

데이터 로딩 및 처리

  • 로더(loader) 액션(action)을 통해 데이터를 로딩하거나 특정 작업을 처리할 수 있는 기능을 제공한다.
  •  이 기능은 createBrowserRouterRouterProvider와 함께 사용된다.

 

/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을 이용하여 자식 컴포넌트에 전역 변수를 넘겨줄 수 있다. 
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;

 

참고 사이트

 

Guides | React Router

 

reactrouter.com

 

728x90
728x90

리액트 라우터(React Router)들어가며리액트 라우터(React Router)개념구성 요소설치하기사용하기기본 사용법라우트 정의동적 라우팅(Dynamic Routing)중첩 라우트리다이렉션(Redirection)404 에러 페이지 처리여러가지 훅데이터 로딩 및 처리createBrowserRouter와 RouterProvider개념사용하기 : children사용하기 : errorElement로더(Loader)와 액션(Action)로더(Loader)액션(Action)종합 예제 코드참고 사이트