【Go】ログイン機能でウェブアプリを作ってみる（3）

こんにちは。

Part 3は準備（MySQL & docker-compose.yml）についてです。

# 目標

- MySQLを起動する
- GoからMySQLに接続する

## MySQLを起動する

### MySQLの設定ファイルを準備します。

早速、MySQLを起動の前に、後々必要になるかもしれない設定などをしています。
そのために必要なディレクトリを作成します。

```
$ mkdir -p _tools/mysql/{init.d,conf.d}
```

今回で言うとMySQLに関するファイルを_toolsディレクトリ以下にまとめます。

_tools/mysql/conf.dにはMySQLの設定ファイルを置きます。

_tools/mysql/conf.d/my.cnf
```
[mysqld]
character-set-server=utf8mb4

[client]
default-character-set=utf8mb4
```

_tools/mysql/init.dには今回使うユーザーのテーブル作成用のファイルを作成します。

_tools/mysql/init.d/init.sql
```
CREATE TABLE `user` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `email` VARCHAR(255) NOT NULL UNIQUE,
  `password` VARCHAR(60) NOT NULL,
  `salt` VARCHAR(30) NOT NULL,
  `state` VARCHAR(8) NOT NULL,
  `activate_token` VARCHAR(8) NOT NULL,
  `updated_at` DATETIME(6) NOT NULL,
  `created_at` DATETIME(6) NOT NULL,
  PRIMARY KEY (`id`),
  INDEX email_idx (email)
) Engine=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=100001;
```

これで設定ファイルの準備は完了です。

### MySQLを起動する

docker-compose.yml
```
version: "3.8"

services:
  api:
    container_name: login-example-api
    build:
      dockerfile: Dockerfile
      context: .
    volumes:
      - ".:/app"
    ports:
      - "8000:8000"
    environment:
      DB_USER: login-user
      DB_PASSWORD: login-pass
      DB_NAME: login-db
      DB_HOST: db
      DB_PORT: 3306
    depends_on:
      db:
        condition: service_healthy
  db:
    container_name: login-example-db
    image: mysql:8.0.33
    platform: linux/amd64
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: login-db
      MYSQL_USER: login-user
      MYSQL_PASSWORD: login-pass
    ports:
      - "33306:3306"
    volumes:
      - type: volume
        source: login-example-db
        target: /var/lib/mysql
      - type: bind
        source: ./_tools/mysql/conf.d
        target: /etc/mysql/conf.d
      - type: bind
        source: ./_tools/mysql/init.d
        target: /docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD", "mysqladmin", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5
  mail:
    container_name: login-example-mail
    image: mailhog/mailhog
    platform: linux/x86_64
    ports:
      - "8025:8025"
      - "1025:1025"
volumes:
  login-example-db:
```

part2で作成したdocker-compose.ymlを修正いたしました。

サービスを起動します。
```
$ docker compose up -d # -d：バックグラウンドで起動
```
これで起動ができました。
これでMySQLの準備はOKです！

## GoからMySQLに接続する

接続するために必要なパッケージをダウンロードしておきます。

```
$ go get github.com/go-sql-driver/mysql
$ go get github.com/jmoiron/sqlx
```

必要なパッケージが準備できたら接続用のコードを書いていきます。
db/db.go
```
package db

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

func NewDB() (*sqlx.DB, error) {
	// 環境変数からDBの情報を取得します
	dbUser := os.Getenv("DB_USER")         // login-user
	dbPassword := os.Getenv("DB_PASSWORD") // login-pass
	dbHost := os.Getenv("DB_HOST")         // db
	dbName := os.Getenv("DB_NAME")         // login-db
	dbPort := os.Getenv("DB_PORT")         // 3306
	src := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true",
		dbUser, dbPassword, dbHost, dbPort, dbName)

	db, err := sql.Open("mysql", src)
	if err != nil {
		return nil, fmt.Errorf("failed to Open DB: %w", err)
	}

	// mysqlに実際に接続できているかpingで確認します。
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	if err := db.PingContext(ctx); err != nil {
		db.Close()
		return nil, fmt.Errorf("failed to Ping DB: %w", err)
	}

	xdb := sqlx.NewDb(db, "mysql")
	return xdb, nil
}
```

MySQLに接続するためのコードが書けました。

では、本当にこのコードで接続できるか確認いたします。

### 確認

MySQLに本当に接続できるか確認するためのmain.goを修正します。

main.go
```
package main

import (
	"fmt"
	"login-example/db"
)

func main() {
	db, err := db.NewDB()
	if err != nil {
		fmt.Println(err)
		return
	}
	defer db.Close()

	fmt.Println("MySQL接続OK")
}
```

logで確認
```
$ docker compose logs api -f

# 最後に「MySQL接続OK」と出れば接続できています！
login-example-api   |   __    _   ___  
login-example-api   |  / /\  | | | |_) 
login-example-api   | /_/--\ |_| |_| \_ , built with Go 
login-example-api   | 
login-example-api   | mkdir /app/tmp
login-example-api   | watching .
login-example-api   | watching _tools
login-example-api   | watching _tools/mysql
login-example-api   | watching _tools/mysql/conf.d
login-example-api   | watching _tools/mysql/init.d
login-example-api   | watching db
login-example-api   | !exclude tmp
login-example-api   | building...
login-example-api   | go: downloading github.com/go-sql-driver/mysql v1.7.1
login-example-api   | go: downloading github.com/jmoiron/sqlx v1.3.5
login-example-api   | running...
login-example-api   | MySQL接続OK

```

# まとめ

ディレクトリ構成は次のようになりました。
```
.
├── Dockerfile
├── .air.toml
├── _tools
│   └── mysql
│       ├── conf.d
│       │   └── my.cnf
│       └── init.d
│           └── init.sql
├── db
│   └── db.go
├── docker-compose.yml
├── go.mod
├── go.sum
└── main.go
```


今日は以上です。
ありがとうございました。

今日作成したアプリをgithubに追加しました。
必要な場合は[こちらのリンク](https://github.com/pinpointdev90/login-example-go-part3)をクリックしてください。

次のPart 4はdocker-compose.ymlの解説です。
よろしくお願いいたします。