React

[React] React Router v6.4 새로운 Data API와 사용법 정리

jundyu 2024. 10. 12. 19:48

React

들어가며

예전 강의 시간에 JSX문법 기반의 BrowserRouter, Routes, Route 컴포넌트로 SPA의 라우트를 정의하는 법을 배웠습니다. 그러고 최근에 팀 프로젝트를 하면서 공식 문서를 다시 읽어보게 되었는데, 새롭게 추가된 데이터 라우팅 방식이 더 편해보여서 이 방식을 선택하게 되었습니다.
 
 

React Router v6.4

React Router v6.4가 출시되면서, 새로운 Data API를 도입하여 라우팅 방식을 개선했습니다. 이전에는 BrowserRouter, Routes, Route 컴포넌트를 사용해 라우트를 정의했지만, v6.4부터는 데이터 객체를 통해 라우트를 설정하고, RouterProvider로 이를 제공합니다. 이 방식은 라우트를 코드에서 분리해 데이터로 관리할 수 있게 하여, 더 구조적이고 유지보수하기 쉬운 라우팅을 가능하게 합니다. React Router 공식 문서에서도 이 새로운 방식의 사용을 웹 프로젝트에서 권장하고 있으며, React Native도 곧 지원한다고 나와있습니다.
 
자세한 내용은 아래의 공식문서에서 확인할 수 있습니다.

 

Picking a Router  | React Router

 

reactrouter.com

 
 

createBrowserRouter vs createHashRouter

