タイトル: 導入方法と基本動作
SEOタイトル: Thymeleaf 導入と基本動作完全ガイド (Spring Boot 連携)
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>Thymeleaf</strong> は HTML5 と互換のテンプレートエンジン。Spring Boot で最も使われる</li><li>導入は <code>pom.xml</code> に <code>spring-boot-starter-thymeleaf</code> を追加するだけ</li><li>テンプレートは <code>src/main/resources/templates/</code> に配置 → <code>Controller</code> 戻り値の文字列でファイル名指定</li><li>HTML タグに <code>th:text</code> / <code>th:if</code> / <code>th:each</code> / <code>th:href</code> 等の属性を追加して動的化</li><li>名前空間 <code>xmlns:th="http://www.thymeleaf.org"</code> を <code><html></code> タグに宣言する (任意だが慣例)</li></ul></td></tr></table>
<h2>Thymeleaf とは</h2>
<p>Thymeleaf は HTML5 と互換のあるテンプレートエンジン。Spring Boot の標準的なビュー技術として広く使われます。最大の特徴は <strong>「ブラウザでテンプレートを直接開いてもデザインが崩れない」</strong>こと (Natural Templates) で、JSP の代替としてデファクトの座にあります。</p>
<h2>導入手順 (Spring Boot)</h2>
<h3>1. 依存関係の追加</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-xml"><!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency></code></pre>
<p>Gradle の場合:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-groovy">// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
}</code></pre>
<h3>2. ディレクトリ構成</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">src/main/
├── java/com/example/demo/
│ ├── DemoApplication.java
│ └── HelloController.java
└── resources/
├── application.properties
├── static/
│ └── css/style.css
└── templates/
├── index.html
└── layout/base.html</code></pre>
<h3>3. Controller</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-java">// HelloController.java
package com.example.demo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("/")
public String index(Model model) {
model.addAttribute("name", "太郎");
model.addAttribute("messages", List.of("こんにちは", "Hello", "Bonjour"));
return "index"; // → src/main/resources/templates/index.html
}
}</code></pre>
<h3>4. テンプレート (index.html)</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf サンプル</title>
</head>
<body>
<h1>こんにちは、<span th:text="${name}">ゲスト</span> さん</h1>
<ul>
<li th:each="msg : ${messages}" th:text="${msg}">メッセージ</li>
</ul>
</body>
</html></code></pre>
<p>ブラウザで開くと「ゲスト」「メッセージ」と表示される (Natural Templates)。Spring Boot 経由なら動的に「太郎」「こんにちは / Hello / Bonjour」に置き換わります。</p>
<h3>5. 起動と確認</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-bash"># Maven
mvn spring-boot:run
# Gradle
./gradlew bootRun
# ブラウザで http://localhost:8080/ にアクセス</code></pre>
<h2>主要な Thymeleaf 属性</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>属性</th><th>用途</th><th>例</th></tr></thead>
<tbody>
<tr><td><code>th:text</code></td><td>テキスト置換 (HTML エスケープ)</td><td><code><span th:text="${name}"></span></code></td></tr>
<tr><td><code>th:utext</code></td><td>テキスト置換 (エスケープなし)</td><td><code><div th:utext="${html}"></div></code></td></tr>
<tr><td><code>th:if</code> / <code>th:unless</code></td><td>条件分岐</td><td><code><p th:if="${user.admin}">管理者</p></code></td></tr>
<tr><td><code>th:switch</code> / <code>th:case</code></td><td>switch</td><td>後述</td></tr>
<tr><td><code>th:each</code></td><td>繰り返し</td><td><code><li th:each="m : ${list}"></li></code></td></tr>
<tr><td><code>th:href</code></td><td>リンク URL</td><td><code><a th:href="@{/users/{id}(id=${u.id})}"></code></td></tr>
<tr><td><code>th:src</code></td><td>画像 URL</td><td><code><img th:src="@{/img/logo.png}"></code></td></tr>
<tr><td><code>th:action</code></td><td>フォーム action</td><td><code><form th:action="@{/login}"></code></td></tr>
<tr><td><code>th:value</code></td><td>input の値</td><td><code><input th:value="${name}"></code></td></tr>
<tr><td><code>th:field</code></td><td>フォームバインディング</td><td><code><input th:field="*{name}"></code></td></tr>
<tr><td><code>th:object</code></td><td>フォームオブジェクト</td><td><code><form th:object="${user}"></code></td></tr>
<tr><td><code>th:fragment</code></td><td>断片定義</td><td><code><div th:fragment="header"></code></td></tr>
<tr><td><code>th:insert</code> / <code>th:replace</code></td><td>断片の埋め込み</td><td><code><div th:replace="~{layout/base :: header}"></code></td></tr>
<tr><td><code>th:class</code></td><td>class 属性</td><td><code><div th:class="${active} ? 'active' : ''"></code></td></tr>
</tbody>
</table>
<h2>条件・繰り返しの例</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- if / unless -->
<p th:if="${user.admin}">管理者専用</p>
<p th:unless="${user.admin}">一般ユーザ向け</p>
<!-- switch -->
<div th:switch="${role}">
<p th:case="'admin'">管理者</p>
<p th:case="'user'">一般</p>
<p th:case="*">ゲスト</p> <!-- default -->
</div>
<!-- each + iterStat -->
<tr th:each="item, stat : ${items}">
<td th:text="${stat.count}">1</td> <!-- 1 始まりカウント -->
<td th:text="${stat.index}">0</td> <!-- 0 始まりインデックス -->
<td th:text="${stat.size}">10</td> <!-- 総数 -->
<td th:text="${stat.first} ? '先頭' : ''"></td>
<td th:text="${stat.last} ? '末尾' : ''"></td>
<td th:text="${item.name}"></td>
</tr></code></pre>
<h2>URL 表現 @{...}</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- 静的リソース -->
<link rel="stylesheet" th:href="@{/css/style.css}">
<img th:src="@{/img/logo.png}">
<!-- パスパラメータ -->
<a th:href="@{/users/{id}(id=${user.id})}">詳細</a>
<!-- → /users/123 -->
<!-- クエリパラメータ -->
<a th:href="@{/search(q=${keyword},page=${page})}">検索</a>
<!-- → /search?q=java&page=2 -->
<!-- 外部 URL -->
<a th:href="@{https://example.com/(ref=${ref})}">外部</a></code></pre>
<h2>レイアウト (Fragment)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- templates/layout/base.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${title}">タイトル</title>
</head>
<body>
<header th:fragment="header">
<h1>サイト名</h1>
<nav>...</nav>
</header>
<footer th:fragment="footer">
<p>© 2026</p>
</footer>
</body>
</html>
<!-- templates/index.html で利用 -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:replace="~{layout/base :: header}"></div>
<main>
<h2>本文</h2>
</main>
<div th:replace="~{layout/base :: footer}"></div>
</body>
</html></code></pre>
<h2>application.properties の設定</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-properties"># キャッシュ (開発中は false)
spring.thymeleaf.cache=false
# テンプレートディレクトリ
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
# モード (LEGACYHTML5 で XHTML 強制を緩和)
spring.thymeleaf.mode=HTML
# 文字コード
spring.thymeleaf.encoding=UTF-8
# Content-Type
spring.thymeleaf.servlet.content-type=text/html</code></pre>
<h2>FAQ</h2>
<p><strong>Q: 変更がブラウザに反映されない</strong><br />
A: <code>spring.thymeleaf.cache=false</code> + Spring Boot DevTools 導入。</p>
<p><strong>Q: テンプレートが見つからない</strong><br />
A: ファイルが <code>src/main/resources/templates/</code> 下にあるか、Controller の戻り値の文字列とファイル名が一致しているか確認。拡張子は付けない。</p>
<p><strong>Q: th:text と th:utext の使い分け</strong><br />
A: 通常は <code>th:text</code> (XSS 対策)。HTML を出力したい安全なデータだけ <code>th:utext</code>。</p>