Go で始める gRPC 入門 — Protocol Buffers, `protoc` から自動生成されるコードのしくみ

## 概要
この記事では、Go 言語における gRPC 開発の初歩的な流れをまとめます。Protocol Buffers (以下 Protobuf) で定義した `.proto` ファイルから自動生成されるコードや、Go Modules におけるモジュールパスの考え方など、PHP や Composer など他言語のエコシステムに慣れた方には少し驚きがあるかもしれません。ここでは、そのポイントをわかりやすく解説します。

## 前提
**Protobuf**:
データを効率よくバイナリシリアライズ／デシリアライズする仕組み。言語を超えたデータ交換が容易になる。

**gRPC**:
Protobuf を使った RPC (リモート呼び出し) フレームワーク。Protobuf がデフォルトだが、実は gRPC では他の形式も拡張次第で扱えなくはない。

**使いどころ**:
高速・軽量なデータ交換が求められる場面全般（ファイル保存、メッセージキュー、他の通信フレームワークなど）で、必ずしも gRPC がセットではない。Protobuf 単独での利用例も多々ある。

## 1. Go Modules の基礎知識

### Go Modules とは？

Go は依存関係管理のために **Go Modules** を使います。プロジェクトルートで以下のコマンドを実行し、モジュールを初期化します。

```bash
go mod init github.com/user_id/project_name
```

- **`go.mod` ファイル** が生成され、これが依存関係を管理する上でのエントリーポイントとなります。
- `github.com/user_id/project_name` の部分はモジュールパスで、一般的に **リポジトリの URL**（または一意な識別子）を指定します。
  - 一意性を確保するため、URL 形式にするのが慣例です。
  - PHP/Composer では「vendor/package」形式を使うのが一般的で、ここが Go の文化と大きく異なります。

---

## 2. Protocol Buffers (.proto) ファイルの役割

### `.proto` ファイルとは？

`.proto` ファイルは Protobuf で定義したメッセージやサービス（gRPC を使用する場合）を記述するスキーマファイルです。たとえば、以下のように定義します。

```proto
syntax = "proto3";

package hello;

// Go 用のパッケージ指定 (インポートパス; パッケージ名)
option go_package = "github.com/user_id/project_name/proto/hello;hello";

// Greeter サービスの定義
service Greeter {
  // リクエストとレスポンスの型を指定
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

// リクエストメッセージ
message HelloRequest {
  string name = 1;
}

// レスポンスメッセージ
message HelloResponse {
  string message = 1;
}
```

- **`package hello;`**  
  Protobuf 上の名前空間を示すものです。
- **`option go_package = ...`**  
  Go 用にコード生成する際の **インポートパス** と **パッケージ名** を指定します。  
  例：
  ```go
  option go_package = "github.com/user_id/project_name/proto/hello;hello";
  ```
  - `github.com/user_id/project_name/proto/hello` はインポートパス
  - `hello` はパッケージ名

gRPC を利用しない単なる Protobuf シリアライズ（データを効率よくバイナリシリアライズ／デシリアライズする仕組みのこと）だけなら `service` は不要ですが、gRPC で遠隔呼び出しするサービスを作るには `service` を定義します。

---

## 3. `protoc` を使った Go コード生成

Go で Protobuf と gRPC を使うには、以下のプラグインが必要です。

- `protoc-gen-go`
- `protoc-gen-go-grpc`

これらは `go install` コマンドでインストール可能です。

```bash
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
```

### コード生成コマンドの例

`.proto` ファイルから Go コードを生成するには、以下のようにコマンドを実行します。

```bash
protoc --go_out=. --go-grpc_out=. proto/hello.proto
```

- `--go_out=.`  
  Protobuf のメッセージ定義を Go の構造体として出力
- `--go-grpc_out=.`  
  gRPC サービスのインターフェースやクライアントスタブを Go で出力

このとき、`.proto` ファイルに書いた `option go_package` の設定に基づいて、**ディレクトリ構造** が自動的に作成され、そこに `hello.pb.go` や `hello_grpc.pb.go` が生成されます。

#### 例

`hello.proto` 内で指定したパッケージが  
`option go_package = "github.com/user_id/project_name/proto/hello;hello";`  
となっている場合、以下のように生成されます。

```
.
├── proto
│   └── hello.proto
└── github.com
    └── user_id
        └── project_name
            └── proto
                └── hello
                    ├── hello.pb.go
                    └── hello_grpc.pb.go
```

**「こんな階層ができるのはエキセントリックだ！」** と感じる方もいるかもしれませんが、これは **Go がモジュールパスをそのままディレクトリ構造にマッピングする仕様** に起因しています。

※もし、`.proto` ファイルのある場所に直接生成したい場合は、`paths=source_relative` オプションを使います。

### 補足説明: go_package と .proto ファイルの配置に関する注意点

