はじめに

DX開発事業部では、Claude 4のリリースに伴い、わずか1週間でClaude Codeを全面的に導入しました。
本記事では、Google CloudのVertex AI経由での実行環境構築から、GitHub Actionsとの統合、そして現場での実践的な活用方法までを詳しくご紹介します。

Claude Codeの導入構成

実行環境

  • 実行環境: Google Cloud Vertex AI経由でClaude Codeを実行
  • 開発環境: VSCodeとClaude Code拡張機能の完全統合
  • CI/CD統合: Claude Code ActionによりGitHub Actionsワークフローに統合
  • セキュリティ: Vertex AIのエンタープライズグレードセキュリティを活用

導入メリット

  1. クラウドベースでの高速処理
  2. GitHub Actionsとのシームレスな連携
  3. VSCodeとの完全統合による開発体験の向上

基本的な使用方法

※以降の内容は以下の構成を前提としております。
言語:PHP
フレームワーク:Laravel 12

1. 設計書を元にコードを生成

設計書例:ユーザー管理機能 (user_management.md)

# ユーザー管理機能設計書

# 概要
ユーザーの登録、更新、削除、一覧表示を行う機能

# テーブル構造
- users
  - id: bigint (primary key)
  - name: varchar(255)
  - email: varchar(255) (unique)
  - password: varchar(255)
  - role: enum('admin', 'user', 'guest')
  - is_active: boolean (default: true)
  - created_at: timestamp
  - updated_at: timestamp

# API仕様
- GET /api/users - ユーザー一覧取得
- GET /api/users/{id} - ユーザー詳細取得
- POST /api/users - ユーザー作成
- PUT /api/users/{id} - ユーザー更新
- DELETE /api/users/{id} - ユーザー削除

VSCode Claude Code拡張機能でのプロンプト

user_management.mdを基に、Laravel 12でユーザー管理機能を実装してください。
以下を含めてください:
- マイグレーション
- モデル
- コントローラー
- ルーティング
- バリデーションリクエスト

生成されたコード例

マイグレーション (database/migrations/2025_01_16_000000_create_users_table.php)

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->enum('role', ['admin', 'user', 'guest'])->default('user');
            $table->boolean('is_active')->default(true);
            $table->rememberToken();
            $table->timestamps();
        });

        Schema::create('password_reset_tokens', function (Blueprint $table) {
            $table->string('email')->primary();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });

        Schema::create('sessions', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->foreignId('user_id')->nullable()->index();
            $table->string('ip_address', 45)->nullable();
            $table->text('user_agent')->nullable();
            $table->longText('payload');
            $table->integer('last_activity')->index();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
        Schema::dropIfExists('password_reset_tokens');
        Schema::dropIfExists('sessions');
    }
};

モデル (app/Models/User.php)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
        'role',
        'is_active',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
            'is_active' => 'boolean',
        ];
    }
}

コントローラー (app/Http/Controllers/UserController.php)

<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Hash;

class UserController extends Controller
{
    public function index(): JsonResponse
    {
        $users = User::all();
        return response()->json($users);
    }

    public function show(User $user): JsonResponse
    {
        return response()->json($user);
    }

    public function store(StoreUserRequest $request): JsonResponse
    {
        $validated = $request->validated();
        $validated['password'] = Hash::make($validated['password']);

        $user = User::create($validated);

        return response()->json($user, 201);
    }

    public function update(UpdateUserRequest $request, User $user): JsonResponse
    {
        $validated = $request->validated();

        if (isset($validated['password'])) {
            $validated['password'] = Hash::make($validated['password']);
        }

        $user->update($validated);

        return response()->json($user);
    }

    public function destroy(User $user): JsonResponse
    {
        $user->delete();

        return response()->json(['message' => 'User deleted successfully']);
    }
}

ルーティング (routes/api.php)

<?php

use App\Http\Controllers\UserController;

Route::apiResource('users', UserController::class);

バリデーションリクエスト (app/Http/Requests/StoreUserRequest.php)

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:8',
            'role' => 'required|in:admin,user,guest',
            'is_active' => 'sometimes|boolean'
        ];
    }

    public function messages(): array
    {
        return [
            'name.required' => '名前は必須です',
            'email.required' => 'メールアドレスは必須です',
            'email.email' => '有効なメールアドレスを入力してください',
            'email.unique' => 'このメールアドレスは既に使用されています',
            'password.required' => 'パスワードは必須です',
            'password.min' => 'パスワードは8文字以上で入力してください',
            'role.required' => 'ロールは必須です',
            'role.in' => 'ロールはadmin、user、guestのいずれかを選択してください'
        ];
    }
}

