Loading
  • LIGHT

  • DARK

ROUTE

ルートゼロの
アクティビティ

Thymeleaf入門│Spring Bootで10分実践サンプル5選

6

10分でわかるThymeleaf入門—Spring Bootで動かすサンプル5選

SpringBootで動的HTMLを手軽に書けるテンプレートエンジンとして人気のThymeleaf。しかし「基本構文はわかったけど、実際にどう使えばいいかイメージが湧かない…」という声も多いはず。本記事では、実務3年目エンジニアの佐藤大輔さんが直面しがちな「コードは動くけど味気ない」「たったひと手間でUXを格上げしたい」という悩みを解消すべく、①基本構文のポイント解説+②実践サンプル5選を10分で理解できる構成でお届けします。すぐにコピペできるコード&「なぜこう書くのか」の背景解説で、今すぐプロジェクトに導入可能です!


1. Thymeleafとは?Spring Bootで選ばれる理由

テンプレートエンジンの役割

テンプレートエンジンは、HTMLに動的な値埋め込みや条件分岐、ループ処理を行うライブラリです。JSPやFreemarkerと比べ、Thymeleafはナチュラルテンプレートを採用し、ブラウザで開いてもプレーンHTMLとして動作するため、デザイナーやフロントエンド担当との協業が非常にスムーズになります。

用語解説:ナチュラルテンプレート

  • ナチュラルテンプレート:HTMLファイル自体に直接Thymeleafの構文を記述する形式。テンプレートエンジンを通さずにブラウザで開いても、プレーンなHTMLとして表示されるのが特徴です。

Thymeleafの特徴と優位点


2. 導入手順:Spring Bootプロジェクトへの組み込み

2.1 依存設定(Maven/Gradle)

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

2.2 application.properties の設定

spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false # 開発時はfalse推奨

2.3 基本ディレクトリ構成

src/
└ templates/
  ├ index.html
  └ fragments/
    ├ header.html
    └ footer.html

3. 基本構文5選+深掘り解説

ここからはThymeleafの代表的な属性を、「なぜ必要か」「どんな場面で活きるか」「実装時の注意点」とともに具体例でご紹介します。

3.1 th:text:変数埋め込みの基本

解説

HTML要素のテキストノード部分にサーバーサイドの変数値を安全に埋め込む構文。自動でHTMLエスケープが行われるため、ユーザー入力値をそのまま出力してもXSS攻撃を防げます。

用語解説:XSS(クロスサイトスクリプティング)

  • XSS:Webサイトの脆弱性を悪用した攻撃手法。悪意のあるスクリプトを埋め込み、訪問ユーザーのブラウザ上で実行させることで、個人情報の窃取などを行うことができます。

例:ユーザー名を表示

// Controller
@GetMapping("/profile")
public String profile(Model model) {
  model.addAttribute("username", "<script>alert('XSS')</script>");
  return "profile";
}
<p>ようこそ、<span th:text="${username}">[User]</span>さん!</p>
  • 動作ポイント

    • <script>タグは文字列としてレンダリングされ、実行されません。
    • 必ずth:textを使い、直書きの生HTMLを避けましょう。

3.2 th:each:リスト/コレクションのループ処理

解説

JavaのListや配列をHTML要素として反復出力します。ステータス変数を使えばインデックスや初回・最終回判定も可能です。

// Controller
@GetMapping("/products")
public String products(Model model) {
  List<Product> products = List.of(
    new Product(1, "リンゴ", 120),
    new Product(2, "バナナ", 80));
  model.addAttribute("products", products);
  return "products";
}
<table>
  <tr><th>#</th><th>商品名</th><th>価格</th></tr>
  <tr th:each="p, stat : ${products}">
    <td th:text="${stat.index + 1}">1</td>
    <td th:text="${p.name}">商品名</td>
    <td th:text="${p.price} + '円'">価格</td>
  </tr>
  <tr th:if="${products.empty}">
    <td colspan="3">商品が見つかりません</td>
  </tr>
</table>

動作ポイント

  • th:each="p, stat : ${products}" で、stat.index(0開始)のインデックスが取得できます。
  • 空リスト時は products.empty により「商品が見つかりません」の行が表示されます。
  • テーブル表示イメージ
# 商品名 価格
1 リンゴ 120円
2 バナナ 80円
  • 注意点

    • コレクションのgetterが正しく命名されていないとデータが渡りません。
    • 大量データを一度に出力するとパフォーマンスに影響が出るため、ページネーションや部分更新(Ajax)を検討しましょう。

3.3 th:ifth:unless:条件分岐の使い分け

解説

th:ifは条件がtrueのときに要素を出力し、th:unlessはfalseのときに出力。ネストが深くなる場合は<th:block>でグループ化できます。

例:管理者のみ編集ボタン表示

<div>
  <button th:if="${user.admin}" th:onclick="'location.href=\'/edit\''">編集</button>
  <p th:unless="${user.admin}">※編集権限がありません</p>
