Spring BootでサーバーサイドKotlin入門

![kotlin (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/e84d625a-344c-5249-ae25-5e481f51f3e0.png)

普段の業務ではPHP(Laravel)を使っています。
この間Android開発でKotlinを経験し、一度は静的型付言語でWeb開発をしてみたいとも思っていたのでKotlin×Spring Bootで簡単なWebアプリを作ってみることにしました。

Kotlin触ってて思ったのが、Javaの知識を前提に書かれている情報が多いな〜[^1]ということでした。
自分は基本情報対策くらいしかJavaやってなくて、結構調べなきゃいけなかったりと面倒だったので、できるだけJavaの知識がなくても分かるように書いていこうと思います。
[^1]: 大量にいるJavaユーザーへの学習コストが低い＆Javaのエコシステムをそのまま流用できるっていうのがkotlinの強力なメリットなので仕方ない部分だとは思います
# 環境
- OS: **macOS Catalina**
- 言語: **Kotlin**
- フレームワーク: **Spring Boot**
- ビルドツール: **Gradle**
- IDE: **Intellij IDEA**

フレームワークをSpring Bootにした理由はKotlinでWeb開発するなら一番スタンダードっぽかったからです。
KtorというAPIサーバーに良さそうなフレームワークも気になるので今度試してみたいです。

# 事前準備
1. **JDK**(Java Development Kit)をインストールします。
JDKについては次の項目で解説していますので、分からない方は[こちら](https://qiita.com/kawasaki_dev/items/1a188878eb6928880256#jdk)を読んでみてください。

2. [Intellij IDEA](https://www.jetbrains.com/ja-jp/idea/download/#section=mac)をインストールし、セットアップをします。
Intellij IDEAはKotlinの開発元であるJetBrainsが作っているIDEなこともあって、ほぼ完璧にkotlinをサポートしています。セットアップ自体は誘導に従えば大丈夫だと思います。[参考になりそうな記事](https://qiita.com/suin/items/e941aa249c235350b8eb)はこちらに。

↑自分はどっちもHomebrewでインストールしました。
ちなみにKotlinはIntellij IDEAに付属しているため、別にインストールしなくても使えます。

# 予備知識
## JDK
準備のところでJDKをインストールしましたが、"*JDKって何のために必要なんだ？KotlinなんだからKotlin用のコンパイラとかビルドツールがあれば実行できるんじゃないの？*"と疑問に思った人もいるかもしれません。
そういう人のためにJavaの実行環境について少し解説します。

図1![jvm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/b07883f3-b2ef-1751-d248-cd55c27a2304.png)

図1を見てください。まず、通常のコンパイル言語と違ってJavaのプログラムはコンパイルしてもいきなり実行可能ファイルができる訳ではありません。**Javaバイトコード**で書かれた**Javaクラスファイル**と呼ばれるものが生成されます。

そして、バイトコードは**JVM**(Java Virtual Machine)という各OS用に用意された仮想マシン上で逐一解釈されながら実行されます。この仕組みのおかげで、わざわざ環境ごとにコンパイルを行う必要なく、一度コンパイルするだけでJVMが用意された環境でならどこでも動かせるようになっています。

知ってはいると思いますが、Kotlinは**JVM言語**[^2]です。
その意味するところは、**コンパイルされるとJVMが解釈できるJavaバイトコードを生成する言語**であるということです。(図2)
図2![kotlin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/fb6daba2-5611-7032-417d-ad03b0cac898.png)

つまり、kotlinを実行したり開発する環境ではJVMが動いていなければいけません。
ここでやっと**JDK**の話が出てきます。

JDKにはJVM・Javaコンパイラ・ライブラリ等のJavaの開発に必要な物のセットです。したがって、準備の項目でJDKをインストールしたという訳です。
おそらく、純粋なKotlinのコードを実行するだけなら、KotlinのコンパイラとJVMがあれば動くと思いますが[^3]、Javaのライブラリを使ったり、Javaと混在して使ったりする場合がほとんどだと思うのでJDKは入れておきましょう。

[^2]: 他にもScala、GroovyなどのたくさんのJVM言語があります。
[^3]: 間違っていたらすみません。その時はご指摘いただけると助かります。

## Gradle
![gradle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/f955fe16-dcc0-4722-4d21-0d0ca4e768be.png)

Gradleはビルドツールで、コンパイル・リンク・テストなどを自動で行ってくれます。
ただ、自分は他のビルドツール[^4]を使ったことがないのでここがいいぞ！みたいなことは良く分かってません。
設定をGroovyという言語で行うことは大きな特徴の一つだと思うので、その書き方くらいはある程度分かってるといいと思います。以下の記事が参考になります。
[Groovyを知らない人のためのbuild.gradle読み書き入門](https://qiita.com/opengl-8080/items/a0bb31fb20cb6505188b)
[^4]: Make、Ant、Mavenなど

## Spring Boot
![springboot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/14b0364f-8287-1331-71eb-a412ec054a38.png)

Spring Bootは従来のJavaのフレームワークであるSpring Frameworkを簡単に使えるようにしたフレームワークです。Spring Frameworkの複雑なxmlでの設定をなくし、アノテーションを使うことでコードを簡略化しているのが特徴のようです。
Spring Frameworkを触っていないので、どのくらい楽になったとかは分かりませんが、実際の動きは後々見ていきます。
# 環境構築
前置きが長くなってしまいましたが、Spring Bootの環境構築をしていきましょう。
## プロジェクトの雛形を作成
1. [Spring Initializr](https://start.spring.io/)にアクセスします。
Spring Initializrを使うと簡単にSpring Bootプロジェクトの雛形を作ることができます。CLIもあるみたいです。

2. 以下の画像の通り設定。
![_2020-12-02_21.31.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/428e3735-b2ac-7445-b31e-82ec0d8d2818.png)
Groupはパッケージ名、Artifactはビルドして生成される.jarファイルの名前になります。
Dependenciesには以下のものを追加します。
    - **Spring Web**: Webアプリ作成に必要な依存関係定義のセット
    - **Thymleaf**: テンプレートエンジン
    - **Spring Data JPA**: ORM
    - **H2 Database**: 軽量な組み込みDB

3. "Generate"をクリックすると、雛形のzipがダウンロードされるので、解凍します。

## Intellij IDEAでプロジェクトを設定
1. Intellij IDEAを開き、"Open or Import"をクリックして、先ほど解凍したディレクトリを選択します。
![スクリーンショット 2020-12-05 13.43.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/a7ce65eb-5716-9774-7390-075c88967209.png)

2. "springboot/src/main/kotlin/com/example/springboot/SpringbootApplication.kt"を開いてみます。
![スクリーンショット 2020-12-05 13.56.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/d494c82f-ba7b-f70d-1614-afd972768627.png)
この`SpringbootApplication.kt`ファイルのトップレベルに定義された`main`メソッドがプロジェクトのエントリポイントになります。

3. `main`メソッドの左側にある緑の`▷`マークを押すと、アプリケーションが起動します。
![スクリーンショット 2020-12-02 21.44.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/3dbd9b1c-52c0-2f6e-51c7-887739f27500.png)
次からは、右上の緑の`▷`マークからアプリケーションを起動できるようになります。

# ブラウザで登録・表示をできるようにする
今回は簡素ですがブラウザからユーザーを追加、一覧表示できるアプリケーションを作成しました。
## 成果物
![springboot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/fd6b68a6-2820-5e06-a268-0074476eeb52.gif)

**ディレクトリ構造**

```
src
├── kotlin
│   └── com
│       └── example
│           └── springboot
│               ├── SpringbootApplication.kt
│               ├── controller
│               │   └── MainController.kt
│               ├── entity
│               │   └── User.kt
│               └── repository
│                   └── UserRepository.kt
└── resources
    ├── application.properties
    └── templates
        ├── add.html
        └── index.html
```
<details><summary>**コード**</summary><div>

```kotlin:User.kt

package com.example.springboot.entity

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id

@Entity
data class User(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Int = 0,
        var name: String = ""
)
```

```kotlin:UserRepository.kt

package com.example.springboot.repository

import com.example.springboot.entity.User
import org.springframework.data.repository.CrudRepository

interface UserRepository : CrudRepository<User, Int>
```

```kotlin:MainController.kt

package com.example.springboot.controller

import com.example.springboot.entity.User
import com.example.springboot.repository.UserRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam

@Controller
class MainController {
    @Autowired
    lateinit var userRepository: UserRepository

    @GetMapping("/")
    fun showUsers(model: Model): String {
        val users = userRepository.findAll()
        model.addAttribute("users", users)
        return "index"
    }

    @GetMapping("/add")
    fun showAddPage(): String  {
        return "add"
    }

    @PostMapping("/add")
    fun addNewUser(@RequestParam name: String): String {
        userRepository.save(User(0, name))
        return "redirect:/"
    }
}
```

```html:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1>Users</h1>
    <input type="button" onclick="location.href='/add'" value="追加ページ">
    <ul>
        <li th:each="user : ${users}">[[${user.name}]]</li>
    </ul>
</body>
</html>
```

```html:add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Add</title>
</head>
<body>
    <h1>Add</h1>
    <form action="/add" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
        <button type="submit">追加</button>
    </form>
</body>
</html>
```

```properties:application.properties

spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/db_example
spring.datasource.username=username
spring.datasource.password=password
```
</div></details>
## 詳細
それでは以下に分けてアプリケーションの処理を説明していきます。

1. トップページの表示
2. ユーザー追加ページへの遷移
3. ユーザーの追加

### 1. トップページを表示
![スクリーンショット 2020-12-07 22.45.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/abc7a668-25c7-477d-a327-6254db2543c9.png)

まず、ユーザーが`/`にアクセスしてきた時に、リクエストを受け取って処理を行うコントローラーから説明します。

```kotlin:MainController.kt

package com.example.springboot.controller

略

@Controller
class MainController {
    @Autowired
    lateinit var userRepository: UserRepository

    @GetMapping("/")
    fun showUsers(model: Model): String {
        val users = userRepository.findAll()
        model.addAttribute("users", users)
        return "index"
    }

略
```
#### `@Controller`
コントローラーのクラス名の前に`@Controller`アノテーションもしくは`@RestController`をつけます。
後述するテンプレートを使う場合は、`@Controller`を使用します。`@RestController`を使うと、メソッドの戻り値がそのままレスポンスボディになります。
HTMLを返したい時は`@Controller`、APIのエントリポイントにしたいなら`@RestController`という認識で大丈夫だと思います。

#### `@Autowired`
`@Autowired`アノテーションはDIするクラスを指定する為に使います。
今回では、DBからのデータの取得を行う`UserRepository`のインスタンスが注入されます。

#### `@GetMapping`
`@GetMapping("/")`アノテーションは、`/`に対して`GET`メソッドでリクエストが飛んできた場合、アノテートされたメソッド(今回は`showUsers()`)が呼び出されるということを意味しています。

#### `Model`
テンプレートに値を渡したい時は、メソッドの引数にModelを指定し、Modelの`addAttribute()`メソッドの第一引数にテンプレートでの変数名、第二引数に値を渡します。

#### `userRepository.findAll()`
DIした`UserRepository`インスタンスの全ユーザーを取得するメソッドを実行しています。
`~Repository`クラスはデータの取得を抽象化[^5]するレイヤです。`UserRepository`クラスの中身については後述します。

[^5]: データの取得先(DBやAPI)を隠蔽する。本来はControllerとRepositoryは直接やりとりをせず、Serviceレイヤが間に入るべきですが簡単の為省いています。

#### `return "index"`
テンプレートに"templates/index.html"を指定しています。
`@Controller`がついたコントローラー内のメソッドの返り値に文字列を指定すると、そのファイル名のテンプレートをViewに指定します。

それでは、Viewに指定されているテンプレートを見てみましょう。テンプレートエンジンには前述した通り、**Thymleaf**が使われています。

```html:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1>Users</h1>
    <input type="button" onclick="location.href='/add'" value="追加ページ">
    <ul>
        <li th:each="user : ${users}">[[${user.name}]]</li>
    </ul>
</body>
</html>
```

#### `${users}`
コントローラーから渡された値は`${}`で囲って使うことができます。

#### `th:each=""`
`th:each="各要素を格納する変数名 : ${iterableな変数}"`で繰り返しタグを生成出来ます。

#### `[[${user.name}]]`
これをタグで挟むと`[[]]`中身がその要素のテキストとして出力されます。
`<タグ名 th:text="中身">`も同じことを表します。

次に、データの取得を抽象化するリポジトリを説明します。

```kotlin:UserRepository.kt

package com.example.springboot.repository

import com.example.springboot.entity.User
import org.springframework.data.repository.CrudRepository

interface UserRepository : CrudRepository<User, Int>
```

#### `CrudRepository`
Spring Data JPAが用意した、テーブル操作の基本である**CRUD**(Create、Read、Upload、Delete)を行うインターフェースです。このインターフェースを実装したインターフェースを用意することでCRUD操作が可能になります。
また、`CrudRepository`の第一型引数には操作するエンティティクラス、第二型引数にはエンティティのIDの型を指定します。
はじめは、コントローラーに`UserRepository`が注入されているのに、`UserRepository`の定義はインターフェースになっていて、どうやって実装クラスなしにインスタンスを生成するのか不思議でしたが、どうやらSpring Data JPAが実行時によしなにやってくれているようです。（適当ですみません）

最後に、リポジトリで使用されていたエンティティの中身をみます。エンティティはDBの1テーブルに対応します。

```kotlin:User.kt
略

@Entity
data class User(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Int = 0,
        var name: String = ""
)
```
#### `@Entity`
このアノテーションがついたクラスと同名のテーブルがマッピングされます。

#### `@Id`
主キーに対応するフィールドに付けます。

#### `@GeneratedValue(strategy = GenerationType.IDENTITY)`
Mysqlでいう`AUTO_INCREMENT`のような指定。
`GenerationType.`の後にくる値で意味が変わってきます。

| 値 | 意味 |
|:-:|:-:|
| IDENTITY | AUTO_INCREMENTを指定 |
|  SEQUENCE | DBのシーケンシャルオブジェクトを使用する |
|  TABLE | シーケンシャルな値を管理するテーブルを生成する |
|  AUTO | DBごとに異なる方法を選択する  |

### 2. ユーザー追加ページへの遷移
![スクリーンショット 2020-12-07 22.46.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/9b64f062-b01d-916e-c83f-34daaa6cab2d.png)

次に、ユーザーが"追加ページ"ボタンを押して`/add`にアクセスした場合のコントローラー動きを見ます。


```kotlin:MainController.kt

略

@Controller
class MainController {

略

    @GetMapping("/add")
    fun showAddPage(): String  {
        return "add"
    }

略
}
```

ここでは`/add`に`GET`メソッドで来たリクエストを`showAddPage()`メソッドにマッピングして、テンプレートに"add.html"を指定しています。

では、そのテンプレートの中身を見ます。

```html:add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Add</title>
</head>
<body>
    <h1>Add</h1>
    <form action="/add" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
        <button type="submit">追加</button>
    </form>
</body>
</html>
```
ここでは特別なことはしておらず、単純に`name`という項目のフォームとsubmitボタンがあるだけです。

### 3. ユーザーの追加
![スクリーンショット 2020-12-07 22.47.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235997/ff231970-fb3e-8650-3263-6c10b22d66d2.png)

最後に、`/add`に`POST`メソッドのリクエストが来た時のコントローラーの動きを確認します。

```kotlin:MainController.kt

略

@Controller
class MainController {
    @Autowired
    lateinit var userRepository: UserRepository

略

    @PostMapping("/add")
    fun addNewUser(@RequestParam name: String): String {
        userRepository.save(User(0, name))
        return "redirect:/"
    }
}
```

#### `@PostMapping`
前述の`@GetMapping`アノテーションから推測できると思いますが、`/add`に対して`POST`メソッドでリクエストが飛んできた場合、アノテートされたメソッド（今回は`addNewUser()`）が呼び出されます。

#### `@RequestParam`
指定した引数がリクエストパラメータであることを示します。ここではフォームで入力した文字列がnameという変数に入ります。

#### `userRepository.save(User(0, name))`
ここではフィールド`name`が`addNewUser()`に渡された`name`であるようなインスタンスを生成し、DBに保存しています。
`0`になっている箇所はIDで、ここに既存のIDを入れてしまうとUpdateになってしまうので、存在しない`0`を渡し、Createするようにしています。

#### `return "redirect:/"`
`/`にリダイレクトさせるという意味になります。

以上で、今回のアプリケーションで行っている処理については終わりました。ここで今更ですが、`application.propeties`で行っているDBの接続情報の設定を少しだけ説明します。

```properties:application.properties

spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:./h2db/db_example
spring.datasource.username=username
spring.datasource.password=password
```
#### `spring.jpa.hibernate.ddl-auto=update`
アプリケーションの起動時にDBのスキーマに対する操作を設定します。有効にすると`@Entity`が付与されたクラス通りにスキーマを生成してくれます。設定値は以下の通りです。

| 値 | 意味 |
|:-:|:-:|
| note  |  何もしない |
|  validate | 検証のみ行う  |
| update  | 必要なテーブルがない場合は作成する  |
|  create | 必要なテーブルを作成する。既存のテーブルは削除し、新規作成する  |
|  create-drop |  必要なテーブルを作成する。セッション終了後に削除する |

#### `spring.datasource.driver-class-name=org.h2.Driver`
DBの接続ドライバの指定です。

#### `spring.datasource.url=jdbc:h2:./h2db/db_example`
DBの接続先URLです。今回はファイルに保存をしています。

#### `spring.datasource.username=username`
DBの接続ユーザーです。

#### `spring.datasource.password=password`
DBの接続パスワードです。

# 最後に
初めて静的型付言語でweb開発やってみました。まだまだ規模が小さくてPHP×Laravelとの比較など行えませんが、これから色々いじってみて良いところ、悪いところを探して行こうかなという感じです。
ktorも気になりますし、マイクロサービスなんかも気になるのでこれから勉強して行けたらいいなと思っています。


# 参考情報
横山恭大, 『[入門！実践！サーバーサイドkotlin](https://www.amazon.co.jp/%E5%85%A5%E9%96%80%EF%BC%81%E5%AE%9F%E8%B7%B5%EF%BC%81%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%82%B5%E3%82%A4%E3%83%89Kotlin-%E6%8A%80%E8%A1%93%E3%81%AE%E6%B3%89%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%EF%BC%88NextPublishing%EF%BC%89-%E6%A8%AA%E5%B1%B1-%E6%81%AD%E5%A4%A7-ebook/dp/B082H8GWMR/ref=sr_1_7?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=kotlin&qid=1607099730&sr=8-7)』, 株式会社インプレス R&D, 2019
[Spring Boot と Kotlin を使用した Web アプリケーションの構築](https://spring.pleiades.io/guides/tutorials/spring-boot-kotlin/)
[Spring Boot で Thymeleaf 使い方メモ](https://qiita.com/opengl-8080/items/eb3bf3b5301bae398cc2)
[Spring Boot 使い方メモ](https://qiita.com/opengl-8080/items/05d9490d6f0544e2351a)