バリデーションリクエスト (app/Http/Requests/UpdateUserRequest.php)

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdateUserRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        $userId = $this->route('user')->id ?? null;

        return [
            'name' => 'sometimes|string|max:255',
            'email' => 'sometimes|string|email|max:255|unique:users,email,' . $userId,
            'password' => 'sometimes|string|min:8',
            'role' => 'sometimes|in:admin,user,guest',
            'is_active' => 'sometimes|boolean'
        ];
    }

    public function messages(): array
    {
        return [
            'name.string' => '名前は文字列で入力してください',
            'email.email' => '有効なメールアドレスを入力してください',
            'email.unique' => 'このメールアドレスは既に使用されています',
            'password.min' => 'パスワードは8文字以上で入力してください',
            'role.in' => 'ロールはadmin、user、guestのいずれかを選択してください',
            'is_active.boolean' => '有効/無効はtrue/falseで入力してください'
        ];
    }
}

2. テストコードの作成

プロンプト

user_management.mdを基に、Featureテストを作成してください。
全てのAPIエンドポイントに対するテストケースを含めてください。

生成されたファクトリー (database/factories/UserFactory.php)

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
 */
class UserFactory extends Factory
{
    /**
     * The current password being used by the factory.
     */
    protected static ?string $password;

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'role' => fake()->randomElement(['admin', 'user', 'guest']),
            'is_active' => fake()->boolean(80), // 80% chance of being active
            'remember_token' => Str::random(10),
        ];
    }

    /**
     * Indicate that the model's email address should be unverified.
     */
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }
}

生成されたテストコード (tests/Feature/UserManagementTest.php)

<?php

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;

class UserManagementTest extends TestCase
{
    use RefreshDatabase;

    public function test_can_get_users_list(): void
    {
        User::factory()->count(5)->create();

        $response = $this->getJson('/api/users');

        $response->assertStatus(200)
                ->assertJsonCount(5);
    }

    public function test_can_get_empty_users_list(): void
    {
        $response = $this->getJson('/api/users');

        $response->assertStatus(200)
                ->assertJsonCount(0);
    }

    public function test_can_get_single_user(): void
    {
        $user = User::factory()->create([
            'name' => 'Test User',
            'email' => 'test@example.com',
            'role' => 'user',
            'is_active' => true
        ]);

        $response = $this->getJson("/api/users/{$user->id}");

        $response->assertStatus(200)
                ->assertJsonPath('id', $user->id)
                ->assertJsonPath('name', 'Test User')
                ->assertJsonPath('email', 'test@example.com')
                ->assertJsonPath('role', 'user')
                ->assertJsonPath('is_active', true);
    }

    public function test_returns_404_for_non_existent_user(): void
    {
        $response = $this->getJson('/api/users/999');

        $response->assertStatus(404);
    }

