サーバーサイドなんもわからんアナタに向けたハンズオン　Unity × gRPC × Go × Firebase × Docker 〜第一章、環境構築〜

# 最初に

本記事の内容は、サーバーサイドにほとんど触れてこなかったUnityエンジニアに向けて、最近の主流構成を学びながら少しずつサーバーの知識を身につけられるハンズオン形式で進めます。この記事が、サーバーサイドに対する理解を深める一助になれば幸いです。

本記事の対象者は以下の通りです。

- A Tour of Goを一通り終わらせたが次に何をするべきかわからない初心者サーバーエンジニア
- gRPCについて学びたいが、Unityとの関連性も知りたい初学者
- なんとなくGoやgRPCについては理解できるけど、具体的な仕組みについて知りたいUnityエンジニアの方

また、今回のハンズオン記事は全７章が存在します。

こちらから他の章も見ていただけると嬉しいです！
[「第一章、環境構築」](https://qiita.com/4_mio_11/items/08e972ae361c55b317f0)
[「第二章、Cloud Runへのデプロイ」](https://qiita.com/4_mio_11/items/2027978feb5f131fdd48)
[「第三章、CI/CDパイプライン構築」](https://qiita.com/4_mio_11/items/1148aa1603f4acb32088)
[「第四章、Firebaseプロジェクトの作成とFirebaseSDK導入」](https://qiita.com/4_mio_11/items/6a9b61fe2f1db2b5c38a)
[「第五章、FirebaseSDKを用いたアカウント作成とログイン実装」](https://qiita.com/4_mio_11/items/aabefeac56e700afc6e4)


### 今回の環境

- OS : MacOS
- IDE : GoLand
- Gitクライアント : Fork

### 前提

- [A Tour of Go](https://go-tour-jp.appspot.com/welcome/1)を一通り終わらせていること
- gRPCの基本的な概要を理解していること
    - こちらの本がおすすめです（Chapter 02まで読んでおけば本記事の内容についていけます）：[作ってわかる！はじめてのgRPC](https://zenn.dev/hsaki/books/golang-grpc-starting)
- 簡単なUnityの操作は行えること
- Git、GitHubを使用したことがあること

また、記事の内容を自由に学習できるよう、GitHub上に公開リポジトリを作成しました。各ステップに対応したバージョンをReleasesとして登録しているので、興味のあるステップから学習を始められます。

https://github.com/rossam/grpc-chat-app-sample/releases

上記リポジトリ内容を使用して特定のステップから始める場合はリポジトリをForkするか、手元にDownloadなどをしてから進めるようお願いします。

<br>

# 0. なぜバックエンドでGoを選択するか
バックエンドの技術選定は、開発効率や運用性にも大きく影響を与えます。では、なぜソーシャルゲームプロジェクトではバックエンドにgRPCとGoの組み合わせが選択されることが多いのでしょうか？

:::note
私がこれまで関わってきたプロジェクトでは少なくとも上記の選択が行われてました…。
:::

### 高速で効率的
Goは低レイテンシかつ高スループットな処理を実現します。コンパイル言語として、ランタイムオーバーヘッドが少なく、パフォーマンスも安定して出ます。特にGoのゴルーチンは、非常に軽量なスレッドのようなもので、数万単位のゴルーチンを同時に実行しても、システムリソースへの負担が少なくすみます。

大量の同時接続が発生するソーシャルゲームプロジェクト開発では、プレイヤーからの大量のリクエストをリアルタイムで処理する必要があるため、この特徴はかなりの強みと言えます。

### シンプルさと柔軟性
Goは設計がシンプルにでき、柔軟性も高いです。また、マイクロサービスアーキテクチャとの相性が良く、サービスをスケールアウトする際にも柔軟に対応できます。サーバーサイドに特化したライブラリやフレームワーク（gRPC含む）も豊富にあるため、複雑な処理の構築も行いやすいです。

### 学習コストの低さ
ゲームプロジェクトでは、バックエンド開発者とクライアント開発者が異なるスキルセットを持つケースが一般的で、Goはバックエンドエンジニアにとって学習コストが低いことも強みです。


これらの点を踏まえて、Goはサーバーサイドのパフォーマンスや運用効率を最大化したい場合に選択されているのだと思います。

以下のDeNAの記事も参考にしてみてください！

[DeNAがゲームサーバ基盤のリニューアルにGoを選んだ理由](https://logmi.jp/main/technology/322333)

<br>

# 1. Gitリポジトリ作成

まずは、このハンズオン用のGitリポジトリを自分で作成していきましょう。

GitHubにログインして新しいリポジトリを作成してください。

リポジトリ名、説明（Description）、公開設定などは自由に設定してOKです。
今回は「grpc-chat-app-sample」とします。

`.gitignore`ファイルは、Goのテンプレートを選択してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/2900ddc0-af51-5976-284b-faf1c451d232.png)

:::note
**Goの.gitignoreテンプレートに含まれる主な項目は？**
- **`/bin/`**：Goのビルドで生成されるバイナリファイルを除外する
- **`/pkg/`**：ビルドキャッシュや依存関係のパッケージを除外する
- **`.exe`、`.dll`、`.so`**：ビルド結果の実行ファイルを除外する
- **`.test`**：テスト用の実行ファイルを除外する
:::

リポジトリが作成できたら、任意の場所にCloneする用の新規ディレクトリ「chat-app」を作成しましょう。

本記事ではコマンド操作で用意していきます。

**1.ディレクトリ作成**

```bash
mkdir chat-app
```

**2.ディレクトリ内へ移動**

```bash
cd chat-app/
```

**3.先ほど作成したリポジトリをClone**

```bash
git clone {リポジトリのクローン用URL}
```

コマンド結果:

```bash
Cloning into 'grpc-chat-app-sample'...
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (4/4), done.
```

<br>

# 2. **Goのインストール**

:::note
タグv0.1に対応しています。
:::

Go言語を使用するために、公式サイトからGoの最新版をダウンロードしてインストールしましょう。

安定して動作させたい場合は、`Go 1.23.0`をインストールしてください。

[Go公式ダウンロードページ](https://golang.org/dl/)　
（MacユーザーでM1チップ以降をお使いの方は、**arm64**版をダウンロードしてください）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/5ae58419-6dde-583f-cc12-27dde26c115d.png)

ダウンロードが完了したら、インストールを進めてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/115b5672-e238-2140-2768-61b73b373b41.png)

インストール後、ターミナル（またはコマンドプロンプト）を開き、以下のコマンドを使用してGoが正しくインストールされているか確認しましょう。

```bash
go version
```

**コマンド結果**：
```bash
go version go1.23.0 darwin/arm64
```

<br>

# 3. Goプロジェクトの初期化

先ほど作成したリポジトリを、新しいGoのプロジェクトとして初期化していきましょう。

まずはリポジトリのあるディレクトリへ移動します。

```bash
cd grpc-chat-app-sample/
```

以下のコマンドを実行してください。

このコマンドを実行すると、プロジェクトのルートディレクトリに `go.mod` というファイルが作成され、外部ライブラリや依存関係の管理ができるようになります。

```bash
go mod init grpc-chat-app-sample
```

**コマンド結果**：

```bash
go: creating new go.mod: module grpc-chat-app-sample
```

次に、以下のコマンドを実行してください。

これはGo用のgRPCライブラリをインストールするコマンドで、GoでgRPCサーバーやクライアントを実装する際に必要となります。

```docker
go get google.golang.org/grpc
```

**コマンド結果**：

```bash
go: added golang.org/x/net v0.25.0
go: added golang.org/x/sys v0.20.0
go: added golang.org/x/text v0.15.0
go: added google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157
go: added google.golang.org/grpc v1.65.0
go: added google.golang.org/protobuf v1.34.1
```

:::note
**go.modファイル、go.subファイルについてもっと詳しく！**
    
- **`go.mod`** ファイル：
プロジェクトで使用するGoのバージョンや依存するパッケージのバージョンを記録します。

- **`go.sum` ファイル**：
`go.mod` に記載された依存パッケージの具体的なバージョンや、そのチェックサム（パッケージの整合性を確認するためのもの）を記録します。
:::    

<br>

# 4. Dockerのインストール

続いて、Dockerを導入していきます…が、その前にDockerとは何かを簡単におさらいしましょう。

すでにDockerを知っている方は、この部分を飛ばして構いません。

## Dockerとは？

Dockerは、**アプリケーションとその依存関係を一つの「コンテナ」にまとめて実行できるプラットフォーム**です。
この「コンテナ」というのは、軽量でポータブルな仮想環境のようなものです。

<br>

主な特徴は以下の通りです。


- **コンテナ化**：
    Dockerでは、アプリケーションとそれに必要なすべてのライブラリや設定ファイルを一つのパッケージ（コンテナ）にまとめます。コンテナは、WindowsやMacなど、どんな環境でも同じように動作が可能です。

    
- **軽量**：
Dockerコンテナは、従来の仮想マシンよりも軽量で、高速に起動します。コンテナはホストマシンのOSカーネルを共有するため、リソースを効率的に使用できます。
    
- **再現性**：  
Dockerを使えば、特定の環境や依存関係を簡単に再現できます。これにより、開発者間で「自分の環境では動いた」という問題が減ります。

<br>

次に、Dockerで使われる基本的な概念や用語を見ていきましょう。

- **Dockerfile**：
    Dockerイメージを作成するための「設計図」です。アプリケーションの環境設定や必要なソフトウェアのインストール手順を記述したテキストファイルで、この内容に基づいてDockerイメージが作られます。
    
- **Dockerイメージ**：
    アプリケーションとその依存関係をパッケージ化したもので、コンテナを実行するための「実行可能なパッケージ」です。イメージは、Dockerfileという「設計図」から作成されます。
    
- **Dockerコンテナ**：
Dockerイメージを実行して動作するインスタンスを指します。コンテナは、アプリケーションの動作環境を提供します。
    
- **Docker Hub**：
Dockerイメージを共有するためのオンラインリポジトリです。公式イメージや、他の開発者が作成したイメージをここから取得できます。

一気に色々書いても頭に入らないかもしれません。
Dockerに関する書籍は色々出ているので、一度、どれか一冊を通して読むことをお勧めします。

:::note
ちなみに私のおすすめは[「仕組みと使い方がわかる Docker＆Kubernetesのきほんのきほん」](https://amzn.asia/d/g44u8bc)という本です。
図解が多くて非常にわかりやすいので、初心者のかたにおすすめです。
:::
    
<br>

最後に、使い方の流れについて簡単におさらいです。

1. **イメージの取得 or イメージの作成**
2. **コンテナの実行**
3. **コンテナの管理**
    
また、コマンドを使用することで、実行中のコンテナを確認したり、停止したりすることができます。
（例: `docker ps`, `docker stop <コンテナID>`）


今回は、上記の説明をなんとなくでも良いので理解しておいてもらえれば十分です。

### Dockerのインストール

それでは実際に公式サイトからDocker Desktopをダウンロードしましょう。

https://www.docker.com/

:::note
**Docker Desktopとは？**
    
Dockerは元々、Linux環境での使用を前提としていますが、WindowsやMacOSで使うためには、特別なセットアップが必要です。しかし、それではDockerを使用する難易度が上がってしまいます。Docker Desktopは最新版のDockerと、各OSでの実行に必要なツールをパッケージ化しており、簡単にセットアップできます。
:::


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/48949bee-2c7b-451b-fce3-ecf55b05524b.png)

ダウンロードが終わったらインストールを進めてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/d1b6f6c7-0ba5-f1ef-31ab-e9cadcb4c8c4.png)

実際に立ち上げてみましょう。

以下の画面が表示されたら「Continue without signing in」をクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/edbdbe3c-e4bc-9209-f3e8-61887eaeb00a.png)

これでDockerを使う準備ができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/6dec8dcb-8e40-b159-4795-1ce7b4a988f9.png)

尚、Dockerアップデートなどの更新お知らせがある場合は右下のベルマークから通知が来るので、もしも通知があれば適宜確認してみてください。

<br>

# 5. protoファイルの作成

:::note
タグv0.2に対応しています。
:::

一通り、必要最低限の環境構築が完了しました。

ここからはgRPCコードを生成するために、`.proto`ファイルを作成します。

`.proto`ファイルは、 **Protocol Buffers（プロトコルバッファ）** を使用して、クライアントとサーバー間でやり取りする用のメッセージやメソッドを定義するために使います。`.proto`ファイルの内容を元に、gRPCが使うコードが自動生成され、クライアントとサーバーが同じルールで通信を行えるようになります。

:::note
**Protocol Buffersって？**
    
**Protocol Buffers**は、Googleが開発したデータのシリアライズ（エンコードとデコード）形式です。プレーンテキストよりもデータをコンパクトに保存・送信でき、多くの言語やプラットフォームで使えるため、効率的にデータをやり取りできます。
:::

:::note
**どうして自動生成するの？**
    
クライアントとサーバーの通信コードを手動で書くのは大変で、ミスも起こりやすくなります。自動生成を利用することで、定義に基づいた一貫したコードが生成され、クライアントとサーバーがスムーズに通信できるようになります。
:::
    

新たにprotoというフォルダを作成し、その中に`helloworld.proto`ファイルを作成しましょう。

`helloworld.proto`の中身は以下のようになります。

```protobuf
// Protocol Bufferのバージョンを指定
// 3はバージョン2よりも簡潔で使いやすい構文を提供する
syntax = "proto3";

// Protocol Buffersのパッケージ名を指定
// 名前空間だと思ってください
package helloworld;

// 生成するGoコードをどのパッケージに配置するか指定
// 実はディレクトリ構造も色々議論の余地があったり（後述）
option go_package = "gen/api/helloworld";

// Greeterという名前のサービスを定義
// ここで定義されたサービスはRPCインターフェース（後述）として扱われる
// gRPCは、このインターフェースの定義に基づいて通信を行うgRPCコードを自動生成する
service Greeter {
	// クライアントからHelloRequestというメッセージを受け取り、
	// それに基づいてHelloReplyというメッセージを返す、
	// SayHelloというRPC（後述）を提供
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// クライアントから送られてくるメッセージの定義（後述）
message HelloRequest {
  string name = 1;
}

// サーバーからクライアントに返すメッセージの定義（後述）
message HelloReply {
  string message = 1;
}
```

コードの中に出てきた用語のいくつかを追加で説明します。

### **RPC（Remote Procedure Call）とは**

RPCは、あるコンピュータ（クライアント）から別のコンピュータ（サーバー）上のメソッドを呼び出すための技術です。

gRPCはこの概念を取り入れ、ネットワーク越しにメソッドを実行し、その結果をクライアントに返します。

`SayHello`はこのRPCの一例で、クライアントが`HelloRequest`をサーバーに送り、サーバーが`HelloReply`を返すという流れになります。

<br>

### RPCインターフェースとは

RPCインターフェースは、クライアントがサーバー上で定義されたメソッドをリモートで呼び出す際に、どのメソッドを使用し、どのデータを送受信するかを決定するインターフェースです。

通常、`proto`ファイルの`service`ブロックで定義されます。gRPCでは、サービスとして定義されたメソッドが、クライアントとサーバー間の通信エンドポイントになります。

:::note
**エンドポイントって？**

**通信の入り口**とも言えます。
クライアントがサーバーにリクエストを送る際に、サーバー上のどの機能やリソースにアクセスするかを指定するために使われます。エンドポイントは、通常、URLやURI（Uniform Resource Identifier）として表されます。
    
gRPCでは、各メソッドがエンドポイントとして機能します。エンドポイントには、リクエストの送信先と、そのリクエストに対応するメソッドが含まれます。
:::

<br>

### **メッセージ**とは

メッセージは、gRPCを通じて送受信されるデータの形式です。

ここで定義されている`HelloRequest`と`HelloReply`は、それぞれクライアントから送られるリクエストと、サーバーから返されるレスポンスのデータ形式を表しています。

メッセージの各フィールドには、データの型と順序を指定する番号が付けられています。これにより、データが正確にエンコード・デコードされます。

<br>

### ディレクトリ構造について

ディレクトリ構成には様々なパターンがあり、プロジェクトの規模や目的に応じて最適な構成が異なります。

以下に、それぞれのディレクトリ構成の意味と、今回のプロジェクトに最適な構成を説明します。

- **`pkg/proto`**:
    - この構成では、`proto`ディレクトリにProtocol Buffers関連のコードを配置します。
    - `pkg`は再利用可能なコードをまとめる場所で、他のプロジェクトでも使い回せることを意図しています。
- **`pkg/grpc`**:
    - この構成では、`grpc`ディレクトリにgRPCサービス関連のコードを配置します。通常、gRPCのサービスコードやインターフェースがここに置かれます。
    - gRPCサービスを中心に据えたアプリケーションや、gRPC特化のプロジェクトでよく使われます。
- **`gen/api`**:
    - `gen`は「generated（生成された）」の略で、通常、コード生成プロセスによって自動生成されたファイルを格納するためのディレクトリです。
    - `api`はAPI定義やその関連コードを意味し、`gen/api`はProtocol BuffersやgRPCを使用して自動生成されたAPI関連のコードを格納する場所として使用されます。

<br>

今回はシンプルに **`gen/api`** を使用する形にしています。

<br>

# 6. gRPCのコード自動生成

protoファイルの準備ができたので、早速gRPCコードを自動生成していきましょう。

**`protoc`** というProtocol Buffersのコンパイラを使用して、Go言語用のコードをコマンドから生成します。そのためにも、まずは **`protoc`** と **`protoc-gen-go`** プラグイン、合わせて **`protoc-gen-go-grpc`** プラグインをインストールする必要があります。

今回もコマンドで実行を行います。

まずは以下のコマンドで`protoc`をインストールします。

:::note
今回はHomebrewを使ってインストールしますが、もしもHomebrewを使わないのであれば以下の記事に沿ってインストールを行うのが良いかもしれません。

[Protocol Buffersのインストール方法 - Harukaのお道具箱](https://techblog.nullstack.engineer/entry/install_protocol_buffers/)
:::

```bash
brew install protobuf
```

問題なくinstallができたか、バージョンチェックのコマンドを打ってみましょう。

```bash
protoc --version
```

**コマンド結果**：

```bash
libprotoc 27.3
```

<br>

続いて、`protoc-gen-go`プラグインをinstallします。

このプラグインは`.proto`ファイルに定義されたメッセージ（例えば、`HelloRequest`や`HelloReply`）を、Goで扱える構造体に変換します。これにより、Goのコードで簡単にProtocol Buffersを使ったデータのシリアライズ（エンコード）やデシリアライズ（デコード）ができるようになります。

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

<br>

gRPCのコード生成のためには、`protoc-gen-go-grpc`も必要です。

このプラグインは`.proto`ファイルに定義されたgRPCサービス（例えば、`Greeter`サービスや`SayHello`メソッド）に対応するGoのサーバー側とクライアント側のコードを生成します。

以下のコマンドでインストールします。 

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

<br>

プラグイン側の準備が整ったところで、gRPCコードを自動生成してみましょう。

プロジェクトルートディレクトリをワーキングディレクトリとした状態で、以下のコマンドを実行してください。

```protobuf
protoc --go_out=. --go-grpc_out=. ./proto/helloworld.proto
```

以下のように２つのgoファイルが新たに生成されていれば成功です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/ad700198-860a-7f1e-d475-095f38645c1b.png)

上記２つのファイルについては以下の通りです。
- **`helloworld.pb.go`**：
このファイルには、Protocol Buffersのメッセージ定義に基づいたGoのデータ構造が含まれています。これはクライアントとサーバーの両方で使用されます。
- **`helloworld_grpc.pb.go`**：
このファイルには、gRPCサービスのコードが含まれています。具体的には、**サーバー側のインターフェース**と**クライアント側のコード**の両方が含まれています。

**`helloworld_grpc.pb.go`**　には、サーバー側のインターフェースと一緒にGo言語で書かれたクライアント側のコードも含まれていますが、実際にはUnity側のコードがクライアントとなるため、使用しません。

ただ、サーバー側だけを生成するコマンドは無く、クライアント実装だけを削除するのは少し難易度が高いので、ここでは一旦、自動生成されたクライアントコードは無視することにします。



# 7. サーバー側の実装

:::note
タグv0.3に対応しています。
:::

それでは、サーバーをGoで実装していきましょう。
まず、ルートディレクトリに `cmd` フォルダと `server` フォルダを作成してください。

そのあとに、serverフォルダに`main.go`というファイルを作成します。

作成後のディレクトリ構造は以下のようになります。

```protobuf
grpc-chat-app-sample/
│
├── cmd/
│   └── server/             ← サーバーのエントリーポイントを格納するディレクトリ
│       └── main.go         ← gRPCサーバーのエントリーポイント
│
├── gen/
│   └── api/
│       ├── helloworld.pb.go
│       └── helloworld_grpc.pb.go
│
├── proto/
│   └── helloworld.proto
│
├── go.mod
└── go.sum

```

:::note
**cmdディレクトリって？**
    
`cmd`は「command」の略で、**コマンドラインアプリケーションやサーバーアプリケーションのエントリーポイントとなるコード**を格納するために使用されます。

このディレクトリには、通常プロジェクトの中で実行可能なアプリケーションを構築するための`main.go`ファイルが置かれます。複数プロジェクトを含む場合はcmdの下にさらにプロジェクト名を持ったディレクトリを作成し、その中に`main.go`ファイルを置くことが多いようです。
:::

早速`main.go`に実装を追加していきます。

```go
// 実行可能なGoアプリケーションであることを宣言する
package main;

// どの言語でも大抵お馴染みのパッケージインポート
import (
  // クライアントからサーバーへのリクエストのスコープやキャンセル、タイムアウトの管理に使用する
  "context"
  // プログラムの実行中にログ（メッセージ）を記録するためのパッケージ
  "log"
  // ネットワーク関連の機能を提供
  // ここでは、サーバーがリクエストを受け付けるためのネットワークリスナーを作成するためにimport
  "net"

  // gRPCサーバーを作成するためのパッケージ
  "google.golang.org/grpc"
  // 生成されたgRPCコードをインポート
  // pbはこのパッケージを参照するための別名
  pb "grpc-chat-app-sample/gen/api/helloworld"
)

const (
  // ポート番号を定義
  // クライアントはこのポートに接続してリクエストを送信する
  port = ":50051"
)

// サーバー構造体の定義
// gRPCで定義されたサービスを実装する
type server struct {
  // gRPCのサービスを実装する際に必要なデフォルトの設定を提供（後述）
  pb.UnimplementedGreeterServer
}

// 関数の定義
// server型に属すメソッドで、inという名前の引数（*pb.HelloRequest型のポインタ）を受け取り
// HelloReply型のポインタと、エラー情報を戻り値としている
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
  return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
  // サーバーが指定したポート番号（ここでは50051）でTCP接続を待ち受けるリスナーを作成
  // リスナーは、クライアントからの接続を待機する
  // ちなみに := は変数の宣言と初期化を同時に行う「短縮変数宣言」
  lis, err := net.Listen("tcp", port)
  if err != nil {
    log.Fatalf("failed to listen: %v", err)
  }
  
  // 新しいgRPCサーバーを作成
  // このサーバーがクライアントからのリクエストを処理する
  s := grpc.NewServer()
  
  // GreeterサービスをgRPCサーバーに登録
  // ここで、&server{}は先ほど定義したserver構造体のインスタンスを渡している
  pb.RegisterGreeterServer(s, &server{})
  
  // Addr()メソッドは、net.Listenerインターフェースに定義されているメソッド
  // リスナーが現在待ち受けているアドレス（IPアドレスとポート番号の組み合わせ）を返す
  // これにより、サーバーがどのアドレスで接続を待機しているかを確認できる
  log.Printf("server listening at %v", lis.Addr())
  
  // s.Serve(lis)でサーバーを起動し、クライアントからの接続をリスナーを通じて受け付ける
  if err := s.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %v", err)
  }
}

```

:::note
**protoファイルのpackage名と、main.goのpackage名の違いは？**

 - **`proto`ファイルの`package helloworld`**:
Protocol Buffersのメッセージやサービスが属する名前空間を定義し、生成されるGoコードがどのパッケージに属するかを決定します。
        
- **`main.go`の`package main`**:
Goプログラムのエントリーポイントを定義し、実行可能なプログラムとして動作させるためのものです。
:::

こちらでも、コードの中に出てきた用語を追加で説明します。

### `UnimplementedGreeterServer`

`UnimplementedGreeterServer`は、gRPCサービスのすべてのメソッドに対してデフォルトの未実装のバージョンを提供します。

これにより、サービスを部分的に実装する際に役立ちます。例えば、あるメソッドだけを実装して、他のメソッドは未実装のままにしておくことができます。    

`pb.UnimplementedGreeterServer`を構造体`server`に埋め込むことで、`GreeterServer`インターフェースの、すべてのメソッドに対してデフォルトの未実装の実装を提供します。

`main.go`の実装が済んだら手元で実行してみましょう。

ワーキングディレクトリをルートディレクトリにした状態で以下のコマンドを実行してください。

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

以下の表示がされれば成功です。

```go
2024/08/29 14:44:12 server listening at [::]:50051
```

念のため、確認ができた時点で、上記コマンドを実行したターミナルを終了するか、`Ctrl + C`を押下してください。

<br>

# 8. Dockerコンテナ立ち上げ・実行

:::note
タグv0.4に対応しています。
:::

次に、Dockerのセットアップを行い、実際にDockerコンテナを立ち上げて、先ほどのサーバーをコンテナ上で実行してみましょう。

Dockerの特徴である「コンテナ化」については、以下の通りです。

> Dockerでは、アプリケーションとそれに必要なすべてのライブラリや設定ファイルを一つのパッケージ（コンテナ）にまとめます。コンテナは、WindowsやMacなど、どんな環境でも同じように動作が可能です。
> 

コンテナを実行するには、まずDockerイメージを作成する必要があります。

Dockerイメージを作成するためのDockerfile（Dockerイメージの「設計図」）をルートディレクトリに作成しましょう。

```go
grpc-chat-app-sample/
│
├── cmd/
│   └── server/
│       └── main.go       
│
├── gen/
│   └── api/
│       ├── helloworld.pb.go
│       └── helloworld_grpc.pb.go
│
├── proto/
│   └── helloworld.proto
│
├── Dockerfile  ← ココに配置されるよう作成！
├── go.mod
├── go.sum
└── README.md

```

中身は以下の通りとなります。

```docker
# ベースイメージとしてGoの公式イメージを使用
FROM golang:1.20

# コンテナ内での作業ディレクトリを設定
# 以降のコマンドは、このディレクトリ内で実行される
WORKDIR /app

# ホストマシン（あなたのPC）上のプロジェクトディレクトリにあるGoモジュールと依存関係を
# コンテナの作業ディレクトリにコピーする
COPY go.mod .
COPY go.sum .

# プロジェクト全体のコピー
# アプリケーションのソースコードやその他のファイルがすべてコンテナ内に入る
COPY . .

WORKDIR /app/cmd/server

# go.modとgo.sumに基づいて、Goプロジェクトに必要な依存パッケージをダウンロードし、
# コンテナ内にインストール
RUN go mod download

# アプリケーションをビルド
# -o /grpc-chat-serverは、ビルドされた実行ファイルの名前と出力先を指定している
# この実行ファイルが、後でコンテナ内で実行されるgRPCサーバーになる
RUN go build -o /grpc-chat-server

# コンテナがリッスンするネットワークポートを指定
# これにより、コンテナ外部から50051番ポートを通じてgRPCサーバーにアクセスできるようになる
EXPOSE 50051

# CMDは、コンテナが起動したときに実行されるコマンドを指定するもの
# ここでは、先ほどビルドした/grpc-chat-serverという実行ファイルを実行するよう指定している
# これにより、コンテナが起動すると、自動的にgRPCサーバーが立ち上がり、リクエストを受け付ける状態になる
CMD ["/grpc-chat-server"]
```

Dockerfileの準備ができたら、以下のコマンドを実行して、Dockerfileをビルドし、Dockerイメージを作成しましょう。

```docker
docker build -t grpc-chat-server .
```

**コマンド結果**：

```docker
[+] Building 5.8s (13/13) FINISHED                                                                                                           docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                         0.0s
 => => transferring dockerfile: 230B                                                                                                                         0.0s
 => [internal] load metadata for docker.io/library/golang:1.23                                                                                               0.8s
 => [internal] load .dockerignore                                                                                                                            0.0s
 => => transferring context: 2B                                                                                                                              0.0s
 => [1/8] FROM docker.io/library/golang:1.23@sha256:{ランダムな文字列}                                           0.0s
 => [internal] load build context                                                                                                                            0.0s
 => => transferring context: 6.46kB                                                                                                                          0.0s
 => CACHED [2/8] WORKDIR /app                                                                                                                                0.0s
 => CACHED [3/8] COPY go.mod .                                                                                                                               0.0s
 => CACHED [4/8] COPY go.sum .                                                                                                                               0.0s
 => CACHED [5/8] RUN go mod download                                                                                                                         0.0s
 => [6/8] COPY . .                                                                                                                                           0.0s
 => [7/8] WORKDIR /app/cmd/server                                                                                                                            0.0s
 => [8/8] RUN go build -o /grpc-chat-server                                                                                                                  4.7s
 => exporting to image                                                                                                                                       0.2s
 => => exporting layers                                                                                                                                      0.2s
 => => writing image sha256:{ランダムな文字列}                                                                 0.0s
 => => naming to docker.io/library/grpc-chat-server                                                                                                          0.0s
```

これでDockerイメージが作成されました。

このDockerイメージを使ってコンテナ（アプリケーションの実行環境）を立ち上げてみましょう。もしもこの時点で`go run`しているターミナルがまだ存在している場合は該当のターミナルを終了するか、該当のターミナル上で`Ctrl + C`を押下してください。

Dockerコンテナを立ち上げるコマンドは以下の通りとなります。

```docker
docker run -p 50051:50051 grpc-chat-server
```

**コマンド結果**：

```docker
2024/08/29 08:18:53 server listening at [::]:50051
```

# まとめ

今回の第一章では、gRPCとGoの環境構築、そしてDockerでのコンテナ化を体験しました。

今回のハンズオンでは以下のことを学びました。

- Goのインストールとプロジェクトの初期化
- Dockerのインストールとその基本概念の理解
- Protocol Buffersと.protoファイルの作成方法
- protocおよび関連プラグインを使用したコードの自動生成
- サーバーのエントリーポイントとなるmain.goの実装
- UnimplementedGreeterServerを活用した部分的なサービス実装
- Dockerfileの作成とビルド
- Dockerイメージを使ったコンテナの実行

これで、次のステップであるCI/CDやCloud Runの設定に進む準備が整いました。

次回は、Cloud Runの設定に挑戦していきます。

もしも記事の中で進められない箇所があったら、Xなどでご連絡ください。