React Router
는 React 기반의 웹 애플리케이션에서 라우팅을 관리
하기 위한 라이브러리 입니다. 라우팅은 사용자가 웹 내에서 다른 페이지로 이동하는 것을 말하며 React Router
는 이를 쉽게 구현할 수 있도록 도와줍니다.
즉 SPA로 구성된 React 애플리케이션의 각컴포넌트를 URL 경로와 매핑
시키는 방식으로 동작합니다.
설치
npm install react-router-dom localforage match-sorter sort-by
npx create-react-app react-router-tutorial
react-router 공식문서에서 제공해주는 다음 코드를 넣으면 css나 비동기 서버통신을 미리 셋팅할 수 있습니다.
src/index.css
src/contacts.js
src
├── contacts.js
├── index.css
└── index.jsx
클라이언트 사이드 랜더링을 위해 다음코드를 추가해 줍시다.
src/index.js
import * as React from "react";
import * as ReactDOM from "react-dom/client";
import {
createBrowserRouter,
RouterProvider,<L
} from "react-router-dom";
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <div>Hello world!</div>,
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
router변수에서 모든 라우팅을 세팅한다음 브라우저에 그리게 됩니다. 가장 기본적인 셋팅이기 때문에 처음에 이해가 가지 않는다면 우선은 받아들입시다.
해당 코드에서는 ‘/’
라는 path
에 element
를 그리게 됩니다.
가장 기본적인 라우팅이라고 할 수 있다.
이 앱에서 전역 레이아웃을 추가해 봅시다.
다음경로에 파일을 추가적으로 만들어줍니다.
src/routes/root.jsx
export default function Root() {
return (
<>
<div id="sidebar">
<h1>React Router Contacts</h1>
<div>
<form id="search-form" role="search">
<input
id="q"
aria-label="Search contacts"
placeholder="Search"
type="search"
name="q"
/>
<div
id="search-spinner"
aria-hidden
hidden={true}
/>
<div
className="sr-only"
aria-live="polite"
></div>
</form>
<form method="post">
<button type="submit">New</button>
</form>
</div>
<nav>
<ul>
<li>
<a href={`/contacts/1`}>Your Name</a>
</li>
<li>
<a href={`/contacts/2`}>Your Friend</a>
</li>
</ul>
</nav>
</div>
<div id="detail"></div>
</>
);
}
다음과 같은 Root 컴포넌트
를 만들고 아까 만든 “/”
경로에 추가해 줍니다.
scr/index.jsx
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Root from "./routes/Root";
// ahem
const router = createBrowserRouter([
{
path: "/",
element: <Root/>, //해당경로에 컴포넌트 추가
},
]);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
‘/’경로
가 아까 추가해둔 Root 컴포넌트
로 변경되었습니다! 이제 라우팅을 어떻게 하면 좋을지 감이 오셨을 겁니다.
이번엔 your Name
이라는 버튼을 한번 눌러봅시다.
http://localhost:3000/contacts/1
경로로 라우팅을 설정한적이 없기 때문에 에러가 발생합니다. 경고 메시지를 보니 errorElement
나 ErrorBoundary
를 설정하라고 나옵니다.
해당 에러를 처리하기 위해 기본적인 오류페이지를 만들어 봅시다.
src/error-page.jsx
import { useRouteError } from "react-router-dom";
export default function ErrorPage() {
const error = useRouteError(); // 에러 발생 여부를 가져옵니다.
console.error(error);
return (
<div id="error-page">
<h1>Oops!</h1>
<p>Sorry, an unexpected error has occurred.</p>
<p>
<i>{error.statusText || error.message}</i>
</p>
</div>
);
}
root 경로에 에러페이지를 추가해 줍시다
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement : <ErrorPage/>
},
]);
<Root/>
에서 발생하는 모든에러는 <ErrorPage/>
로 이동하게 됩니다.
이제 “/”경로에서 에러가 발생할 경우 다음과 같은 에러페이지가 화면에 나타나게 됩니다.
이제 다른 페이지를 만들고 라우팅 해봅시다.
왼쪽 sidebar에 있는것이 연락처 목록입니다.
연락처 구성요소 UI 추가
src/routes/contact.jsx
import { Form } from "react-router-dom";
export default function Contact() {
const contact = {
first: "Your",
last: "Name",
avatar: "https://placekitten.com/g/200/200",
twitter: "your_handle",
notes: "Some notes",
favorite: true,
};
return (
<div id="contact">
<div>
<img
key={contact.avatar}
src={contact.avatar || null}
/>
</div>
<div>
<h1>
{contact.first || contact.last ? (
<>
{contact.first} {contact.last}
</>
) : (
<i>No Name</i>
)}{" "}
<Favorite contact={contact} />
</h1>
{contact.twitter && (
<p>
<a
target="_blank"
href={`https://twitter.com/${contact.twitter}`}
>
{contact.twitter}
</a>
</p>
)}
{contact.notes && <p>{contact.notes}</p>}
<div>
<Form action="edit">
<button type="submit">Edit</button>
</Form>
<Form
method="post"
action="destroy"
onSubmit={(event) => {
if (
// eslint-disable-next-line no-restricted-globals
!confirm(
"Please confirm you want to delete this record."
)
) {
event.preventDefault();
}
}}
>
<button type="submit">Delete</button>
</Form>
</div>
</div>
</div>
);
}
function Favorite({ contact }) {
// yes, this is a `let` for later
let favorite = contact.favorite;
return (
<Form method="post">
<button
name="favorite"
value={favorite ? "false" : "true"}
aria-label={
favorite
? "Remove from favorites"
: "Add to favorites"
}
>
{favorite ? "★" : "☆"}
</button>
</Form>
);
}
어떤 구조로 되어있는지는 화면으로 살펴보고 붙여넣기 합니다.
라우팅을 추가해 줍니다.
import Contact from "./routes/contact";
// 라우팅
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement : <ErrorPage/>
},
{
path: "contacts/:contactId", //이경로로 이동하면 <Contact/> 가 보인다.
element: <Contact />,
},
]);
해당 경로로 들어가면 귀여운 고양이가 보이게 됩니다.
여기서 :contactId
는 파라미터
가 됩니다. (1,2,3,”123”,…) 등 하나의 페이지에서 다양한 값이 필요할 때 사용됩니다. 자세한 내용은 뒤에 살펴봅시다.
이 페이지에는 하나의 문제점이 있습니다. Root컴포넌트
안에 존재하지 않는다는 것입니다. Root 컴포넌트와 경로가 다르기 때문에 sidebar
는 더이상 보이지 않습니다. sidebar
까지 함께 보이게 하기 위해 Root컴포넌트
안에 Contact
를 넣어보도록 하겠습니다.
코드를 다음과 같이 작성해 봅시다.
src/index.jsx
// 라우팅
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement : <ErrorPage/>,
children:[
{
path:"contacts/:contactId",
element:<Contact/>
}
]
},
]);
아까와 달리 <Root/> 컴포넌트
의 ‘/’ 경로
밑에 <Contact/> 컴포넌트
가 위치해 있습니다. 해당<Contact/> 컴포넌트
를 사용하기 위해서는 <Outlet/>
이라는 컴포넌트가 필요합니다.
src/routes/root.jsx
import { Outlet } from "react-router-dom";
export default function Root() {
return (
<>
{/* all the other elements */}
<div id="detail">
<Outlet /> // <Contact/>는 이곳에 위치하게 됩니다.
</div>
</>
);
}
Root 컴포넌트
의 children
의 컴포넌트가 다음 Outlet 컴포넌트
에 위치하게 됩니다. 이제 실제 화면을 살펴 봅시다.
다음 경로에 Root컴포넌트
와 Contact 컴포넌트
가 모두 포함된걸 볼수있습니다!
이제까지의 코드에서는 사이드바에서 링크를 클릭하면 브라우저가 React Routrer
를 사용해서 클라언트 라우팅을 하는게 아닌 URL에 대한 전체 문서요청을 수행해서 페이지
를 그렸습니다. 이는 html css javascript를 전부 불러와야하기 때문에 자원낭비 심합니다.
이러한 문제를 해결하기 위해서 앞으로는 <Link/>
를 사용해 URL을 업데이트해서 UI를 즉시 랜더링
할 수 있게 해봅시다.
수정하기전
<ul>
<li>
<a href={`/contacts/1`}>Your Name</a>
</li>
<li>
<a href={`/contacts/2`}>Your Friend</a>
</li>
</ul>
수정후
import { Outlet, Link } from "react-router-dom";
...
<ul>
<li>
<Link to={`/contacts/1`}>Your Name</Link>
</li>
<li>
<Link to={`/contacts/2`}>Your Friend</Link>
</li>
</ul>
이제페이지를 이동하는 과정에서 새로고침이 발생하지 않습니다.
클라이언트측에서 url를 업데이트하고 html css javascript를 전부 다시 가져오는것이 아닌 이미 받아온 정적 파일들로 화면을 즉시 만드는것
이기 때문에 새로고침이 발생하지 않고 부드러운 페이지 전환이 가능해집니다.
react-router를 설치하고 router를 세팅하는 방법에 대해 알아보았습니다. 모든 자료는 react-router-dom 공식홈페이지에서 제공합니다.