    public function test_can_create_user_with_valid_data(): void
    {
        $userData = [
            'name' => 'New User',
            'email' => 'new@example.com',
            'password' => 'password123',
            'role' => 'user',
            'is_active' => true
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(201)
                ->assertJsonPath('name', 'New User')
                ->assertJsonPath('email', 'new@example.com')
                ->assertJsonPath('role', 'user')
                ->assertJsonPath('is_active', true)
                ->assertJsonMissing(['password']);

        $this->assertDatabaseHas('users', [
            'name' => 'New User',
            'email' => 'new@example.com',
            'role' => 'user',
            'is_active' => true
        ]);

        $user = User::where('email', 'new@example.com')->first();
        $this->assertTrue(Hash::check('password123', $user->password));
    }

    public function test_can_create_admin_user(): void
    {
        $userData = [
            'name' => 'Admin User',
            'email' => 'admin@example.com',
            'password' => 'password123',
            'role' => 'admin'
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(201)
                ->assertJsonPath('role', 'admin');
    }

    public function test_can_create_guest_user(): void
    {
        $userData = [
            'name' => 'Guest User',
            'email' => 'guest@example.com',
            'password' => 'password123',
            'role' => 'guest'
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(201)
                ->assertJsonPath('role', 'guest');
    }

    public function test_cannot_create_user_with_missing_required_fields(): void
    {
        $response = $this->postJson('/api/users', []);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['name', 'email', 'password', 'role']);
    }

    public function test_cannot_create_user_with_invalid_email(): void
    {
        $userData = [
            'name' => 'Test User',
            'email' => 'invalid-email',
            'password' => 'password123',
            'role' => 'user'
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['email']);
    }

    public function test_cannot_create_user_with_duplicate_email(): void
    {
        User::factory()->create(['email' => 'existing@example.com']);

        $userData = [
            'name' => 'Test User',
            'email' => 'existing@example.com',
            'password' => 'password123',
            'role' => 'user'
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['email']);
    }

    public function test_cannot_create_user_with_short_password(): void
    {
        $userData = [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => '123',
            'role' => 'user'
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['password']);
    }

    public function test_cannot_create_user_with_invalid_role(): void
    {
        $userData = [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password123',
            'role' => 'invalid_role'
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['role']);
    }

    public function test_can_update_user_with_valid_data(): void
    {
        $user = User::factory()->create([
            'name' => 'Original Name',
            'email' => 'original@example.com',
            'role' => 'user',
            'is_active' => true
        ]);

        $updateData = [
            'name' => 'Updated Name',
            'email' => 'updated@example.com',
            'role' => 'admin',
            'is_active' => false
        ];

        $response = $this->putJson("/api/users/{$user->id}", $updateData);

        $response->assertStatus(200)
                ->assertJsonPath('name', 'Updated Name')
                ->assertJsonPath('email', 'updated@example.com')
                ->assertJsonPath('role', 'admin')
                ->assertJsonPath('is_active', false);

        $this->assertDatabaseHas('users', [
            'id' => $user->id,
            'name' => 'Updated Name',
            'email' => 'updated@example.com',
            'role' => 'admin',
            'is_active' => false
        ]);
    }

    public function test_can_update_user_password(): void
    {
        $user = User::factory()->create();

        $updateData = [
            'password' => 'newpassword123'
        ];

        $response = $this->putJson("/api/users/{$user->id}", $updateData);

        $response->assertStatus(200);

        $user->refresh();
        $this->assertTrue(Hash::check('newpassword123', $user->password));
    }

    public function test_can_partial_update_user(): void
    {
        $user = User::factory()->create([
            'name' => 'Original Name',
            'email' => 'original@example.com'
        ]);

        $updateData = ['name' => 'Updated Name Only'];

        $response = $this->putJson("/api/users/{$user->id}", $updateData);

        $response->assertStatus(200)
                ->assertJsonPath('name', 'Updated Name Only')
                ->assertJsonPath('email', 'original@example.com');
    }

    public function test_cannot_update_user_with_duplicate_email(): void
    {
        $existingUser = User::factory()->create(['email' => 'existing@example.com']);
        $userToUpdate = User::factory()->create(['email' => 'update@example.com']);

        $updateData = ['email' => 'existing@example.com'];

        $response = $this->putJson("/api/users/{$userToUpdate->id}", $updateData);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['email']);
    }

    public function test_cannot_update_user_with_invalid_role(): void
    {
        $user = User::factory()->create();

        $updateData = ['role' => 'invalid_role'];

        $response = $this->putJson("/api/users/{$user->id}", $updateData);

        $response->assertStatus(422)
                ->assertJsonValidationErrors(['role']);
    }

    public function test_cannot_update_non_existent_user(): void
    {
        $updateData = ['name' => 'Updated Name'];

        $response = $this->putJson('/api/users/999', $updateData);

        $response->assertStatus(404);
    }

    public function test_can_delete_user(): void
    {
        $user = User::factory()->create();

        $response = $this->deleteJson("/api/users/{$user->id}");

        $response->assertStatus(200)
                ->assertJson(['message' => 'User deleted successfully']);

        $this->assertDatabaseMissing('users', ['id' => $user->id]);
    }

    public function test_cannot_delete_non_existent_user(): void
    {
        $response = $this->deleteJson('/api/users/999');

        $response->assertStatus(404);
    }

    public function test_user_factory_creates_valid_users(): void
    {
        $user = User::factory()->create();

        $this->assertDatabaseHas('users', [
            'id' => $user->id,
            'name' => $user->name,
            'email' => $user->email
        ]);

        $this->assertNotNull($user->role);
        $this->assertContains($user->role, ['admin', 'user', 'guest']);
        $this->assertIsBool($user->is_active);
    }
}

テスト実行結果

php artisan test tests/Feature/UserManagementTest.php

