観点1:スキーマ・契約(Contract)
API における「契約」とは、クライアントとサーバーが合意する入出力の型と振る舞いのことだ。ここが両者で最も思想が分かれる。
.proto が唯一かつ強制的な契約。ここからコードを生成するため、型の不一致はコンパイル時に検出される。スキーマファーストが前提。REST でも OpenAPI を「スキーマファースト」で運用すれば gRPC に近い厳密さを得られる。 ただし強制力が違う。gRPC はスキーマからしかコードを書けないのに対し、REST はスキーマを無視して実装することもできてしまう。 この「強制力の差」は、チーム規模が大きくなるほど効いてくる。
💡 実装者が悩むポイント
REST + OpenAPI を採用する場合、スキーマを正とする(コード生成する)か、コードを正とする(スキーマを生成する)かを最初に決めておく必要がある。どちらでもよいが、混在させるとドリフトが必ず起きる。gRPC ではこの迷いがそもそも発生しない。
観点2:データ表現とペイロード
ネットワークを流れるバイト列の表現形式の違い。これがサイズ・速度・デバッグ性に直結する。
curl でそのまま中身を確認できる。同じデータを JSON と protobuf で表したときの違いを概念的に示す。
# JSON(テキスト・フィールド名を毎回送る)— 約 52 バイト
{"id":42,"name":"Taro","email":"taro@example.com"}
# protobuf(バイナリ・フィールド番号で表現)— イメージ
# 08 2A -> field#1 (id) = 42
# 12 04 54 61 72 6F -> field#2 (name) = "Taro"
# ...
# フィールド名を送らないぶん小さく、パースも高速
💡 実装者が悩むポイント
protobuf は速い反面、障害調査でパケットを覗いても中身が読めない。grpcurl など専用ツールやサーバーリフレクションの整備が前提になる。「JSON ならログを grep すれば終わる」調査が、gRPC では一手間増えることを許容できるかが分かれ目。
観点3:通信モデル
「1リクエストに1レスポンス」以外の通信ができるかどうか。リアルタイム性が要るシステムで効いてくる。
| gRPC の通信方式 | 形 | 典型ユースケース |
|---|---|---|
| Unary | 1 リクエスト → 1 レスポンス | 通常の API 呼び出し(REST と同等) |
| Server streaming | 1 → 複数 | 進捗通知・大量データの分割配信 |
| Client streaming | 複数 → 1 | ログ・センサーデータのアップロード集約 |
| Bidirectional streaming | 複数 ⇄ 複数 | チャット・リアルタイム同期 |
💡 実装者が悩むポイント
ストリーミングは強力だが、ロードバランサ・タイムアウト・再接続の設計が一気に複雑化する。長命なストリームは L4/L7 LB のアイドルタイムアウトに引っかかりやすい。「本当にストリーミングが要るのか、ポーリングや SSE で足りないか」を最初に問うべき。
観点4:エラー設計
失敗をどう表現し、クライアントにどう伝えるか。リトライ可否の判断にも関わる重要な観点だ。
google.rpc.Status で詳細やリトライ情報も構造化して返せる。| 意味 | REST(HTTP) | gRPC status |
|---|---|---|
| 正常 | 200 OK | OK |
| 不正な引数 | 400 Bad Request | INVALID_ARGUMENT |
| 未認証 | 401 Unauthorized | UNAUTHENTICATED |
| 権限なし | 403 Forbidden | PERMISSION_DENIED |
| 未存在 | 404 Not Found | NOT_FOUND |
| 競合 | 409 Conflict | ALREADY_EXISTS / ABORTED |
| タイムアウト | 504 等 | DEADLINE_EXCEEDED |
| サーバー内部 | 500 | INTERNAL |
💡 実装者が悩むポイント
gRPC の status code は数が限られ意味がブレにくい反面、表現したいエラーが既存コードに綺麗に当てはまらないこともある。REST は自由度が高いが、その自由ゆえに「404 か 400 か」がチームで割れる。どちらもリトライ可能なエラー(一時障害)と不可能なエラー(入力ミス)の区別を明示することが肝心。
観点5:API 進化・互換性
API は必ず育つ。フィールド追加やバージョンアップを既存クライアントを壊さず行えるかは運用コストを大きく左右する。
/v1/ → /v2/)が一般的。JSON はフィールド追加に寛容だが、規約はチーム任せで破壊的変更が起きやすい。reserved で禁止できる。message User {
int64 id = 1;
string name = 2;
string email = 3;
// 追加:番号 4 を新規採番すれば旧クライアントは無視するだけ(後方互換)
string phone = 4;
// 削除したフィールドの番号は reserved で凍結し、再利用を禁止する
reserved 5;
reserved "legacy_field";
}
⚠️ 実装者が悩むポイント
protobuf の互換性は「フィールド番号を変えない/再利用しない」という規律の上に成り立つ。番号を使い回すと、古いデータが別の型として読まれて静かに壊れる。REST/JSON は手軽な反面、レスポンスのキー名変更や型変更がそのまま破壊的変更になりやすく、結局バージョニング運用の規律が必要になる点は同じ。
観点6:エコシステムと相互運用
「誰がこの API を叩くのか」で評価が大きく変わる観点。ブラウザ・CDN・既存ツールとの相性だ。
curl・Postman でそのまま叩け、HTTP キャッシュや CDN がそのまま効く。あらゆる言語・基盤が標準対応。💡 実装者が悩むポイント
gRPC は HTTP/2 のトレーラーを使うため、HTTP/2 を完全に通せない中間装置(古いプロキシ・一部の WAF・L7 LB)でハマることがある。ブラウザ向けには gRPC-Web、社外公開には gRPC-Gateway で REST/JSON を生やす、といった追加レイヤーの検討が必要になる。「叩く相手がブラウザか、自社の別サービスか」が最初の分岐点。
観点7:パフォーマンスと運用
速度だけでなく、ロードバランシング・可観測性まで含めた「運用しやすさ」の観点。
gRPC は1本の HTTP/2 接続上で多数のリクエストを多重化する。これは速い反面、 接続単位で振り分ける L4 ロードバランサだと、最初に繋がったサーバーに負荷が固定されてしまう。 そのため gRPC では「リクエスト単位で分散する L7 LB」やクライアントサイドLB、サービスメッシュ(Envoy 等)が事実上前提になる。 可観測性も、HTTP ステータスやパスで見る既存ダッシュボードがそのままでは使えず、gRPC 用のメトリクス・トレーシング整備が要る。
⚠️ 実装者が悩むポイント
「gRPC は速い」はペイロードとレイテンシの話であって、運用の総コストが下がるわけではない。LB・監視・デバッグ手段を gRPC 仕様に作り直すコストを見落とすと、移行後に「速くなったが運用が辛い」状態に陥る。
観点別比較表(まとめ)
ここまでの7観点を1枚に俯瞰する。本文を読まなくても、この表で全体像を掴めるようにした。
| 観点 | REST(HTTP + JSON) | gRPC(HTTP/2 + protobuf) | 向いている状況 |
|---|---|---|---|
| スキーマ・契約 | OpenAPI は任意。ドリフトしやすい | .proto が強制。型不一致を静的検出 |
厳密さ重視 → gRPC |
| データ表現 | JSON(テキスト・可読) | protobuf(バイナリ・高効率) | デバッグ容易さ → REST |
| 通信モデル | リクエスト/レスポンス中心 | 4種(双方向ストリーミング含む) | リアルタイム → gRPC |
| エラー設計 | HTTP ステータス+自由なボディ | 固定の status code +構造化詳細 | 意味の安定 → gRPC |
| 互換性 | URL バージョニング・規律次第 | フィールド番号で後方互換を担保 | 進化の安全性 → gRPC |
| エコシステム | ブラウザ・curl・CDN がそのまま | gRPC-Web / プロキシが必要 | 外部公開・ブラウザ → REST |
| 性能・運用 | 既存 LB・監視が流用可 | 低レイテンシだが L7LB・専用監視前提 | 内部・低遅延 → gRPC |
✅ 次の章では…
PART 03 では、この比較表を「どんな条件ならどちらに倒すか」という判断軸に落とし込む。外部公開/内部通信、ブラウザ有無、レイテンシ要件などから選定フローを描き、両者を併用する現実解(外向き REST+内部 gRPC、gRPC-Gateway)も扱う。