𝕏 f B! L
案件・求人数 12,345
案件を探す(準備中) エージェントを探す(準備中) お役立ち情報 ログイン
案件・求人数 12,345
Claude CodeでgRPC APIを設計・実装する方法|Protocol Buffers定義から負荷テストまで

Claude CodeでgRPC APIを設計・実装する方法|Protocol Buffers定義から負荷テストまで

Claude CodegRPCAPI開発Protocol Buffersマイクロサービス
目次

REST APIは広く普及していますが、マイクロサービス間通信やリアルタイムデータストリーミングでは、gRPCの方が圧倒的に有利なケースがあります。しかしgRPCの学習コストは高く、Protocol Buffersの定義やコード生成の仕組みに慣れるまで時間がかかるのも事実です。

本記事では、Claude Codeを活用してgRPC APIの設計から実装、テスト、運用まで効率的に行う方法を解説します。Claude Code 完全攻略シリーズの一環として、AIがgRPC開発にどのようなブレークスルーをもたらすか、実践的なコード例とともにお伝えします。

Claude CodeでのgRPC API開発プロセス

gRPCとは何か:REST APIとの違いを理解する

gRPC(Google Remote Procedure Call)は、Googleが開発した高性能なRPCフレームワークです。HTTP/2をトランスポート層に使い、Protocol Buffers(protobuf)をインターフェース定義言語(IDL)およびシリアライゼーションフォーマットとして採用しています。

gRPCの主な特徴

  • 高パフォーマンス: バイナリシリアライゼーションにより、JSONと比較して最大10倍の高速化を実現
  • 型安全: Protocol Buffersによる厳密な型定義で、コンパイル時にエラーを検出
  • 双方向ストリーミング: HTTP/2ベースでクライアント・サーバー間の双方向通信が可能
  • コード自動生成: .protoファイルから各言語のクライアント・サーバーコードを自動生成
  • 多言語対応: Go、Java、Python、Node.js、C++など主要言語をサポート

REST API vs gRPC:比較表

項目REST APIgRPC
プロトコルHTTP/1.1HTTP/2
データ形式JSON(テキスト)Protocol Buffers(バイナリ)
型定義OpenAPI/Swagger(任意)Protocol Buffers(必須)
ストリーミングWebSocket別途必要ネイティブサポート
ブラウザ対応◎ 完全対応△ gRPC-Web経由
パフォーマンス標準的高速(2〜10倍)
学習コスト低いやや高い
デバッグ容易性curl等で簡単専用ツール必要

SES案件では、マイクロサービスアーキテクチャを採用するプロジェクトでgRPCの需要が急増しています。2026年のSESエンジニア市場では、gRPCスキルを持つエンジニアの単価が平均15〜20%高い傾向にあります。

Claude CodeでgRPC開発環境を構築する

Claude Codeを使えば、gRPC開発環境のセットアップからProtocol Buffers定義まで、対話形式で効率的に進めることができます。

環境セットアップの自動化

# Claude Codeに環境構築を依頼
claude "Go言語でgRPCサーバーを開発したい。
protoc、protoc-gen-go、protoc-gen-go-grpcをインストールして、
プロジェクト構造を作成してください。
ディレクトリ構成は以下を想定:
- proto/: .protoファイル
- gen/: 自動生成コード
- server/: サーバー実装
- client/: クライアント実装
- internal/: ビジネスロジック"

Claude Codeは以下のようなプロジェクト構造を生成します。

grpc-service/
├── proto/
│   ├── user/
│   │   └── v1/
│   │       └── user.proto
│   └── buf.yaml
├── gen/
│   └── user/
│       └── v1/
│           ├── user.pb.go
│           └── user_grpc.pb.go
├── server/
│   └── main.go
├── client/
│   └── main.go
├── internal/
│   ├── service/
│   │   └── user_service.go
│   └── repository/
│       └── user_repository.go
├── Makefile
├── go.mod
└── buf.gen.yaml

CLAUDE.mdにgRPC開発ルールを設定する

プロジェクトのルートにCLAUDE.mdを配置して、gRPC開発のコーディング規約をClaude Codeに理解させましょう。

# gRPCプロジェクト開発ルール

