ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
| この記事の要点 |
|
比較
| エンジン | 拡張子 | Spring Boot | 特徴 |
|---|---|---|---|
| Thymeleaf | .html | 標準 | ブラウザで直接開いても崩れない。情報が多い |
| FreeMarker | .ftlh | 公式対応 | 速い。表現力が高い。Spring 外でも使える |
| Mustache | .mustache | 公式対応 | ロジックを書けない。学習が最も簡単 |
| Groovy Templates | .tpl | 公式対応 | Groovy で書く。使用例は少ない |
| JSP + JSTL | .jsp | 非推奨 | 古い資産向け。jar では動かない |
| Pebble / Jte | — | サードパーティ | 速度重視。Jte は型安全 |
新規なら Thymeleaf、既存の JSP 資産があるなら JSP のまま、というのが実務での分かれ目です。
Thymeleaf
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
@Controller
public class UserController {
@GetMapping("/users")
public String index(Model model) {
model.addAttribute("users", userService.findAll());
return "users/index"; // templates/users/index.html
}
}
<!-- templates/users/index.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1 th:text="${title}">ここは開発中のダミー</h1>
<ul>
<li th:each="u : ${users}" th:text="${u.name}">サンプル</li>
</ul>
<p th:if="${#lists.isEmpty(users)}">該当なし</p>
<a th:href="@{/users/{id}(id=${u.id})}">詳細</a>
<img th:src="@{/img/logo.png}" alt="ロゴ">
</body>
</html>
Thymeleaf の最大の特徴は「そのまま HTML として開ける」ことです。th:text は属性なので、ブラウザで直接開くとタグの中身(ダミー)が表示され、デザイナーがサーバーなしで確認できます。
JSP との書き方の違い
| やりたいこと | JSP + JSTL | Thymeleaf |
|---|---|---|
| 値の表示 | ${user.name} | th:text="${user.name}" |
| 繰り返し | <c:forEach> | th:each |
| 条件 | <c:if> | th:if / th:unless |
| URL | <c:url> | @{/path} |
| 共通部品 | <jsp:include> | th:replace / th:insert |
| エスケープ | <c:out>(${} は素通し) | th:text が既定でエスケープ |
<!-- Thymeleaf: 既定でエスケープされる(安全) -->
<p th:text="${comment}"></p>
<!-- エスケープしない(HTML として解釈させる) -->
<p th:utext="${comment}"></p>
th:utext に利用者の入力を渡すと XSS になります。使ってよいのは、自分で組み立てた安全な HTML か、サニタイズ済みの値だけです。
共通部分をまとめる
<!-- templates/fragments/layout.html -->
<header th:fragment="header">
<nav><a th:href="@{/}">トップ</a></nav>
</header>
<!-- 使う側 -->
<div th:replace="~{fragments/layout :: header}"></div>
th:replace はその要素ごと置き換え、th:insert は中身に差し込みます。詳細は テンプレートフラグメント(ヘッダー等の共有化) を参照してください。
FreeMarker
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
<!-- templates/users/index.ftlh -->
<h1>${title}</h1>
<ul>
<#list users as u>
<li>${u.name}</li>
<#else>
<li>該当なし</li>
</#list>
</ul>
<#-- null なら既定値 -->
${name!"名無し"}
<#if user??>ログイン中</#if>
拡張子を .ftlh にすると自動的に HTML エスケープされます。.ftl はエスケープされないため、必ず .ftlh を使ってください。
Spring Boot で JSP を避ける理由
- 実行可能 jar では動かない。war にして外部の Tomcat に置く必要がある
- 組み込み Jetty / Undertow では動かない
src/main/webapp/WEB-INF/jsp/という Maven / Gradle の標準から外れた場所に置く必要がある${}がエスケープされないため、<c:out>の書き忘れが XSS に直結する
# どうしても JSP を使う場合の設定
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
設定
# 開発中はキャッシュを切る(保存すると即反映される)
spring.thymeleaf.cache=false
# 置き場所と拡張子(既定値)
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
# テンプレートが見つからないときにエラーにする
spring.thymeleaf.check-template-location=true
本番では cache=true に戻してください。毎回テンプレートを読み直すと大きく遅くなります。
選び方
| 状況 | 選ぶもの |
|---|---|
| Spring Boot で新規に画面を作る | Thymeleaf |
| 大量のページを高速に描画したい | FreeMarker / Jte |
| テンプレートにロジックを書かせたくない | Mustache |
| 既存の JSP が大量にある | JSP のまま(war で運用) |
| 画面は React / Vue で作る | テンプレートエンジンを使わない(@RestController で JSON を返す) |
Thymeleaf でよく使う書き方
| やりたいこと | 書き方 |
|---|---|
| テキストを出す | th:text="${name}" |
| HTML として出す | th:utext="${html}"(XSS 注意) |
| 属性に入れる | th:value th:src th:alt th:placeholder |
| URL を組み立てる | th:href="@{/users/{id}(id=${u.id})}" |
| クラスを付け外し | th:classappend="${active} ? 'on' : ''" |
| 繰り返し | th:each="u, stat : ${users}" |
| 条件 | th:if / th:unless / th:switch |
| 文字列の連結 | th:text="|${a} と ${b}|" |
| 既定値 | ${name} ?: '名無し' |
| タグを出さずに囲む | <th:block th:each="..."> |
<!-- 繰り返しの状態変数 -->
<tr th:each="u, stat : ${users}"
th:class="${stat.odd} ? 'odd' : 'even'">
<td th:text="${stat.count}"></td> <!-- 1 から -->
<td th:text="${stat.index}"></td> <!-- 0 から -->
<td th:text="${u.name}"></td>
</tr>
<!-- 分岐 -->
<div th:switch="${user.role}">
<p th:case="'admin'">管理者</p>
<p th:case="*">一般</p>
</div>
<!-- ユーティリティオブジェクト -->
<span th:text="${#strings.abbreviate(text, 20)}"></span>
<span th:text="${#temporals.format(date, 'yyyy/MM/dd')}"></span>
<span th:text="${#numbers.formatInteger(price, 1, 'COMMA')}"></span>
<span th:if="${#lists.isEmpty(users)}">0 件</span>
JavaScript に値を渡す
<!-- インラインで埋め込む(JSON としてエスケープされる) -->
<script th:inline="javascript">
const user = /*[[${user}]]*/ null;
const name = /*[[${user.name}]]*/ "ダミー";
</script>
<!-- data 属性で渡す(こちらのほうが安全で分かりやすい) -->
<div id="app" th:data-user-id="${user.id}"></div>
<script>
const id = document.getElementById("app").dataset.userId;
</script>
/*[[...]]*/ の後ろに書いたダミー値は、ブラウザで直接開いたときだけ使われます。文字列連結でスクリプトを組み立てないでください。値に </script> や引用符が入ると抜け出されます。
エラー画面のテンプレート
src/main/resources/templates/error.html 全エラー共通
src/main/resources/templates/error/404.html 404 専用
src/main/resources/templates/error/5xx.html 500 番台
この場所にファイルを置くだけで、Spring Boot 既定の Whitelabel Error Page が置き換わります。詳細は 独自のエラーページを定義する方法 を参照してください。
関連
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
- インストール(eclipseプラグイン)
- クイックスタート
- プロジェクトの作成
- Spring Bootプロジェクトの作成
- Spring Bootプロジェクトの実行
- Spring BootでHello World!
- アノテーション一覧
- DB接続設定からエンティティおよびリポジトリの作成、値の取得まで(JPA編)
- DB接続設定や値の取得(JdbcTemplate編)
- ビューから値をモデルに格納しコントローラーで受け取る方法
- コントローラーにてモデルに値を格納してビューに渡す方法
- テンプレートエンジン
- ModelとModelAndViewの違い
- AOPの使用方法
- classpath: 内部ファイルの読み込み
- file: 外部ファイルの読み込み
- CSVファイルアップロード方法(Ajax)
- CSVファイルダウンロード方法(Ajax)
- Spring Bootプロジェクトのビルドと本番環境へのデプロイ方法(内部tomcat使用)
- Application.propertiesの環境依存設定の分割方法
- JPAにおけるEntityManagerの取得方法
- JPAにおけるjava.sql.Connectionの取得方法
- エラー一覧
- jarの引数を受け取る方法
- Spring BootでGmailからメール送信
- 複数のDBに接続する設定(Spring Boot & JPA編)
- ポート番号の変更
- Basic認証の実装と特定のURLに限定する方法
- Spring SecurityのBasic認証の無効化
- 独自のエラーページを定義する方法
- プロパティファイルの値やjar実行時の引数を取得する方法
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- 【django】ログイン 認証機能 2026-09-08 03:22:09
- MySQL ERROR 1063 Incorrect column specifier for column|原因と直し方 2026-09-08 03:11:09
- 【PHPエラー】Object of class stdClass could not be converted to string 2026-09-07 07:46:04
- Julia Genie ローカル開発サーバ起動完全ガイド 2026-09-07 07:46:04
- Wi-Fi とは|規格と世代(Wi-Fi 4〜7)・周波数帯・CSMA/CA・WPA3 NEW 2026-09-07 07:45:13
- set コマンドでシェルオプションと位置パラメータを操作 | bash 2026-09-07 07:45:13
- JPEG(.jpg/.jpeg)画像形式の完全ガイド — 仕様・マジックナンバー・他形式との比較・EXIF 2026-09-07 07:45:13
- UE5 Get Overlapping Actorsで特定クラスだけ処理|Class Filter・Cast To 2026-09-07 07:45:13
- Linuxで特定拡張子のファイルを再帰削除|find -deleteの安全手順 2026-09-07 07:45:13
- Django CBVでテンプレートに値を渡す|get_context_data・extra_context 2026-09-07 07:45:13
- Linuxで複数ファイルの文字列を一括置換|find・sed・xargsの安全手順 2026-09-07 07:45:13
- Laravelルートグループ|prefix・middleware・nameで一括管理 2026-09-07 07:45:13
- Linux whichコマンドの使い方|絶対パス取得とtype・command -vの違い 2026-09-07 07:45:13
- Linux catコマンドの使い方|行番号表示・複数ファイル連結 2026-09-07 07:45:13
- unable to execute 'gcc': No such file or directory 2026-09-07 07:45:13
コメントを削除してもよろしいでしょうか?