【godotenv】.envファイルへ環境変数をまとめる

## はじめに
Go開発にて、```main.go```で以下のようにハードコーディングしていたDB接続情報を```godotenv```を使って設定ファイルに書き出すように変更したのでメモしておきます。
```golang:main.go
// DB 接続
func dbConnect() *gorm.DB {
	DBMS := "mysql"
	USER := "go_test"
	PASS := "password"
	PROTOCOL := "tcp(db:3306)"
	DBNAME := "go_database"
    ...
```

## 目次
[1. godotenv とは？](#godotenv-とは)
[2. .env ファイルの作成・読み込み](#env-ファイルの作成読み込み)
[3. 参考文献](#参考文献)

## godotenv とは？
設定(.env)ファイルへ環境変数を定義したい時に使用するライブラリです。
環境変数の定義および管理の煩雑化を防止できます。

以下でインストールを行います。
コマンド実行は、<b>プロジェクトのルートディレクトリ直下</b>です。
```
go get github.com/joho/godotenv
```

## .env ファイルの作成・読み込み
### .env ファイル作成
ディレクトリ構成は以下です。
```
/
|- .env
|- main.go
|- templates/
      |- index.html
```
.env ファイルは、<b>プロジェクトのルートディレクトリ直下</b>へ作成します。
ちなみに```sample.env```等、ファイル名は自由で構いません。
```.env
DB_USER=hogehoge
DB_PASSWORD=password
DB=go_database
DB_HOST=db
DB_PORT=3306
DB_PROTOCOL=tcp
DBMS=mysql
```

### パッケージ・ライブラリ定義
今回の目的には直接関連はないのでサラッとご紹介しておきます。
　```log``` ：日付、時間、メッセージの表示が可能
　```os``` ：ファイルの読み書きが可能

```golang:main.go
package main

import (
	"fmt"
	"log" // 追加
	"os" // 追加

	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	"github.com/joho/godotenv" // 追加
)
```

### .env ファイル読み込み
```main.go```へ .env ファイル読み込み用のメソッドを追加します。
```golang:main.go
func envLoad() {
	err := godotenv.Load(".env")
	if err != nil {
		log.Fatalf("Error loading env target")
	}
}
```

### DB接続
```golang:main.go
func dbConnect() *gorm.DB {
	// 環境変数から接続文字列を作成
	connect := fmt.Sprintf("%s:%s@%s(%s:%s)/%s", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_PROTOCOL"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB"))

	// 環境変数からDBMSの種類を取得・DB接続
	db, err := gorm.Open(os.Getenv("DBMS"), connect)
	if err != nil {
		panic(err.Error())
	}
	return db
}
```

## まとめ
繰り返しになりますが、.env ファイルへ情報をまとめる事で以下の利点が得られます。
　・<b>情報の管理が煩雑化しない</b>
　・ハードコーディングがなくなり、機能改修等に伴う<b>プログラムの変更も一箇所で済む</b>

### 改変後
以下が今回変更を加えた後の内容です。
.env の情報を取得 ⇨ DB接続 ⇨ users テーブルのデータを画面一覧へ描画する処理です。
```.env
DB_USER=hogehoge
DB_PASSWORD=password
DB=go_database
DB_HOST=db
DB_PORT=3306
DB_PROTOCOL=tcp
DBMS=mysql
```
```golang:main.go
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	"github.com/joho/godotenv"
)

type User struct {
	gorm.Model
	Name       string
	Department string
	Email      string
	Phone      string
}

func main() {
	// 設定ファイルから環境変数の情報を取得
	envLoad()

	db := dbConnect()
	db.AutoMigrate(&User{})
	defer db.Close()

	router := gin.Default()
	router.LoadHTMLGlob("templates/*.html")

    // users テーブルデータを取得して、index.html へ描画
	router.GET("/", func(ctx *gin.Context) {
		db := dbConnect()
		var users []User
		db.Order("created_at asc").Find(&users)
		defer db.Close()

		ctx.HTML(200, "index.html", gin.H{
			"users": users,
		})
	})

	router.Run()
}

// .env ファイル読み込み
func envLoad() {
	err := godotenv.Load(".env")
	if err != nil {
		log.Fatalf("Error loading env target")
	}
}

// DB　接続
func dbConnect() *gorm.DB {
	// 環境変数から接続文字列を作成
	connect := fmt.Sprintf("%s:%s@%s(%s:%s)/%s", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_PROTOCOL"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB"))

	// 環境変数からDBMSの種類を取得
	db, err := gorm.Open(os.Getenv("DBMS"), connect)
	if err != nil {
		panic(err.Error())
	}
	return db
}
```
```html:templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>My App</title>
  <style>
    label {
      width: 200px;
      float:left;
      margin: 0 50px 25px 0;
    }
    p {
      margin-bottom: 5px;
    }
    input {
      height: 20px;
      margin-bottom: 10px;
    }
    tr {
      width: 50%;
    }
    th,td{
      border:1px #333 solid;
    }
    table{
      margin-top: 5px;
      width: 100%;
      border-collapse: collapse;
    }
    #tdSample {
      margin-left: 5px;
    }
    button {
      width: 75px;
      height: 30px;
      color: #FFF;
      background-color: #3CB371;
      border: 0;
      border-radius: 3px;
      cursor: pointer;
      font-weight: bold;
    }

  </style>
</head>
<body>
  <h2>ユーザー一覧</h2>
  <form method="post" action="/new" style="margin: 10px 50px 10px 50px;">
    <div style="width: 100%;">
      <label>
        <p>氏名</p>
        <input type="text" name="name" size="30" placeholder="入力してください">
      </label>
      <label>
        <p>所属部署</p>
        <input type="text" name="department" size="30" placeholder="入力してください">
      </label>
      <label>
        <p>メールアドレス</p>
        <input type="text" name="email" size="30" placeholder="入力してください">
      </label>
      <label>
        <p>電話番号</p>
        <input type="text" name="phone" size="30" placeholder="入力してください">
      </label>
      <div style="padding-top: 43px;">
        <button type="submit">登録</button>
      </div>
    </div>
  </form>


    <table style="width: 90%;margin: 10px 50px 10px 50px;">
      <tr>
        <th style="width: 50px;"></th>
        <th>氏名</th>
        <th>所属部署</th>
        <th>メールアドレス</th>
        <th>電話番号</th>
      </tr>
      {{ range .users }}
        <tr>
          <td>
            <form method="post" action="/delete/{{.ID}}">
              <div align="center">
                <button type="submit">削除</button>
              </div>
            </form>
          </td>
          <td><div id="tdSample">{{ .Name }}</div></td>
          <td><div id="tdSample">{{ .Department }}</div></td>
          <td><div id="tdSample">{{ .Email }}</div></td>
          <td><div id="tdSample">{{ .Phone }}</div></td>
        </tr>
      {{ end }}
    </table>
</body>
</html>
```

## 参考文献
<a href="https://codelab.website/golang-godotenv/">[Go 言語]環境変数を使えるようにする方法( godotenv )</a>
<a href="https://golang.hateblo.jp/entry/golang-how-to-use-struct">log package - log - Go Packages</a>
<a href="https://pkg.go.dev/log">Go言語(golang) 構造体の定義と使い方</a>
<a href="https://pkg.go.dev/os">os package - os - Go Packages</a>
<a href="https://pkg.go.dev/strconv">strconv package - strconv - Go Packages</a>