## Protocol Buffers
- proto3構文を使用すること
- パッケージ名は`<service>.<version>`形式(例: user.v1)
- フィールド番号は予約を考慮して1-15を頻出フィールドに割り当てる
- Deprecatedフィールドはreservedで管理する

## コード生成
- `buf generate`でコード生成を行う
- 生成コードは`gen/`配下に出力し、手動編集しない
- 生成コードはGitにコミットする

## サーバー実装
- UnaryInterceptorでログ・認証・エラーハンドリングを統一する
- ヘルスチェックエンドポイントを必ず実装する
- Graceful shutdownを実装する

## エラーハンドリング
- gRPCステータスコードを正しく使い分ける
- エラー詳細にはerrdetailsパッケージを使用する

Protocol Buffersの設計:Claude Codeで型安全なAPI定義を作る

Protocol Buffersの設計は、gRPC APIの品質を左右する最も重要なフェーズです。Claude Codeは、ビジネス要件からprotobuf定義を自動生成するだけでなく、設計上のベストプラクティスも提案してくれます。

ユーザー管理APIのproto定義

claude "以下の要件でユーザー管理APIのProtocol Buffers定義を作成してください:
- ユーザーCRUD操作
- ページネーション付きリスト取得
- フィールドマスクによる部分更新
- バリデーションルール付き
- 将来の拡張性を考慮したフィールド番号設計"

Claude Codeが生成する高品質なproto定義の例:

syntax = "proto3";

package user.v1;

option go_package = "github.com/example/gen/user/v1;userv1";

import "google/protobuf/timestamp.proto";
import "google/protobuf/field_mask.proto";
import "google/api/annotations.proto";

// ユーザーサービス定義
service UserService {
  // ユーザー作成
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  
  // ユーザー取得
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  
  // ユーザーリスト取得(ページネーション付き)
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
  
  // ユーザー更新(部分更新対応)
  rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
  
  // ユーザー削除(ソフトデリート)
  rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
  
  // ユーザーストリーム(サーバーストリーミング)
  rpc WatchUsers(WatchUsersRequest) returns (stream UserEvent);
}

// ユーザーリソース
message User {
  // ユーザーID(UUID形式)
  string id = 1;
  
  // メールアドレス(一意制約)
  string email = 2;
  
  // 表示名
  string display_name = 3;
  
  // ユーザーの役割
  UserRole role = 4;
  
  // ステータス
  UserStatus status = 5;
  
  // プロフィール情報
  UserProfile profile = 6;
  
  // 作成日時
  google.protobuf.Timestamp created_at = 14;
  
  // 更新日時
  google.protobuf.Timestamp updated_at = 15;
}

// ユーザー役割
enum UserRole {
  USER_ROLE_UNSPECIFIED = 0;
  USER_ROLE_ADMIN = 1;
  USER_ROLE_MEMBER = 2;
  USER_ROLE_VIEWER = 3;
}

// ユーザーステータス
enum UserStatus {
  USER_STATUS_UNSPECIFIED = 0;
  USER_STATUS_ACTIVE = 1;
  USER_STATUS_SUSPENDED = 2;
  USER_STATUS_DELETED = 3;
}

// ユーザープロフィール
message UserProfile {
  string avatar_url = 1;
  string bio = 2;
  string company = 3;
  string location = 4;
}

// リクエスト・レスポンスメッセージ
message CreateUserRequest {
  string email = 1;
  string display_name = 2;
  UserRole role = 3;
  UserProfile profile = 4;
}

message CreateUserResponse {
  User user = 1;
}

message GetUserRequest {
  string id = 1;
}

message GetUserResponse {
  User user = 1;
}

message ListUsersRequest {
  int32 page_size = 1;
  string page_token = 2;
  string filter = 3;
  string order_by = 4;
}

message ListUsersResponse {
  repeated User users = 1;
  string next_page_token = 2;
  int32 total_count = 3;
}

message UpdateUserRequest {
  User user = 1;
  google.protobuf.FieldMask update_mask = 2;
}

message UpdateUserResponse {
  User user = 1;
}

message DeleteUserRequest {
  string id = 1;
}

message DeleteUserResponse {}

message WatchUsersRequest {
  string filter = 1;
}

