Claude Codeでエラーハンドリング実装|堅牢なアプリケーションを構築するパターン集

kento_morota 16分で読めます

「動くコード」と「堅牢なコード」の違いは、エラーハンドリングの質に表れます。正常系だけを考慮したコードは、予期しない入力やネットワーク障害、外部サービスのダウンなどが発生した瞬間に、ユーザーに不親切なエラーを見せたり、最悪の場合はデータを破損させたりします。Claude Codeを活用すれば、プロのエンジニアが実践するエラーハンドリングパターンを効率的に実装できます。本記事では、Claude Codeへの具体的な指示方法とともに、堅牢なアプリケーションを構築するためのエラーハンドリングパターンを体系的に解説します。

エラーハンドリングの基本原則

なぜエラーハンドリングが重要なのか

ソフトウェアが扱うデータや接続先が増えるほど、エラーが発生する可能性は高まります。データベースへの接続が切れる、外部APIがタイムアウトする、ユーザーが不正な値を入力する。これらは「もし起きたら」ではなく「いつ起きるか」の問題です。

適切なエラーハンドリングは、以下の価値を提供します。

  • ユーザー体験の保護:エラーが発生しても、ユーザーに分かりやすいメッセージを表示し、次のアクションを案内できる
  • データの整合性維持:処理の途中でエラーが発生しても、データが中途半端な状態にならないようにトランザクションを管理できる
  • デバッグの効率化:適切なログが残っていれば、問題の原因を素早く特定できる
  • セキュリティの確保:内部の技術的な情報がユーザーに漏洩しないように制御できる

Claude Codeにエラーハンドリングを依頼する基本アプローチ

Claude Codeにエラーハンドリングの実装を依頼する際は、以下のポイントを押さえましょう。

このAPIエンドポイントにエラーハンドリングを追加してください。

考慮すべきエラーケース:
1. バリデーションエラー(不正な入力値)
2. 認証・認可エラー
3. リソースが見つからない
4. データベース接続エラー
5. 外部API呼び出しの失敗
6. 予期しないサーバーエラー

各エラーに対して:
- 適切なHTTPステータスコードを返す
- ユーザー向けのメッセージと開発者向けの詳細情報を分離する
- 構造化されたエラーレスポンスを返す
- 適切なログレベルでログを出力する

このように具体的なエラーケースと期待する振る舞いを明示することで、Claude Codeは網羅的なエラーハンドリングを実装してくれます。

カスタムエラークラスの設計

なぜカスタムエラークラスが必要なのか

JavaScriptやTypeScriptの標準のErrorクラスは、メッセージとスタックトレースしか保持できません。しかし実際のアプリケーションでは、エラーの種類(バリデーション、認証、DB接続など)、HTTPステータスコード、ユーザー向けメッセージなど、より多くの情報をエラーオブジェクトに含めたいケースがほとんどです。

Claude Codeに以下のように指示して、プロジェクト全体で使えるカスタムエラークラスを設計しましょう。

アプリケーション全体で使用するカスタムエラークラス群を作成してください。

要件:
- 基底クラス: AppError(Errorを継承)
  - statusCode: HTTPステータスコード
  - code: アプリケーション固有のエラーコード(文字列)
  - message: ユーザー向けメッセージ
  - details: 開発者向け詳細情報(オプション)
  - isOperational: 運用エラーかプログラミングエラーかの区別

- 派生クラス:
  - ValidationError (400)
  - AuthenticationError (401)
  - ForbiddenError (403)
  - NotFoundError (404)
  - ConflictError (409)
  - InternalError (500)
  - ExternalServiceError (502)

src/errors/ディレクトリに配置してください。

カスタムエラークラスの実装例

Claude Codeが生成するカスタムエラークラスは、以下のような構造になります。

// src/errors/AppError.ts
export class AppError extends Error {
  public readonly statusCode: number;
  public readonly code: string;
  public readonly isOperational: boolean;
  public readonly details?: unknown;

  constructor(
    message: string,
    statusCode: number,
    code: string,
    isOperational = true,
    details?: unknown
  ) {
    super(message);
    this.statusCode = statusCode;
    this.code = code;
    this.isOperational = isOperational;
    this.details = details;
    Object.setPrototypeOf(this, new.target.prototype);
  }
}

// src/errors/ValidationError.ts
export class ValidationError extends AppError {
  constructor(message: string, details?: unknown) {
    super(message, 400, 'VALIDATION_ERROR', true, details);
  }
}

// src/errors/NotFoundError.ts
export class NotFoundError extends AppError {
  constructor(resource: string, id?: string) {
    const message = id
      ? `${resource}(ID: ${id})が見つかりません`
      : `${resource}が見つかりません`;
    super(message, 404, 'NOT_FOUND', true);
  }
}

このように、エラーの種類ごとにクラスを分離することで、catchブロックでinstanceofによる型判定が可能になり、エラーの種類に応じた適切な処理が記述しやすくなります。

try-catch戦略とエラーの伝播