   PASS  Tests\Feature\UserManagementTest
  ✓ can get users list                                 1.97s
  ✓ can get empty users list                           0.03s
  ✓ can get single user                                0.04s
  ✓ returns 404 for non existent user                  0.04s
  ✓ can create user with valid data                    0.64s
  ✓ can create admin user                              0.25s
  ✓ can create guest user                              0.25s
  ✓ cannot create user with missing required fields    0.06s
  ✓ cannot create user with invalid email              0.05s
  ✓ cannot create user with duplicate email            0.05s
  ✓ cannot create user with short password             0.04s
  ✓ cannot create user with invalid role               0.03s
  ✓ can update user with valid data                    0.05s
  ✓ can update user password                           0.45s
  ✓ can partial update user                            0.04s
  ✓ cannot update user with duplicate email            0.04s
  ✓ cannot update user with invalid role               0.04s
  ✓ cannot update non existent user                    0.03s
  ✓ can delete user                                    0.04s
  ✓ cannot delete non existent user                    0.03s
  ✓ user factory creates valid users                   0.06s

  Tests:    21 passed (67 assertions)
  Duration: 5.07s

3. アプリケーション仕様の質問

プロンプト例

このLaravelアプリケーションで実装されている認証機能について説明してください。
特に以下の点について詳しく教えてください:
- 使用している認証パッケージ
- 認証フロー
- セキュリティ対策

出力結果

※前提:本サンプルでは Laravel Sanctum はインストール済みだが未適用の状態で確認しています。

・Laravel 12アプリケーションの認証機能について

使用している認証パッケージ

Laravel Sanctum v4.0 - composer.json:11
- Laravel 12のデフォルト認証パッケージ
- API認証とSPA認証の両方をサポート
- トークンベース認証とセッションベース認証の統合

認証フロー

1. 認証ガード設定 (config/auth.php:38-43):
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
],

2. ユーザープロバイダー (config/auth.php:62-66):
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
],

3. Sanctum設定 (config/sanctum.php):
- ステートフル認証: SPAアプリケーション用セッション認証
- ステートレス認証: APIトークン認証
- デフォルトガード: ['web']

4. 現在のAPI実装:
- /api/users エンドポイントで auth:sanctum ミドルウェア使用
- ユーザー管理機能は実装済みだが認証は未適用

セキュリティ対策

1. パスワードセキュリティ:
- パスワードハッシュ化(Hash::make())
- 最小8文字のパスワード要求

2. セッションセキュリティ (config/session.php):
- セッションドライバー: database(デフォルト)
- セッション有効期限: 120分
- セッション暗号化オプション

3. パスワードリセット (config/auth.php:93-100):
- トークン有効期限: 60分
- スロットリング: 60秒
- password_reset_tokensテーブル使用

4. Sanctumセキュリティ機能:
- CSRF保護
- Cookie暗号化
- トークンプレフィックス(セキュリティスキャン対応)
- ステートフルドメイン制限

5. アプリケーションレベル:
- デバッグモード制御(本番環境では無効)
- 環境変数による設定管理

認証の現状と推奨事項

現在の状態:
- 認証パッケージ(Sanctum)は導入済み
- UserControllerは作成済みだが認証ミドルウェア未適用
- 基本的な認証設定は完了

認証を完全実装するために必要:
1. APIルートに認証ミドルウェア追加
2. ログイン/ログアウトエンドポイント作成
3. トークン発行機能実装
4. 認証済みユーザー情報取得エンドポイント

4. ドキュメント作成

プロンプト

実装したユーザー管理APIのOpenAPI仕様書を作成してください。

生成されたOpenAPI仕様書 (openapi.yaml)

openapi: 3.0.3
info:
  title: User Management API
  description: Laravel 12 ユーザー管理API
  version: 1.0.0
  contact:
    name: API Support
    email: support@example.com

servers:
  - url: http://localhost:8000/api
    description: Development server
  - url: https://api.example.com
    description: Production server

security:
  - sanctumAuth: []