message UserEvent {
  enum EventType {
    EVENT_TYPE_UNSPECIFIED = 0;
    EVENT_TYPE_CREATED = 1;
    EVENT_TYPE_UPDATED = 2;
    EVENT_TYPE_DELETED = 3;
  }
  EventType type = 1;
  User user = 2;
  google.protobuf.Timestamp occurred_at = 3;
}

Claude Codeが提案するproto設計のベストプラクティス

Claude Codeに「このproto定義をレビューして」と依頼すると、以下のような改善提案をしてくれます。

  1. フィールド番号の戦略的配置: 1-15は1バイトでエンコードされるため、頻出フィールドに割り当てる
  2. enum値のUNSPECIFIED: 全てのenumの0番にUNSPECIFIEDを設定し、未設定状態を明示する
  3. FieldMaskの活用: PATCH的な部分更新にはFieldMaskパターンを使用する
  4. ページネーションパターン: Google AIP-158に準拠したカーソルベースのページネーションを推奨
  5. バージョニング: パッケージ名にv1を含め、後方互換性を保ちながらAPIを進化させる

gRPCサーバー実装:Claude Codeで堅牢なサーバーを構築する

Interceptorによる横断的関心事の処理

claude "gRPCサーバーのインターセプターを実装してください:
1. ロギング(リクエスト/レスポンスのメタデータとレイテンシ)
2. 認証(JWTトークン検証)
3. リカバリー(パニックからの復帰)
4. メトリクス(Prometheus用)
各インターセプターは独立してテスト可能な設計にしてください"

Claude Codeが生成するインターセプター実装:

package interceptor

import (
    "context"
    "time"

    "go.uber.org/zap"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"
)

// LoggingInterceptor はリクエスト/レスポンスのログを記録する
func LoggingInterceptor(logger *zap.Logger) grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,
        req interface{},
        info *grpc.UnaryServerInfo,
        handler grpc.UnaryHandler,
    ) (interface{}, error) {
        start := time.Now()
        
        // メタデータからリクエストIDを取得
        md, _ := metadata.FromIncomingContext(ctx)
        requestID := ""
        if vals := md.Get("x-request-id"); len(vals) > 0 {
            requestID = vals[0]
        }
        
        // ハンドラー実行
        resp, err := handler(ctx, req)
        
        // レイテンシ計算
        duration := time.Since(start)
        
        // ログ出力
        fields := []zap.Field{
            zap.String("method", info.FullMethod),
            zap.Duration("duration", duration),
            zap.String("request_id", requestID),
        }
        
        if err != nil {
            st, _ := status.FromError(err)
            fields = append(fields, 
                zap.String("code", st.Code().String()),
                zap.String("message", st.Message()),
            )
            logger.Error("gRPC request failed", fields...)
        } else {
            fields = append(fields, zap.String("code", codes.OK.String()))
            logger.Info("gRPC request completed", fields...)
        }
        
        return resp, err
    }
}

// AuthInterceptor はJWTトークンを検証する
func AuthInterceptor(verifier TokenVerifier) grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,
        req interface{},
        info *grpc.UnaryServerInfo,
        handler grpc.UnaryHandler,
    ) (interface{}, error) {
        // ヘルスチェックは認証スキップ
        if info.FullMethod == "/grpc.health.v1.Health/Check" {
            return handler(ctx, req)
        }
        
        md, ok := metadata.FromIncomingContext(ctx)
        if !ok {
            return nil, status.Error(codes.Unauthenticated, "missing metadata")
        }
        
        tokens := md.Get("authorization")
        if len(tokens) == 0 {
            return nil, status.Error(codes.Unauthenticated, "missing authorization token")
        }
        
        claims, err := verifier.Verify(tokens[0])
        if err != nil {
            return nil, status.Error(codes.Unauthenticated, "invalid token")
        }
        
        // コンテキストにユーザー情報を追加
        ctx = context.WithValue(ctx, userClaimsKey{}, claims)
        return handler(ctx, req)
    }
}

