Go言語とMySQLを使ってログイン機能と単語を記録するWebアプリケーションを作ってみた

# はじめに
GoでなにかしらのWebアプリケーションを作成したいと思いました。
今回は単語アプリを作りました。
githubのページです。
[word_and_meaning_keep](https://github.com/Yuorei/word_and_meaning_keep)
## このサイトの使い方
1.このサイトは単語とその意味を記録できるサイトです<br>
アカウントを持っている場合はそのままログイン<br>
お持ちでない場合には「新規ユーザーの登録」より<br>
登録をお願いします<br><br>
2.単語が登録されている場合はログインすると表示されます<br>
登録されていない場合は登録をしてお使いください<br>

## こだわりポイント
ログインユーザーごとにwordを表示させたこと<br>
**[デモ動画](https://youtu.be/HNB_35jIqaE)**<br>
<img width="200" src="https://drive.google.com/uc?id=1PMJsuWOXSCZVrY7W0UC1fIu9uW4RN4fH">
<img width="200" src="https://drive.google.com/uc?id=1f74mlXXil0uLY6R4hhDI_4IZBVg24W6e">
<img width="200" src="https://drive.google.com/uc?id=1SZuIV_sqmBytzfMdgh1ElCJnBUSSqpkH">
<img width="200" src="https://drive.google.com/uc?id=1XlyBqqQv6SoPn5z3llq7utSkZnHkDerC">

## 作るもの
単語とその意味をMySQLに記録していくものです。さらにはログイン機能も実装してユーザーごとに単語一覧を表示していくものとなります。

# 勉強したもの
今回、私はGoを[A Tour of Go](https://go-tour-jp.appspot.com/list)で基礎知識を習得しました。
次にWebアプリケーションを作成するにあたって[Goプログラミング実践入門: 標準ライブラリでゼロからWebアプリを作る](https://www.amazon.co.jp/Go%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%E6%A8%99%E6%BA%96%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%A7%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%BD%9C%E3%82%8B-impress-gear%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Sheong-Chang-ebook/dp/B06XKPNVWV)を読みました。
MySQLについては[一週間で身につくMySQL](http://web.sevendays-study.com/mysql/day0.html)で簡単に勉強しました。

## Webサーバー
今回はWebサーバーをGoで立てました。net/httpパッケージをつかいました。
```go
package main
import(
"net/http"
)
func main(){
http.ListenAndServe(":8080", nil)
}
```
とても簡単に立てることができます。これはなにもレスポンスを返しません。レスポンスを返すにはハンドラを扱います

## ハンドラ
これを使ってURLごとに処理をします。

```go
func HandlerUserForm(w http.ResponseWriter, r *http.Request) {
処理内容
}
func main(){
http.HandleFunc("/user-form", HandlerUserForm){
処理の中身
}
```
## テンプレート
Goのテンプレート機能を使用。'html/template'を使用します。これを使うとhtmlにアクションとして表示することができます。
```go
// テンプレートをパースする
func HandlerUserConfirm(w http.ResponseWriter, req *http.Request) {
tpl := template.Must(template.ParseFiles("templates/user-confirm.html"))
// テンプレートに出力する値をマップにセット
values := map[string]string{
    "account": req.FormValue("account"),
    "passwd":  req.FormValue("passwd"),
}
fmt.Println("登録", values["account"]) //登録した名前が出る
//dbに登録
db.User_db(values["account"], values["passwd"])
// マップを展開してテンプレートを出力する
if err := tpl.ExecuteTemplate(w, "user-confirm.html", values); err != nil {
    fmt.Println(err)
}
}
```

htmlの文章中に以下のようにします
```html
<p>{{ . }}</p>
```
これでhtml上でGoからの結果を受け取り表示することができます。
## MySQLを扱う
GoでMySQLを扱うには'Go-MySQL-Driver'を使わなければいけません。そこでインストールするためにgo.modを作らなければいけないのです。
```
go mod init  wordapp
```
これでgo.modが作られます'wordapp'の部分は任意の文字で大丈夫です。
これを作った後は次のコマンドでMySQLのドライバーを入手します。
```
go get -u github.com/go-sql-driver/mysql
```
これで一通りの準備は完了です。

今度はgoを書いていきます。
```register_db_account.go 
package db

import (
	"database/sql"
	"fmt"
	"log"

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

// ユーザー登録の関数
func User_db(name string, password string) {
	//sql.Open("mysql", "user:password@/dbname")
	db, err := sql.Open("mysql", "root:yourpassword@tcp(localhost:3306)/english_word_app")

	if err != nil {
		panic(err.Error())
	}

	defer db.Close()
	rows, err := db.Query("SELECT id FROM user WHERE id = (SELECT MAX(id) FROM user);")
	if err != nil {
		log.Fatal(err)
	}
	var id *int
	for rows.Next() {
		if err := rows.Scan(&id); err != nil {
			fmt.Println("idスキャンに失敗")
			log.Fatal(err)
		}
	}
	// プリペアードステートメントを使用
	in, err := db.Prepare("INSERT INTO user(id ,name,password) VALUES(?,?,?)")
	if err != nil {
		fmt.Println("データベース接続失敗")
		panic(err.Error())
	} else {
		fmt.Println("データベース接続成功")
	}
	defer db.Close() //最後に閉じる
	//insert
	result, err := in.Exec(*id+1, name, password)
	if err != nil {
		panic(err.Error())
	}
	lastId, err := result.LastInsertId()
	if err != nil {
		panic(err.Error())
	}
	fmt.Println(lastId)
}
```
これを使うとDBに接続してINSERETができます。
'english_word_app'はDB名です
このファイルはdbというディレクトリの下にあるので
```go
package db
```
となります。
```user_login.go
package db

import (
	"database/sql"
	"fmt"
	"log"
	_ "github.com/go-sql-driver/mysql"
)

// ログインのための関数
func User_login(name string, password string) int {
	//sql.Open("mysql", "user:password@/dbname")
	db, err := sql.Open("mysql", "root:yourpassword@tcp(localhost:3306)/english_word_app")

	if err != nil {
		panic(err.Error())
	}

	defer db.Close()
	rows, err := db.Query("SELECT id,name,password FROM user WHERE name=\"" + name + "\"and password=\"" + password + "\";")
	if err != nil {
		fmt.Println("select失敗")
		log.Fatal(err)
	}
	var (
		id *int
	)

	for rows.Next() {
		if err := rows.Scan(&id, &name, &password); err != nil {
			fmt.Println("スキャンに失敗")
			log.Fatal(err)
		}
	}
	m := 0
	if id == nil {
		//何もない場合は入力が間違いとして戻り値を0で返す
		id = &m
		return *id
	} else {
		return *id
	}
}
```
ここではSELECTをしています。userからid,name,passwordを取得します

## userとwordのDB管理
今回はuserの新規登録の際にidを登録順に付与します。idを使ってログインユーザーの管理と単語の管理をしました。単語には(id,word,meaning)を入れてユーザーごとに単語の表示を行います。

## ファイル分割
このプログラムを実行するsever.goのカレントディレクトリに'db'というファイルを置きました。その中にDBを扱うファイルを置きました。これを扱うには
```sever.go
package main
import(
	"fmt"
	"html/template"
	"net/http"
	//ファイル分割したモジュール
	//DB関係
	"wordapp/db"
)
```
'wordapp/db'は'パッケージ名/使いたいディレクトリ名'をimportします。パッケージ名はgo.modを作ったときの名前です。dbの中のファイルには
```go
package db
```
とします。これで使えるようになります。

# まとめ
上記のことを組み合わせることで今回のようなものを作ることができました。
そしてseverとMySQLを起動して使えるようになります。
http://localhost:8080/user-login
改善点も多くありますのでこれからも進めていきたいです。

# 参考にしたもの

## GoとHTMLについて
>[【Go実践】フォームで入力された情報をデータベースに登録する](https://leben.mobi/go/form-database/practice/web/)
>[【Go実践】POSTされたデータを受け取って処理する ― 入力フォームと確認画面の作成](https://leben.mobi/go/post/practice/web/)
>[【Go実践】同一のテンプレートを使って新規登録画面と編集画面を実装する ― テンプレートの分岐処理](https://leben.mobi/go/template-if/practice/web/)
>[【Golang】MySQLの基本的な操作を行う](https://www.chuken-engineer.com/entry/2021/09/24/162120)
>[Go言語(golang) テンプレートの使い方](https://golang.hateblo.jp/entry/golang-text-html-template#range)

## GoとMySQLについて
>[【Go実践】GoでMySQLを使おう(1) – ドライバのインストールからデータベース接続まで](https://leben.mobi/go/mysql-connect/practice/)
>[GO言語（golang）のデータベース（MariaDB・MySQL）操作（１）／database/sqlで最低限のクエリ](https://arakan-pgm-ai.hatenablog.com/entry/2021/06/01/000000)
>[go言語 mysqlに接続してinsertを実行する](https://mebee.info/2022/06/23/post-53678/)
>[Go言語でMySQL の基本的操作（SELECT、UPDATE、INSERT）を行う](https://qiita.com/ShinyaIshikawa/items/fede44cee7c71721247a)
>[GoでSQLのSELECTをする](https://qiita.com/tomokon/items/b0a19289fd0907c731ae)

## Goについて
>[nilとの付き合い方（panic: runtime error: invalid memory address or nil pointer dereference）](https://qiita.com/kskumgk63/items/e9d560db0342c11072ac)
>[golang　文字列→数値、数値→文字列変換](https://qiita.com/quicksort/items/c9522793a941edf074fd)
>[[Go言語] ファイル分割とローカルパッケージ](https://zenn.dev/fm_radio/articles/ca2ff1dfcf89b5)