【Go】Go言語Web開発プロジェクト Part1 - プロジェクト固有の設定を行いgitにcommitするまで - 

## はじめに

こんにちは。ここ最近Rails以外の言語を触ることがなくなってきた、また今後の可能性としてプライベートでGo言語によるWeb開発を進めるかもしれないため、今回はGoデビューとのことでここにそのエビデンスを残します。Goに関しては知識がほとんどないため、まずは資料集めです。それではいってみましょう。

## なぜGo言語を選ぶのか?

ある友人からGo言語は学習コストが低いからとそれまでRailsで開発していたプロジェクトをGo言語にリプレースしていると言うことを聞きました。Go言語の学習コストが低いかどうかはともかくとして、僕が興味を持ったのは事実であり、実際にGo言語を強く推しているエビデンスを探してみると以下のような記事がありました。

[Why should you learn Go?](https://medium.com/exploring-code/why-should-you-learn-go-f607681fad65)

要約程度に

```
* ハードウェアの進歩は年々向上しているが、シングルスレッドのパフォーマンスとプロセッサの周波数は頭打ちになってきた
* 上の問題を解決するために、プロセッサにキャッシュの追加やハイパースレッディング、コアを増やすと言った解決法を行ってきたが物理的な限界はくるし、コストもかかる
* したがってハードウェア以外の観点からパフォーマンスを向上させるためにソフトウェアでパフォーマンスを向上させる
* 現代のプログラミング言語(Java, Pythonなど)は最初はシングルスレッドでの開発のみを想定していた
* Go言語は初めからマルチスレッドによる開発を想定して実装されている
* Goはスレッドの代わりにGoルーチンを採用している
* GoはJava,Pythonと言った高級言語とC,C++と言った低水準言語の良い所取りだ
* Go言語はクラスや継承、例外などをサポートしていない
```

英語は勉強中ですが、なんとなくの理解では読めました。記事を読んでいたのですが、これで学習コストが低いと言われてもあまりピンときていませんが、しかし最初からパフォーマンスを考慮して設計された言語であるというのは、とても魅力的な言語であることは確かです。これでGo言語を学ぶモチベーションは出来たので実際に開発に進んでいきましょう。

## Goのインストール

まず初めに、Goを自分の環境にインストールする所から始めます。ここ最近は、グローバルに言語をインストールするのを辞めて、[rbenv](https://github.com/rbenv/rbenv)や[pyenv](https://github.com/pyenv/pyenv)と言ったようなプロジェクトリポジトリ毎にプログラム言語のVersionを管理できるツールを使ってインストールしています。
Goにもそういったものあるかなー?と思って調べたらすぐ出てきました。

[https://github.com/syndbg/goenv](https://github.com/syndbg/goenv)

このgoenvを使ってGoをインストールします。

```bash
% mkdir dairy_report
% cd dairy_report
% goenv install 1.11.0
% goenv local 1.11.0
```

goenvでインストールしたGoを実行できるようにPATHの環境変数にgoenvのディレクトリを指定してあげます。

```bash:.zshrc
export PATH="$HOME/.rbenv/bin:$PATH" # rbenv                                
export PATH="$HOME/.pyenv/bin:$PATH" # pyenv                                
export PATH="$HOME/.goenv/shims:$PATH" # goenv                              
export RBENV_ROOT="$HOME/.rbenv"                                            
eval "$(rbenv init -)"                                                      
eval "$(pyenv init -)"                                                      
eval "$(goenv init -)
```

また、Goでは開発時の作業ディレクトリとして`GOPATH`の環境変数で指定するディレクトリの指定が必要となります。
このGOPATH配下のディレクトリに必要なパッケージ群がインストールされます。今回は、プロジェクト毎にGOPATHを分けたいため、`direnv`を利用して必要な環境変数をプロジェクト特有のものとして読み込めるようにします。
direnvについてはこちら→[https://github.com/zimbatm/direnv](https://github.com/zimbatm/direnv)

```bash
% brew install direnv
```

```zsh:.zshrc
EDITOR=vim
eval "$(direnv hook zsh)"
```

direnvでは、ディレクトリ直下に`.envrc`というスクリプトファイルを作成しこの.direnvを読み込むことでプロジェクトの特有の設定を行ってくれます。

まずは、この.envrcにGOPATHの指定のみをしてあげましょう。`direnv edit .`により.envrcを作成してくれますが、この時事前に`EDITOR`環境変数で指定したエディタが実行できるようにしてあげる必要があります。環境変数程度の編集のつもりなので、vimにしておきます。ここのvendorの指定は後述するgomによるパッケージ一括管理のツールでインストールするパッケージの場所と統合させるために同じ設定をしておきます。更に最後にbinをPATH指定しておきたいため、PATHの指定でGOBINの定義を追加しておきます。

```bash:.envrc
export GOPATH="$(pwd)/vendor/"                                              
export GOBIN="$(pwd)/vendor/bin" 
export PATH="$GOBIN:$PATH"
```

以上で保存を行えばloadingしてくれます。

```bash
direnv: loading .envrc                                                          
direnv: export +GOPATH
% echo $GOPATH
~/Desktop/Develop/dairy_report/go:
```

## Goチュートリアル

公式サイトのチュートリアルに大体の事は書かれています。これを参考にコードをゴリゴリ書いていきたいと思います。

[https://tour.golang.org/welcome/1](https://tour.golang.org/welcome/1)

## Go言語によるWeb開発

Go言語によるWeb開発のお作法的なところを知りたかったので、以下のようなスライドも参考にしていました。
著者がRubyistだったため、僕の場合でも適合できると思い十分に参考になりました。

[GoによるWebアプリ開発のキホン](https://www.slideshare.net/hico_horiuchi/goweb-58764875)

### フレームワークについて

さて、いざGo言語によるWeb開発を進めていくわけですがその前にGoで使われている主流なフレームワークは何があるのか調べていました(前述のスライドでスタンダードのようなものはまだないとの事ですが)。
そんな中で以下のようなブログを見つけました。

[Why I Don’t Use Go Web Frameworks](https://medium.com/code-zen/why-i-don-t-use-go-web-frameworks-1087e1facfa4)

要約するとこんな感じ

```
* Go言語自体がすでにWebフレームワーク化している。
* 多くのGo言語のフレームワークはHTTPリクエストとレスポンスを隠して抽象化したメソッドを提供している。
* しかし、それはわざわざ単純にできることをパフォーマンスを低下させてまでやっているのではないか
```

気になったのは上の三文でしょうか。要はGo言語は元々`net/http`のスタンダードなライブラリで大体のことはできるのでわざわざフレームワークを使う必要がないと読めました。ということで本当にそうなのか、検証して試していきたいと思います。

## ひとまずnet/httpでWebサーバーを書いてみる

Goのプログラムは、`packages`で構成されます。プログラムは`main`パッケージから開始させるため下記のプログラムで実行時に8080番ポートで待ち受けを行うサーバープログラムを書いてみました。

```go:main.go
package main

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

func main() {
	http.HandleFunc("/",
		func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Hello, World!")
			log.Printf("%+v¥n", r)
		})
	http.ListenAndServe(":8080", nil)
}
```

以上で、http://0.0.0.0:8080 にアクセスすると以下のような結果が返ってきます。

![スクリーンショット 2018-09-22 23.37.35.png](https://qiita-image-store.s3.amazonaws.com/0/144991/15c67f04-41b3-ee85-c38c-c6319dc05079.png)

ここからゴリゴリコードを書いていくわけですが、その前に開発しやすいようにそれぞれの環境を整えていきます。

### パッケージマネージャー

長らく、Railsに親しんできたので今回は[gom](https://github.com/mattn/gom)というパッケージマネージャーを採用しました。
先ほど、goenvとdirenvでGOPATHはプロジェクト配下のディレクトリでインストールするようにしたいので、以下のようなコマンドでgomを取得するようにしました。

```bash
% goenv exec go get github.com/mattn/gom
```

使い勝手はBundler, Gemfileに似ているのでこれを採用することにしました。

## マイグレーション

DBのバージョン管理を行うためにマイグレーションツールとして`goose`を採用することにしました。
これをgomで一括管理できるようにGomfile(Gemfileのようなもの)に追記することとしました。

```go:Gomfile
gom bitbucket.org/liamstask/goose
```

追加したgooseパッケージをgom経由でインストールできるようにします。
今度はgom経由でインストールするため、前述で指定したPATHの指定によりbin配下のgomを呼び出せるようになっているため、gomを実行するだけでOKです。

```bash
% gom install
downloading bitbucket.org/liamstask/goose
# bitbucket.org/liamstask/goose/db-sample/migrations
runtime.main_main·f: function main is undeclared in the main package
gom:  exit status 2
```

異常終了していますが、これでgooseをインストールすることができました。

```bash
% goose
goose is a database migration management system for Go projects.

Usage:
    goose [options] <subcommand> [subcommand options]

Options:
  -env string
    	which DB environment to use (default "development")
  -path string
    	folder containing db info (default "db")
  -pgschema string
    	which postgres-schema to migrate (default = none)

Commands:
    up         Migrate the DB to the most recent version available
    down       Roll back the version by 1
    redo       Re-run the latest migration
    status     dump the migration status for the current DB
    create     Create the scaffolding for a new migration
    dbversion  Print the current version of the database
```

## Commit

ここからいよいよ進めていくわけですが、長くなりそうでしたのでここで一旦コミットすることとします。
.gitignoreでは以下のような設定を行いました。

```
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

*vendor/
*.idea
*.envrc
```

```bash
% git add . 
% git commit -m "first commit"
[master 72e578c] first commit
 4 files changed, 34 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .go-version
 create mode 100644 Gomfile
 create mode 100644 main.go
% git push origin master
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 812 bytes | 812.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:himrock922/dairy_report.git
   bfb5b01..72e578c  master -> master
```

## まとめ

今回は事前準備しか行っておらず、あまり深い所までいけていませんが、次からはコードを書いていこうと思います。
今日は一旦ここまで

## 追記

```bash
% gom install
downloading bitbucket.org/liamstask/goose
# bitbucket.org/liamstask/goose/db-sample/migrations
runtime.main_main·f: function main is undeclared in the main package
gom:  exit status 2
```

この問題の原因がわかりました。
`go get`はリモートリポジトリからソースを取得した後にbuildを実行しています。
そのbuildの際にマイグレーションサンプルファイルのディレクトリの中にmain関数が存在するGoプログラムがないということでエラーを起こしています。

build時にこのディレクトリを除外できないものでしょうか。いずれにせよバグ挙動のようなので後々issue報告しておきます。

## 更に追記

すみません。ちゃんとgo getのディレクトリ先の指定パスを読んでいませんでした。

[https://bitbucket.org/liamstask/goose/src/master/](https://bitbucket.org/liamstask/goose/src/master/)

```
Install
$ go get bitbucket.org/liamstask/goose/cmd/goose
This will install the goose binary to your $GOPATH/bin directory.
```

ということなので、ちゃんとcmd配下のみのバイナリをインストールするようにGomfileを書き換えます。

```go:Gomfile
gom 'bitbucket.org/liamstask/goose/cmd/goose'
```

```bash
% gom install
downloading bitbucket.org/liamstask/goose/cmd/goose
```

これにより、正しくgooseをインストールすることができました。

## 参考文献

* [https://tour.golang.org/welcome/1](https://tour.golang.org/welcome/1)
* [https://github.com/zimbatm/direnv](https://github.com/zimbatm/direnv)
* [GoによるWebアプリ開発のキホン](https://www.slideshare.net/hico_horiuchi/goweb-58764875)
* [Why I Don’t Use Go Web Frameworks](https://medium.com/code-zen/why-i-don-t-use-go-web-frameworks-1087e1facfa4)

## シリーズ

[Part1](https://qiita.com/himrock922/items/1d70686aabaac49b54fd) → [Part1.5](https://qiita.com/himrock922/items/b0a1173be4bf078db023)