Golang ローカル開発環境構築ガイド 

この記事では、Golang を用いた開発環境を Docker 上に構築する手順を紹介します。これまでの実践やトラブルシュート（たとえば、go.mod が見つからないエラー、Air のインストールパスの変更、ポートマッピングの不一致など）を踏まえ、初心者でもわかりやすく手順を整理しました。

---

## 目次

1. プロジェクトのディレクトリ構成の準備
2. Go モジュールの初期化
3. Dockerfile の作成と Air のインストール
4. Air の設定ファイル（.air.toml）の詳細解説
5. サンプル Go コードの作成（HTTP サーバーの実装）
6. docker-compose.yml の作成とボリューム／ポートマッピング設定
7. ローカル環境の起動と動作確認
8. まとめ

---

## 1. プロジェクトのディレクトリ構成の準備

まず、プロジェクト全体の親ディレクトリ（例：`CPI-Explorer`）を作成し、その中に Golang のサービス用ディレクトリ（ここでは **cpi-fetcher**）を用意します。以下のような構成を目標とします。

```
CPI-Explorer/                     ← プロジェクト全体のルート
├── docker-compose.yml           ← 全体のコンテナオーケストレーション用
└── cpi-fetcher/                 ← Golang サービス用ディレクトリ
    ├── Dockerfile               ← Docker イメージのビルド手順
    ├── .air.toml                ← Air の設定ファイル（ホットリロード用）
    ├── go.mod                   ← Go モジュールの初期化ファイル
    ├── go.sum                   ← 依存パッケージのチェックサム（自動生成）
    ├── src/                     ← ソースコード用ディレクトリ
    │   └── main.go              ← アプリケーションのエントリーポイント
    └── tmp/                     ← ビルド成果物出力先（Air の設定で指定）
```

ターミナルで以下のコマンドを実行して準備してください。

```sh
mkdir -p ~/projects/CPI-Explorer/cpi-fetcher/src
cd ~/projects/CPI-Explorer/cpi-fetcher
```

---

## 2. Go モジュールの初期化

Golang では依存管理に「Go Modules」を利用します。  
**cpi-fetcher** ディレクトリ内で以下のコマンドを実行してモジュール初期化を行います。

```sh
go mod init github.com/your-username/CPI-explorer/cpi-fetcher
```

※ 実際の GitHub リポジトリ URL に合わせて `your-username` は適宜変更してください。  
これにより、`go.mod` が生成され、外部ライブラリの依存管理ができるようになります。

---

## 3. Dockerfile の作成と Air のインストール

Dockerfile を作成し、コンテナ内で Golang の環境と Air をセットアップします。  
Air はホットリロードツールで、ソースコードの変更検知時に自動でビルド・再起動してくれます。

**注意:** 以前は `github.com/cosmtrek/air` を使っていましたが、現在はモジュールパスが変更され、正しくは `github.com/air-verse/air` です。

**cpi-fetcher/Dockerfile**

```Dockerfile
# ベースイメージ（Go 1.23）
FROM golang:1.23

# 作業ディレクトリを /app に設定
WORKDIR /app

# ホットリロードツール Air をインストール（正しいパスを使用）
RUN go install github.com/air-verse/air@latest

# ソースコードはホスト側からマウントするので、コピーは不要

# コンテナ起動時に Air を実行する（設定ファイル .air.toml を利用）
CMD ["air", "-c", ".air.toml"]
```

---

## 4. Air の設定ファイル（.air.toml）の詳細解説

Air の設定は `.air.toml` で行います。以下はサンプルと各項目の詳細です。

**cpi-fetcher/.air.toml**

```toml
root = "."
tmp_dir = "tmp"

[build]
  # ソースコードをビルドするコマンド。src ディレクトリ内のコードをコンパイルして tmp/main に出力
  cmd = "go build -o tmp/main ./src"
  
  # 出力されたバイナリのパス
  bin = "tmp/main"
  
  # 監視対象の拡張子。これらのファイルが変更されたら再ビルドをトリガー
  include_ext = ["go", "tpl", "tmpl", "html"]
  
  # 監視対象から除外するディレクトリ。特にビルド成果物の tmp は除外することで無限ループを防止
  exclude_dir = ["assets", "tmp", "vendor"]

[log]
  # ログにタイムスタンプを表示。デバッグに役立つため true を設定
  time = true

[run]
  # ビルド後に実行するコマンド。ここではビルド済みのバイナリ tmp/main を実行
  cmd = "tmp/main"
```

### 各設定項目のポイント

- **root:**  
  プロジェクトのルートディレクトリを指定。通常は `"."` でOKです。

- **tmp_dir:**  
  ビルド成果物（バイナリファイル）を出力する一時ディレクトリ。ここで指定したディレクトリ内に、`go build` の出力結果が保存されます。

- **[build] セクション:**  
  - **cmd:** 実際のビルドコマンド。`go build -o tmp/main ./src` は、`src` 内のコードをコンパイルし、`tmp/main` にバイナリを作成します。  
  - **bin:** ビルド後の実行ファイルのパス。Air が実行時にこの値を使用します。  
  - **include_ext:** 監視対象のファイル拡張子。これらのファイルが変更されると、Air が再ビルドを行います。  
  - **exclude_dir:** 監視から除外するディレクトリ。特にビルド成果物が出力される `tmp` は除外するのがポイントです。

