REST APIは広く普及していますが、マイクロサービス間通信やリアルタイムデータストリーミングでは、gRPCの方が圧倒的に有利なケースがあります。しかしgRPCの学習コストは高く、Protocol Buffersの定義やコード生成の仕組みに慣れるまで時間がかかるのも事実です。
本記事では、Claude Codeを活用してgRPC APIの設計から実装、テスト、運用まで効率的に行う方法を解説します。Claude Code 完全攻略シリーズの一環として、AIがgRPC開発にどのようなブレークスルーをもたらすか、実践的なコード例とともにお伝えします。

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 API | gRPC |
|---|---|---|
| プロトコル | HTTP/1.1 | HTTP/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-15は1バイトでエンコードされるため、頻出フィールドに割り当てる
- enum値のUNSPECIFIED: 全てのenumの0番にUNSPECIFIEDを設定し、未設定状態を明示する
- FieldMaskの活用: PATCH的な部分更新にはFieldMaskパターンを使用する
- ページネーションパターン: Google AIP-158に準拠したカーソルベースのページネーションを推奨
- バージョニング: パッケージ名に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つの通信パターン
- Unary RPC: 1リクエスト→1レスポンス(通常のAPI呼び出し)
- Server Streaming: 1リクエスト→Nレスポンス(リアルタイム通知)
- Client Streaming: Nリクエスト→1レスポンス(ファイルアップロード)
- 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開発の要点をまとめます。
- Proto定義の自動生成: ビジネス要件からproto定義を迅速に作成し、ベストプラクティスに沿った設計を実現
- サーバー実装の効率化: インターセプター、サービス実装、エラーハンドリングをパターン化して高品質なコードを生成
- ストリーミング対応: 4つの通信パターンを適材適所で活用し、リアルタイム通信を実装
- テスト自動化: ユニットテスト・統合テスト・負荷テストを包括的に自動生成
- 運用品質の確保: メトリクス、ロギング、ヘルスチェックを標準装備
SES案件では、gRPCスキルは高単価案件へのパスポートとなります。Claude Codeを相棒にして、効率的にgRPC開発スキルを身につけましょう。