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
)을 통해 데이터를 로딩하거나 특정 작업을 처리할 수 있는 기능을 제공한다. - 이 기능은
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을 이용하여 자식 컴포넌트에 전역 변수를 넘겨줄 수 있다.
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
'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 |