// RecoveryInterceptor はパニックから復帰する
func RecoveryInterceptor(logger *zap.Logger) grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,
        req interface{},
        info *grpc.UnaryServerInfo,
        handler grpc.UnaryHandler,
    ) (resp interface{}, err error) {
        defer func() {
            if r := recover(); r != nil {
                logger.Error("panic recovered",
                    zap.Any("panic", r),
                    zap.String("method", info.FullMethod),
                )
                err = status.Error(codes.Internal, "internal server error")
            }
        }()
        return handler(ctx, req)
    }
}

サービス実装のパターン

package service

import (
    "context"
    
    userv1 "github.com/example/gen/user/v1"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
    "google.golang.org/protobuf/types/known/timestamppb"
)

type UserService struct {
    userv1.UnimplementedUserServiceServer
    repo UserRepository
}

func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}
}

func (s *UserService) CreateUser(
    ctx context.Context,
    req *userv1.CreateUserRequest,
) (*userv1.CreateUserResponse, error) {
    // バリデーション
    if req.Email == "" {
        return nil, status.Error(codes.InvalidArgument, "email is required")
    }
    if req.DisplayName == "" {
        return nil, status.Error(codes.InvalidArgument, "display_name is required")
    }
    
    // 重複チェック
    existing, err := s.repo.FindByEmail(ctx, req.Email)
    if err != nil {
        return nil, status.Error(codes.Internal, "failed to check email")
    }
    if existing != nil {
        return nil, status.Error(codes.AlreadyExists, "email already registered")
    }
    
    // ユーザー作成
    user, err := s.repo.Create(ctx, &User{
        Email:       req.Email,
        DisplayName: req.DisplayName,
        Role:        req.Role,
        Profile:     req.Profile,
    })
    if err != nil {
        return nil, status.Error(codes.Internal, "failed to create user")
    }
    
    return &userv1.CreateUserResponse{
        User: toProtoUser(user),
    }, nil
}

func (s *UserService) ListUsers(
    ctx context.Context,
    req *userv1.ListUsersRequest,
) (*userv1.ListUsersResponse, error) {
    pageSize := int(req.PageSize)
    if pageSize <= 0 || pageSize > 100 {
        pageSize = 20
    }
    
    users, nextToken, total, err := s.repo.List(ctx, ListParams{
        PageSize:  pageSize,
        PageToken: req.PageToken,
        Filter:    req.Filter,
        OrderBy:   req.OrderBy,
    })
    if err != nil {
        return nil, status.Error(codes.Internal, "failed to list users")
    }
    
    protoUsers := make([]*userv1.User, len(users))
    for i, u := range users {
        protoUsers[i] = toProtoUser(u)
    }
    
    return &userv1.ListUsersResponse{
        Users:         protoUsers,
        NextPageToken: nextToken,
        TotalCount:    int32(total),
    }, nil
}

gRPCストリーミング:Claude Codeでリアルタイム通信を実装する

gRPCの最大の強みの一つが、4種類の通信パターンをネイティブでサポートしていることです。

4つの通信パターン

  1. Unary RPC: 1リクエスト→1レスポンス(通常のAPI呼び出し)
  2. Server Streaming: 1リクエスト→Nレスポンス(リアルタイム通知)
  3. Client Streaming: Nリクエスト→1レスポンス(ファイルアップロード)
  4. Bidirectional Streaming: N対N(チャット、双方向データ同期)

サーバーストリーミングの実装例

claude "ユーザーの変更をリアルタイムで通知するServer Streaming RPCを実装してください。
以下の要件を満たすこと:
- チャネルベースのイベント配信
- フィルタリング機能
- クライアント切断時のクリーンアップ
- バックプレッシャー対応"
func (s *UserService) WatchUsers(
    req *userv1.WatchUsersRequest,
    stream userv1.UserService_WatchUsersServer,
) error {
    // イベントチャネルを購読
    eventCh := s.eventBus.Subscribe(req.Filter)
    defer s.eventBus.Unsubscribe(eventCh)
    
    for {
        select {
        case event := <-eventCh:
            if err := stream.Send(event); err != nil {
                // クライアント切断
                return status.Error(codes.Canceled, "client disconnected")
            }
        case <-stream.Context().Done():
            // コンテキストキャンセル
            return stream.Context().Err()
        }
    }
}

双方向ストリーミングの実装例