</div>
  • 動作ポイント

    • Java側でuserがnullだとNPEになるため、事前チェックを。
    • 複数要素をまとめて制御したい場合は<th:block th:if="…">…</th:block>を活用。

3.4 @{...}:パス解決/リンク生成

解説

アプリケーションのコンテキストパスを含めてURLを自動生成。パラメータ付きリンクやクエリ文字列も簡単に記述できます。

例:ユーザー詳細ページへのリンク

<a th:href="@{/users/{id}(id=${user.id})}">詳細へ</a>
  • 動作ポイント

    • {id}の名前と変数名を一致させる。
    • クエリ文字列は @{/search(q=${q},page=1)} のようにカンマ区切りで。

3.5 th:fragment:フラグメントによる再利用

解説

部分テンプレート(ヘッダー、フッター、モーダル部品など)を外部ファイルに定義し、必要に応じて挿入・置換できます。

例:共通ヘッダーの定義と挿入

<header th:fragment="mainHeader(title)">
  <h1 th:text="${title}">サイトタイトル</h1>
  <nav>…</nav>
</header>
<!DOCTYPE html>
<html>
  <body>
    <!-- 挿入 -->
    <div th:insert="fragments/header :: mainHeader('ホーム')"></div>
    <p>本文コンテンツ</p>
  </body>
</html>
  • 動作ポイント

    • th:insertは中身だけ挿入、th:replaceはタグごと置換。
    • 引数付きフラグメントで汎用性を高める。

4. 実践サンプル5選

サンプル①:動的なテーブル一覧表示

(前節3.2の例参照)

サンプル②:フォームのプリセット値&バリデーション連携

<form th:action="@{/user/save}" th:object="${user}" method="post">
  <input th:field="*{name}" placeholder="名前" />
  <div th:if="${#fields.hasErrors('name')}" th:errors="*{name}">エラー</div>
  <button type="submit">送信</button>
</form>

用語解説:フォームのプリセット値 / バリデーション

  • プリセット値:フォームの入力欄にあらかじめ設定しておく値のこと。編集画面などで既存のデータを表示させる際に使います。

  • バリデーション:入力されたデータが正しい形式や条件を満たしているか検証する仕組み。例えば「メールアドレスの形式が正しいか」などをチェックします。

サンプル③:ネスト構造でのループ表示(DTOリスト)

<div th:each="order : ${orders}">
  <h2 th:text="${order.id} + '件目'"></h2>
  <ul>
    <li th:each="item : ${order.items}" th:text="${item.productName}"></li>
  </ul>
</div>

用語解説:DTO(Data Transfer Object)

  • DTO:データ転送オブジェクトの略。複数のデータをひとまとめにして、コントローラやビュー間でやり取りする際に使用します。無駄なデータを含めず、必要な情報だけを渡すのがポイントです。

サンプル④:カスタムフラグメントを使った共通ヘッダー/フッター

(前節3.5の例参照)

サンプル⑤:Ajax連携で部分更新するビュー

<div id="commentList" th:fragment="list">
  <p th:each="c : ${comments}" th:text="${c.text}"></p>
</div>
<script>
  fetch('/comments/fragment')
  .then(res => res.text())
  .then(html => document.getElementById('commentList').innerHTML = html);
</script>

用語解説:Ajax(Asynchronous JavaScript + XML)


5. 実務でありがちな「落とし穴」と対策

  • BindingResultを忘れてバリデーションが飛ぶ
public String save(@Valid @ModelAttribute User user, BindingResult result) { … }

順序を@ValidBindingResultに。(Spring Boot バリデーション入門|MVC統合とカスタムアノテーション実践例

  • ネストDTOでth:eachが動かない
    → プロパティ名とJavaのgetter名を一致させる。
  • テンプレートキャッシュが開発反映されない
    spring.thymeleaf.cache=falseを設定。

6. ChatGPTでThymeleafコードを自動生成するプロンプト例

「Spring Boot + Thymeleaf で、リスト表示用の<table>と<th:each>によるループ処理、@ControllerからのModel連携まで含めたサンプルコードを教えてください。」

7. FAQ(よくある質問)

Q1. ThymeleafとFreemarker、どちらを選べばいい?
自然なHTML編集が優先ならThymeleaf、タグカスタムや高速レンダリングならFreemarker。

Q2. テンプレートキャッシュを無効化するには?
application.propertiesspring.thymeleaf.cache=falseを追加。

Q3. prefers-reduced-motion対応は可能?
CSSで@media (prefers-reduced-motion: reduce)を使い、アニメーションを抑制。


8. まとめと次に読むべき記事リンク

本記事では、10分で理解できるThymeleafの基本構文実践サンプル5選を紹介しました。手早くコピペ実装しつつ「なぜこう書くか」まで押さえられる構成なので、ぜひ既存プロジェクトに取り入れてみてください。


参考文献・情報源

RANKINGranking-icon

LATEST POSTS

もっとルートゼロを知りたいなら

DISCOVER MORE