[REACT] loader, action

이크·2024년 6월 15일

react

목록 보기
2/5

loader? action?

react-router-dom에는 loader와 action 프로퍼티가 존재한다. 이들은 라우터가 렌더링 되기 전이나 요청을 보낼 때 사용한다.
이전 포스트에 이어서 작성할 예정이다.

loader

loader는 라우터가 렌더링 되기 전에 실행되는 함수를 등록한다.
간단하게 아래와 같이 사용할 수 있다.

	index: true,
  	element: <ArticlesPage />,
    loader: () => {
      console.log("articles!!");
    },

위 코드를 이용하면 'articles' URL로 이동하면 콘솔에 "articles!!"이 출력된다. 일반적으로 loader는 라우터가 렌더링 되기 전에 서버에서 API 통신 후 응답을 받는 데이터를 가져오기 위해 사용된다.

API를 사용하기 위해서 jsonplaceholder를 간편하게 사용해보겠다. jsonplaceholder는 간단한 dummy data를 제공하는 REST API이다.
https://jsonplaceholder.typicode.com/

{
  	index: true,
 	element: <ArticlesPage />,
    loader: async () => {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/posts/1"
        );

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

참고로 https://jsonplaceholder.typicode.com/posts/1 는 아래를 반환한다.

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

이제 이를 ArticlesPage에서 사용할 수 있다. API를 "https://jsonplaceholder.typicode.com/posts" 로 바꾸고 코드를 수정하면,

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

function ArticlesPage() {
  const posts = useLoaderData();

  return (
    <div>
      <p>ArticlesPage</p>
          {(posts) => (
            <ol>
              {posts.map((post) => (
                <li key={post.id}>{post.title}</li>
              ))}
            </ol>
          )}
    </div>
  );
}

export default ArticlesPage;

useLoaderData()를 사용하여 라우터의 loader에서부터 데이터를 가져올 수 있다.

그런데 라우터에 loader 함수를 정의하니까 지저분한 감이 없지 않아 있다. 이때, loader 함수는 실제 loader가 필요한 컴포넌트에 넣는 것이 일반적이다. 이 경우에는 ArticlesPage 컴포넌트에 아래와 같이 loader를 넣을 수 있다.

...
export default ArticlesPage;

export async function loader() {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts");
  console.log(response);

  if (response.status !== 200) {
    throw new Response(JSON.stringify({ message: "POST 불러오기 실패" }), {
      status: 500,
    });
  } else {
    const resData = await response.json();
    return resData;
  }
}

이제 App.js에서 함수를 불러오기만 하면 된다.

import ArticlesRootLayout from "./pages/ArticleRootLayout";
...
  {
    index: true,
    element: <ArticlesPage />,
    loader: ArticlesLoader,
  },

이렇게 간단하게 사용할 수 있다.

만약 fetch 중 에러가 발생하면?
ErrorPage 컴포넌트에서 에러를 받아올 수 있다.

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

function ErrorPage() {
  const error = useRouteError();

  let message = "오류 발생";

  if (error.status === 500) {
    message = JSON.parse(error.data).message;
  }
  if (error.status === 404) {
    message = "잘못된 주소";
  }
  return (
    <>
      <h1>Error</h1>
      <p>{message}</p>
    </>
  );
}

export default ErrorPage;

useRouteError 훅을 이용하면 에러가 발생할 경우 해당 에러의 정보를 가져올 수 있다. 그리고 간단하게 에러의 상태에 따라 메시지를 출력할 수 있다.

action

action는 라우터가 요청을 보낼 때 실행되는 함수를 등록한다.

import { Form, redirect } from "react-router-dom";

function NewArticlePage() {
  function submitHandler(event) {
    event.preventDefault();
  }

  return (
    <div>
      <p>New Article Page</p>
      <Form method="post">
        <span>Title</span>
        <input type="text" name="title" />
        <span>Body</span>
        <input type="text" name="body" />
        <button>New</button>
      </Form>
    </div>
  );
}

export default NewArticlePage;

export async function action({ request, params }) {
  const data = await request.formData();

  const articleData = {
    title: data.get("title"),
    body: data.get("body"),
  };

  const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
    method: request.method,
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(articleData),
  });

  if (!response.ok) {
    return new Response({ message: "Article 생성 오류" }, { status: 500 });
  }

  return redirect("/articles");
}

...
// App.jsx
import NewArticlePage, { action as NewArticleAction } from "./pages/NewArticlePage";
          {
            path: "new",
            element: <NewArticlePage />,
            action: NewArticleAction,
          },

loader와 마찬가지로 요청이 들어올 시 action 함수를 사용할 수 있다. Form 컴포넌트에서 요청을 할 경우 서버로 보내는 것이 아니라 클라이언트에 요청을 보낸다. 이 요청을 action 함수가 받아서 사용한다.
action 함수를 보면 request에서 formData를 가져온다. 이때 formData는 Form 컴포넌트 내부에 있는 name 속성이 있는 태그들을 가져올 수 있다. 이제 가져온 데이터를 서버에 보내서 POST 요청을 보낼 수 있다.

Form 컴포넌트와 redirect의 자세한 내용은 나중에 정리를 해야겠다.

profile
뭐라도 해보자

0개의 댓글