レイヤーごとのエラーハンドリング

アプリケーションのアーキテクチャに応じて、各レイヤーでのエラーハンドリングの責務を明確にすることが重要です。

レイヤー 責務 エラー処理の例
コントローラー/ルート エラーをHTTPレスポンスに変換 AppErrorをJSONレスポンスに変換して返す
サービス/ユースケース ビジネスロジックのエラーを検出 業務ルール違反をValidationErrorとして投げる
リポジトリ/データアクセス データベースエラーをアプリケーションエラーに変換 レコード未検出をNotFoundErrorに変換する
外部サービス連携 外部APIのエラーをラップ タイムアウトをExternalServiceErrorに変換する

Claude Codeに対して「各レイヤーの責務に基づいてエラーハンドリングを実装してください」と指示し、具体的なレイヤー構成を伝えると、適切な場所にtry-catchが配置されたコードが生成されます。

グローバルエラーハンドラーの実装

個別のtry-catchで捕捉されなかったエラーを処理するために、グローバルエラーハンドラーを設置しましょう。

Express.jsアプリケーション用のグローバルエラーハンドリングミドルウェアを
作成してください。

要件:
- AppErrorのインスタンスは、statusCodeとmessageをそのまま返す
- AppError以外のエラーは500 Internal Server Errorとして処理する
- 本番環境ではスタックトレースを返さない
- 開発環境ではデバッグ用にスタックトレースを含める
- すべてのエラーをログ出力する
- エラーレスポンスは統一されたJSON形式で返す

Claude Codeはこの指示から、以下のような構造のエラーハンドラーを生成します。

// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { AppError } from '../errors/AppError';
import { logger } from '../lib/logger';

export const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  _next: NextFunction
) => {
  if (err instanceof AppError) {
    logger.warn({
      code: err.code,
      message: err.message,
      path: req.path,
      method: req.method,
    });

    return res.status(err.statusCode).json({
      success: false,
      error: {
        code: err.code,
        message: err.message,
        ...(process.env.NODE_ENV === 'development' && {
          details: err.details,
          stack: err.stack,
        }),
      },
    });
  }

  // 予期しないエラー
  logger.error({
    message: err.message,
    stack: err.stack,
    path: req.path,
    method: req.method,
  });

  return res.status(500).json({
    success: false,
    error: {
      code: 'INTERNAL_ERROR',
      message: 'サーバー内部エラーが発生しました',
    },
  });
};

APIエラーレスポンスの設計

統一されたエラーレスポンス形式

APIのエラーレスポンスは、すべてのエンドポイントで統一された形式であるべきです。フロントエンド開発者がエラーを扱いやすくなり、エラー表示コンポーネントも共通化できます。

Claude Codeに以下のように指示して、エラーレスポンスの型定義を作成しましょう。

APIのエラーレスポンスの型定義と、成功レスポンスの型定義を作成してください。

エラーレスポンスの形式:
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "ユーザー向けメッセージ",
    "details": [
      {
        "field": "email",
        "message": "メールアドレスの形式が正しくありません"
      }
    ]
  }
}

成功レスポンスの形式:
{
  "success": true,
  "data": { ... }
}

フロントエンドでのエラーハンドリング

APIのエラーレスポンスを統一しておくと、フロントエンド側のエラーハンドリングもシンプルになります。Claude Codeに以下のように依頼しましょう。

統一されたAPIエラーレスポンスに対応する
フロントエンドのエラーハンドリングユーティリティを作成してください。

要件:
- fetchラッパー関数で自動的にエラーを検出
- HTTPステータスコードに応じた処理の分岐
  - 401: 自動的にログインページにリダイレクト
  - 403: 権限エラーのトースト表示
  - 404: Not Foundページの表示
  - 422: バリデーションエラーをフォームに反映
  - 500: 汎用エラーメッセージの表示
- リトライ可能なエラー(408, 429, 503)は自動リトライ
- React用のuseApiErrorフックも作成

非同期処理のエラーハンドリング

Promise・async/awaitでのエラー処理

非同期処理のエラーハンドリングは、特に見落としやすいポイントです。Claude Codeに以下のパターンを指示して実装しましょう。

以下の非同期処理パターンにエラーハンドリングを追加してください:

1. 単一のAPI呼び出し(基本的なtry-catch)
2. 複数のAPI並列呼び出し(Promise.allSettled)
3. 順序依存のAPI連続呼び出し(チェーン処理)
4. タイムアウト付きのAPI呼び出し
5. リトライ付きのAPI呼び出し(指数バックオフ)

各パターンで、失敗時の挙動を明確にしてください。

リトライパターンの実装

一時的な障害(ネットワークの瞬断など)に対しては、自動リトライが有効です。Claude Codeに指数バックオフ付きのリトライ関数を作成してもらいましょう。

指数バックオフ付きのリトライユーティリティ関数を作成してください。