// チャット機能の双方向ストリーミング
func (s *ChatService) Chat(
    stream chatv1.ChatService_ChatServer,
) error {
    // ユーザー認証
    claims, err := extractClaims(stream.Context())
    if err != nil {
        return status.Error(codes.Unauthenticated, "unauthorized")
    }
    
    // チャットルームに参加
    room := s.rooms.Join(claims.UserID)
    defer s.rooms.Leave(claims.UserID)
    
    // 受信ゴルーチン
    errCh := make(chan error, 1)
    go func() {
        for {
            msg, err := stream.Recv()
            if err != nil {
                errCh <- err
                return
            }
            room.Broadcast(claims.UserID, msg)
        }
    }()
    
    // 送信ループ
    for {
        select {
        case msg := <-room.Messages(claims.UserID):
            if err := stream.Send(msg); err != nil {
                return err
            }
        case err := <-errCh:
            return err
        case <-stream.Context().Done():
            return nil
        }
    }
}

エラーハンドリング:gRPCステータスコードの正しい使い方

gRPCには豊富なステータスコードが定義されており、適切に使い分けることでクライアント側のエラーハンドリングが格段に楽になります。

ステータスコードの使い分けガイド

コード用途
OK (0)成功正常レスポンス
InvalidArgument (3)クライアントの入力エラーバリデーション違反
NotFound (5)リソースが存在しない指定IDのユーザーなし
AlreadyExists (6)リソースが既に存在メール重複
PermissionDenied (7)権限不足管理者のみの操作
Unauthenticated (16)認証失敗トークン無効
ResourceExhausted (8)リソース上限レートリミット
Internal (13)サーバー内部エラーDB接続エラー
Unavailable (14)一時的利用不可メンテナンス中

エラー詳細(errdetails)の活用

import (
    "google.golang.org/genproto/googleapis/rpc/errdetails"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
)

func validateCreateUserRequest(req *userv1.CreateUserRequest) error {
    var violations []*errdetails.BadRequest_FieldViolation
    
    if req.Email == "" {
        violations = append(violations, &errdetails.BadRequest_FieldViolation{
            Field:       "email",
            Description: "メールアドレスは必須です",
        })
    }
    
    if len(req.DisplayName) < 2 || len(req.DisplayName) > 50 {
        violations = append(violations, &errdetails.BadRequest_FieldViolation{
            Field:       "display_name",
            Description: "表示名は2〜50文字で入力してください",
        })
    }
    
    if len(violations) > 0 {
        st := status.New(codes.InvalidArgument, "validation failed")
        detail := &errdetails.BadRequest{FieldViolations: violations}
        st, _ = st.WithDetails(detail)
        return st.Err()
    }
    
    return nil
}

gRPCテスト戦略:Claude Codeでテストを自動生成する

ユニットテストの自動生成

claude "UserServiceのCreateUser、GetUser、ListUsersメソッドの
ユニットテストを作成してください。
以下をカバーすること:
- 正常系
- バリデーションエラー
- 重複エラー
- リポジトリエラー
モックにはgomockを使用してください"

Claude Codeが生成するテストコード:

package service_test

import (
    "context"
    "testing"

    "github.com/golang/mock/gomock"
    "github.com/stretchr/testify/assert"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
    
    userv1 "github.com/example/gen/user/v1"
    "github.com/example/internal/service"
    "github.com/example/internal/service/mock"
)

func TestCreateUser_Success(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()
    
    mockRepo := mock.NewMockUserRepository(ctrl)
    svc := service.NewUserService(mockRepo)
    
    // メール重複なし
    mockRepo.EXPECT().
        FindByEmail(gomock.Any(), "test@example.com").
        Return(nil, nil)
    
    // ユーザー作成成功
    mockRepo.EXPECT().
        Create(gomock.Any(), gomock.Any()).
        Return(&service.User{
            ID:          "uuid-1234",
            Email:       "test@example.com",
            DisplayName: "Test User",
        }, nil)
    
    resp, err := svc.CreateUser(context.Background(), &userv1.CreateUserRequest{
        Email:       "test@example.com",
        DisplayName: "Test User",
        Role:        userv1.UserRole_USER_ROLE_MEMBER,
    })
    
    assert.NoError(t, err)
    assert.NotNil(t, resp.User)
    assert.Equal(t, "test@example.com", resp.User.Email)
}

