Thymeleaf nullチェックを行う理由と注意点|Spring Controllerで安全に存在チェックを行う方法
新人
「先輩、Thymeleafでデータを表示しようとしたら、画面に何も出ないんです。これってエラーですか?」
先輩
「実はそれ、Thymeleafのnullチェックが関係している可能性があるね。オブジェクトがnullの場合、そのプロパティを参照するとエラーや空白になることがあるんだ。」
新人
「なるほど…。でも、Spring Controllerで渡したデータがちゃんとあるのに、なぜそんなことが起きるんですか?」
先輩
「いい質問だね。Thymeleafでは、テンプレート内で変数がnullのまま使われると、何も表示されなかったり、NullPointerExceptionが発生することもあるんだ。だから、nullチェックはとても重要なんだよ。」
新人
「なるほど!じゃあ、まずThymeleafそのものがどんな仕組みなのかから教えてください!」
1. Thymeleafとは?テンプレートエンジンの基本を理解しよう
Thymeleaf(タイムリーフ)は、Spring Frameworkでよく使われるテンプレートエンジンです。テンプレートエンジンとは、HTMLファイルの中に変数や制御文を埋め込み、サーバー側のデータを画面上に表示するための仕組みのことです。
たとえば、Springの@Controllerで設定したModelオブジェクトからデータを取り出し、HTMLに動的に表示することができます。これは、JSPよりも直感的で、HTML構造を壊さずに動的な値を埋め込めるのが特徴です。
Thymeleafでは、HTMLの属性にth:textやth:ifなどを使って値を埋め込みます。次のような例を見てみましょう。
<p th:text="${user.name}">ユーザー名がここに表示されます</p>
上記のコードは、Spring Controllerで渡されたuserオブジェクトのnameプロパティを表示する処理です。しかし、もしuserがnullだった場合、このテンプレートを描画するとエラーが発生したり、空白のままになることがあります。
このようなトラブルを防ぐために、Thymeleafではnullチェック(存在チェック)が欠かせないのです。特に、Spring MVCの開発では、Controllerが返すModel属性がnullであるケースを想定しておくことが大切です。
ここで、簡単なSpring Controllerの例を紹介します。pleiades+Gradle環境で実行できるサンプルです。
@Controller
public class UserController {
@GetMapping("/user")
public String user(Model model) {
User user = null; // データが存在しないケースを想定
model.addAttribute("user", user);
return "user"; // user.htmlを表示
}
}
このように、Controller側でnullを渡してしまうと、テンプレート内で${user.name}を呼び出したときにエラーになることがあります。これが、Thymeleafでnullチェックを行うべき理由の一つです。
2. nullチェックを行う理由|なぜ必要なのか?
Thymeleafでnullチェックを行う最大の理由は、エラーの防止と安全な画面描画のためです。Spring Controllerから渡されたデータが常に存在するとは限りません。たとえば、ユーザー情報をデータベースから取得する際、該当データがなければnullになることがあります。
その状態でテンプレート内の${user.name}を呼び出すと、Thymeleafは「userが存在しない」という状況に直面します。これにより、画面が正しく表示されなかったり、場合によっては例外エラー(NullPointerException)を引き起こすことがあります。
たとえば、次のようなHTMLテンプレートを考えてみましょう。
<p th:text="${user.name}">ユーザー名</p>
もしuserがnullの場合、Thymeleafはこのプロパティにアクセスできず、表示結果は空白になります。開発中なら気づきやすいですが、本番環境では表示崩れや不具合としてユーザーに見えてしまうこともあります。
このような問題を防ぐには、Thymeleafのth:if属性を使って、「値が存在する場合だけ表示する」という条件分岐を行うのが一般的です。
<div th:if="${user != null}">
<p th:text="${user.name}"></p>
</div>
<div th:unless="${user != null}">
<p>ユーザー情報が見つかりませんでした。</p>
</div>
このように記述することで、userがnullであれば代わりのメッセージを表示し、エラーを回避できます。これが「存在チェック」の基本的な考え方です。
また、条件分岐をもう少しシンプルに書く方法として、Thymeleafが提供するユーティリティオブジェクト#objectsを使うこともできます。
<p th:if="${#objects.nonNull(user)}" th:text="${user.name}"></p>
<p th:unless="${#objects.nonNull(user)}">ユーザー情報がありません。</p>
#objects.nonNull()は「オブジェクトがnullでない場合にtrueを返す」関数で、読みやすく安全なコードになります。Thymeleafではこのような便利な関数を使うことで、コードの意図を明確にし、エラーを防止できます。
つまり、Thymeleafのnullチェックは単なる防御策ではなく、安全な画面設計と保守性の向上にもつながります。特にpleiades+Gradle構成でSpring Controllerを使う場合、テンプレートエラーを未然に防ぐために必ず意識しておきましょう。
新人
「なるほど…。テンプレートの中で値がnullかどうかをちゃんと見ておかないと、思わぬ不具合が起きるんですね。」
先輩
「そうそう。Spring ControllerとThymeleafの連携では、nullチェックが安定動作の鍵なんだ。これを理解しておくと、どんな画面開発でも応用できるよ。」
3. nullチェックの基本的な書き方(th:ifとth:unlessの使い方)
Thymeleafでnullチェックを行う最も基本的な方法は、th:ifとth:unless属性を使うことです。これらはテンプレート内で条件分岐を行うための構文で、Javaのif文に近い動きをします。
th:ifは「条件がtrueのときに表示」、th:unlessは「条件がfalseのときに表示」されるというルールです。これを利用して、オブジェクトがnullかどうかで表示内容を切り替えます。
<div th:if="${user != null}">
<p th:text="${user.name}"></p>
</div>
<div th:unless="${user != null}">
<p>ユーザー情報が存在しません。</p>
</div>
このように記述すると、userが存在する場合のみユーザー名を表示し、存在しない場合は「ユーザー情報が存在しません」と表示されます。これがThymeleafにおける基本的なnullチェックの書き方です。
特にSpring Controllerから渡されたデータがnullの可能性があるときには、このような構文をテンプレート内で明示的に使うことが重要です。例外を未然に防ぐだけでなく、ユーザーに適切なメッセージを表示できます。
新人
「つまり、if文の代わりにth:ifとth:unlessを使えばいいんですね!」
先輩
「その通り。ThymeleafではJavaのif文を直接書けないから、属性として条件を書くんだよ。HTMLタグの中で完結するのがポイントだね。」
また、th:ifの条件式には論理演算子も使えます。たとえば、userがnullでなく、かつ年齢が18歳以上の場合に表示するような複合条件も記述可能です。
成人ユーザーとして登録されています。
このように、Thymeleafではテンプレート内で柔軟な条件式を書けるため、複数のチェックを同時に行いたいときにも便利です。
4. 存在チェックの方法(#objects.nonNullやisEmptyの使い方)
Thymeleafには、nullチェックをより読みやすく記述するための便利な関数が用意されています。その中でも特によく使われるのが、#objects.nonNull()と#lists.isEmpty()です。
まず、#objects.nonNull()は、指定したオブジェクトがnullでない場合にtrueを返す関数です。JavaのObjects.nonNull()に似た働きをします。
<p th:if="${#objects.nonNull(user)}" th:text="${user.name}"></p>
<p th:unless="${#objects.nonNull(user)}">ユーザー情報が登録されていません。</p>
このように書くことで、userがnullかどうかを明確に判定でき、読み手にも意図が伝わりやすくなります。
一方、リストやコレクションを扱う場合には、#lists.isEmpty()を使ってデータの有無をチェックします。例えば、ユーザー一覧を表示する場合は次のように書けます。
<div th:if="${users != null and !#lists.isEmpty(users)}">
<ul>
<li th:each="u : ${users}" th:text="${u.name}"></li>
</ul>
</div>
<div th:unless="${users != null and !#lists.isEmpty(users)}">
<p>登録されたユーザーは存在しません。</p>
</div>
このように、リストがnullでなく、かつ空でない場合のみ一覧を表示することができます。Thymeleafでは、#lists以外にも#stringsや#numbersといったユーティリティ関数が用意されており、文字列や数値の存在チェックにも応用できます。
これらを組み合わせることで、テンプレート内の条件分岐をより安全かつ可読性高く記述できるようになります。
新人
「#objectsや#listsを使うと、コードが短くて分かりやすいですね!」
先輩
「そうだね。これらの関数はThymeleafのユーティリティオブジェクトと呼ばれていて、テンプレートを安全に書くための便利な道具なんだ。」
5. よくあるエラーとその対処法(nullポインタ例外・比較ミスなど)
Thymeleafを使っていると、初心者がつまずきやすいのがnull関連のエラーです。特に多いのが、NullPointerExceptionと条件式の比較ミスです。
① NullPointerExceptionが発生するケース
テンプレート内で${user.name}のように直接プロパティを参照しているときに、userがnullの場合、このエラーが発生します。
対策としては、必ずth:ifや#objects.nonNull()を併用してnullチェックを行うことです。
<p th:if="${user != null}" th:text="${user.name}"></p>
また、Spring Controller側で初期値を設定しておく方法も有効です。たとえば、Modelに空のオブジェクトを渡すようにすると、テンプレートでのnullリスクを減らせます。
@Controller
public class UserController {
@GetMapping("/userSafe")
public String safeUser(Model model) {
model.addAttribute("user", new User()); // nullではなく空のオブジェクトを渡す
return "userSafe";
}
}
② 比較演算子の誤用による表示ミス
Thymeleafでは、比較式を記述するときに==や!=を使うことができますが、Javaのような厳密な型比較ではありません。たとえば、数値を文字列として比較すると意図した結果にならないことがあります。
次のようなケースでは注意が必要です。
<p th:if="${user.age == '18'}">18歳のユーザーです。</p>
この例では、user.ageが数値型で、右辺が文字列のため、比較が正しく行われません。正しくは数値として比較するようにします。
<p th:if="${user.age == 18}">18歳のユーザーです。</p>
③ 存在しない変数を参照してしまうミス
Thymeleafでは、Controllerから渡されていない変数を参照すると、エラーではなく空白として扱われます。つまり、原因が分かりづらい「表示されないバグ」になりやすいのです。
対策として、テンプレート内で必ずth:ifや#objects.nonNull()を併用すること、また開発時にはth:utextでデバッグ出力を行うことをおすすめします。
<p th:utext="'デバッグ: ' + ${user}"></p>
このように、テンプレート内で変数の存在を確認しながら開発を進めると、原因特定が容易になります。
新人
「なるほど、比較式や存在しない変数の参照も要注意なんですね。」
先輩
「そう。Thymeleaf nullチェックはただの条件分岐ではなく、安全にテンプレートを描画するための重要な仕組みなんだ。開発の初期段階から意識して書いておくのがポイントだよ。」
6. nullチェックを安全に設計するためのベストプラクティス
ここからは、Thymeleafのnullチェックを単なる対処法ではなく、安全な設計の一部として考える方法を紹介します。特に、Spring Controllerとの連携やpleiades+Gradle環境での開発において、初期段階からnullを意識した設計が重要です。
まず基本となる考え方は、「テンプレートでnullを扱わない設計にする」ことです。つまり、Controllerで渡す段階でデータを初期化しておくことで、Thymeleafでのnull判定を最小限にできます。
@Controller
public class SafeUserController {
@GetMapping("/userInfo")
public String userInfo(Model model) {
User user = new User(); // 空のオブジェクトで初期化
user.setName("未登録ユーザー");
model.addAttribute("user", user);
return "userInfo";
}
}
このように、@Controllerで初期値を設定しておくと、Thymeleafテンプレート内で${user.name}を参照してもnullにはなりません。これは、Spring MVCの設計として非常に有効な考え方です。
また、オプションとしてJavaのOptionalを使う設計もあります。これにより、値の存在・非存在を明示的に扱うことができ、テンプレート側でのnullチェックもシンプルになります。
Optional<User> optionalUser = Optional.ofNullable(userService.findUserById(id));
model.addAttribute("user", optionalUser.orElse(new User()));
このような書き方をすれば、nullのままThymeleafに渡ることを防ぎつつ、テンプレート側の記述をシンプルに保てます。
さらに、Thymeleafテンプレート内では、変数が存在しない場合にもエラーにならないため、開発者は気づきにくいことがあります。そのため、プロジェクト全体の設計段階で「nullが発生しうるポイント」を明確にすることが大切です。
新人
「なるほど…。Controllerで初期化しておくと、テンプレート側のチェックが少なくて済むんですね。」
先輩
「そうなんだ。nullチェックをテンプレートに任せすぎると、どこでデータが欠けているのか分かりづらくなるからね。設計段階でデータの流れを整理しておくのがベストだよ。」
7. Spring Controllerとの連携例|安全に値を渡す方法
次に、実際にSpring ControllerとThymeleafを連携させて、安全にデータをやり取りする方法を見ていきましょう。ここでは、pleiades環境でGradleプロジェクトを作成している前提です。
Controller側で値をセットし、テンプレートに渡すときには、必ずModelを通じてデータを保持します。その際に、nullを避けるための工夫をしておくと安心です。
@Controller
public class ProductController {
@GetMapping("/product")
public String getProduct(Model model) {
Product product = findProduct(); // DBなどから取得
if (product == null) {
product = new Product();
product.setName("不明な商品");
}
model.addAttribute("product", product);
return "product";
}
}
上記のように、Controller内でnullを検知して代替データを設定しておくと、Thymeleaf側でのnullチェックが簡潔になります。
テンプレート側では次のように安全に値を表示します。
<div th:if="${product != null}" class="info">
<p th:text="${product.name}">商品名</p>
</div>
<div th:unless="${product != null}">
<p>商品情報が存在しません。</p>
</div>
このように設計しておけば、ControllerとThymeleafのどちらでも安全にnull判定を行えます。特にpleiades環境では、テンプレート更新時の即時プレビューが可能なので、nullチェックの動作確認にも最適です。
新人
「Controllerで初期値を設定しておけば、テンプレート側でエラーを気にしなくていいんですね!」
先輩
「その通り。Spring Controllerはデータの入口だから、ここで安全な値を渡す設計をしておくと、Thymeleaf側でのトラブルも激減するよ。」
8. nullチェックを怠ったときの典型的なトラブル例
最後に、Thymeleafでnullチェックを怠った場合に起こりがちなトラブルを紹介します。これらは初心者が特に陥りやすい落とし穴です。
① 空白のまま表示される
Controllerから値がnullのまま渡された場合、テンプレート内の変数は単に「空白」として描画されます。エラーが出ないため気づきにくく、本番環境で「データが消えた」と報告されることもあります。
<p th:text="${user.email}">メールアドレス</p>
上記のようなコードでuserがnullなら、画面には何も表示されません。開発中は気づかなくても、ユーザー側には不具合として見えてしまいます。
対策としては、th:ifや#objects.nonNull()を用いた条件表示を必ず組み込むことです。
<p th:if="${#objects.nonNull(user)}" th:text="${user.email}"></p>
<p th:unless="${#objects.nonNull(user)}">メールアドレスが登録されていません。</p>
② NullPointerExceptionの発生
テンプレート内で多段アクセス(例:${order.customer.name})をしている場合、途中のオブジェクトがnullだと例外が発生します。
このような場合には、Controllerで各オブジェクトの存在を保証するか、テンプレート側で条件分岐を明示的に行う必要があります。
<div th:if="${order != null and order.customer != null}" th:text="${order.customer.name}"></div>
③ 条件式の誤用による誤表示
ThymeleafではJavaに似た構文を使えますが、実際には式言語(SpEL)として評価されます。そのため、文字列比較や論理演算で意図しない結果を招くことがあります。
<p th:if="${user.status == 'active'}">アクティブユーザー</p>
このコードは一見正しく見えますが、実際にはnullの場合にfalseと判定され、何も表示されません。安全のために、null判定を組み合わせるのが確実です。
<p th:if="${user != null and user.status == 'active'}">アクティブユーザー</p>
新人
「なるほど…。見た目は正常でも、内部でnullが潜んでいると怖いですね。」
先輩
「そうなんだ。だからこそ、Thymeleaf null判定を丁寧に書いておくことが大事なんだよ。Spring Controllerとの連携でしっかり設計すれば、トラブルを未然に防げる。」
このように、Thymeleaf nullチェックは単なる記法の問題ではなく、安全なSpring MVC設計の要です。Gradle構成やpleiades環境でも同様に活用できるため、どんなプロジェクトでも実践できる考え方として覚えておきましょう。
まとめ
ここまで、Spring Boot開発において避けては通れない「Thymeleafでのnullチェック(存在チェック)」について、その重要性から具体的な実装方法、さらには設計レベルでの対策まで詳しく解説してきました。Webアプリケーションの開発において、サーバー側から渡されるデータが常に完璧に揃っているとは限りません。データベースの検索結果が0件だったり、入力フォームの任意項目が空だったりと、実務では「値がない状態」に遭遇する場面が多々あります。
Thymeleaf nullチェックの要点おさらい
Thymeleafで安全に画面を描画するためには、以下の3つのポイントを意識することが大切です。
- th:ifとth:unlessを活用する: HTMLタグの表示・非表示を制御する基本中の基本です。単純な比較だけでなく、論理演算子(and, or)を組み合わせることで、複雑な条件にも柔軟に対応できます。
- ユーティリティオブジェクトを活用する:
#objects.nonNull()や#lists.isEmpty()、さらには#strings.isEmpty()など、Thymeleafが提供する専用のツールを使うことで、直感的でミスの少ないコードが書けます。 - Controller側での事前対策: テンプレート側にチェックを丸投げするのではなく、Spring Controller側で
Optionalを利用したり、空のインスタンスを生成してModelに渡したりすることで、フロントエンドでの負担を軽減できます。
実践的なサンプルプログラム
これまでの内容を踏まえて、より実戦に近いコードを書いてみましょう。例えば、ユーザー詳細画面を表示する際、住所情報(Address)が登録されていないケースを想定したコードです。
// Controller側での安全なデータ準備
@GetMapping("/profile")
public String showProfile(Model model) {
// 本来はDBから取得。ここでは見つからなかった場合を想定。
User user = userService.findById(1L);
if (user == null) {
// ユーザー自体がいない場合は、別のエラー画面やメッセージ用オブジェクトを渡す
model.addAttribute("errorMessage", "該当するユーザーは見つかりませんでした。");
return "error";
}
model.addAttribute("user", user);
return "profile";
}
<div class="user-container">
<h3>ユーザー情報</h3>
<div th:if="${user != null}" class="profile-card">
<p>名前:<span th:text="${user.name}">名無しさん</span></p>
<div th:if="${user.address != null}">
<p>住所:<span th:text="${user.address.fullAddress}">東京都...</span></p>
</div>
<div th:unless="${user.address != null}">
<p class="text-muted">※住所情報は登録されていません。</p>
</div>
</div>
</div>
このように、プログラム側とテンプレート側の両面でガードを固めることで、ユーザーに対して「画面が真っ白になる」「変なエラーメッセージが出る」といった不快な体験をさせずに済みます。特にPleiadesでの開発中は、ホットデプロイ機能などを使って、条件分岐が正しく動いているか、nullの時にどう見えるかを都度確認しながら進めるのが上達の近道です。
Thymeleafの魅力は、HTMLとしての形を保ちながら動的な処理を組み込める点にあります。nullチェックをマスターすれば、より堅牢で、かつユーザーに優しいSpring Bootアプリケーションが構築できるようになります。今日学んだテクニックを、ぜひ明日からのコーディングに活かしてみてください。
生徒
「先生、ありがとうございました!Thymeleafのnullチェックって、単にエラーを防ぐだけじゃなくて、ユーザーに『情報がないですよ』って親切に伝えるためのものでもあるんですね。」
先生
「その通りだね。システム開発において『データがない』という状態は、異常ではなく一つの『状態』なんだ。それをどう見せるかがUI/UXの設計においてとても重要だよ。」
生徒
「さっきのサンプルコードを見て気づいたんですけど、Controllerでif (user == null)ってチェックして、テンプレートでもth:if="${user != null}"って書くのは、二重チェックになって無駄じゃないんですか?」
先生
「いいところに気づいたね。一見無駄に見えるかもしれないけれど、これは『多重防御』という考え方なんだ。万が一Controller側でチェックを漏らしてしまったり、後から仕様変更でnullが渡るようになったりしても、テンプレート側でチェックしていれば画面がクラッシュすることはないからね。」
生徒
「なるほど…。『絶対に大丈夫』と思わず、どこでエラーが起きてもいいように備えておくんですね。プログラミングの奥深さを感じます!」
先生
「ははは、そうだね。特にSpring Bootのような大規模なフレームワークでは、チーム開発も多い。他の人が書いたコードからnullが飛んでくることもあるから、自分の担当するテンプレートは自分で守る、という意識を持つと素晴らしいエンジニアになれるよ。」
生徒
「自分の担当は自分で守る!カッコいいですね。今日教わった#objects.nonNullや#lists.isEmpty、さっそく今のプロジェクトの修正に使ってみます!」
先生
「その意気だ。もし困ったらまたいつでも聞きにおいで。Pleiadesのエディタ上で波線が出たり、ログにNullPointerExceptionが出ていたりしたら、まずは今回の内容を思い出してチェックしてみよう。」