액션(Action): 데이터 변경 처리하기

액션(Action): 데이터 변경 처리하기

라우트에서 데이터를 읽는 건 로더가 맡는다면, 데이터를 바꾸는 일은 액션(action)이 맡아요. 액션은 서버 전용 함수로, 데이터 변경(mutation)과 그 밖의 동작을 처리해요. 라우트에 GET이 아닌 요청(DELETE, PATCH, POST, PUT)이 들어오면 로더가 호출되기 전에 액션이 먼저 불려요. 재미있는 점은 액션이 로더와 API가 똑같다는 거예요 — 호출되는 시점만 다를 뿐이죠.

출처: Remix 공식 문서 — action

데이터 읽기·쓰기·그리기를 한 파일에

액션이 로더와 같은 API를 쓰기 때문에, 한 데이터셋에 관한 모든 것을 하나의 라우트 모듈에 함께 둘 수 있어요. 데이터를 읽는 로더, 그 데이터를 그리는 컴포넌트, 데이터를 쓰는 액션이 한곳에 모이는 거죠.

import type { ActionFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json, redirect } from "@remix-run/node"; // or cloudflare/deno
import { Form } from "@remix-run/react";
import { TodoList } from "~/components/TodoList";
import { fakeCreateTodo, fakeGetTodos } from "~/utils/db";

export async function action({ request }: ActionFunctionArgs) {
  const body = await request.formData();
  const todo = await fakeCreateTodo({
    title: body.get("title"),
  });

  return redirect(`/todos/${todo.id}`);
}

export async function loader() {
  return json(await fakeGetTodos());
}

export default function Todos() {
  const data = useLoaderData<typeof loader>();

  return (
    <div>
      <TodoList todos={data} />
      <Form method="post">
        <input type="text" name="title" />
        <button type="submit">Create Todo</button>
      </Form>
    </div>
  );
}

<Form method="post">로 폼을 제출하면 POST 요청이 해당 라우트로 가고, 액션이 request.formData()로 제출된 값을 읽어 새 데이터를 만들고, 완료되면 redirect로 목록 페이지로 보내는 흐름이에요.

액션은 어느 라우트로 가는가

URL에 POST가 일어날 때, 라우트 계층에서 여러 라우트가 그 URL과 매칭돼요. 하지만 GET 로더와 달리(로더는 화면을 그리려고 전부 호출되죠) 액션은 하나만 호출돼요. 매칭된 라우트 중 가장 깊은 라우트가 호출되는데, 그 깊은 라우트가 index 라우트면 예외가 있어요. index 라우트와 부모는 같은 URL을 공유하니까, 이 경우엔 부모 라우트로 POST가 가요.

index 라우트에 POST를 보내고 싶다면 액션 URL에 ?index를 쓰면 돼요. <Form action="/accounts?index" method="post" />처럼요.

action url 라우트 액션
/accounts?index app/routes/accounts._index.tsx
/accounts app/routes/accounts.tsx

그리고 action prop이 없는 폼(<Form method="post">)은 자신이 렌더링된 같은 라우트로 자동으로 POST돼요. 그래서 ?index 파라미터로 부모·index 라우트를 구분할 필요가 있는 건, index 라우트가 아닌 다른 곳에서 index 라우트로 보낼 때뿐이에요. 자기 자신으로 보낼 땐 <Form action>을 아예 생략해도 돼요.

더 알아보기