func TestCreateUser_ValidationError(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()
    
    mockRepo := mock.NewMockUserRepository(ctrl)
    svc := service.NewUserService(mockRepo)
    
    tests := []struct {
        name    string
        req     *userv1.CreateUserRequest
        wantCode codes.Code
    }{
        {
            name:     "empty email",
            req:      &userv1.CreateUserRequest{DisplayName: "Test"},
            wantCode: codes.InvalidArgument,
        },
        {
            name:     "empty display_name",
            req:      &userv1.CreateUserRequest{Email: "test@example.com"},
            wantCode: codes.InvalidArgument,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            _, err := svc.CreateUser(context.Background(), tt.req)
            assert.Error(t, err)
            st, ok := status.FromError(err)
            assert.True(t, ok)
            assert.Equal(t, tt.wantCode, st.Code())
        })
    }
}

func TestCreateUser_DuplicateEmail(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()
    
    mockRepo := mock.NewMockUserRepository(ctrl)
    svc := service.NewUserService(mockRepo)
    
    mockRepo.EXPECT().
        FindByEmail(gomock.Any(), "existing@example.com").
        Return(&service.User{ID: "existing-id"}, nil)
    
    _, err := svc.CreateUser(context.Background(), &userv1.CreateUserRequest{
        Email:       "existing@example.com",
        DisplayName: "Duplicate User",
    })
    
    st, _ := status.FromError(err)
    assert.Equal(t, codes.AlreadyExists, st.Code())
}

統合テストとE2Eテスト

claude "bufconnectを使ったgRPCの統合テストを作成してください。
実際のgRPCサーバーを起動せずにインメモリで通信するテストです。
テストヘルパーも作成して、各テストケースで簡単にセットアップできるようにしてください"
package integration_test

import (
    "context"
    "net"
    "testing"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/grpc/test/bufconn"
    
    userv1 "github.com/example/gen/user/v1"
)

const bufSize = 1024 * 1024

func setupTestServer(t *testing.T) (userv1.UserServiceClient, func()) {
    t.Helper()
    
    lis := bufconn.Listen(bufSize)
    srv := grpc.NewServer()
    
    // テスト用のサービスを登録
    userSvc := service.NewUserService(NewInMemoryRepo())
    userv1.RegisterUserServiceServer(srv, userSvc)
    
    go func() {
        if err := srv.Serve(lis); err != nil {
            t.Logf("server exited: %v", err)
        }
    }()
    
    conn, err := grpc.DialContext(
        context.Background(),
        "bufnet",
        grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
            return lis.Dial()
        }),
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
        t.Fatalf("failed to dial: %v", err)
    }
    
    client := userv1.NewUserServiceClient(conn)
    
    cleanup := func() {
        conn.Close()
        srv.Stop()
    }
    
    return client, cleanup
}

func TestIntegration_UserCRUD(t *testing.T) {
    client, cleanup := setupTestServer(t)
    defer cleanup()
    
    ctx := context.Background()
    
    // Create
    createResp, err := client.CreateUser(ctx, &userv1.CreateUserRequest{
        Email:       "integration@test.com",
        DisplayName: "Integration Test User",
        Role:        userv1.UserRole_USER_ROLE_MEMBER,
    })
    assert.NoError(t, err)
    assert.NotEmpty(t, createResp.User.Id)
    
    // Get
    getResp, err := client.GetUser(ctx, &userv1.GetUserRequest{
        Id: createResp.User.Id,
    })
    assert.NoError(t, err)
    assert.Equal(t, "integration@test.com", getResp.User.Email)
    
    // List
    listResp, err := client.ListUsers(ctx, &userv1.ListUsersRequest{
        PageSize: 10,
    })
    assert.NoError(t, err)
    assert.GreaterOrEqual(t, len(listResp.Users), 1)
}

gRPCの負荷テスト:ghzを使ったパフォーマンス検証

ghzによる負荷テスト

claude "ghzを使ったgRPCの負荷テストスクリプトを作成してください。
以下のシナリオをカバーすること:
1. Unary RPCのスループット測定
2. ストリーミングRPCのレイテンシ測定
3. 同時接続数を増やした場合のスケーラビリティ
結果をJSON形式で出力し、Grafanaダッシュボードで可視化できるようにしてください"
#!/bin/bash
# gRPC負荷テストスクリプト

