Thymeleaf th:ifとth:unlessの使い方を比較!初心者向け条件分岐ガイド
新人
「先輩、Thymeleafで条件分岐をするときにth:ifとth:unlessがあるって聞いたんですが、どう違うんですか?」
先輩
「良い質問だね。Thymeleafには条件分岐をするための属性がいくつかあって、その代表的なものがth:ifとth:unlessなんだ。どちらも表示制御に使うけど、条件が真か偽かで動作が逆になるんだよ。」
新人
「なるほど!でも初心者にはどっちをどう使えばいいか迷いそうですね。」
先輩
「安心して。今回はThymeleaf th:ifとThymeleaf th:unlessの基本を比較しながら解説するから、すぐに理解できるようになるよ。」
1. Thymeleafとは?
ThymeleafはSpring MVCやSpring Bootで利用されるテンプレートエンジンで、HTMLとJavaのオブジェクトを結びつけて動的に画面を生成することができます。従来のJSPと比べても学習しやすく、初心者が直感的に扱えるのが特徴です。特にThymeleaf th:ifやThymeleaf th:unlessのような条件分岐属性を使うことで、ユーザーの状態や入力値によって表示を切り替えることが簡単にできます。
例えばログインしているユーザーだけに特定のメニューを見せたい場合や、エラーメッセージを表示したい場合など、動的なWebアプリケーションでは条件分岐は欠かせません。ここで登場するのがThymeleaf th:ifとThymeleaf th:unlessです。
2. th属性の概要と役割
Thymeleafではth:ifやth:unlessのほかに、th:text、th:eachなどさまざまなth属性が提供されています。これらはHTMLの拡張として利用でき、サーバーサイドのデータを簡単にHTMLに埋め込めます。
th属性は「テンプレート専用の指示」であり、ブラウザに返却される最終的なHTMLからは削除されるため、見た目には通常のHTMLと同じになります。この仕組みによって、デザイン担当者が扱うHTMLと、開発者が扱うJavaのコードを分離できるのも大きなメリットです。
その中でもThymeleaf th:ifは「条件が真のときに要素を表示」、Thymeleaf th:unlessは「条件が偽のときに要素を表示」という役割を持っています。
3. th:ifの基本的な使い方
Thymeleaf th:ifはもっともシンプルな条件分岐の方法で、式がtrueのときに要素を表示し、falseのときにはその要素が出力されません。具体的な例を見てみましょう。
@Controller
public class SampleController {
@GetMapping("/status")
public String status(Model model) {
model.addAttribute("isLogin", true);
return "status";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>ログイン状態の確認</title></head>
<body>
<p th:if="${isLogin}">ログイン中です</p>
<p th:if="${!isLogin}">ログインしていません</p>
</body>
</html>
ログイン中です
この例ではisLoginがtrueなので「ログイン中です」というメッセージだけが表示されます。逆にfalseなら「ログインしていません」が表示されます。
ポイントは、Thymeleaf th:ifを使うと要素そのものが出力から消えるという点です。CSSで非表示にするのではなく、HTMLとして生成されないため、不要な要素が残らずクリーンな出力が得られます。
初心者が条件分岐を試す場合、まずはこのth:ifを理解することから始めるとよいでしょう。
4. th:unlessの基本的な使い方
Thymeleaf th:unlessはth:ifの逆の動作をする条件分岐です。式がfalseのときに要素を表示し、式がtrueのときには要素を出力しません。つまり「〜でない場合に表示する」という否定条件に使うのが基本です。
例えばログインしていないユーザーにだけ特定のメッセージを表示したい場合にはThymeleaf th:unlessが便利です。条件を反転させる必要がないので、コードがすっきりして初心者にもわかりやすいでしょう。
@Controller
public class UnlessController {
@GetMapping("/welcome")
public String welcome(Model model) {
model.addAttribute("isLogin", false);
return "welcome";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>ようこそページ</title></head>
<body>
<p th:if="${isLogin}">ようこそ、会員ページへ</p>
<p th:unless="${isLogin}">ログインしていない方はゲストとして閲覧中です</p>
</body>
</html>
ログインしていない方はゲストとして閲覧中です
この例ではisLoginがfalseなのでth:unlessの条件が成立し、ゲスト用のメッセージが表示されます。もしisLoginがtrueなら会員ページのメッセージだけが表示されます。
このようにThymeleaf th:unlessは否定条件のときに直感的に書けるので、初心者でも理解しやすい条件分岐の方法です。
5. th:ifとth:unlessの違いと使い分けのポイント
Thymeleaf th:ifとThymeleaf th:unlessは一見すると逆の条件を表すだけのように見えますが、使い分けのポイントを押さえるとコードが読みやすくなります。例えば「ある条件が真のときだけ表示する」場合にはth:ifを使い、「ある条件が偽のときに表示する」場合にはth:unlessを使うのが基本です。
無理にth:ifだけを使って!条件のように書いてしまうと、初心者には読みにくいコードになってしまいます。そのため否定条件を表すときには素直にth:unlessを使うと理解しやすいです。
またth:ifとth:unlessを組み合わせて使うと「条件が真ならAを表示」「偽ならBを表示」といった分岐もシンプルに表現できます。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>条件分岐の比較</title></head>
<body>
<p th:if="${isLogin}">ログイン済みのユーザー向けメニュー</p>
<p th:unless="${isLogin}">ゲストユーザー向けメニュー</p>
</body>
</html>
このようにThymeleaf th:ifとThymeleaf th:unlessをセットで使うことで、条件ごとの表示内容を直感的に書くことができます。可読性が上がるため、実務でもよく利用される書き方です。
6. @Controllerから渡された変数を条件分岐で利用する具体例
ここまででThymeleaf th:ifとThymeleaf th:unlessの基本を学びました。次に実際にSpring MVCの@Controllerから値を渡して、その値を使って条件分岐を行う例を見てみましょう。開発環境はpleiadesでGradleを使い、依存関係はpleiadesで追加する前提です。
@Controller
public class UserController {
@GetMapping("/user")
public String user(Model model) {
model.addAttribute("username", "山田太郎");
model.addAttribute("isAdmin", false);
return "user";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>ユーザー画面</title></head>
<body>
<h3 th:text="${username} + ' さん、ようこそ!'">ユーザー名</h3>
<!-- 管理者なら表示 -->
<p th:if="${isAdmin}">管理者メニューにアクセスできます</p>
<!-- 一般ユーザーなら表示 -->
<p th:unless="${isAdmin}">一般ユーザーとしてログイン中です</p>
</body>
</html>
山田太郎 さん、ようこそ!
一般ユーザーとしてログイン中です
この例では@ControllerからusernameとisAdminをビューに渡しています。テンプレート側ではThymeleaf th:ifを使って管理者専用のメッセージを制御し、Thymeleaf th:unlessを使って一般ユーザー用のメッセージを制御しています。条件が変わると表示内容も切り替わるため、動的な画面を簡単に作成できます。
このようにSpring MVCの@ControllerとThymeleafを組み合わせれば、アプリケーションの状態やユーザー属性に応じた柔軟な画面制御が可能になります。Thymeleaf th:ifとThymeleaf th:unlessを使い分けながら、わかりやすいコードを意識するのが初心者にとって大切な練習になります。
7. th:ifとth:unlessを使うときのよくある間違い(条件式の誤解、変数未設定時のエラーなど)
まずThymeleaf th:ifとThymeleaf th:unlessでよく起きるつまずきを整理します。最初は条件式の誤解です。テンプレート式では、空文字やnullや空コレクションは偽として扱われます。ところが、値が文字列のときに==や!=で比較してしまい、思ったとおりに分岐しないという混乱が起きがちです。文字列は等価比較よりも値の有無を判定する方が安全です。また、未設定の変数をそのまま出そうとして何も表示されなかったり、否定の二重化で読みづらくなるのも典型例です。
次は名前空間の宣言忘れです。テンプレートのhtmlタグにxmlns:thを宣言しないと、Thymeleaf th:ifやThymeleaf th:unlessが効かず、条件分岐が動作しません。雛形を毎回確実にコピーする習慣をつけましょう。さらに、th:ifとth:unlessを同じ条件で同時に使って重複出力を招く例もあります。片方だけで十分な場面が多いので、読みやすさを優先して整理します。
最後に、未設定時の初期値です。モデルに値が入らない可能性があるときは、エルビス演算子?:で安全な既定値を出しておくと、画面崩れを防げます。以下に誤りやすいコードと修正版を並べます。
@Controller
public class PitfallController {
@GetMapping("/pitfall")
public String pitfall(Model model) {
// 値が未設定になることがある想定
String role = null; // 途中の処理で未設定のままになるケース
model.addAttribute("role", role);
model.addAttribute("nickname", "");
model.addAttribute("items", java.util.Collections.emptyList());
return "pitfall";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>よくある間違い</title></head>
<body>
<!-- 誤りやすいパターン:二重否定で読みづらい -->
<p th:if="${!(role == null)}">ロールが設定されています</p>
<!-- 誤りやすいパターン:空文字は偽になるが意図が伝わりにくい -->
<p th:if="${nickname}">ニックネームあり</p>
<!-- 誤りやすいパターン:空リストでもeachは回らず、メッセージが出ない -->
<ul>
<li th:each="it : ${items}" th:text="${it}">項目</li>
</ul>
<!-- 読みやすい修正版1:th:unlessで素直に否定条件を書く -->
<p th:unless="${role}">ロールが未設定です</p>
<!-- 読みやすい修正版2:既定値で画面崩れを防ぐ -->
<p th:text="${nickname} ?: '名称未設定'">名称</p>
<!-- 読みやすい修正版3:空コレクション時のフォールバック -->
<ul th:if="${#lists.isEmpty(items)}">
<li>項目がありません</li>
</ul>
<ul th:unless="${#lists.isEmpty(items)}">
<li th:each="it : ${items}" th:text="${it}">項目</li>
</ul>
</body>
</html>
ロールが未設定です
名称未設定
項目がありません
このように、Thymeleaf th:ifとThymeleaf th:unlessは場面に応じて使い分けると読みやすさが向上します。未設定や空の可能性を常に意識し、既定値やヘルパー関数を併用することで、堅牢な条件分岐を実現できます。
8. th:ifやth:unlessと他のth属性(th:eachやth:switchなど)との組み合わせ例
次に、Thymeleaf th:ifやThymeleaf th:unlessを他の属性と組み合わせて、より現実的なテンプレートを作る例を見ます。代表的なのは一覧の繰り返しを担当するth:eachと、状態に応じて分岐するth:switchです。まずは注文一覧を想定し、空の場合はメッセージ、要素がある場合は明細を表示します。
@Controller
public class OrderController {
@GetMapping("/orders")
public String orders(Model model) {
// 状態は PENDING / SHIPPED / CANCELED のいずれかを想定
java.util.List<OrderVm> list = java.util.List.of(
new OrderVm("A-1001", "PENDING"),
new OrderVm("A-1002", "SHIPPED"),
new OrderVm("A-1003", "CANCELED")
);
model.addAttribute("orders", list);
return "orders";
}
static class OrderVm {
String no; String status;
OrderVm(String n, String s){ this.no=n; this.status=s; }
public String getNo(){ return no; }
public String getStatus(){ return status; }
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>注文一覧</title></head>
<body>
<!-- 空かどうかで全体を切り替え -->
<div th:if="${#lists.isEmpty(orders)}">
<p>注文はまだありません</p>
</div>
<div th:unless="${#lists.isEmpty(orders)}">
<table>
<thead><tr><th>番号</th><th>状態</th><th>案内</th></tr></thead>
<tbody>
<tr th:each="o : ${orders}">
<td th:text="${o.no}">番号</td>
<td th:text="${o.status}">状態</td>
<td th:switch="${o.status}">
<span th:case="'PENDING'">出荷準備中です</span>
<span th:case="'SHIPPED'">発送済みです</span>
<span th:case="'CANCELED'">キャンセル済みです</span>
<span th:case="*">不明な状態です</span>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
A-1001 PENDING 出荷準備中です
A-1002 SHIPPED 発送済みです
A-1003 CANCELED キャンセル済みです
ここでは、一覧の有無をThymeleaf th:ifとThymeleaf th:unlessで切り替え、各行の状態ごとの文言をth:switchで整理しました。さらに、行単位での強調表示にth:ifを併用する例も実務ではよくあります。次はお知らせ一覧で、重要フラグが立っているときだけ注意文を添える例です。
@Controller
public class NoticeListController {
@GetMapping("/notices")
public String notices(Model model) {
java.util.List<NoticeVm> list = java.util.List.of(
new NoticeVm("定期メンテナンスのお知らせ", true),
new NoticeVm("機能追加のご案内", false)
);
model.addAttribute("notices", list);
return "notices";
}
static class NoticeVm {
String title; boolean important;
NoticeVm(String t, boolean i){ this.title=t; this.important=i; }
public String getTitle(){ return title; }
public boolean isImportant(){ return important; }
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>お知らせ</title></head>
<body>
<ul>
<li th:each="n : ${notices}">
<span th:text="${n.title}">件名</span>
<em th:if="${n.important}">※重要</em>
<small th:unless="${n.important}">参考情報</small>
</li>
</ul>
</body>
</html>
定期メンテナンスのお知らせ ※重要
機能追加のご案内 参考情報
このように、Thymeleaf th:ifとThymeleaf th:unlessはth:eachやth:switchと組み合わせることで、状態に応じた分岐と繰り返しを読みやすく表現できます。構造はth:each、条件はth:if/th:unless、分類はth:switchと役割を分けるのがコツです。
9. 初心者におすすめの練習方法(簡単な表示切り替えアプリを作るなど)
仕上げに、Thymeleaf th:ifとThymeleaf th:unlessの習熟に役立つ練習メニューを示します。手順は短い単位で反復し、毎回「モデルにどのキーで何を入れ、テンプレートでどう参照したか」をメモすると理解が定着します。まずはログイン状態、次にロールやステータス、最後に一覧と個別の組み合わせへ段階的に進みます。
最初の課題は、メッセージ入力の有無で表示を切り替える超小型アプリです。未入力なら案内文、入力済みなら確認文を出します。コントローラは@Controllerで、ビルドはGradle、実行はpleiadesという前提を保ちます。
@Controller
public class PracticeController {
@GetMapping("/toggle")
public String toggle(@RequestParam(value = "msg", required = false) String msg, Model model) {
model.addAttribute("msg", msg);
model.addAttribute("level", "INFO"); // INFO / WARN / ERROR を想定
return "toggle";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>表示切り替え練習</title></head>
<body>
<!-- 未入力なら案内、入力済みなら確認 -->
<p th:if="${#strings.isEmpty(msg)}">メッセージが未入力です</p>
<p th:unless="${#strings.isEmpty(msg)}" th:text="'入力内容:' + ${msg}">入力内容</p>
<!-- レベルに応じた分類表示 -->
<p th:switch="${level}">
<span th:case="'INFO'">情報</span>
<span th:case="'WARN'">注意</span>
<span th:case="'ERROR'">エラー</span>
<span th:case="*">その他</span>
</p>
</body>
</html>
メッセージが未入力です
情報
次の課題は、一覧と条件分岐の併用です。空リスト時のフォールバック、各要素の状態によるバッジ表示、ヘッダーやフッターの有無切り替えをひとつの画面で練習します。これにより、Thymeleaf th:ifとThymeleaf th:unlessを過不足なく使い分ける感覚が身につきます。最後は、入力フォームからの値で一覧を絞り込み、その結果の有無でメッセージを出すところまで到達すれば、実務の基礎に十分です。
学習のコツは、否定条件を無理に!条件で書かず、読み手が瞬時に理解できるようにth:unlessを選ぶこと、そして未設定や空のケースを常に先回りして扱うことです。加えて、テンプレートの先頭にxmlns:thを忘れない、モデルのキー名を統一する、既定値を用意するという三点をチェックリスト化すると、エラーの大半を事前に防げます。これらを反復することで、Thymeleaf th:ifとThymeleaf th:unlessを中核にしたわかりやすい条件分岐設計が自然に身につきます。