paths:
  /users:
    get:
      summary: ユーザー一覧取得
      description: 全ユーザーの一覧を取得します
      tags:
        - Users
      responses:
        '200':
          description: ユーザー一覧の取得に成功
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
              example:
                - id: 1
                  name: "田中太郎"
                  email: "tanaka@example.com"
                  role: "user"
                  is_active: true
                  created_at: "2024-01-01T00:00:00.000000Z"
                  updated_at: "2024-01-01T00:00:00.000000Z"
                - id: 2
                  name: "佐藤花子"
                  email: "sato@example.com"
                  role: "admin"
                  is_active: true
                  created_at: "2024-01-02T00:00:00.000000Z"
                  updated_at: "2024-01-02T00:00:00.000000Z"
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      summary: ユーザー作成
      description: 新しいユーザーを作成します
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserCreateRequest'
            example:
              name: "山田次郎"
              email: "yamada@example.com"
              password: "password123"
              role: "user"
              is_active: true
      responses:
        '201':
          description: ユーザーの作成に成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
              example:
                id: 3
                name: "山田次郎"
                email: "yamada@example.com"
                role: "user"
                is_active: true
                created_at: "2024-01-03T00:00:00.000000Z"
                updated_at: "2024-01-03T00:00:00.000000Z"
        '422':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /users/{id}:
    get:
      summary: ユーザー詳細取得
      description: 指定されたIDのユーザー詳細を取得します
      tags:
        - Users
      parameters:
        - name: id
          in: path
          required: true
          description: ユーザーID
          schema:
            type: integer
            minimum: 1
          example: 1
      responses:
        '200':
          description: ユーザー詳細の取得に成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
              example:
                id: 1
                name: "田中太郎"
                email: "tanaka@example.com"
                role: "user"
                is_active: true
                created_at: "2024-01-01T00:00:00.000000Z"
                updated_at: "2024-01-01T00:00:00.000000Z"
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'

    put:
      summary: ユーザー更新
      description: 指定されたIDのユーザー情報を更新します
      tags:
        - Users
      parameters:
        - name: id
          in: path
          required: true
          description: ユーザーID
          schema:
            type: integer
            minimum: 1
          example: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserUpdateRequest'
            example:
              name: "田中太郎(更新)"
              email: "tanaka.updated@example.com"
              role: "admin"
              is_active: false
      responses:
        '200':
          description: ユーザーの更新に成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
              example:
                id: 1
                name: "田中太郎(更新)"
                email: "tanaka.updated@example.com"
                role: "admin"
                is_active: false
                created_at: "2024-01-01T00:00:00.000000Z"
                updated_at: "2024-01-03T12:00:00.000000Z"
        '422':
          $ref: '#/components/responses/ValidationError'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'

    delete:
      summary: ユーザー削除
      description: 指定されたIDのユーザーを削除します
      tags:
        - Users
      parameters:
        - name: id
          in: path
          required: true
          description: ユーザーID
          schema:
            type: integer
            minimum: 1
          example: 1
      responses:
        '200':
          description: ユーザーの削除に成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResponse'
              example:
                message: "User deleted successfully"
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'

components:
  securitySchemes:
    sanctumAuth:
      type: http
      scheme: bearer
      bearerFormat: token
      description: Laravel Sanctum認証トークン

  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          description: ユーザーID
          example: 1
        name:
          type: string
          description: ユーザー名
          maxLength: 255
          example: "田中太郎"
        email:
          type: string
          format: email
          description: メールアドレス
          maxLength: 255
          example: "tanaka@example.com"
        role:
          type: string
          enum: [admin, user, guest]
          description: ユーザーロール
          example: "user"
        is_active:
          type: boolean
          description: アクティブ状態
          example: true
        email_verified_at:
          type: string
          format: date-time
          nullable: true
          description: メール認証日時
          example: "2024-01-01T00:00:00.000000Z"
        created_at:
          type: string
          format: date-time
          description: 作成日時
          example: "2024-01-01T00:00:00.000000Z"
        updated_at:
          type: string
          format: date-time
          description: 更新日時
          example: "2024-01-01T00:00:00.000000Z"
      required:
        - id
        - name
        - email
        - role
        - is_active
        - created_at
        - updated_at

    UserCreateRequest:
      type: object
      properties:
        name:
          type: string
          description: ユーザー名
          maxLength: 255
          example: "山田次郎"
        email:
          type: string
          format: email
          description: メールアドレス(一意)
          maxLength: 255
          example: "yamada@example.com"
        password:
          type: string
          description: パスワード
          minLength: 8
          example: "password123"
        role:
          type: string
          enum: [admin, user, guest]
          description: ユーザーロール
          example: "user"
        is_active:
          type: boolean
          description: アクティブ状態
          default: true
          example: true
      required:
        - name
        - email
        - password
        - role

    UserUpdateRequest:
      type: object
      properties:
        name:
          type: string
          description: ユーザー名
          maxLength: 255
          example: "田中太郎(更新)"
        email:
          type: string
          format: email
          description: メールアドレス(一意)
          maxLength: 255
          example: "tanaka.updated@example.com"
        password:
          type: string
          description: パスワード
          minLength: 8
          example: "newpassword123"
        role:
          type: string
          enum: [admin, user, guest]
          description: ユーザーロール
          example: "admin"
        is_active:
          type: boolean
          description: アクティブ状態
          example: false
      description: 全てのフィールドは任意です(部分更新対応)

    DeleteResponse:
      type: object
      properties:
        message:
          type: string
          description: 削除完了メッセージ
          example: "User deleted successfully"
      required:
        - message

    ValidationError:
      type: object
      properties:
        message:
          type: string
          description: エラーメッセージ
          example: "The given data was invalid."
        errors:
          type: object
          description: フィールド別エラー詳細
          additionalProperties:
            type: array
            items:
              type: string
          example:
            email:
              - "このメールアドレスは既に使用されています"
            password:
              - "パスワードは8文字以上で入力してください"

    Error:
      type: object
      properties:
        message:
          type: string
          description: エラーメッセージ
      required:
        - message

  responses:
    ValidationError:
      description: バリデーションエラー
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'
          example:
            message: "The given data was invalid."
            errors:
              email:
                - "このメールアドレスは既に使用されています"
              role:
                - "ロールはadmin、user、guestのいずれかを選択してください"

    NotFound:
      description: リソースが見つかりません
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: "No query results for model [App\\Models\\User] 999"

    Unauthorized:
      description: 認証が必要です
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: "Unauthenticated."