# 基本設定
HOST="localhost:50051"
PROTO="proto/user/v1/user.proto"

echo "=== gRPC Load Test ==="

# 1. Unary RPC スループット測定
echo "--- Unary RPC: GetUser ---"
ghz --insecure \
    --proto "$PROTO" \
    --call user.v1.UserService.GetUser \
    --data '{"id": "test-user-1"}' \
    --total 10000 \
    --concurrency 50 \
    --format json \
    --output results/unary-getuser.json \
    "$HOST"

# 2. 高負荷シナリオ
echo "--- High Load: ListUsers ---"
ghz --insecure \
    --proto "$PROTO" \
    --call user.v1.UserService.ListUsers \
    --data '{"page_size": 20}' \
    --total 5000 \
    --concurrency 100 \
    --connections 20 \
    --format json \
    --output results/highload-listusers.json \
    "$HOST"

# 3. スケーラビリティテスト(同時接続数を段階的に増加)
echo "--- Scalability Test ---"
for conc in 10 50 100 200 500; do
    echo "  Concurrency: $conc"
    ghz --insecure \
        --proto "$PROTO" \
        --call user.v1.UserService.GetUser \
        --data '{"id": "test-user-1"}' \
        --total 5000 \
        --concurrency "$conc" \
        --format json \
        --output "results/scale-c${conc}.json" \
        "$HOST"
done

echo "=== Tests Complete ==="

SES現場でのgRPC活用パターン

パターン1: マイクロサービス間通信

大規模SES案件で最も多いgRPCの活用パターンです。REST APIからの移行により、サービス間通信のレイテンシを60%削減した事例があります。

パターン2: リアルタイムデータパイプライン

IoTデバイスからのデータ収集や、金融系のリアルタイムデータフィードにgRPCストリーミングを活用するパターンです。WebSocketと比較して、コネクション管理の複雑さが大幅に軽減されます。

パターン3: BFF(Backend for Frontend)

モバイルアプリ向けのBFFレイヤーでgRPCを使い、バックエンドマイクロサービスと高速に通信するパターンです。フロントエンドにはgRPC-Webを経由してRESTライクなインターフェースを提供します。

gRPC開発でClaude Codeを最大活用するためのプロンプト集

Proto定義の生成

「{サービス名}のProtocol Buffers定義を作成してください。
要件: {ビジネス要件}
遵守事項: Google AIPs、proto3、フィールド番号戦略」

サーバー実装

「{proto定義}に基づいてgRPCサーバーを{言語}で実装してください。
含めるもの: インターセプター、ヘルスチェック、Graceful shutdown」

テスト生成

「{サービス名}の以下のRPCメソッドのテストを作成してください: {メソッド名}
カバレッジ: 正常系、異常系、境界値、モック使用」

まとめ:Claude Code × gRPCで開発速度を最大化する

Claude Codeを活用したgRPC開発の要点をまとめます。

  1. Proto定義の自動生成: ビジネス要件からproto定義を迅速に作成し、ベストプラクティスに沿った設計を実現
  2. サーバー実装の効率化: インターセプター、サービス実装、エラーハンドリングをパターン化して高品質なコードを生成
  3. ストリーミング対応: 4つの通信パターンを適材適所で活用し、リアルタイム通信を実装
  4. テスト自動化: ユニットテスト・統合テスト・負荷テストを包括的に自動生成
  5. 運用品質の確保: メトリクス、ロギング、ヘルスチェックを標準装備

SES案件では、gRPCスキルは高単価案件へのパスポートとなります。Claude Codeを相棒にして、効率的にgRPC開発スキルを身につけましょう。

関連記事

SES案件をお探しですか?

SES記事をもっと読む →
🏗️

SES BASE 編集長

SES業界歴10年以上のメンバーが在籍する編集チーム。SES企業での営業・エンジニア経験、フリーランス独立経験を持つメンバーが、業界のリアルな情報をお届けします。

📊 業界データに基づく記事制作 🔍 IPA・経済産業省データ参照 💼 SES実務経験者が執筆・監修