2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおけるデータ検証とバリデーション設計:型チェック・必須判定・ロジック分離による堅牢な入力制御

Posted at

概要

入力を信じるな、検証せよ。
ユーザーの入力、外部APIのレスポンス、ストレージからの復元――これら**“信用ならない値”**に対して、
何を必須とし、どの形式を受け入れ、どう誤りを検出・通知するかが、アプリケーションの安全性とUXを左右する。

本稿では、バリデーションを次の観点から整理する:

  • 型検査・null/undefinedの検出
  • 複数条件を持つフィールド検証
  • 同期/非同期バリデーション
  • UI表示との責務分離
  • 汎用的なバリデーション構造の構築

1. 型チェックと存在確認

✅ 単純なプリミティブチェック

function isString(value) {
  return typeof value === 'string';
}

function isNonEmptyString(value) {
  return isString(value) && value.trim().length > 0;
}

→ ✅ typeof / Array.isArray() / instanceof による型ガードを基本とする


2. 必須チェックと複数条件の設計

function validateEmail(value) {
  if (!value) return 'メールアドレスは必須です';
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return '形式が正しくありません';
  return null;
}

→ ✅ nullなら成功 / エラーメッセージを返す方式が実用的


3. フィールド単位のバリデータ関数群

const validators = {
  name: (v) => v?.trim() ? null : '名前は必須です',
  age: (v) => v > 0 ? null : '年齢は1以上を入力してください',
  email: validateEmail
};

→ ✅ 各フィールドに責任を持たせ、ルールを分離
→ ✅ フォーム設計において再利用可能


4. フォーム全体の検証戦略

function validateForm(data) {
  const errors = {};

  for (const key in validators) {
    const error = validators[key](data[key]);
    if (error) errors[key] = error;
  }

  return Object.keys(errors).length > 0 ? errors : null;
}
  • ✅ エラーがあるかどうかは null または errors の有無で判定
  • ✅ フロントエンドでの事前検証 → API送信 → サーバー検証の流れに統合可能

5. 非同期バリデーション(重複チェックなど)

async function checkUsernameExists(username) {
  const res = await fetch(`/api/check-username?name=${username}`);
  const { exists } = await res.json();
  return exists ? 'このユーザー名はすでに使われています' : null;
}
  • ✅ 入力中でも検証可能に(例:Debounce + 非同期検証)
  • ✅ UI側で非同期の状態管理が必要(isCheckingフラグなど)

6. UIとバリデーションの責務分離

  • validateXXX() 関数は純粋関数とする(DOM操作しない)
  • ✅ 表示(例:赤枠・メッセージ)はコンポーネント側が担当
  • ✅ バリデーション関数はテスト可能なロジックに留める

7. 汎用バリデーションユーティリティの構築

function createValidator(rules) {
  return (value) => {
    for (const rule of rules) {
      const error = rule(value);
      if (error) return error;
    }
    return null;
  };
}

// 使用例
const validatePassword = createValidator([
  (v) => v.length >= 8 ? null : '8文字以上必要です',
  (v) => /[A-Z]/.test(v) ? null : '大文字を含めてください'
]);

→ ✅ チェックルールを配列で柔軟に組み合わせ可能
→ ✅ 共通ルールの再利用が容易に


設計判断フロー

① 値の型が保証されない? → 型ガード関数を定義

② 必須項目? → 空チェック + 明示的なエラーメッセージ

③ ルールが複数ある? → 順序付きの関数配列で検証

④ APIと連携したい? → 非同期検証関数を導入

⑤ UI更新と検証を分離したい? → 検証は純粋関数として定義

よくあるミスと対策

❌ バリデーション関数内でDOMを直接操作

→ ✅ 検証は検証、表示は表示。役割を分ける


❌ エラー表現がコード内にハードコーディングされすぎる

→ ✅ 国際化や再利用性を考慮し、メッセージ辞書化を検討


❌ 同期/非同期検証が混在して管理不能に

→ ✅ ステータスを分離(例:同期検証結果 + 非同期検証中フラグ)


結語

バリデーションとは“値の正しさ”ではなく、**“正しさの判断基準を構造として設計する行為”**である。

  • チェックは明確に
  • メッセージは意味を持って
  • 責務は関数に封じて
  • 検証は構造で繰り返せるように

ユーザーの安心は、信頼できる入力から生まれる。
堅牢な検証は、UIの品位と信頼性を支える基盤である。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?