폼 검증

폼 검증(Form Validation): 회원가입 폼으로 배우는 검증 흐름

이 가이드는 Remix에서 간단한 회원가입 폼을 구현하며 폼 검증을 적용하는 과정을 단계별로 보여줘요. 여기서는 action, 액션 데이터, 에러 렌더링처럼 Remix 폼 검증의 핵심 요소를 이해하는 데 집중할게요. 검증 규칙 자체보다는 메커니즘의 큰 그림을 잡는 데 목표를 두죠.

출처: Remix 공식 문서 — Form Validation

본문

1단계: 회원가입 폼 만들기

Remix의 Form 컴포넌트를 사용해 기본 회원가입 폼부터 만드세요.

import { Form } from "@remix-run/react";

export default function Signup() {
  return (
    <Form method="post">
      <p>
        <input type="email" name="email" />
      </p>

      <p>
        <input type="password" name="password" />
      </p>

      <button type="submit">Sign Up</button>
    </Form>
  );
}

2단계: action 정의하기

다음으로 Signup 컴포넌트와 같은 파일에 서버 action을 정의해요. 여기서는 폼 검증 규칙이나 에러 객체 구조를 깊이 파기보다는 동작 메커니즘을 넓게 보여주는 게 목적이라, 이메일과 비밀번호에 대한 기초적인 검사만 사용할게요.

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";

export default function Signup() {
  // omitted for brevity
}

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const email = String(formData.get("email"));
  const password = String(formData.get("password"));

  const errors = {};

  if (!email.includes("@")) {
    errors.email = "Invalid email address";
  }

  if (password.length < 12) {
    errors.password =
      "Password should be at least 12 characters";
  }

  if (Object.keys(errors).length > 0) {
    return json({ errors });
  }

  // Redirect to dashboard if validation is successful
  return redirect("/dashboard");
}

검증 에러가 있으면 action에서 클라이언트로 반환돼요. 이게 "고쳐야 할 게 있다"고 UI에 알리는 신호이고, 문제가 없다면 사용자는 대시보드로 리다이렉트되죠.

3단계: 검증 에러 표시하기

마지막으로 Signup 컴포넌트를 수정해 검증 에러가 있을 때 표시하게 해요. useActionData로 에러에 접근해 보여줄게요.

import type { ActionFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json, redirect } from "@remix-run/node"; // or cloudflare/deno
import { Form, useActionData } from "@remix-run/react";

export default function Signup() {
  const actionData = useActionData<typeof action>();

  return (
    <Form method="post">
      <p>
        <input type="email" name="email" />
        {actionData?.errors?.email ? (
          <em>{actionData?.errors.email}</em>
        ) : null}
      </p>

      <p>
        <input type="password" name="password" />
        {actionData?.errors?.password ? (
          <em>{actionData?.errors.password}</em>
        ) : null}
      </p>

      <button type="submit">Sign Up</button>
    </Form>
  );
}

export async function action({
  request,
}: ActionFunctionArgs) {
  // omitted for brevity
}

결론

이렇게 해서 Remix에서 기본 폼 검증 흐름을 성공적으로 구성했어요. 이 방식의 장점은 에러가 action 데이터에 기반해 자동으로 표시되고, 사용자가 폼을 다시 제출할 때마다 갱신된다는 점이에요. 그 덕분에 작성해야 하는 보일러플레이트 코드가 줄어들고 개발 과정이 더 효율적이 돼요.

더 알아보기