Frontend Study

React _ loader 란?

jimmmy_jin 2025. 1. 8. 14:44

React Router의 loader 기능에 대해 알아보자

loader란 무엇인가?

React Router v6.4 이상에서 소개된 loader는 컴포넌트가 렌더링되기 전에 데이터를 가져오는 데 사용되는 새로운 기능이다. 이를 통해 서버에서 데이터를 가져오거나 비동기 작업을 수행한 뒤 컴포넌트에 데이터를 제공할 수 있다. loader는 주로 라우트와 연결되며, 데이터를 컴포넌트에 prop 형태로 전달한다.

loader의 주요 특징

  1. 비동기 데이터 가져오기 loader는 fetch나 axios와 같은 비동기 API 호출을 통해 데이터를 가져올 수 있다. 이 과정은 라우트가 렌더링되기 전에 이루어진다.
  2. 라우트와 연결된 데이터 로딩 loader는 라우트에 특정 데이터를 미리 로드하여 컴포넌트가 데이터를 바로 사용할 수 있도록 한다.
  3. 통합된 에러 처리 loader에서 오류가 발생하면 React Router는 해당 라우트에 정의된 에러 경로를 자동으로 렌더링하여 에러 처리를 간소화한다.

loader 사용 방법

기본 설정

loader를 사용하려면 라우트 객체에 loader 속성을 추가해야 한다. 다음은 기본적인 예제이다:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Home />,
    loader: async () => {
      const response = await fetch('/api/data');
      return response.json();
    },
  },
]);

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

위 예제에서 loader는 /api/data에서 데이터를 가져오고 이를 Home 컴포넌트에 전달한다.

loader에서 반환된 데이터 사용하기

loader에서 반환된 데이터는 컴포넌트 내에서 useLoaderData 훅을 사용하여 접근할 수 있다.

import { useLoaderData } from 'react-router-dom';

function Home() {
  const data = useLoaderData();

  return (
    <div>
      <h1>Loaded Data</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

useLoaderData를 통해 loader에서 가져온 데이터를 바로 사용할 수 있다.

loader의 장점

  1. SSR(Server-Side Rendering) 지원 loader는 서버에서 데이터를 미리 로드하기 때문에 React Router 기반의 SSR 구현이 쉬워진다.
  2. 코드 분리 데이터를 로드하는 로직과 컴포넌트를 분리하여 코드의 가독성과 유지보수성을 높일 수 있다.
  3. 중앙 집중식 데이터 관리 loader를 사용하면 라우트 수준에서 데이터를 로드할 수 있으므로 각 컴포넌트에서 API 호출을 중복해서 작성할 필요가 없다.

주의 사항

  1. 동기화 문제 loader는 라우트가 렌더링되기 전에 데이터를 가져오기 때문에, loader의 작업이 완료될 때까지 사용자는 잠시 기다려야 한다.
  2. 에러 처리 loader에서 발생하는 에러를 처리하려면 에러 경로를 명시적으로 정의해야 한다.
{
  path: '/',
  element: <Home />,
  loader: async () => {
    throw new Response('Not Found', { status: 404 });
  },
  errorElement: <ErrorPage />,
}

 

추가적으로 loader를 기본 라우터 페이지에 전부 다 적게 되면 너무 방대해지기 때문에

아래와 같은 방식으로 적을 수도 있다.

이 방식대로 적으면 loader의 하위에서만 그 loader를 사용할 수 있다는 걸 잊지 말아야한다.

import { createBrowserRouter, RouterProvider } from "react-router-dom";

import RootPage from "./pages/RootPage";
import HomePage from "./pages/Home";
import EventsPage, { loader as eventLoader } from "./pages/EventsPage";
import EventDetailPage from "./pages/EventDetailPage";
import NewEventPage from "./pages/NewEventPage";
import EditEventPage from "./pages/EditEventPage";
import EventsRootLayout from "./pages/EventsRoot";
import { Provider } from "react-redux";
import store from "./store/index";

const router = createBrowserRouter([
  {
    path: "/",
    element: <RootPage />,
    children: [
      { index: true, element: <HomePage /> },
      {
        path: "events",
        element: <EventsRootLayout />,
        children: [
          {
            index: true,
            element: <EventsPage />,
            loader: eventLoader,
          },
          { path: ":eventId", element: <EventDetailPage /> },
          { path: "new", element: <NewEventPage /> },
          { path: ":eventId/edit", element: <EditEventPage /> },
        ],
      },
    ],
  },
]);
function App() {
  return (
    <Provider store={store}>
      <RouterProvider router={router} />
    </Provider>
  );
}

export default App;

 

또 위의 로더는 아래의 페이지에 정의 해뒀고, 이 방법이 좀 더 깔끔한 거 같다.

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

import EventsList from "../components/EventsList";

function EventsPage() {
  const events = useLoaderData();
  return (
    <>
      <EventsList events={events} />
    </>
  );
}

export default EventsPage;

export async function loader() {
  const response = await fetch("http://localhost:8080/events");

  if (!response.ok) {
    //...
  } else {
    const resData = await response.json();
    return resData.events;
  }
}

결론

React Router의 loader 기능은 데이터를 컴포넌트에 제공하는 과정을 단순화하고, 라우트와 데이터 로드를 밀접하게 통합할 수 있는 강력한 도구다. 이를 활용하면 더 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있다. 하지만 loader를 사용할 때는 성능과 사용자 경험을 고려하여 적절히 구현하는 것이 중요하다.

 

 

 

++ 추가

Router에서 loader를 공유할때 아래와 같은 코드로 공유할 수 있는데

{
            path: ":eventId",
            id: "event-detail",
            loader: eventDetailLoader,
            children: [
              { index: true, element: <EventDetailPage /> },
              { path: "edit", element: <EditEventPage /> },
            ],
          },

 

loader가 자신의 위치보다 상위에 있다면 "useRouteLoaderData" 를 사용해야한다.

아래는 사용 예시이다.

import EventForm from "../components/EventForm";
import { useRouteLoaderData } from "react-router-dom";

function EditEventPage() {
  const data = useRouteLoaderData("event-detail");
  const event = data.event;
  console.log(event);
  return <EventForm event={event} />;
}

export default EditEventPage;