JSONとの違い・書き方・gRPCの仕組みまで完全解説!
本記事は、Proto / gRPC をこれから触る方、buf lint に苦しんでいる方を主な対象にしています。
はじめに
🎅 この記事は RetailAI Advent Calendar 2025 の25日目の記事です。
ちょうどクリスマスのタイミングということで、皆さん素敵な一日をお過ごしください🎄✨
@le_thi_hang (レーティハン)が担当させていただきます。
昨日は @fujihara_hideyuki さんの『Strategy Pattern』を用いてcleanにcodingするでした。Strategy Pattern を使う理由や適用タイミングが、サンプルコード付きでとても分かりやすかったです。実務でも参考になる良い記事でした。
近年、マイクロサービスや gRPC の普及により、Protocol Buffers(protobuf / proto) は急速に浸透しています。
しかし、初めて触れるとこんな疑問が出てきませんか?
- JSON と何が違うの?
- proto の書き方がわからない…
- フィールド番号(tag)って何? なんで必要なの?
-
buf lintのエラー多すぎ…
私自身、最初はまったく理解できず、buf lint で大量のエラーに困った経験があります。
そこで本記事では、初心者向けに図解付きで Proto の基本をやさしく解説します。この記事を読み終えれば、proto の基礎理解は完全にクリアできます。
この記事でわかること
✔ Protocol Buffers の全体像
✔ JSON と Proto の違い
✔ .proto ファイルの書き方
✔ message / enum / repeated の使い方
✔ フィールド番号(tag)が“絶対に重要”な理由
✔ gRPC の仕組み
✔ buf lint / buf generate の基本
✔ 実務で気をつける後方互換性のルール
1. Protocol Buffers とは?
Protocol Buffers(Protobuf)は Google が開発したデータ定義 & シリアライズ方式 です。
一言でいうと:
データの型を定義し、コンパクトなバイナリ形式で通信する仕組み
特に gRPC では、API のリクエスト & レスポンス定義を proto で書くことが必須 になります。
2. Protocol Buffers の全体イメージ
Proto の最大の特徴は、定義ファイルから各言語のコードを自動生成できる点です。
.proto → 自動生成 → 各言語のクラス or Struct
Go, Java, TypeScript などに対応したコードが生成されます。
👉 この仕組みにより、
バックエンド・フロントエンド間の型ズレを防げます。
3. JSON と何が違うのか?
結論から言うと
-
JSON:人間向け(テキスト)
-
Proto:機械向け(バイナリ)
→ Proto は 高速・軽量・通信向き
| 比較項目 | JSON | Protocol Buffers |
|---|---|---|
| データ形式 | テキスト | バイナリ |
| 読みやすさ | 人間に読みやすい | 機械向け(高速) |
| 型定義 | あいまい | 厳密 |
| データサイズ | 大きい | とても小さい |
| 後方互換性 | 弱い | 強い(tag管理) |
| gRPC | 非推奨 | 必須 |
■ JSON vs Proto
📌 JSON は「キー名」も送るためサイズが大きくなります。
Proto は フィールド番号(tag)だけを送るため、非常に軽量です。
4. .proto ファイルの基本構造
syntax = "proto3";
package example;
message User {
int32 id = 1; // tag(フィールド番号)
string name = 2;
string email = 3;
}
.proto ファイルは
データ構造と API 仕様を定義する設計書のような存在です。
-
syntax = "proto3"
→ 使用する Protobuf のバージョン -
package
→ 名前空間(生成コードや import に影響) -
message
→ データ構造(struct / class 相当)
Proto の本質は「最初に型を厳密に決めること」 にあります。
5. よく使う型一覧
| 型 | 説明 |
|---|---|
| int32 / int64 | 整数 |
| string | 文字列 |
| bool | 真偽値 |
| double / float | 小数 |
| bytes | バイナリ |
Proto の型は 言語に依存しない共通型 です。
例えば int32 は:
-
Go →
int32 -
Java →
int -
TypeScript →
number
のように、各言語に適した型へ 自動変換 されます。
この仕組みにより、
👉 「フロントとバックエンドで型がズレる」問題を根本的に防げます。
6. repeated(配列)
message UserList {
repeated User users = 1;
}
repeated は 0 件以上の配列を表します。
-
0 件 → 空配列
-
1 件以上 → 通常の配列
という扱いになり、null は存在しません。
生成される型の例:
-
Go →
[]User -
TypeScript →
User[] -
Java →
List<User>
📌 ポイント
JSON では null / [] / 未定義 の違いで事故が起きがちですが、Proto では repeated により 配列の扱いが明確になります。
7. enum(列挙)
最初の値は必ず 0 にする必要があります。
enum Gender {
GENDER_UNKNOWN = 0; // 必須
MALE = 1;
FEMALE = 2;
}
これは Proto の重要なルールで、未設定・不正な値を受け取った場合は 0 が使われる仕様になっています。
そのため、
-
UNKNOWN -
UNSPECIFIED -
NONE
といった 「不明・未指定」用の値を 0 番に置くのが定番です。
8. gRPC のサービス定義
service UserService {
rpc GetUser (GetUserRequest) returns (GetUserResponse);
}
この定義から、
-
クライアント用 Stub
-
サーバー用インターフェース
が 自動生成 されます。
👉 通信処理は gRPC に任せて、ビジネスロジックに集中できるのが大きなメリットです。
9. フィールド番号(tag)は 最重要ポイント
⚠ tag 番号は絶対に変更してはいけません。
Proto が送っているのは、
✔ フィールド番号
❌ フィールド名
番号を変えるとデータ破壊が起こります。
10. 後方互換性ルール(実務で超重要)
❌ やってはいけない
- 既存フィールドの tag 番号変更
- 削除した番号を再利用
- enum の番号を変更
⭕ やっていい
- 新しいフィールド追加(新しい番号)
- enum の新規値追加
- 廃止したフィールドを
reservedに移動
11. reserved の使い方
message User {
reserved 4;
reserved "old_name", "old_age";
}
👉 削除したフィールドは必ず reserved に入れる
これが実務ではほぼ必須です。
12. buf を使った proto 管理
代表的なコマンド:
buf lint # スタイルチェック
buf generate # コード生成
buf breaking # 破壊的変更チェック
特に buf breaking は tag 番号変更の事故防止に超有効です。
13. JSON と Proto のサイズ比較
JSON
{"id":123,"name":"Bob"}
→ 約 25 bytes
Proto(バイナリ)
08 7B 12 03 42 6F 62
→ 約 7 bytes
📌 1/3 以下のサイズ! 大量通信では大きく効きます。
14. よくあるエラーと解決方法
❌ フィールド番号の重複
string name = 1;
string email = 1; // NG
❌ enum に 0 番が無い
enum Status {
ACTIVE = 1; // NG
}
❌ package とフォルダ構造の不一致
→ buf lint が検知します。
まとめ
この記事で学べるポイント:
✔ Proto の役割 → 厳密な型定義 & 高速通信
✔ JSONとの違い → バイナリで軽量
✔ 最重要 → tag は絶対に変更禁止
✔ buf を使うと開発が快適に
✔ 実務では後方互換性の意識が必須
Proto は難しく見えますが、ルールが明確なので慣れるととても扱いやすい技術だと思います。
おわりに
RetailAI / TRIAL では一緒に働くエンジニアを募集しています。
興味がある方はご連絡ください!