createBrowserRouter와 createHashRouter는 모두 라우터 생성 함수입니다. 둘 다 라우팅을 처리하지만 URL을 처리하는 방식에서 차이가 있습니다.

  • createBrowserRouter
  • 1. HTML5의 History API 방식을 이용해서 라우팅 처리 ≫ 깔끔한 URL을 사용할 수 있음
  • 2. 검색 엔진에서 URL을 그대로 크롤링 가능 ≫ SEO 친화적
  • 3. 서버측에서 모든 경로를 index.html로 리디렉션 처리 ≫ 서버 설정 필요
  • createHashRouter 
  • 1. 해시(#)를 사용하여 라우팅 처리 ≫ 지저분한 URL이 될 수 있음
  • 2. 검색 엔진은 # 이후의 내용을 무시할 수 있음 ≫ SEO 불리
  • 3. 브라우저에서 해시 기반 라우팅이 자체적으로 처리됨 ≫ 서버 설정 불필요

 
아래는 두 함수의 특징을 정리한 표입니다.

  createBrowserRouter createHashRouter
라우팅 방식 HTML5 History API 사용 해시(#) 기반 라우팅
URL 형식 /about
깔끔함
/#/about
해시가 포함되어 지저분함
서버 설정 서버 설정 필요
모든 경로를 index.html로 리디렉션
서버 설정 불필요
어떤 URL로 접근해도 동작
SEO SEO에 유리 SEO에 불리
지원 브라우저 최신 브라우저에 최적화 구형 브라우저도 잘 지원함

 
위의 표를 정리하면 다음과 같습니다.
createBrowserRouter는 서버 사이드 설정이 가능하고 SEO가 중요한 큰 프로젝트인 경우에 적절합니다.

  • 블로그
  • 마케팅 페이지

createHashRouter는 서버 설정이 어렵거나 SEO가 중요하지 않고 빠르게 배포해야 하는 경우에 적절합니다.

  • 내부용 대시보드
  • 관리 페이지

 
 

React Router 사용하기

기본적인 흐름은 다음과 같습니다.

  1. 라우트를 데이터 형식으로 정의
  2. createBrowserRouter 메서드로 Router 객체 생성
  3. App.jsx에서 RouterProvider를 사용해 Router 객체 전달

1. 라우트를 데이터 형식으로 정의

// src/RouterList.jsx
// import 구문 생략 // 

export const RouterList = () => [
  {
    // Login and Registration
    path: "/",
    element: <LoginLayout />,
    children: [
      {
        path: "intro",
        element: <IntroForm />,
      },
      {
        path: "login",
        element: <LoginForm />,
      },
      {
        path: "signup",
        element: <SignUpForm />,
      },
    ],
  },
];

(프로젝트의 RouterList 중 일부를 가져온 코드)

데이터 형식으로 path, element, children 등의 속성을 정의할 수 있습니다. 자세한 내용은 아래에서 설명하겠습니다.

  • path : 컴포넌트를 라우팅할 경로
  • element : 해당 url에서 라우팅할 컴포넌트
  • children : 중첩된 url을 사용하는 경우 해당 url에 라우팅할 컴포넌트
  • index : 중첩된 라우터에서 부모 라우트의 기본 라우트로 매칭
  • loader : 라우트별 필요한 데이터를 사전에 로딩하는 함수
  • action : 사용자의 입력을 처리하는 함수

2. createBrowserRouter 메서드로 Router 객체 생성

// src/RouterList.jsx

export const RouterList = () => [
	// 라우트 코드 생략 //
];

export const router = createBrowserRouter(RouterList());

Router를 데이터로 정의한 코드 아래에서 createBrowserRouter로 라우터 객체를 생성합니다.

3. App.jsx에서 RouterProvider를 사용해 Router 객체 전달

import router from "./RouterList";

function App() {
  return (
    <RouterProvider router={router} />
  );
}

App.jsx에서 RouterProvider로 Router 객체를 전달합니다.
 
 

Route Property

1. path

라우트의 url 경로를 정의하는 속성입니다. 브라우저가 특정 경로에 접근했을 때 매칭될 url 경로를 지정합니다. 콜론(:)을 이용해서 동적 경로를 정의할 수 있고, 와일드카드(*)를 이용해서 모든 경로에 대한 url 매칭도 가능합니다.

// src/RouterList.jsx

export const RouterList = () => [
  {
    path: "/posts",
    element: <PostLayout />,
    children: [
      {
        path: ":postId",
        element: <Post />,
      },
    ],
  },
  { path: "*", element: <WrongPath /> },
];

(path 속성을 설명하기 위한 예제 코드)

http://도메인/posts/24 경로에 접근한 경우는 다음과 같이 해석할 수 있습니다.
url의 24는 postId와 매칭되기 때문에 Post 컴포넌트에서 useParams 훅을 사용해 postId(=24)에 해당하는 값을 사용할 수 있습니다. 다음은 실제로 useParams 훅을 사용해 url 파라미터를 가져오는 코드입니다.

import { useParams } from "react-router-dom";

const Post = () => {
	const {postId} = useParams();
    return <div>{postId}</div> // => 24 출력
}

RouterList에서 정의한 url 파라미터 이름(=postId)과 똑같이 작성해야합니다.
 
또한, 정의한 패턴과 일치하지 않는 모든 경로(*)에 WrongPath 컴포넌트를 정의함으로써 잘못된 접근 경로에 대한 처리를 하고 있습니다. 일반적으로 와일드카드는 잘못된 경로 접근 시 나타나는 404 에러 페이지에 대한 처리에 사용됩니다.

2. element

사용자가 특정 경로로 이동했을 때 렌더링할 React 컴포넌트입니다.

// src/RouterList.jsx

export const RouterList = () => [
  {
    path: "/posts",
    element: <PostLayout />,
  },
];

(element 속성을 설명하기 위한 예제 코드)

/posts 경로에 접근하면 PostLayout 컴포넌트가 렌더링됩니다.

3. children

children은 중첩된 라우트를 정의하는 속성입니다.

// src/RouterList.jsx

export const RouterList = () => [
  {
    path: "/posts",
    element: <PostLayout />,
    children: [
      { 
      	path: ":postId",
      	element: <Post />,
      },
    ],
  },
];

(children 속성을 설명하기 위한 예제 코드)

RouterList.jsx에서 PostLayout 컴포넌트에 children 속성으로 Post 컴포넌트가 중첩되어있습니다. http://도메인/posts/24과 같은 경로로 접근하면 Post 컴포넌트가 렌더링됩니다. 이때 부모 컴포넌트인 PostLayout.jsx에 Outlet을 지정해야 자식 컴포넌트인 Post를 렌더링 할 수 있습니다.
 
다음은 PostLayout.jsx 코드입니다.

import {Outlet} from "react-router-dom";

const PostLayout = () => {
    return (
    	<>
            <div>1</div>
            <Outlet />
            <div>2</div>
    	</>
    )
}

export default PostLayout;

Outlet 컴포넌트는 자식 라우트를 렌더링할 위치를 지정하는 역할입니다. 위의 코드처럼 1과 2 사이에 Outlet 컴포넌트를 지정하면 1과 2 사이에서 자식 컴포넌트가 라우팅됩니다.

4. index

index 속성은 boolean 값으로, 부모 컴포넌트가 렌더링될 때 자동으로 매칭할 자식 컴포넌트를 지정합니다. index 라우트는 기본 경로로 설정되므로 path 속성을 가질 수 없습니다. 이 속성은 중첩된 라우터에서만 사용할 수 있기 때문에, children 배열의 라우트에만 적용할 수 있습니다. 또한 children 배열 내에서 단 하나의 라우트만 true 값을 가질 수 있습니다. index 값은 따로 설정하지 않으면 기본적으로 false입니다.
 
다음은 index 설명을 위한 예시 코드입니다.

// src/RouterList.jsx

export const RouterList = () => [
    {
    // User
    path: "user",
    element: <PrivateRoute><UserLayout /></PrivateRoute>,
    children: [
      {
      	index: true,
        element: <UserMain />,
      },
      {
        path: "upload/:userDocId",
        element: <UserUploadMoreList />,
      },
      {
        path: "interest/:userDocId",
        element: <UserInterestMoreList />,
      },
    ],
  },
];

(index 속성을 설명하기 위한 예제 코드)

위의 코드에서 "main/:userDocId" 경로의 라우트에 index 속성을 true로 정의했습니다. 그러면 http://도메인/user 경로로 접근을 하면 자동으로 UserMain 컴포넌트까지 렌더링됩니다.

5. loader & action

loader 함수는 라우트가 렌더링되기 전에 데이터를 비동기적으로 로드하여 컴포넌트에 전달하는 함수입니다.
그리고 action 함수는 사용자의 입력을 처리하는 함수입니다. loader와 다르게 컴포넌트를 렌더링하고 실행되는 함수입니다. 
 
위의 두 함수는 프로젝트에서 사용하지 않아 간략하게만 적었습니다. 더 공부해서 꼭 블로그에 정리해보겠습니다.
 

.
.
.

 

마치며

이번에 새로운 React Router의 Data API 방식을 처음으로 사용해봤지만 개인적으로 6.4 버전으로 업데이트 된 React Router가 사용하기 훨씬 편했습니다. 라우트들을 따로 파일에 분리해서 관리하니까 유지/보수도 쉬웠고, 눈에 더 잘 들어왔습니다. 글 작성하는데 시간이 많이 들었지만 React Router의 디테일한 개념까지 공부할 수 있어서 유익한 시간이었습니다. 다다음주에 프로젝트 또 예정되어있는데 그때 loader와 action 함수 써보면서 더 자세히 공부해보겠습니다! 피드백이나 궁금한 점은 댓글 부탁드립니다!