tags:
  - name: Users
    description: ユーザー管理機能

5. コードレビュー

プロンプト

実装したユーザー管理APIをレビューして、改善点を指摘してください

レビュー結果

・ユーザー管理APIレビュー結果

◆ 重要な改善点

1. セキュリティの欠如

- 認証ミドルウェア未適用 (routes/api.php:5)
Route::apiResource('users', UserController::class);
- 問題: 誰でもユーザー情報の閲覧・作成・削除が可能
修正: ->middleware('auth:sanctum')を追加

2. 認可制御の未実装

- 問題: 一般ユーザーが他ユーザーを削除できる
- 修正: ロールベース認可またはPolicy実装が必要

3. N+1クエリの可能性

- 問題: User::all() (UserController.php:15)でページネーション未実装
- 修正: paginate()またはsimplePaginate()使用

◆ 中程度の改善点

4. パスワード強度検証

// StoreUserRequest.php:19
'password' => 'required|string|min:8',
改善: 複雑性要件追加(大小文字、数字、記号)

5. エラーハンドリング不足

- データベース例外処理未実装
- トランザクション未使用

6. レスポンス一貫性

- 削除時のレスポンス形式が他と異なる
- HTTPステータスコードの統一化

◆ 軽微な改善点

7. コード品質

- DocBlockコメント不足
- 型ヒント強化の余地

8. バリデーション

// UpdateUserRequest.php:16
$userId = $this->route('user')->id ?? null;
改善: より安全な取得方法

9. ファクトリー設定

// UserFactory.php:32
'is_active' => fake()->boolean(80),
問題: テスト時の予測困難性

◆ 修正優先度

最優先(セキュリティ):
1. 認証ミドルウェア追加
2. 認可制御実装

高優先度(パフォーマンス):
3. ページネーション実装
4. N+1クエリ対策

中優先度(堅牢性):
5. エラーハンドリング強化
6. パスワード強度向上

低優先度(保守性):
7. コード品質向上
8. ドキュメント充実

推奨実装

最低限必要な修正:
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('users', UserController::class);
});

// UserController.php
public function index(): JsonResponse {
    $users = User::paginate(15);
    return response()->json($users);
}

現在の実装は基本機能は動作しますが、セキュリティ面で本番環境使用不可です。認証・認可の実装が最優先事項です。

6. コードレビューにより提案された改善点を修正する

プロンプト

提案された改善点を修正してください

修正結果

・改善点修正完了報告

実装された改善点:

◆ 最優先(セキュリティ)

1. 認証ミドルウェア追加 (routes/api.php)
Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('users', UserController::class);
});

2. 認可制御実装 (app/Policies/UserPolicy.php)
- 管理者のみユーザー一覧・作成可能
- 本人または管理者のみ詳細表示・更新可能
- 管理者のみ削除可能(自分以外)
- 各コントローラーメソッドに$this->authorize()追加

◆ 高優先度(パフォーマンス)

3. ページネーション実装 (UserController.php:18-24)
$users = User::select(['id', 'name', 'email', 'role', 'is_active', 'created_at', 'updated_at'])
            ->paginate(15);
- N+1クエリ対策のため必要フィールドのみ選択
- 15件ずつのページネーション

4. エラーハンドリング強化
- 全CRUD操作にtry-catch追加
- 適切なHTTPステータスコード返却
- 日本語エラーメッセージ