- **[log] セクション:**  
  ログの出力形式を設定します。タイムスタンプ付きにすると、ビルドや実行のタイミングがわかりやすくなります。

- **[run] セクション:**  
  ビルドが成功した後に実行するコマンドを指定します。ここでは、先ほどビルドした `tmp/main` を実行しています。

---

## 5. サンプル Go コードの作成

ここでは、簡単な HTTP サーバーのサンプルコードを用意します。  
**cpi-fetcher/src/main.go**

```go
package main

import (
	"fmt"
	"log"
	"net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	// クライアントにレスポンスを返す
	fmt.Fprintln(w, "Hello from CPI Explorer - cpi-fetcher!")
}

func main() {
	// ルートパスに対してハンドラーを設定
	http.HandleFunc("/", helloHandler)
	port := "8080"
	log.Printf("cpi-fetcher is running on port %s...\n", port)
	
	// すべてのインターフェースからのアクセスを受け付けるため、0.0.0.0 を指定
	log.Fatal(http.ListenAndServe("0.0.0.0:"+port, nil))
}
```

このコードは、コンテナ内で 8080 番ポートにて HTTP サーバーを起動し、アクセスがあれば「Hello from CPI Explorer - cpi-fetcher!」と返すシンプルなものです。

---

## 6. docker-compose.yml の作成とボリューム／ポートマッピング設定

親ディレクトリ **CPI-Explorer** に docker-compose.yml を作成し、サービス全体を管理します。  
特に、ホスト側の **cpi-fetcher** ディレクトリ全体をコンテナ内の `/app` にマウントすることで、`go.mod` やその他のファイルが正しく利用されるようにします。

**CPI-Explorer/docker-compose.yml**

```yaml
version: '3.8'

services:
  cpi-fetcher:
    build: ./cpi-fetcher
    container_name: cpi-fetcher
    volumes:
      - ./cpi-fetcher:/app
    ports:
      - "8080:8080"
    working_dir: /app
```

### ポイント

- **Volumes:**  
  `./cpi-fetcher:/app` とすることで、ホストの cpi-fetcher ディレクトリ全体がコンテナ内の `/app` にマッピングされ、`go.mod`、`src/`、`.air.toml` などが正しく利用されます。  
  ※ 個別にファイルをマウントする方法もありますが、全体マウントがシンプルです。

- **Ports:**  
  コンテナ側の 8080 番ポートとホスト側の 8080 番ポートをマッピングします。  
  ※ 初期のトラブルで、ホストとコンテナ間のポートマッピングが不一致だと「ERR_EMPTY_RESPONSE」などのエラーが発生するため注意してください。

- **working_dir:**  
  コンテナ内での作業ディレクトリを `/app` に設定しています。これにより、Air や `go build` が正しいパスで実行されます。

---

## 7. ローカル環境の起動と動作確認

すべての設定が完了したら、Docker Compose を使ってコンテナを起動します。

1. 親ディレクトリ **CPI-Explorer** に移動し、以下のコマンドを実行します。

   ```sh
   cd ~/projects/CPI-Explorer
   docker-compose up --build -d
   ```

2. コンテナのログを確認して、Air のビルドやアプリケーションの起動が正しく行われているか確認します。

   ```sh
   docker-compose logs cpi-fetcher
   ```

   ログには「cpi-fetcher is running on port 8080...」と表示されるはずです。

3. ブラウザや curl でアクセスし、レスポンスが返ってくるかテストします。

   ```sh
   curl http://localhost:8080
   ```

   正常であれば、「Hello from CPI Explorer - cpi-fetcher!」と表示されます。

---

## 8. まとめ

この記事では、Golang のローカル開発環境を Docker と Air を使って構築する手順を整理しました。  
これまでのトラブル（go.mod ファイルが見つからない、Air のインストールパスの変更、ポートマッピングの不一致など）も解決しながら、以下の手順で環境を整えました。

- **プロジェクトのディレクトリ構成の準備:**  
  必要なディレクトリとファイル（go.mod、src/、.air.toml など）を用意。

- **Go モジュールの初期化:**  
  `go mod init` コマンドで依存管理を開始。

- **Dockerfile の作成と Air のインストール:**  
  正しいモジュールパス（github.com/air-verse/air）を使用し、Air をインストール。

- **.air.toml の詳細設定:**  
  ビルドコマンド、監視対象、除外ディレクトリなど、ホットリロードに必要な設定を実施。

- **サンプルコードの実装:**  
  簡単な HTTP サーバーを実装し、実際にレスポンスが返るか確認。

- **docker-compose.yml の設定:**  
  ホスト側のディレクトリ全体をコンテナ内にマウントし、ポートマッピングを正しく設定。

- **起動と検証:**  
  Docker Compose でコンテナをビルド・起動し、ログや curl で動作確認。

これらの手順を参考に、快適な Golang の開発環境を構築してください。  
もし疑問や問題が発生した場合は、各設定ファイルやログを再確認し、適切な調整を行って頂ければ解決できるはずです。dockerでのローカル環境構築では、`docker compose logs container_name`をデバッグで頻繁に利用します。覚えておくと便利です。

以上、Docker と Air を利用した Golang のローカル開発環境構築手順の解説でした。