Go言語で学ぶWebアプリケーション開発3：[Docker + gRPCを組み込む]

## はじめに

前回の記事では、バックエンドにGo + Ginを使用、フロントエンドにはReact + Viteを使用して作成したWebアプリケーションにデータベース連携（GORM + SQLite）とJWT認証機能を追加しました。
今回は、さらにDockerを使ってアプリケーションをコンテナ化し、gRPCを組み込むことで、より高度なWebアプリケーションへと発展させます。
自分でもこのあたりは初めてやった時には複雑だったので、知識の整理も兼ねて解説します。

**対象読者**

- GoでWebアプリを作成し、Dockerを活用して環境構築を行いたい方
- REST APIだけでなく、gRPCを導入してさらにパフォーマンスの向上を図りたい方
- Webアプリケーションをより実用的な形でデプロイしたい方

**目次**

1. Dockerを使った環境構築
   - そもそものDockerの基本概念
   - Dockerfileの作成
   - Docker Composeを使ったサービス管理
2. gRPCの組み込み
   - gRPCの基本概念
   - Protocol Buffers(.proto)の定義
   - gRPCサーバーの実装
   - gRPCクライアントの実装
3. フロントエンドとの統合
   - ReactからREST API & gRPCを呼び出す方法
   - フロントエンドでのgRPCの利用方法

---

## 1. Docker を使った環境構築

**1.1 Docker の基本概念**

Dockerは、アプリケーションを **コンテナ** という単位で管理・実行するツールです。\
コンテナを使うことで、**環境の差異をなくし、どこでも同じように動作するアプリケーション**を作ることができます。

**1.2 Dockerfileの作成**

まず、アプリケーションをDockerで動かせるようにするための `Dockerfile` を作成します。

**Dockerfile**

```dockerfile
# ベースイメージ
FROM golang:1.19

# 作業ディレクトリを作成
WORKDIR /app

# 必要なファイルをコピー
COPY go.mod .
COPY go.sum .
RUN go mod download

# ソースコードをコピー
COPY . .

# アプリケーションをビルド
RUN go build -o main .

# ポート 8080 を開放
EXPOSE 8080

# アプリケーションを実行
CMD ["./main"]
```

**1.3 Docker Composeを使ったサービス管理**

`docker-compose.yml`を作成し、バックエンド、データベース、フロントエンドをまとめて管理できるようにします。

**docker-compose.yml**

```yaml
version: '3.8'
services:
  backend:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - database
  database:
    image: sqlite
  frontend:
    build: ./frontend
    ports:
      - "5173:5173"
    depends_on:
      - backend
```

`docker-compose up`を実行すれば、すべてのサービスがコンテナとして起動します。

```sh
docker-compose up --build
```

---

## 2. gRPCの組み込み

**2.1 gRPC の基本概念**

**gRPC**は、Googleが開発した**高性能なRPC（Remote Procedure Call）フレームワーク** です。

- **軽量 & 高速**：バイナリ形式のProtocol Buffers（protobuf）を使用するため、REST APIよりも効率的。
- **双方向ストリーミング**：クライアントとサーバーの双方向通信が可能。
- **マイクロサービスに最適**：マルチ言語対応で、拡張性が高い。

**2.2 Protocol Buffers (.proto) の定義**

まず、gRPCの通信を定義する`.proto`ファイルを作成します。

**proto/todo.proto**

```proto
syntax = "proto3";
package todo;

service TodoService {
    rpc GetTodos (Empty) returns (TodoList);
}

message Empty {}

message Todo {
    int32 id = 1;
    string task = 2;
    bool status = 3;
}

message TodoList {
    repeated Todo todos = 1;
}
```

**2.3 gRPC サーバーの実装**

**server.go**

```go
package main

import (
    "context"
    "net"
    "log"

    "google.golang.org/grpc"
    pb "todo/proto"
)

type server struct {
    pb.UnimplementedTodoServiceServer
}

func (s *server) GetTodos(ctx context.Context, req *pb.Empty) (*pb.TodoList, error) {
    todos := []*pb.Todo{
        {Id: 1, Task: "Learn Go", Status: false},
        {Id: 2, Task: "Build a Web App", Status: true},
    }
    return &pb.TodoList{Todos: todos}, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterTodoServiceServer(s, &server{})
    log.Println("gRPC server is running on port 50051")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}
```

---

## 3. フロントエンドとの統合

**3.1 ReactからREST API & gRPCを呼び出す方法**

- REST API: Axiosを使用

```jsx
axios.get("http://localhost:8080/api/todos")
    .then(response => console.log(response.data));
```

- gRPC: gRPC-Webを使用

```jsx
import { TodoServiceClient } from "./proto/TodoServiceClientPb";
const client = new TodoServiceClient("http://localhost:50051");
client.getTodos({}, (err, response) => console.log(response));
```

---

## まとめ

| 項目                      | 説明                        |
| ----------------------- | ------------------------- |
| **Docker**              | アプリをコンテナ化し、どこでも同じ環境で動作させる |
| **gRPC**                | 高速なバイナリ通信が可能なRPCフレームワーク |
| **REST API & gRPC の統合** | フロントエンドで両方の通信方式を活用        |

本記事では、Dockerを使ってアプリをコンテナ化し、gRPCを導入する方法を解説しました。
少し難しめの概念ですが、とても効率的なアプリケーション開発が可能になるため、この辺りもマスターしていきましょう！