리액트 라우터는 클라이언트 사이드 렌더링으로 SPA를 구현하게 해 주는 도구다.
이전 포스팅에서 리액트 라우터의 아주 기본적인 사용법에 대해서 알아보았다.
[React] React Router 사용해 SPA 만들기
1. SPA(Single Page Application) SPA는 사용자가 느끼기에는 여러 페이지가 존재하는 것 같지만 실제로는 하나의 페이지인 것을 말한다. 멀티 페이지 애플리케이션 기반으로는 페이지를 이동할 때마다
sulki.tistory.com
이번 포스팅에서는 리액트 라우터 v6.4부터 지원된 data api 사용법에 대해서 다뤄보겠다.
data api를 이용하면 서버로부터 데이터를 가져오고 서버에 데이터를 보내는 작업을 처리할 수 있다.
createBrowserRouter
리액트 라우터 v6.4의 data API를 지원하는 새로운 라우터는 다음과 같은 것이 있다.
• createBrowserRouter
• createMemoryRouter
• createHashRouter
• createStaticRouter
여기서는 웹 프로젝트의 라우터에 권장되는 createBrowserRouter에 대해서 알아보자.
createBrowserRouter는 내부적으로 DOM History API를 사용해 url을 업데이트하고 히스토리 스택을 관리한다.
createBrowserRouter([
{
path: "/",
element: <Root />,
loader: rootLoader,
children: [
{
path: "events/:id",
element: <Event />,
loader: eventLoader,
},
],
},
]);
createBrowserRouter의 가장 기본적인 형태는 위와 같다.
필수적으로 들어가야 할 것은 라우트 객체가 담긴 배열이다.
라우트 객체는 URL 세그먼트를 컴포넌트, 데이터 로딩, 데이터 뮤테이션과 연결해 준다.
라우트 객체의 가장 상단에는 루트 경로를 설정해주는 값들이 담겨 있다. path로 루트 경로를 설정해 주고, element에는 루트 경로와 연결할 컴포넌트를 넣어 준다.
children 프로퍼티에 루트 경로의 하위에 연결해 줄 라우트 객체들이 담긴 배열을 넣으면 각각의 라우트 경로를 컴포넌트와 연결해 줄 수 있다.
data api
data api 중 대표적인 것은 loader, action, fetcher가 있다.
loader는 서버에서 데이터를 받아오고, action은 서버에 데이터를 업데이트 하고,
fetcher는 내비게이션 이벤트를 일으키지 않고 서버와 상호작용할 수 있게 해준다.
loader
loader는 라우트 요소가 렌더되기 전에 데이터를 전달하는 역할을 한다. 라우트 객체에서 loader 프로퍼티에 값으로 들어가는 것은 데이터를 받아오는 함수다. 이 함수를 통해 받아 온 정보는 그 정보를 받아온 컴포넌트와 같거나 더 낮은 수준의 컴포넌트들 내에서 useLoaderData라는 훅을 통해 사용할 수 있다. useLoaderData를 사용하면 loader 함수에서 프로미스가 리턴된다 하더라도 자동으로 그 프로미스가 resolve된 데이터를 받을 수 있다.
params, request
loader 함수가 실행될 때 이 함수에 객체가 전달되는데, 이 객체에는 params, request가 들어 있다. params 프로퍼티에는 라우트 파라미터를 담겨 있고, request프로퍼티에는 요청 객체가 담겨 있다. params를 통해서 어떤 리소스를 로드하고 싶은지 쉽게 설정할 수 있다. request 객체를 loader가 받는 이유는, 리액트 라우터가 없었다면 브라우저가 서버에 직접 요청을 보냈겠지만 리액트 라우터가 그 과정을 가로막고 있기 때문이다. loader가 요청을 받은 뒤에 리액트 라우터가 그 요청을 서버에 대신 전해 준다.
createBrowserRouter([
{
path: "/teams/:teamId",
loader: ({ params }) => {
return fakeGetTeam(params.teamId);
},
},
]);
function loader({ request }) {
const url = new URL(request.url);
const searchTerm = url.searchParams.get("q");
return searchProducts(searchTerm);
}
action
애플리케이션에서 get요청이 아닌 다른 모든 요청을 보낼 때는 action 함수를 호출한다. action 함수도 loader와 마찬가지로 params와 request가 들어 있는 객체를 받는다.
<Route
path="/song/:songId/edit"
element={<EditSong />}
action={async ({ params, request }) => {
let formData = await request.formData();
return fakeUpdateSong(params.songId, formData);
}}
loader={({ params }) => {
return fakeGetSong(params.songId);
}}
/>
params, request
params를 통해서 어떤 리소스를 업데이트하고 싶은지 알아낼 수 있다. action에서 request 객체는 리액트 라우터가 제공하는 Form 컴포넌트로부터 formData 메서드를 통해 데이터를 받아올 때 가장 흔히 사용된다.
<Route
path="/projects/:projectId/delete"
action={({ params }) => {
return fakeDeleteProject(params.projectId);
}}
/>
<Route
action={async ({ request }) => {
let formData = await request.formData();
// ...
}}
/>
fetcher
useFetcher 훅을 사용하면 전환하지 않고, 즉 라우트를 변경하지 않고 loader나 action과 상호작용할 수 있다.
useFetcher가 반환하는 객체에는 서버와의 통신이 잘 이뤄졌는지를 알 수 있는 데이터들이 담겨 있다.
import { useFetcher } from "react-router-dom";
function SomeComponent() {
const fetcher = useFetcher();
// call submit or load in a useEffect
React.useEffect(() => {
fetcher.submit(data, options);
fetcher.load(href);
}, [fetcher]);
// build your UI with these properties
fetcher.state;
fetcher.formData;
fetcher.formMethod;
fetcher.formAction;
fetcher.data;
// render a form that doesn't cause navigation
return <fetcher.Form />;
}
fetcher.data에는 loader나 action으로부터 받은 응답이 담겨 있다. 로딩이나 제출이 다시 이뤄지더라도 이 데이터는 계속 남아 있다.
fetcher.states는 말 그대로 fetcher의 상태를 말한다. idle/submitting/loading 중 하나의 상태를 가진다. idle은 아무것도 fetch되지 않고 있는 상태를 말하고, submitting은 post, put, patch, delete요청에 의해서 라우트 액션이 호출되고 있는 상태다. loading은 fetcher가 loader를 호출하고 있는 상태, 또는 useRevalidator나 제출 이벤트에 의해서 데이터를 재확인하고 있는 상태를 말한다.
fetcher.load()를 통해서 라우트 loader에서 데이터를 로드할 수 있다.
<fetcher.Form>을 사용하면 일반 From 컴포넌트의 기능을 사용하면서 라우터 변경이 일어나지 않게 할 수 있다.
<fetcher.Form>과 관련된 메서드나 프로퍼티로는 다음과 같은 것들이 있다
fetcher.submit(): 보통은 <fetcher.Form>을 통해서 사용자가 fetching을 시작하지만, 만약 개발자의 권한으로 fetching을 하게 만들고 싶을 때는 fetcher.submit()을 사용한다. 예를 들어 일정 시간 동안 상호작용이 없으면 유저를 로그아웃시킬 수 있다.
fetcher.formData: <fetcher.Form>이나 fetcher.submit()을 통해 제출된 데이터에 접근할 수 있다.
fetcher.formAction: 폼이 제출되는 action url을 가리킨다.
fetcher.formMethod: 폼이 제출되는 메서드를 가리킨다.
리액트 라우터가 그저 url과 라우트를 연결해주는 기능을 하는 것이라고만 알고 있었는데 서버와 데이터를 주고받는 기능까지도 구현할 수 있다는 것을 알게 되었다. 다음 미션은 리액트 라우터를 사용해서 직접 사용자 인증 절차를 구현해 보도록 하겠다!
참고 자료
React Router
'React' 카테고리의 다른 글
client-side routing (0) | 2023.10.09 |
---|---|
[React] 리액트 앱에 인증 추가하기 (2) | 2023.05.01 |
[Redux] 비동기 작업 처리하기(useEffect, thunk) (0) | 2023.04.21 |
[React] 리액트 성능 최적화: React.Memo, useCallback, useMemo (0) | 2023.04.18 |
[Redux] 리덕스 툴킷 사용하기 (2) | 2023.04.14 |