本記事で紹介している手順に従って進めると、`hello.proto` 内で設定した `go_package` のパスと、実際の `.proto` ファイルの配置場所が一致ません。これにより、生成されたコードが想定外のディレクトリに配置され、最終的に `main.go` のインポートでエラーが発生します。

#### 具体的な問題例

- **設定例**  
  `hello.proto` 内の設定:  
  ```proto
  option go_package = "github.com/yourusername/yourproject/proto/hello;hello";
  ```
  
- **配置の問題**  
  指示どおりに `hello.proto` を `proto/` 配下に置いてしまうと、生成されたコードが  
  `yourproject/github.com/yourusername/yourproject/proto/hello/`  
  のような余計なディレクトリ階層に出力される可能性があります。

- **結果としてのエラー**  
  その結果、`main.go` で  
  ```go
  import pb "github.com/yourusername/yourproject/proto/hello"
  ```  
  と記述しても、実際の生成ファイルが存在する場所と一致せず、VSCode やコンパイラから「no required module provides package ...」というエラーが発生します。

#### 解決方法

この問題を回避するためには、以下の2つの方法のいずれかを実施してください。

1. **.proto ファイルの配置場所を調整する**  
   - 具体的には、`hello.proto` を `proto/hello/` フォルダに移動します。
   - その上で、プロジェクトルート（例: `yourproject/`）から以下のように実行します。
     ```bash
     protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. proto/hello/hello.proto
     ```
   - これにより、生成されたファイルが `yourproject/proto/hello/` に正しく配置され、インポートパス `github.com/yourusername/yourproject/proto/hello` と一致します。

2. **protoc コマンドのオプションを利用する**  
   - 既存のディレクトリ構造を維持したい場合は、`paths=source_relative` オプションを用いて生成ファイルを `.proto` ファイルと同じディレクトリに出力するように調整します。

どちらの方法でも、モジュールルートとインポートパスが一致すれば、`main.go` の import エラーは解消されます。プロジェクトの構成や運用に合わせて、適切な方法を選択してください。

詳しくは以下の記事を参考にしてください。
参考記事: [Go + gRPC + Protocol Buffers 入門 〜 go_package の正しい設定とディレクトリ構成でハマらないために 〜](https://qiita.com/ko-suke2020/items/5fcfbc98f320b6705e37)

---

## 4. 自動生成されるコードの正体

`.proto` から生成されるコードには、次のようなものが含まれています。

1. **メッセージ定義**

   - Protobuf の `message` を Go の `struct` として表現
   - バイナリ形式への変換メソッドなどが自動的に含まれる

2. **gRPC サービスのインターフェースとクライアント**
   - `service` に対応する Go インターフェース（例: `GreeterServer`）
   - クライアントスタブ（`GreeterClient`）およびサービスの登録関数（`RegisterGreeterServer`）など

これによって、手書きで低レベルのシリアライズやネットワーク通信コードを書く必要がなく、**生成されたインターフェースを実装したり、クライアントを呼び出したりする形**で gRPC の機能が使えるようになります。

---

## 5. PHP や Composer との比較

- **PHP / Composer**

  - `composer.json` で `vendor/package` のようにパッケージを指定
  - インストールすると `vendor/` ディレクトリに依存ライブラリが配置される
  - リポジトリ URL を直接「ディレクトリ」として使うことは少ない

- **Go / Go Modules**
  - モジュールパスがそのままインポートパスとなる（`github.com/ユーザー名/リポジトリ名` など）
  - ディレクトリ構造とインポートパスが対応している
  - Protocol Buffers のコード生成でも、このモジュールパスが利用されるため、やや複雑に見えるディレクトリ構造が作られる

---

## 6. まとめ

1. **Go Modules を使ってプロジェクトを初期化**
   ```bash
   go mod init github.com/user_id/project_name
   ```
2. **.proto ファイルでメッセージとサービスを定義**
   - `option go_package` で Go のインポートパスとパッケージ名を指定
3. **`protoc` + Go プラグインでコード生成**
   - `protoc --go_out=. --go-grpc_out=. proto/*.proto`
   - ディレクトリ構造は `go_package` に基づく
4. **生成されたコード（.pb.go, \_grpc.pb.go）を使用**
   - サーバー側では、インターフェースを実装 & `Register〜Server` で登録
   - クライアント側では、自動生成されたスタブを呼び出す

Go の世界では「モジュールパス＝インポートパス」が大前提となり、`protoc` のコード生成もこの仕様に強く依存します。PHP エンジニアの方には少しエキセントリックに映るかもしれませんが、慣れてしまうとわかりやすい仕組みです。ぜひ試してみてください！

---

## 参考リンク

- [Official gRPC for Go](https://grpc.io/docs/languages/go/)
- [Go Modules (公式ドキュメント)](https://go.dev/doc/modules)
- [Protocol Buffers (proto3) Basics](https://developers.google.com/protocol-buffers/docs/proto3)

---

以上が、Go と Protocol Buffers による gRPC 開発の超入門的なまとめです。質問やフィードバックがあればお気軽にコメントください！