Google App Engine で始める Go 実践入門 Part 6 【Hello World】

連載を通して簡単なブログアプリを作成しつつ Go/GAE について学んでいきます。
今回は Hello World を出力してみます。

- [Part 1. 『GO / Cloud SDK のインストール』](https://qiita.com/rema424/items/743bc508ef70cd980e17)
- [Part 2. 『GOPATH』](https://qiita.com/rema424/items/009f6345eb75a80bf369)
- [Part 3. 『プロジェクトの作成』](https://qiita.com/rema424/items/19b946c311f3b3b3f953)
- [Part 4. 『ディレクトリ構成』](https://qiita.com/rema424/items/1a6096c77b90ea350a26)
- [Part 5. 『Visual Studio Code』](https://qiita.com/rema424/items/b2ecdd2c7ec7fc1b0a7c)
- [Part 6. 『Hello World』](https://qiita.com/rema424/items/66ce1b02d7de588e63cf) ← イマココ
- [Part 7. 『リモートデバッグ』](https://qiita.com/rema424/items/996aaf0ab69670224af4)
- [Part 8. 『デプロイ』](https://qiita.com/rema424/items/dbf851908dbfc95f9b6f)
- [Part 9. 『Makefile』](https://qiita.com/rema424/items/3a099dfb023aa06086f5)
- [Part 10. 『Circle CI』](https://qiita.com/rema424/items/4dcb37346cecc544dfaa)
- Part 11. 『Dependency Injection』
- Part 12. 『ローカルDB with Docker』
- Part 13. 『gorm』
- Part 14. 『Cloud SQL』
- Part 15. 『Cloud SQL Proxy』
- Part 16. 『HTML』
- Part 17. 『CSS/JS』
- Part 18. 『Google Analytics』
- Part 19. 『Google Search Console』
- Part 20. 『A/B テスト』
- Part 21. 『ユーザーの作成』
- Part 22. 『Firebase Auth』
- Part 23. 『記事の作成』
- Part 24. 『コメントの作成』
- Part 25. 『ページネーション』

# パッケージ管理ツール dep のインストール

パッケージ管理ツールとして今回は `dep` を採用します。
Go 1.11 から導入された `go mod` でもいいとは思いますが、今回は `dep` で。

`dep` は次のコマンドでインストールします。カレントディレクトリの位置はどこでも構いません。

```sh
go get -u -v github.com/golang/dep/cmd/dep
```

次にパッケージ管理の初期化を行います。ディレクトリ名は自分のもので読み替えてください。

```sh
# プロジェクトルートに移動
cd $GOPATH/src/github.com/rema424/go-gae-blog-app-example

# パッケージ管理の初期化
dep init
```

ここまでが完了すると、 GOPATH 配下は次のようになっているかと思います。

```
$GOPATH
  ├── bin
  │     └── dep
  └── src
        └── github.com
              ├── golang
              │     └── dep
              └── rema424
                    └── go-gae-blog-app-example
                          ├── vendor
                          ├── .gitignore
                          ├── Gopkg.lock
                          ├── Gopkg.toml
                          └── README.md
```

# Hello World

ウェブフレームワークには Echo を採用します。
Echo のガイドに App Engine を利用する場合のレシピが載っているので参考にします。

[Google App Engine Recipe | Echo](https://echo.labstack.com/cookbook/google-app-engine)

以下、ターミナルでの作業はプロジェクトルートで行っていきます。

```sh
# ディレクトリ移動
cd $GOPATH/src/rema424/go-gae-blog-app-example

# ディレクトリ作成
mkdir -p module/blog/main module/blog/handler

# ディレクトリ移動
cd module/blog/main

# ファイル作成
touch app.yaml app.go app-engine.go app-standalone.go

# ディレクトリ移動
cd ../handler

# ファイル作成
touch hello_handler.go
```

この時点でのディレクトリ構成は次の通りです。

```
go-gae-blog-app-example
  ├── .vscode
  │     └── settings.json
  └── module
        ├── blog
        │     ├── handler
        │     │     └── hello_handler.go
        │     └── main
        │           ├── app-engine.go
        │           ├── app-standalonde.go
        │           ├── app.go
        │           └── app.yaml
        ├── vendor
        ├── .gitignore
        ├── Gopkg.lock
        ├── Gopkg.toml
        └── README.md
```

ファイルにコードを書いていきます。

```go:modue/blog/handler/hello_handler.go
package handler

import (
	"net/http"

	"github.com/labstack/echo"
)

type (
	// Response ...
	Response struct {
		Message string
	}
)

// HelloHandler ...
func HelloHandler(c echo.Context) error {
	message := "Hello World!"

	res := &Response{
		Message: message,
	}

	return c.JSON(http.StatusOK, res)
}

// ParrotHandler ...
func ParrotHandler(c echo.Context) error {
	message := c.Param("message")

	res := &Response{
		Message: message,
	}

	return c.JSON(http.StatusOK, res)
}
```

```go:modue/blog/main/app.go
package main

import (
	"github.com/rema424/go-gae-blog-app-example/module/blog/handler"
)

var e = createMux()

func init() {
	e.GET("/", handler.HelloHandler)
	e.GET("/:message", handler.ParrotHandler)
}
```

```go:modue/blog/main/app-engine.go
// +build appengine

package main

import (
	"net/http"

	"github.com/labstack/echo"
)

func createMux() *echo.Echo {
	e := echo.New()

	http.Handle("/", e)

	return e
}
```

```go:modue/blog/main/app-standalone.go
// +build !appengine

package main

import (
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func createMux() *echo.Echo {
	e := echo.New()

	e.Use(middleware.Recover())
	e.Use(middleware.Logger())
	e.Use(middleware.Gzip())

	return e
}

func main() {
	e.Logger.Fatal(e.Start(":8080"))
}
```

```yaml:module/blog/main/app.yaml
runtime: go
api_version: go1.9

handlers:
  - url: /.*
    script: _go_app

inbound_services:
  - warmup
```

Go の `Build Constraints` (もしくは `Build Tag` ) と呼ばれる機能を使っています。
これにより App Engine 環境でもその他の環境でも動作するコードになっています。

Build に関する機能を使っているためアプリケーションの動作にはビルドが必要です。
`go run` での起動はできないことに注意です。

ソースコードが出来上がったら次に関連モジュールを `dep` でインストールしていきます。
`dep` はソースコード上で import ブロックに記載されたモジュールを一括でインストールします。

```sh
# ディレクトリ移動
cd $GOPATH/src/rema424/go-gae-blog-app-example

# モジュールのインストール
dep ensure -v
```

モジュールが `vendor` ディレクトリ配下にインストールされているのが分かるかと思います。

これらモジュールは git 管理する必要はないので、`.gitignore` に vendor を追加します。

```sh
echo 'vendor' >> .gitignore
```

それではローカル PC 環境でアプリケーションを起動します。

```sh
# app.yaml のあるモジュールルートに移動
cd module/blog/main/

# ビルドを実行
go build

# バイナリを実行
./main
```

アプリケーションが起動するので、ブラウザで `localhost:8080` にアクセスすると Hello World! が表示されるはずです。

# App Engine ローカル開発サーバーの起動

App Engine はローカル開発サーバーを提供しています。

これにより「ローカルでは動いていたのに App Engine にデプロイしたら動かなかった」という可能性を小さくすることが可能となっています。

```sh
dev_appserver.py module/blog/main/app.yaml
```

サーバーが立ち上がるので、`localhost:8080` にアクセスすると Hello World! が表示されます。
以降の開発は App Engine ローカル開発サーバーで行うと良いでしょう。ホットリロード機能も付いています。

`dev_appserver.py` でサーバーを起動すると、同時に Cloud Datastore Emulator も起動されます。
今回は Cloud Datastore は使わないので、次のようなオプションを指定することによりサーバーの起動を速めることができます。

```sh
dev_appserver.py module/blog/main/app.yaml --support_datastore_emulator=False
```

`--support_datastore_emulator=False` なしで起動すると、 `app.yaml` と同じディレクトリに `index.yaml` というファイルが自動生成されますが、 Cloud Datastore は使わないので削除しておきましょう。削除しておかないと後のデプロイの工程で失敗します。

`dev_appserver.py` ではなく `goapp serve` コマンドでも同様にサーバーを起動することができます。

```sh
goapp serve module/blog/main/app.yaml
```

`goapp serve` コマンドは `dev_appserver.py` のラッパーになっています。
ただし、オリジナルの `dev_appserver.py` よりも利用できるオプションが少ないので `goapp` は基本的には使わなくて良いかと思います。

例えば、サーバー起動時に環境変数を渡すといったことが `dev_appserver.py` ではできますが `goapp serve` ではできません。

App Engine のローカル開発サーバーはホスト OS 上で export された環境変数は読み込みません。環境変数を利用するためにはサーバー起動時にオプションで渡すか、 `app.yaml` ファイルに記載するかのどちらかになります。

しかし、GitHub にアップできないような情報に対して環境変数を使いたいので、GitHub で管理したい `app.yaml` には環境変数は定義できません。したがって、必然的に `goapp` ではなく `dev_appserver.py` を使うことになるかと思います。

# おわりに

次回のテーマは『[リモートデバッグ](https://qiita.com/rema424/items/996aaf0ab69670224af4)』です。

よかったら Twitter フォローしてね。[@_rema424](https://twitter.com/_rema424)