SpringBoot MVCで簡単なWEBアプリを実装してみる

## はじめに
[株式会社ピーアールオー（あったらいいな！を作ります） Advent Calendar 2022](https://qiita.com/advent-calendar/2022/pro-japan1) の7日目になります。
前日は[[AWS]SAMでTypeScriptがサポートされたので5分でAPIを作成する](https://qiita.com/hf7777hi/items/6d2b093d6ed7271cf81b)でした。


前に[Spring Boot + RemoteContainer](https://qiita.com/tajima_haru/items/d4e5082e6ba68ef4e960)の環境構築をしてHelloWorldまで表示させたので、SpringMVCの機能を利用して簡単なWebアプリケーションを作ります。

## build.gradle
build.gradleファイルのdependenciesにThymeleafなど定義します。
[spring initializr](https://start.spring.io/)で設定するでも問題ない。

```gradle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}
```

|spring boot Dependencies|説明|
|---|---|
| Thymeleaf | テンプレートエンジン | 
| Spring Web | web表示に必要 |
| Spring Boot DevTools | javaのソースコードが変更された際にspring boot を自動で再起動するための機能 |
|lombok| コンストラクタ、ゲッター、セッターを自動で作るライブラリ |

## トップページの表示
templates下にindex.htmlを作成
htmlタグに、Thymeleaf用スキーマ定義しておく。

```html
<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>トップページ</title>
</head>
<body>
    <h1>Advent Calendar 2022</h1>
    <ul>
        <li>
            <!-- "@{/issues}" @ はthymeleafでリンクを表す記法 -->
            <!-- springboot 起動せずともhtmlを確認出来る。タイムリーフ挟まないナチュラルテンプレート -->
            <a href="./articles/article_list.html" th:href="@{/articles}">記事一覧</a>
        </li>
    </ul>
</body>
</html>
```

IndexControllerを作成する。
```java
package com.example.its.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {
  
  // GET /
  @GetMapping("/")
  public String index() {
    // 前のパスの拡張子はSpringMVCが補完してくれるので書かなくてよい
    return "index";
  }

}
```
http://localhost:8080/ にアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1628058/99b3ab10-dffd-82a6-2c52-e81e739a3cb3.png)

## MVCの機能を使う
トップページのhtmlに記事一覧ページに遷移するリンクを書いたので遷移先のページを作る。
### Entity
```java
package com.example.its.domain.articles;

import lombok.AllArgsConstructor;
import lombok.Data;

//クラスの全てのフィールドを引数にとる。
@AllArgsConstructor
@Data
public class ArticleEntity {
  private long id;
  private String title;
  private String impressions;
}
```
### Service
```java
package com.example.its.domain.articles;

import java.util.List;
import org.springframework.stereotype.Service;

@Service
public class ArticleService {
  
  public List<ArticleEntity> findAll(){
    return List.of(
      new ArticleEntity(1,"Angular フォームボタンの無効について","very good"),
      new ArticleEntity(2,"Wingetで簡単！開発PCセットアップ（Windows）","very good"),
      new ArticleEntity(3,"DirectMLで試す、非NVIDIA系GPUで機械学習","very good"),
      new ArticleEntity(4,"Wingetで簡単！開発PCセットアップ（Windows）","very good"),
      new ArticleEntity(5,"DirectMLでStable Diffusionを動かしてみる","very good"),
      new ArticleEntity(6,"Googleカレンダーの当日予定をSlackに通知する","very good"),
      new ArticleEntity(7,"[AWS]SAMでTypeScriptがサポートされたので5分でAPIを作成する","very good")
    );
  }
}
```
### Controller
```java
package com.example.its.web.article;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.example.its.domain.articles.ArticleService;
import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor

public class ArticleController {
  // GET /issue
  
  private final ArticleService articleService;
  /**
   * 
   * @param model
   * @return 
   */
  @GetMapping("/articles")
  public String showList(Model model){
    model.addAttribute("articleList", articleService.findAll());
    return "articles/article_list";
  }
}
```

### テンプレート

```html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>記事一覧</title>
</head>
<body>
    <h1>記事一覧</h1>
    <a href="../index.html" th:href="@{/}">トップページ</a>
    <table>
        <thead>
            <tr>
                <th scope="col">タイトル</th>
                <th scope="col">感想</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="article : ${articleList}">
                <th scope="col" th:text="${article.title}">(title)</th>
                <td th:text="${article.impressions}">(impression)</td>
            </tr>
        </tbody>
    </table>
</body>
</html>
```
http://localhost:8080/articles に以下のようなページが出来ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1628058/5a73e745-f764-e57d-5e51-e0a31b87e95b.png)

## 終わりに
SpringBootを利用して簡単なWEBアプリケーションを作成することが出来ました。
知らない機能もまだまだ沢山ありそうなのでまた遊んでみます。

vscodeでやってるが、IntelliJで触った方がいいのだろうか。。
兎にも角にもこないだ作った環境が動くことを確認出来て満足です。


## 参考
[Spring Boot + Spring MVC ～サンプルアプリ実装～](https://qiita.com/t-shin0hara/items/687085ec34ae78ca2260)
[【SpringBoot】Webアプリケーション作成](https://qiita.com/vicugna-pacos/items/6594cb0146cbc4d0a5b6)
[【Java】Thymeleaf基本 (SpringBoot)](https://qiita.com/suema0331/items/764eed5328cf4e328161)