【Go,Docker】簡易的なログインシステムを作成する

# はじめに
先日の記事では、`Go`と`REST Client`を利用して、サーバーにHTTPリクエストを送る簡易的なプログラムを作成、実行しました。今回は、それらに加えて`Docker`を利用してデータベース的なものを作成し、それを持ち得て簡易的なログインシステムを作成していきます。

なお、この記事では`Docker`や先日の記事で紹介した`REST Client`についての説明は省きます。

では、始めていきます。

# ファイルの作成
今回は、`server.go`、`go.mod`、`dockerfile`、`request.http`の４つのファイルを作成しました。それぞれのコードは以下の通りです。（`go.mod`は省略）

```go:server.go
package main

import (
	"encoding/json"
	"net/http"
)

type User struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

var users = make(map[string]string)

func regissterHandler(w http.ResponseWriter, r *http.Request) {
	var user User
	if err := json.NewDecoder(r.Body).Decode((&user)); err != nil {
		http.Error(w, "Invalid Request", http.StatusBadRequest)
		return
	}
	if _, exists := users[user.Username]; exists {
		http.Error(w, "User already exists", http.StatusBadRequest)
		return
	}
	users[user.Username] = user.Password
	w.WriteHeader(http.StatusCreated)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	var user User
	if err := json.NewDecoder(r.Body).Decode((&user)); err != nil {
		http.Error(w, "Invalid Request", http.StatusBadRequest)
		return
	}
	if password, exists := users[user.Username]; exists && password == user.Password {
		w.WriteHeader(http.StatusOK)
	} else {
		http.Error(w, "Invalid Username or Password", http.StatusUnauthorized)
	}
}

func checkHandler(w http.ResponseWriter, r *http.Request) {
	usernames := make([]string, 0, len(users))
	for username := range users {
		usernames = append(usernames, username)
	}
	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(usernames); err != nil {
		http.Error(w, "Failed to encode usernames", http.StatusInternalServerError)
		return
	}
}

func main() {
	http.HandleFunc("/register", regissterHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/check", checkHandler)
	http.ListenAndServe(":8080", nil)
}

```

```dockerfile:dockerfile
# ベースイメージ
FROM golang:1.23

# アプリケーションコードのコピー
WORKDIR /app
COPY . .

# コンパイル
RUN go build -o server .

# 実行
CMD ["/app/server"]

```

```http:request.http
### ユーザー情報を登録 1
POST http://localhost:8080/register HTTP/1.1
Content-Type: application/json

{
  "username": "testuser",
  "password": "testpassword"
}

### ユーザー情報を登録 2
POST http://localhost:8080/register HTTP/1.1
Content-Type: application/json

{
  "username": "taro",
  "password": "123"
}

### ユーザー情報でログイン認証
POST http://localhost:8080/login HTTP/1.1
Content-Type: application/json

{
  "username": "testuser",
  "password": "testpassword"
}

### 登録されたユーザー情報を確認 
GET http://localhost:8080/check HTTP/1.1
Content-Type: application/json
```

# ファイルの実行
まずはじめに`Docker Desktop`を起動させます。そして、作業ディレクトリに移り、以下の流れでコマンドを入力していきます。
ちなみに今回、`Docker Image`につけた名前は`go-login-app`にしました。

まずは`Docker Image`をビルドします。
```
docker build -t go-login-app .
```
次に`Docker Container`を作成、起動します。
```
docker run -d -p 8080:8080 --name go-login-container go-login-app
```
では、`VSCode`上で`request.http`を開いてリクエストを送信します。
リクエストに対し、以下のようにレスポンスがきます。もし良ければ、上記の`request.http`以外にも色々とリクエストを送ってみてください。

```:ユーザー情報を登録（POST）
HTTP/1.1 201 Created
Date: Mon, 09 Dec 2024 02:59:25 GMT
Content-Length: 0
Connection: close
```

```:ユーザー認証に成功
HTTP/1.1 200 OK
Date: Tue, 10 Dec 2024 08:06:15 GMT
Content-Length: 0
Connection: close

```

```:ユーザー認証に失敗（名前またはパスワードが異なる）（POST）
HTTP/1.1 401 Unauthorized
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Mon, 09 Dec 2024 03:08:04 GMT
Content-Length: 29
Connection: close

Invalid Username or Password
```

```:登録したユーザーの確認（GET）
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 10 Dec 2024 07:29:26 GMT
Content-Length: 20
Connection: close

[
  "testuser",
  "taro"
]

```

# おわりに
今回の記事では新しく`Docker`を利用したプログラムファイルを作成、実行しました。途中、慣れない操作に苦戦することもありましたが何とか解決することができて良かったです。今後はもっと触る機会を増やして慣れたいと思います。

では、今回の記事はこれで終わりです。最後までお読みいただきありがとうございました。