はじめに
前回の記事では、バックエンドにGo + Ginを使用、フロントエンドにはReact + Viteを使用して作成したWebアプリケーションにデータベース連携(GORM + SQLite)とJWT認証機能を追加しました。
今回は、さらにDockerを使ってアプリケーションをコンテナ化し、gRPCを組み込むことで、より高度なWebアプリケーションへと発展させます。
自分でもこのあたりは初めてやった時には複雑だったので、知識の整理も兼ねて解説します。
対象読者
- GoでWebアプリを作成し、Dockerを活用して環境構築を行いたい方
- REST APIだけでなく、gRPCを導入してさらにパフォーマンスの向上を図りたい方
- Webアプリケーションをより実用的な形でデプロイしたい方
目次
- Dockerを使った環境構築
- そもそものDockerの基本概念
- Dockerfileの作成
- Docker Composeを使ったサービス管理
- gRPCの組み込み
- gRPCの基本概念
- Protocol Buffers(.proto)の定義
- gRPCサーバーの実装
- gRPCクライアントの実装
- フロントエンドとの統合
- ReactからREST API & gRPCを呼び出す方法
- フロントエンドでのgRPCの利用方法
1. Docker を使った環境構築
1.1 Docker の基本概念
Dockerは、アプリケーションを コンテナ という単位で管理・実行するツールです。
コンテナを使うことで、環境の差異をなくし、どこでも同じように動作するアプリケーションを作ることができます。
1.2 Dockerfileの作成
まず、アプリケーションをDockerで動かせるようにするための 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
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
を実行すれば、すべてのサービスがコンテナとして起動します。
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
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
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を使用
axios.get("http://localhost:8080/api/todos")
.then(response => console.log(response.data));
- gRPC: gRPC-Webを使用
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を導入する方法を解説しました。
少し難しめの概念ですが、とても効率的なアプリケーション開発が可能になるため、この辺りもマスターしていきましょう!