3
5

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におけるフォームバリデーション設計:構造化・再利用性・UI統合・バリデーション戦略の分離

Posted at

概要

バリデーションとは「値のチェック」ではなく、
「正しさの定義」と「設計的責任」の明示である。

UIに紐づいたアドホックなバリデーションは、
保守不能なフォーム構造を生む。

本稿では、バリデーションロジックの抽象化、入力UIとの分離、エラーメッセージの戦略的設計、同期/非同期チェックの構造化、再利用のための分離原則を軸に、信頼できるフォーム構造の構築法を提示する。


1. バリデーションを「責務」で分離する

function validateEmail(email) {
  if (!email) return '必須項目です';
  if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) return 'メール形式が不正です';
  return null;
}
  • ✅ 入力のチェックロジックはUIとは切り離して構築
  • ✅ 戻り値は "エラーメッセージ" or null で統一 → 分岐コストを減らす

2. フィールド単位でルールを宣言する

const validators = {
  email: [isRequired, isEmail],
  password: [isRequired, minLength(8)],
};
function runValidations(value, rules) {
  for (const rule of rules) {
    const error = rule(value);
    if (error) return error;
  }
  return null;
}
  • ✅ 複数ルールをループ評価
  • ✅ 入力フィールドにバリデーションルールを“宣言”できる構造に

3. UIとの接着部分は別レイヤーで制御

const emailInput = document.querySelector('#email');
const errorLabel = document.querySelector('#email-error');

emailInput.addEventListener('blur', () => {
  const msg = runValidations(emailInput.value, validators.email);
  errorLabel.textContent = msg || '';
});

→ ✅ バリデーションロジックとDOM操作のレイヤーを明確に分離


4. 非同期バリデーションの組み込み

async function checkEmailDuplication(email) {
  const res = await fetch(`/api/email-check?email=${email}`);
  const isTaken = await res.json();
  return isTaken ? '既に登録されています' : null;
}
async function validateFieldAsync(value, rules) {
  for (const rule of rules) {
    const result = await rule(value);
    if (result) return result;
  }
  return null;
}
  • ✅ 同期と非同期のルールを分離設計 → validateSync() / validateAsync() 両立設計へ

5. 全体バリデーションの設計構造

async function validateForm(values, validators) {
  const errors = {};
  for (const key in validators) {
    const rules = validators[key];
    const value = values[key];
    const error = await validateFieldAsync(value, rules);
    if (error) errors[key] = error;
  }
  return errors;
}

→ ✅ “全体として正しいか”をチェックするAPI用の検証構造を統合設計


6. メッセージの国際化・汎用化構造

const messages = {
  required: '必須項目です',
  invalidEmail: 'メール形式が不正です',
};

function isRequired(value) {
  return !value ? messages.required : null;
}
  • ✅ ロジックと文言を分離
  • ✅ i18nとの統合が容易な構造に

設計判断フロー

① ロジックがUI内に埋もれていないか? → 関数分離

② 同期/非同期チェックが混在してないか? → 分離して統合設計

③ ルールが再利用可能になっているか? → フィールド単位の宣言型に

④ 文言がロジックにハードコードされていないか? → メッセージ分離

⑤ DOMとの接着部分がテスト不能になっていないか? → UIとロジック分離

よくあるミスと対策

❌ UIイベント内で直接 if (!value) { showError(...) } を繰り返す

→ ✅ ルールごとの関数を定義し、宣言的に登録する構造へ


❌ 非同期チェックを onSubmit に直書きして処理が複雑化

→ ✅ 非同期ルールを同期ルールと同様の形式で処理できる構造


❌ エラー文言がHTMLにハードコードされていて再利用不能

→ ✅ メッセージは定義モジュールから取得する形式に統一


結語

フォームバリデーションとは、“正しいか”を判定する行為ではない。
それは**“何を許可し、どこで止め、どう伝えるか”という、設計上の責任の表明**である。

  • UIとロジックを分離し
  • 責務を関数に還元し
  • メッセージを柔軟に制御する

JavaScriptにおけるバリデーション設計とは、
“意図された操作だけを許容する、安全なインターフェースの設計である”。

3
5
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
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?