Go + gRPC + Protocol Buffers 入門  〜 go_package の正しい設定とディレクトリ構成でハマらないために 〜

## はじめに

Go で gRPC を使い始める際に、`.proto` ファイルから自動生成されたコードが思わぬディレクトリ構造に配置されてしまい、インポートがうまくいかない……という経験はありませんか？  
本記事では、Go Modules を使ったプロジェクトで、gRPC + Protocol Buffers の開発環境を正しく構築し、スムーズにコード生成・インポートを行うまでの流れを整理します。

---

## 前提

- **OS:** Mac など（Homebrew を利用できる環境）  
- **Go バージョン:** go1.23.6 以上  
- **Go Modules:** 有効化済み

---

## 1. プロジェクトの作成と Go Modules の初期化

まずは、新しいディレクトリを作り、そこをプロジェクトのルートとします。ここでは、仮に `my-grpc-project` という名前とします。

```bash
mkdir my-grpc-project
cd my-grpc-project
go mod init github.com/yourusername/my-grpc-project
```

- `go.mod` が作成され、モジュールパスが `github.com/yourusername/my-grpc-project` に設定されます。

---

## 2. Protocol Buffers Compiler (protoc) とプラグインのインストール

### 2-1. protoc のインストール

```bash
brew install protobuf
```

```bash
protoc --version
# libprotoc 23.x など、バージョンが表示されればOK
```

### 2-2. Go 用 gRPC プラグインのインストール

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

- これらは通常 `$(go env GOPATH)/bin` にインストールされます。
- `$PATH` に `$(go env GOPATH)/bin` を含めるよう設定しておきます。

---

## 3. ディレクトリ構成を決める

最初に、`proto` ディレクトリを作成し、その下にサービスごとにフォルダを作るやり方がわかりやすいです。

```bash
my-grpc-project/
├── go.mod
└── proto/
    └── greeter/
        └── greeter.proto
```

ここでは、`greeter` サービスを想定し、`proto/greeter` フォルダに `.proto` ファイルを配置します。

---

## 4. `.proto` ファイルの作成

`greeter.proto` を以下のように作成します。

```proto
syntax = "proto3";

package greeter;

// go_package のインポートパスは、go.mod のモジュールパス + ディレクトリ
option go_package = "github.com/yourusername/my-grpc-project/proto/greeter;greeter";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}
```

- **package greeter;**  
  Protocol Buffers 上のパッケージ名
- **option go_package**  
  生成される Go コードのインポートパスとパッケージ名を指定  
  例: `"github.com/yourusername/my-grpc-project/proto/greeter;greeter"`

---

## 5. コード生成

プロジェクトルート (`my-grpc-project`) で以下のコマンドを実行します。

```bash
protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. proto/greeter/greeter.proto
```

- `paths=source_relative` オプションを使うと、`.proto` ファイルと同じディレクトリ (`proto/greeter`) に生成ファイルが配置されます。
- 成功すると、以下のように `.pb.go` や `_grpc.pb.go` が生成されます。

```
my-grpc-project/
├── go.mod
└── proto/
    └── greeter/
        ├── greeter.proto
        ├── greeter.pb.go
        └── greeter_grpc.pb.go
```

---

## 6. 依存関係を追加 (gRPC パッケージ)

生成されたコードを使ってサーバーやクライアントを実装するには、`google.golang.org/grpc` を依存関係に追加する必要があります。

```bash
go get google.golang.org/grpc@latest
go mod tidy
```

これで `go.mod` と `go.sum` に必要な情報が書き込まれ、VSCode のインポートエラーなども解消されます。

---

## 7. 簡単な gRPC サーバー実装例

`cmd/server/main.go` など、エントリーポイントを用意して以下のように書きます。

```go
package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "github.com/yourusername/my-grpc-project/proto/greeter"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
    log.Printf("Received: %v", in.Name)
    return &pb.HelloResponse{Message: "Hello " + in.Name}, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    log.Printf("server listening at %v", lis.Addr())
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}
```

### 7-1. 実行

```bash
cd cmd/server
go run main.go
```

- `:50051` ポートで gRPC サーバーが起動します。
- 別のターミナルで `grpcurl` などを使えばテストも可能です。

---

## 8. まとめ

1. **go.mod の初期化**  
   - `go mod init github.com/yourusername/my-grpc-project`
2. **Protobuf とプラグインのインストール**  
   - `brew install protobuf`  
   - `go install protoc-gen-go` / `protoc-gen-go-grpc`
3. **.proto ファイルで go_package を設定**  
   - `option go_package = "github.com/yourusername/my-grpc-project/proto/greeter;greeter";`
4. **`paths=source_relative` オプションを付けて protoc 実行**  
   - 生成ファイルを .proto と同じフォルダへ配置
5. **依存関係を go get で追加**  
   - `go get google.golang.org/grpc@latest`
6. **サーバー実装 & 起動**

この流れで、モジュールパスとディレクトリ構造が一致し、VSCode のエラーも起こりにくくなります。  
ぜひ試してみてください。何か詰まったらコメントなどで教えていただければ幸いです。

---

## 参考リンク

- [Official gRPC in Go](https://grpc.io/docs/languages/go/basics/)
- [Protocol Buffers Language Guide (proto3)](https://developers.google.com/protocol-buffers/docs/proto3)

以上が、Go + gRPC + Protocol Buffers を使った開発フローの簡単なまとめです。皆さんの開発がスムーズに進むことを願っています！