要件:
- 最大リトライ回数: 設定可能(デフォルト3回)
- 初回遅延: 1秒
- 指数バックオフ: 1秒 → 2秒 → 4秒
- ジッター(ランダムな遅延)の追加
- リトライ対象のエラーを指定可能
- リトライ時のログ出力
- すべてのリトライが失敗した場合は最後のエラーをthrow

Claude Codeはこの指示から、本番環境で使える品質のリトライ関数を生成してくれます。このような共通ユーティリティを一度作っておけば、プロジェクト全体で再利用できます。

ログ設計とモニタリング

構造化ログの導入

エラーが発生した際に素早く原因を特定するためには、適切なログが不可欠です。Claude Codeに構造化ログの仕組みを作成してもらいましょう。

Winston(またはPino)を使った構造化ログの設定を作成してください。

要件:
- ログレベル: error, warn, info, debug
- 本番環境: JSON形式で出力
- 開発環境: 読みやすい形式で出力
- 各ログに含める情報:
  - タイムスタンプ
  - ログレベル
  - リクエストID(トレーサビリティ用)
  - エラーの場合はスタックトレース
- 機密情報(パスワード、トークン)のマスキング

エラーモニタリングの設計

ログを出力するだけでなく、エラーの発生傾向をモニタリングする仕組みも重要です。以下の情報を収集・分析できるようにしておきましょう。

  • エラー発生率:全リクエストに対するエラーの割合
  • エラーの種類別集計:どのタイプのエラーが多いか
  • エンドポイント別エラー率:特定のAPIに問題が集中していないか
  • レスポンスタイム:タイムアウトエラーの予兆を検知

SentryやDatadogなどの外部サービスとの連携もClaude Codeに依頼できます。「Sentryを導入してエラーを自動通知する設定を作成してください」と指示すれば、初期設定からエラー送信のコードまで一括で生成してくれます。

Claude Codeを使ったエラーハンドリングの実装ワークフロー

段階的な実装手順

エラーハンドリングを体系的に実装するための推奨ワークフローを紹介します。

  1. カスタムエラークラスの作成:プロジェクト全体で使用するエラークラス群を定義する
  2. グローバルエラーハンドラーの設置:未処理のエラーを一括でキャッチする仕組みを作る
  3. 各レイヤーのtry-catch追加:サービス層、データアクセス層に適切なエラーハンドリングを追加する
  4. バリデーションの強化:入力値のバリデーションを網羅的に実装する
  5. ログ基盤の整備:構造化ログとモニタリングの仕組みを導入する
  6. テストの作成:エラーケースのテストを十分に書く

この手順に沿ってClaude Codeに1つずつ依頼していくと、体系的なエラーハンドリングが完成します。各手順をカスタムスラッシュコマンドとして定義しておくと、他のプロジェクトでも同じワークフローを再利用できます。

エラーケースのテスト

エラーハンドリングのコードは、正常系以上にテストが重要です。Claude Codeにエラーケースのテストを網羅的に作成してもらいましょう。

userService.createUser()のエラーケーステストを作成してください。

テストすべきケース:
1. 必須フィールドが不足している場合 → ValidationError
2. メールアドレスが既に使用されている場合 → ConflictError
3. データベース接続エラー → InternalError
4. パスワードが強度要件を満たさない場合 → ValidationError
5. 入力値のサニタイズ(XSS対策)

各テストで、エラーの種類、ステータスコード、メッセージが
正しいことを検証してください。

Claude Codeの拡張思考を有効にすると、より多くのエッジケースを考慮したテストが生成されます。

まとめ:エラーに強いアプリケーションを構築する

エラーハンドリングは地味ですが、アプリケーションの品質を決定づける最も重要な要素の一つです。Claude Codeを活用すれば、以下のパターンを効率的に実装できます。

  • カスタムエラークラス:アプリケーション全体で統一された型安全なエラー体系を構築する
  • レイヤー別try-catch:各レイヤーの責務に基づいた適切なエラーハンドリングを配置する
  • 統一されたAPIエラーレスポンス:フロントエンドとバックエンド間で一貫したエラー通信を実現する
  • 非同期エラー処理:Promise、リトライ、タイムアウトなどの非同期パターンに対応する
  • 構造化ログ:障害の原因を素早く特定できるログ基盤を整備する
  • 網羅的なテスト:エラーケースのテストにより、予期しない障害を防止する

Claude Codeへの指示のコツは、「どのようなエラーケースを想定しているか」「エラー発生時にどう振る舞ってほしいか」を具体的に伝えることです。曖昧な指示では汎用的なtry-catchしか生成されませんが、具体的なエラーシナリオを伝えることで、実践的なエラーハンドリングが実装されます。堅牢なアプリケーション構築の第一歩として、まずはカスタムエラークラスの定義から始めてみてください。デバッグガイドテスト作成ガイドもあわせて参考にすると、より包括的な品質向上が実現できるでしょう。

#Claude Code#エラーハンドリング#設計パターン
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

AI活用のヒントをお探しですか?お気軽にご相談ください。

まずは話だけ聞いてもらう