GOで簡易Webserverを作ってみた

# 概要

### 経緯
Go言語の勉強のため、簡易Webserverを作ってみました。
その結果を忘れないように、まとめてみます。

### メイン機能
- Port8080を監視し、Webserverを立ち上がる
- 指定フォーマットのJSONファイルをアップロードし、DB(SQLite3)へ導入
- DBデータの編集（３テーブル）
- 編集した内容をJSONへ変換し、ダウンロードさせる

### 使ったフレームワーク＆ライブラリー
- **Gin:** Webserverのフレームワーク
- **gin-template:** HTMLのTemplateライブラリー
- **go-sqlite3:** DBライブラリー
- **gorm:** go-sqlite3のサポートライブラリー
- **go.rice:**　Assets管理ライブラリー

### 他に使った機能
- modules・vgo:　ライブラリーのバージョン管理（自作ソースファイルのインポートにも必要）
- rice embed-go: Assertの管理
- go build: バイナリーファイルのビルド

# 環境整備

基本的にはGoのインストール

```
$ go get -u -v github.com/nsf/gocode
```

と、環境変数の設定のみで完成します。

```
$ echo export GOROOT=/usr/local/opt/go/libexec >> ~/.bash_profile
$ echo export GOPATH=$HOME/code/golang >> ~/.bash_profile
$ echo export PATH=$PATH:$GOROOT/bin:$GOPATH/bin >> ~/.bash_profile
$ source ~/.bash_profile
```

参考先の設定は`/code/golang`と設定したので、共通ライブラリーだけではなく、プロジェクトごとのコードの格納パスだと思ってしまい、ワークススペースを作成し、そのフォルダのパスを環境変数に設定しました。
結果：VSのDebug用環境、Modules管理時の環境など、後から色々いじってしまった。今現在は自分もよくわからい状態になっています。時間がある時に改めて整理し直して、記事で結果をまとめます。

[参考資料(https://qiita.com/okatai/items/a6f966d2075d69d21e74)](https://qiita.com/okatai/items/a6f966d2075d69d21e74)

# 開発ツール

VSCodeを使いました。VSCodeのDebug環境は別でインストールしないといけないですが、結構簡単にできました。
ほぼNextの連打でした。最初からVSCodeから開発環境を準備すれば、今よりスムーズにいけったはず。

# goの基本文法

- [**Go tour**](https://go-tour-jp.appspot.com/welcome/1)
- **変数の宣言** `var`の場合には宣言のみ、`:=`は宣言且つ初期化、複数戻り値の関数で変数を初期化する場合に一つも新規関数がない場合に`:=`を使えません。逆に一つでも新規であれば、他の変数は新規でなくでも、`:=`を使えます。
- **配列の宣言** `make()`でサイズ指定の配列を作れます。もちろん、`:=`で配列を初期化することもできます。
- **rangeを使ってforループ**　rangeの戻り値は一つ目はIndex、二つ目は配列の項目なので、二つ目の宣言を忘れると、エラーになります。
- **deferでメモリーリーク対策** `defer`で宣言したコードは関数の最後に呼ばれます。複数宣言した場合に宣言の逆順で実行されます。
- **バックグランド実行**　`go`で宣言したコードや、関数は自動的に別スレッドで実行します。kotlinのCoroutineと同様、Goroutineと呼びます。

# Ginを使ってWebserver

- **ルーティング設定:** PythonのBluePrintみたいなライブラリーが見つかりませんでした。
そのまま、`gin.Default()`でエンジンを取得し、ルーティングを設定しました。
- **POSTパラメーター取得:** 一般的には`gin.Context`から直接取得できます。また、そのままリクエストのBodyを読み取ることも可能です。しかし、今回はせっかくgormでEntityを全部作ったし、POSTデータもシンプルので、BindJSONを使います。

```
var data entities.ActionSerif
err := c.BindJSON(&data)
if err != nil {
	panic(err)
}
```

- **戻り値の設定**
    - `c.HTML()` HTMLを返却
    - `c.JSON()` JSON形式で処理結果を返却
    - `c.FileAttachment()` ファイルを返却し、ダウンロードさせる

# sqlite3 & gorm

データ量が少ないので、DBはSQLiteにしました。また、SQLiteのライブラリーが使いにくいと言われ、gormを導入してみました。簡単な使い方や、注意点をまとめます。
#####
- **初期化** AndroidのSQLiteみたいな初期化処理がなくて、自分で作る必要があります。
- **DB作成＆開く**　`gorm.Open("sqlite3", "./demo.db")` それだけです。`defer db.Close()`を書けば、クローズ処理もできました。DBファイルが存在しない場合に自動的に作ります。（実行フォルダの直下に）
- **テーブルの作成**　`db.AutoMigrate()`を呼ぶと、存在しないテーブルを作成し、存在する場合には列をチェックし、追加してくれます。削除処理は一切しません。不要な列はDBにそのまま残っています。
- **１対Nのデータ取得** `db.Find(&action, id).Related(&actionSerifs)`　親インスタンス経由で、`Related()`関数を呼べば関連するインスタンスを引き出すことができます。
- **Queryの実行** 一般的には作成したEntityの属性名をそのまま使えば、DBどの連携は取れます。しかし、SQL文を書いて、実行する場合にはテーブルのカラム名を書かないといけません。DBを確認する必要があります。
- **外部キー経由の自動保存** Entityを結合できるように設定すれば、親を保存する時に、子も一緒に変更されます。

# パッケージバージョン管理

- 使っているライブラリーのバージョンを統一し、一括管理することができます。ビルドする前に以下のコマンドでgo.modファイルを生成し、ライブラリー情報をまとめます。

```
go mod init
```

うまくビルドできるようになる為、環境変数なども意地ないといけません。
開発環境を準備する時にキレイに済ませた場合は、そのまま実行できるはず。

[参考資料](https://www.wantedly.com/companies/wantedly/post_articles/132270)

# 自作ソースファイルの利用

- 自身のモジュール名を決めって、自身のモジュール名からImport。

# Assertファイルの問題

htmlや、jsファイルなどのリソースファイルはバイナリにビルドされません。
その為、ビルドしたバイナリファイルを実行する時にリソースが見つからなくて、エラーになります。
以下の参考資料にいくつ対応方法を記載していますが、今回はソースファイルへ変換する方法にしました。
プロジェクトフォルダで以下のコマンドを実行すれば、リソースファイルをソースファイルへ変換してくれます。

```
rice embed-go
```

[参考資料](http://blog.ralch.com/tutorial/golang-embedded-resources/)

# クロスコンパイル

- Mac 開発端末はMacのため、そのままビルド

```
go build
```
- Windows sqlite3を利用しているので、Cコンパイラのインストールが必要です。

Cコンパイラのインストール

```
brew install mingw-w64
```

実行ファイルのビルド

``` 
GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 go build
```

[参考資料](https://qiita.com/keijidosha/items/5f4a68a3341a44a25ab9)
[参考資料](https://qiita.com/Jxck_/items/02185f51162e92759ebe)

# 今回のやり残し
- goroutineの活用: [参考資料](https://qiita.com/Azunyan1111/items/bee3941b456967c0783c)
- データのリアルタイム更新
- エラー出力時にソースの階層が開発PCのフォルダ構造になっています。