◆ 中優先度(堅牢性)

5. パスワード強度向上
'password' => [
    'required', 'string', 'min:8',
    'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/',
],
- 大文字・小文字・数字・記号を各1文字以上必須

6. UpdateUserRequestの安全性向上
$user = $this->route('user');
$userId = $user instanceof \App\Models\User ? $user->id : null;

7. レスポンス形式統一
- 削除成功メッセージを日本語化
- エラーレスポンス形式統一

8. DocBlockコメント追加
- 全コントローラーメソッドに詳細説明
- パラメータ・戻り値の型情報

9. UserFactory改善
- テスト予測性向上(デフォルト: role='user', is_active=true)
- 強化されたパスワード('Password123!')
- 便利なstate methods追加(admin(), guest(), inactive())

◆ セキュリティ強化結果

Before: 誰でもアクセス可能
After:
- 認証必須
- ロールベース認可
- 自分のデータのみアクセス可能(管理者除く)

◆ パフォーマンス向上

Before: User::all() - 全データ取得
After: paginate(15) + 必要フィールドのみ選択

◆ データ保護

Before: 弱いパスワード許可
After: 複雑性要件強制

実装が完了し、本番環境でのセキュリティ要件を満たすユーザー管理APIとなりました。

更に応用的な使用方法

※GitHub CLI を事前にインストールしておく。
https://github.com/cli/cli#installation

1. GitHub Issueに記載の内容をClaude Codeで実行

Issueの内容:

# タスク管理機能の実装

# 概要
タスクを管理するためのREST APIを実装する。

# 機能要件
- タスクの作成、更新、削除、一覧表示
- タスクの担当者アサイン
- ステータス管理(未着手、進行中、レビュー中、完了)
- 優先度設定(高、中、低)
- 期限管理

# API仕様

## タスク一覧取得
- エンドポイント: GET /api/tasks
- クエリパラメータ:
  - status: ステータスでフィルター
  - assignee_id: 担当者IDでフィルター
  - priority: 優先度でフィルター
  - due_date_from: 期限開始日
  - due_date_to: 期限終了日

## タスク作成
- エンドポイント: POST /api/tasks
- リクエストボディ:
  - title: タスクタイトル(必須、最大100文字)
  - description: タスク詳細(任意、最大1000文字)
  - assignee_id: 担当者ID(任意)
  - priority: 優先度(必須、high/medium/low)
  - due_date: 期限(任意、YYYY-MM-DD形式)

プロンプト (実装方法の確認)

GitHub Issue #1 の内容を確認して、実装方法を提案してください

出力結果 (実装方法の確認)

・タスク管理機能の実装提案

現在のプロジェクト分析:Laravel12を使用したプロジェクトで、既にUserモデルとAPIリソースが実装済み。

実装アプローチ:

1. データベース設計

tasks テーブル:
- id (Primary Key)
- title (varchar 100, required)
- description (text 1000, nullable)
- assignee_id (foreign key to users, nullable)
- priority (enum: high/medium/low)
- status (enum: pending/in_progress/review/completed)
- due_date (date, nullable)
- created_at, updated_at

2. 実装ステップ

1. Taskモデル作成 - Eloquent関係とバリデーション定義
2. マイグレーション作成 - データベーステーブル定義
3. TaskController作成 - REST API実装(CRUD + フィルタリング)
4. TaskResource作成 - API レスポンス形式統一
5. ルート設定 - /api/tasks エンドポイント追加
6. テスト作成 - Feature テスト実装

3. 技術仕様

- フレームワーク: Laravel 12
- 認証: Laravel Sanctum使用
- レスポンス形式: 既存のApiResponseトレイト活用
- バリデーション: FormRequestクラス使用
- テスト: PHPUnit Feature テスト

プロンプト (機能の実装)

GitHub Issue #1 内容を確認して、機能を実装してください

出力結果 (機能の実装)

・GitHub Issue #1 タスク管理機能の実装完了

実装した機能:

◆ 作成されたファイル

- マイグレーション:
database/migrations/2025_06_17_180000_create_tasks_table.php
- モデル: app/Models/Task.php
- コントローラー: app/Http/Controllers/TaskController.php
- リソース: app/Http/Resources/TaskResource.php
- リクエスト: app/Http/Requests/StoreTaskRequest.php, UpdateTaskRequest.php
- ファクトリー: database/factories/TaskFactory.php
- テスト: tests/Feature/TaskManagementTest.php

◆ 実装されたAPI

