【Java】Spring Boot 2.3.1×Thymeleafでエコーアプリケーション作ったった

## はじめに

こんにちは！
[macでSTSの環境構築を行って、](https://qiita.com/akane_kato/items/fff46dfa34f97f95dc88)[Spring BootでHelloWorldしていた](https://qiita.com/akane_kato/items/a59494b0260f0e648425)者です。
今回は簡単なエコーアプリケーションを作成してみました。

![もょもと.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/76f8e492-7b30-532e-a88e-f3a7a29ef397.gif)

画面遷移図っぽくすると、以下のようになります。

<img width="408" alt="スクリーンショット 2020-06-30 20.30.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/e6675ac1-7e05-ac9d-4d62-a65d7ca470af.png">

↓「送信」押下　　　↑「お返事bot TOP」押下

<img width="368" alt="スクリーンショット 2020-06-30 20.30.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/35e97c5e-8233-a9b9-6791-0e91c74e10d7.png">

前回、前々回同様、[Spring徹底入門](https://www.shoeisha.co.jp/book/detail/9784798142470)を参考に進めました。
本にはSpring MVCでのエコーアプリケーションの作り方と、SpringBootでのHelloWorldのコードは載っているのですが、Spring Bootでのエコーアプリケーションの方法はなかったので、なんとか混ぜ合わせながら作りました。

本当はバリデートとかテストコードとかもチャカチャカ実装するつもりだったのですが…Viewで使っているThymeleafに馴染みがなく（今までJSPを使っていましたが、非推奨らしい…！）、概念と書き方に慣れるのにちょっと時間がかかりました！ので、バリデートとか一切ない、ただのピュアなエコーアプリケーションです！

Thymeleaf？テンプレートエンジン？何それ？な人が読むとちょっと得するかも！な内容となっています。

### 使用環境とバージョン
- macOS Catalina
- jdk14.0.1
- JUnit5
- Maven 3.6.3_1
- STS 4.6.1
- Spring Boot 2.3.1
- spring-boot-starter-thymeleaf-2.3.1

## Spring Initializrでプロジェクトの雛形を作る

前回同様[Spring Initializr](https://start.spring.io/)でプロジェクトの雛形を作成します。
使い方自体は前回と変わらないので、相違点だけ紹介します。

<img width="1288" alt="スクリーンショット 2020-06-28 20.17.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/d0aa1b23-e5fb-8ddc-e470-3de5a2e126ae.png">

（左上から）

- Spring Bootのバージョン
前回は2.3.0があったのですが、今回なくなっていたので2.3.1を使用しました[^1]。


- Project Metadata
前回はデフォルトのdemoのままで行いましたが、同名ファイルだと混ざるのでformに変更しました[^2]。


- Dependencies【重要！】
前回は何も指定しませんでしたが、今回はThymeleafを使用するため、ADD DEPENDENCIESからThymeleafを選択しました。（もちろん後からpom.xmlを書き換えればなんとかなりますが…）

[^1]: 調べたらSpring Boot 2.3.0が[2020年5月15日](https://spring.io/blog/2020/05/15/spring-boot-2-3-0-available-now)、2.3.1が[2020年6月12日](https://spring.io/blog/2020/06/12/spring-boot-2-3-1-available-now)にリリースされているのですね…1ヶ月スパン…早い…。前回はなかった2.4.0のスナップショットも出ていて、移り変わりの速さにびっくりしました。

[^2]: のちにクラス名プレフィクスをEchoに変えたりしているので、特に参考にしなくて大丈夫です。

## ファイル構成
前回はメインメソッドとコントローラーを同じクラス`DemoApplication.java`にしていましたが、今回はメインメソッドは`EchoApplication.java`に、コントローラーは`EchoController.java`に分けて作成しました。ただ、2つのHTMLそれぞれにコントローラーを作るほどでもなかったので、コントローラー1つで2つのページを担当しています。
そのほか、EchoFormクラスは画面に入力された値（今回はお返事botなので入力する値は名前）を受け取るクラスです。`index.html`がお返事bot TOPページ、`echo.html`は名前「もょもと」が送信されたあとで「Hello もょもと」とお返事していたページです[^3]。

<img width="327" alt="スクリーンショット 2020-06-28 21.43.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/6a1d8407-cda8-11a4-722e-addc4048223d.png">


[^3]: HTMLベターッはさすがにやめたくなったので、CSSはこちらのサイトの「ゆっくりゆっくり色変化」を参考にさせていただきました。「サイトの引き立て役はコレ！ おしゃれすぎる背景をコピペで実装 【 HTML/CSS 】」https://deshinon.com/2019/03/06/background-oshare-kopipe/

## ①コントローラーの作成
`EchoApplication.java`はテンプレートそのままですので、割愛します。
`EchoController.java`は以下のようになりました。

```java:EchoController.java
package com.example.form;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping
public class EchoController {
	@ModelAttribute
	public EchoForm setUpEchoForm() {
		EchoForm form = new EchoForm();
		return form;
	}

	@RequestMapping
	public String index(Model model) {
		return "index";
	}

	@RequestMapping(value = "echo", method = RequestMethod.POST)
	public String echo(EchoForm form, Model model) {
		model.addAttribute("name", form.getName());
		return "echo";
	}
}

```
（早くもformというパッケージ名を後悔し始めている、echoだったな）

`setUpEchoForm`メソッドについている`@ModelAttribute`は、そのメソッドの戻り値を自動でModelに追加するためのアノテーションです。Modelの属性名はクラス名（EchoForm）の先頭を小文字にしたものになります。この場合は`echoForm`です[^4]。

[^4]: こちらのサイトを参考にさせていただいています。「2.3. はじめてのSpring MVCアプリケーション」https://macchinetta.github.io/server-guideline-thymeleaf/current/ja/Overview/FirstApplication.html

`@RequestMapping`アノテーションは、[URLリクエストのマッピング](https://qiita.com/TEBASAKI/items/267c261db17f178e33eb#requestmapping)をしています。
自分のざっくりした理解では、`@RequestMapping`単体だと、ディレクトリパスなしのURL直下にアクセスした時の挙動と思っています。valueを指定するとルートページからの相対パスが指定でき、methodは見た通り、受け付けるHTTPリクエストの種類を指定できます。現在の実装だと、以下の挙動になります。

- indexメソッドは`http://{サーバー名}`というURLへのリクエストで起動し、”index”というview名をreturnします。
- echoメソッドは`http://{サーバー名}/echo`というURLへのリクエストで起動し、”echo”というview名をreturnします。
POSTしか受け付けていないため、サーバー起動後直に`http://{サーバー名}/echo`でGETリクエストをすると、以下のエラーが発生します。

<img width="733" alt="スクリーンショット 2020-06-28 23.03.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/d5974159-1ab9-bc95-5908-de3a94efa9a0.png">


## ②フォームクラスの作成

コントローラーで既に出てきましたが、テキストフィールドの値を受け取るためのフォームオブジェクトとして、EchoFormクラスの実装も必要です。getter/setterでnameプロパティをやり取りするだけのシンプルなクラスを実装しました。

```java:EchoForm.java
package com.example.form;

import java.io.Serializable;

public class EchoForm implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

```



## ③Viewの作成…っていうかThymeleafってなんなんだ？

よ〜しロジックはできたぞ〜！あとはガワだ〜！JSPでしょ？知ってる知ってる！

…というように、View＝JSPだとばかり思っていた人間です。
なので『Spring徹底入門』で以下記述を見たときに、控えめに言って絶望しました。

> JSPの利用はさまざまな制約があるため推奨されていないため、本書でも扱いません。（p.602）

おやおや？JSPがないと「「Hello もょもと」ページに遷移」のように、入力値によって生成するHTML自体を変えるような実装はできないのではないか？？と思いました。実際に検索すると、Spring BootではJSPの使用はやはり[非推奨](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-template-engines)となっているようです。

> If possible, JSPs should be avoided. There are several known limitations when using them with embedded servlet containers.

え…どないすればええんや…？汎用なHTMLを作って…都度DOM操作する気か…？？（よく分かってない）

ご安心ください。ちゃんと『Spring徹底入門』12章に書いてありました。

> Thymeleafは、Webアプリケーションと親和性の高いテンプレートエンジンです。テンプレートエンジンとは、雛形となるドキュメント（テンプレート）に対し、可変データを埋め込むことで動的にドキュメントを生成する仕組みです。この仕組みは、MVCフレームワークのModelとViewを分割する考え方と親和性が高く、しばしばMVCフレームワークのViewとして利用されます。（p.554）

なるほどなるほど。テンプレートエンジンってそういうものなのですね。よく分からぬままDependenciesに入れてました。だめな人ですね🙅‍♂️
仕組みとしては、先ほど想像していた形に近く、HTMLをDOMに変換してから処理を行うそうです。

以下が`index.html`の実装です。

```html:index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<link th:href="@{/style.css}" rel="stylesheet" type="text/css">
<title>Echo Application</title>
</head>
<body>
 <h1>お返事bot TOP</h1>
 <form th:object="${echoForm}" th:action="@{/echo}" method="post">
        <label for="name">名前を入れてください:</label>
        <input th:field="*{name}"> 
        <input type="submit">
</form>
</body>
</html>
```

頭にth:とついている属性が、Thymeleaf固有の書き方になっています。
フォームクラスとModelの紐付けはコントローラーに付与した`@ModelAttribute`アノテーションが行っていますが、ThymeleafとModelの紐付けは、以下の部分で行っています。

`th:object="${echoForm}"`

先述の通り`@ModelAttribute`アノテーションは、Modelの属性名をクラス名（EchoForm）の先頭を小文字にしたものにしているため、echoFormという名称で正しいです。Thymeleaf記法の変数式に入れた`${echoForm}`をobjectとして設定することで、入力値の受け渡しができるようになります。

`th:action`はHTMLのaction属性と似たような働きです。ただし、パスの書き方がThymeleaf独自のものとなっています。なお、`th:action`や`th:href`の前に通常のaction属性やhref属性を設定しておくと、アプリの一部として動かす場合だけでなく、普通のHTMLとしても動くようです。ThymeleafのテンプレートはHTML5準拠のため、デザイナーとエンジニア間で共有できるのも強みですね。

「もょもと」を返すecho.htmlは以下のような実装となっています。
ここの変数名は、コントローラーのechoメソッド内の第一引数から来ているため、nameになっています。

```html:echo.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<link th:href="@{/style.css}" rel="stylesheet" type="text/css">
<title>Echo Application</title>
</head>
<body>
<p th:text="|Hello ${name}|"></p> 
<p><a th:href="@{/}">お返事bot TOP</a></p>
</body>
</html>
```

これで、もょもと完成です！！いええええい！！ぺぺぺ！！

![もょもと.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/3811e1db-05bc-7a47-6ecf-bee3f188f3c2.gif)

## おわりに
Springのプロジェクトを再度作ったので、前回より結構分かるようになっていた気がします…！
コントローラーとメインメソッドを分けたこともあって、どのアノテーションがどこに必要なのか、とかも分かりやすくなったのが良かったなと思います！規模が大きくなってきたら、index.htmlとecho.htmlもコントローラー分けてあげなきゃな…と思うなどしました。

Thymeleafは初見だったので、結構いろいろ調べました！最終的に『Spring徹底入門』が最強だったので、Springに手を出したい人は買って損はないです…！辞書的に使えます！

今回もお読みいただき、ありがとうございました！