RemixとZodを使用したバリデーションのサンプルコードを実装

# 概要
- Remixを採用してみて、非常に開発体験が良い一方で、Nextjsなどに比べると情報量が少ないと感じるケースが多くあります。
- バリデーション実装にZodを使用したいというケースは多いと思いますが、RemixでZodを使用するサンプルコードなどはなかなか見つからず、少し苦労したため、他の人にも役立つように、シンプルなサンプルコードを実装しました。

:::note info
今回はRemixのaction内（サーバー側での処理）での入力値検証を行っています。こうすることで、より安全かつ開発体験良く、バリデーションを実装することが可能です。
:::

# サンプルコード
```tsx
import { json, type ActionFunctionArgs, redirect } from "@remix-run/node";
import { Form, useActionData, useNavigation } from "@remix-run/react";

import { z } from "zod";

const Schema = z.object({
  name: z.string().min(1, { message: "名前は必須です。" }),
  job: z.string().min(1, { message: "職業は必須です。" }),
});

export async function action({ request }: ActionFunctionArgs) {
  // リクエストからフォームデータを取得して、オブジェクトとして扱えるように変換。
  const formDataObject = Object.fromEntries(await request.formData());
  // safeParseを使用し、上記オブジェクトを検証。
  const validationResult = Schema.safeParse(formDataObject);
  if (!validationResult.success)
    return json({
      validationMessages: validationResult.error.flatten().fieldErrors,
    });

  // ユーザー登録処理。今回は主題でないので、中身は割愛
  await createUser(formDataObject);
  return redirect("/users");
}

export default function UsersNew() {
  const actionData = useActionData<typeof action>();
  const validationMessages = actionData?.validationMessages;
  const submitting = useNavigation().state === "submitting";

  {/* スタイリングについては主題ではないので、多少雑な部分があります。*/}
  return (
    <Form
      method="post"
      className="space-y-2 w-96 mx-auto border-2 p-6 rounded-md shadow-sm"
    >
      <div>
        <div className="flex flex-col gap-1">
          <label htmlFor="name" className="font-bold text-sm">
            名前
          </label>
          <input
            type="text"
            id="name"
            name="name"
            className="py-1 px-2 border-2 border-gray-300 focus:outline-none  focus:border-blue-300 rounded-md"
          />
        </div>
        {validationMessages?.name && (
          <p className="text-sm font-bold text-red-500">
            {validationMessages.name[0]}
          </p>
        )}
      </div>

      <div>
        <div className="flex flex-col gap-1">
          <label htmlFor="" className="font-bold text-sm">
            職業
          </label>
          <input
            type="text"
            id="job"
            name="job"
            className="py-1 px-2 border-2 border-gray-300 focus:outline-none  focus:border-blue-300 rounded-md"
          />
        </div>
        {validationMessages?.job && (
          <p className="text-sm font-bold text-red-500">
            {validationMessages.job[0]}
          </p>
        )}
      </div>

      <div className="flex">
        <button
          type="submit"
          className="ml-auto bg-blue-500 rounded-lg py-2 px-4 text-white font-bold hover:bg-blue-500/80"
          disabled={submitting}
        >
          {submitting ? "登録中..." : "登録"}
        </button>
      </div>
    </Form>
  );
}
```

# 実装画面
## 初期表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/544b65cb-ab1e-d3ff-4ef9-b48d86739393.png)
## バリデーションエラー(空の状態で登録)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/3cd3b413-9336-f5ad-fb5e-49a7dfcccf3e.png)
## 上記から職業だけ入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/4350d2f2-16a5-f15d-7e05-38ee0aea9081.png)

# 終わりに
- 非常に簡素な記事になりましたが、バリデーションはアプリ開発においてほぼ必須かと思いますので、お役に立てると幸いです。