- GET /api/tasks - タスク一覧(フィルタリング対応)
- POST /api/tasks - タスク作成
- GET /api/tasks/{id} - タスク詳細
- PUT/PATCH /api/tasks/{id} - タスク更新
- DELETE /api/tasks/{id} - タスク削除

◆ 主要機能

- タスクの作成、更新、削除、一覧表示
- ユーザーアサイン機能
- ステータス管理(pending, in_progress, review, completed)
- 優先度設定(high, medium, low)
- 期限管理
- フィルタリング(status, assignee_id, priority, due_date)
- バリデーション(title最大100文字、description最大1000文字)
- 包括的なテストスイート

全ての要件がIssue #1の仕様通りに実装されています。

2. プルリクエストの作成

プロンプト

実装した内容でプルリクエストを作成してください。
タイトル: "feature: タスク管理機能の実装"
説明には実装内容と動作確認方法を含めてください。

作成されたプルリクエストの内容

## Summary
GitHub Issue #1 の要件に基づき、完全なタスク管理REST APIを実装しました。

• Task モデルとマイグレーション作成
• TaskController による CRUD 操作と高度なフィルタリング機能
• TaskResource による統一されたAPI レスポンス形式
• バリデーション機能 (StoreTaskRequest, UpdateTaskRequest)
• 包括的な Feature テスト実装

## 実装されたAPI エンドポイント
- `GET /api/tasks` - タスク一覧取得(フィルタリング対応)
- `POST /api/tasks` - タスク新規作成
- `GET /api/tasks/{id}` - タスク詳細取得
- `PUT/PATCH /api/tasks/{id}` - タスク更新
- `DELETE /api/tasks/{id}` - タスク削除

## 主要機能
- タスクの作成、更新、削除、一覧表示
- ユーザーアサイン機能 (User モデルとのリレーション)
- ステータス管理 (pending, in_progress, review, completed)
- 優先度設定 (high, medium, low)
- 期限管理 (due_date)
- 高度なフィルタリング (status, assignee_id, priority, due_date_from/to)
- データバリデーション (title最大100文字、description最大1000文字)

## Test plan
- [ ] マイグレーション実行: `php artisan migrate`
- [ ] Feature テスト実行: `php artisan test tests/Feature/TaskManagementTest.php`
- [ ] API エンドポイントの動作確認
  - [ ] タスク一覧取得 `GET /api/tasks`
  - [ ] タスク作成 `POST /api/tasks`
  - [ ] フィルタリング機能確認 `GET /api/tasks?status=pending&priority=high`
  - [ ] タスク更新 `PUT /api/tasks/{id}`
  - [ ] タスク削除 `DELETE /api/tasks/{id}`
- [ ] Sanctum認証の動作確認
- [ ] バリデーションエラーハンドリングの確認

Generated with [Claude Code](https://claude.ai/code)

今後の展望

  • Claude Code ActionおよびGithub Actionsを用いたCI/CDの自動化
  • コードレビューの完全自動化
  • 複雑なリファクタリングタスクの自動実行
  • プロジェクト全体の設計最適化提案

まとめ

DX開発事業部では、Claude CodeとVSCodeの完全統合により、以下を実現しました:

  1. 自然な日本語での開発: VSCode内で日本語プロンプトによる直感的な操作
  2. 設計駆動開発: 設計書を単一の真実の源として活用
  3. 完全自動化: Issueからコード生成、PR作成、マージまでの一連の流れ
  4. 品質向上: AIによるコードレビューとテスト自動生成

Google Cloud の Vertex AI を活用することで、Claude Code や Claude Code Action をセキュアに導入し、AI駆動開発を強力に推進できます!
GitHub Actions との連携もスムーズで、専用の Google Cloud プロジェクトと Workload Identity 連携を設定すれば、チーム全体で安全かつ効率的な開発環境をすぐに構築可能です。
今後も Claude Code の新機能を積極的に活用し、さらなる開発効率化を目指していきます。

参考リンク

免責事項

この記事は、Claude Codeの公式機能に基づいて作成されました。最新の機能や詳細については、公式ドキュメントをご確認ください。

最後に

本記事はしっかりと人の目を通して記事のレビューおよび加筆を行っておりますのでご安心ください。

また、DX開発事業部では今回の爆速導入の例のようにAIを開発に取り入れるといった先進的な活動を物凄く積極的に行っています!
今回はClaude4のリリースから“1週間”“全エンジニア”がClaudeCodeを利用できるように導入しました!
ぜひ!ご興味ありましたらカジュアル面談でお話できたらと思います!