Thymeleaf th:srcで画像のURLを動的に設定する方法
新人
「先輩、Springの画面に画像を表示したいんですけど、Thymeleafでどうやって画像のURLを指定するんですか?」
先輩
「いい質問だね。Thymeleafにはth:srcという便利な属性があって、それを使うと画像URLを動的に設定できるんだ。」
新人
「動的ってことは、コントローラから値を渡して、その値をもとに画像を変えられるんですか?」
先輩
「その通り!では、Thymeleafの基本から順番に見ていこう。」
1. Thymeleafとは?
Thymeleaf(タイムリーフ)は、Spring MVCでよく使われるテンプレートエンジンです。Javaのサーバーサイドで用意したデータを、HTMLテンプレートの中に埋め込んで動的な画面を作成できます。特に、HTMLファイルに直接組み込む形で書けるため、フロントエンドの人も違和感なくHTMLとして編集できます。Thymeleafは静的なHTMLとしても表示でき、動的にも使える点が大きな特徴です。
例えば、商品一覧ページにおいて、商品名や価格をサーバーから受け取って、一覧表示するようなときにとても便利です。Spring BootやSpring MVCの標準的な組み合わせとして利用されるため、初心者が最初に学ぶテンプレートエンジンとして最適です。
2. th属性とは?(特にth:srcの役割)
Thymeleafにはth:srcやth:textなどの「th属性」が用意されています。これらはHTMLの標準属性に「th:」を付けて動的に値を設定できるようにしたものです。
特にth:srcは、HTMLのimgタグで画像を表示する際に使います。通常はsrc属性に画像ファイルのパスを直接書きますが、それだと固定された画像しか表示できません。th:srcを使うことで、コントローラから渡された変数を使って画像URLを変化させることができるのです。
例えば、次のように書けます。
<img th:src="@{${imageUrl}}" alt="商品画像">
このようにすると、コントローラで渡したimageUrl変数の値がそのまま画像のURLとして反映されます。
3. 静的な画像URLと動的な画像URLの違い
まず、静的な画像URLとは、HTMLに直接ファイルパスを記述して固定的に表示する方法です。例えば、以下のようになります。
<img src="/images/sample.png" alt="サンプル画像">
この場合、表示される画像は常に/images/sample.pngになります。簡単ですが、ユーザーごとに異なる画像を表示したいときには対応できません。
一方で動的な画像URLは、コントローラから値を渡して変化させる仕組みです。たとえばユーザーごとにプロフィール画像を変える場合、コントローラでimageUrlを渡して、ビューではth:srcを使うことで、ユーザーごとに異なる画像を自動で切り替えることができます。
イメージとしては、次のような違いです。
静的な記述例
<img src="/images/user1.png" alt="固定画像">
動的な記述例
<img th:src="@{${imageUrl}}" alt="動的画像">
これにより、柔軟な画面構成が可能になります。例えば、商品画像を一覧表示するときや、ユーザーごとのプロフィールを表示するときなど、動的な仕組みが必須となる場面で非常に役立ちます。
4. コントローラで画像パスをModelに渡す方法
Spring MVCでThymeleafを利用する際には、まずコントローラからビューに値を渡す仕組みを理解する必要があります。画像のURLを動的に表示したいときも同じで、コントローラのメソッドからModelにデータを格納し、それをテンプレート内で参照する流れになります。
例えば、ユーザーのプロフィール画面で個別のプロフィール画像を表示したい場合を考えてみましょう。コントローラではユーザーIDを受け取り、そのユーザーに対応する画像のURLをModelに追加します。
@Controller
public class ProfileController {
@GetMapping("/profile")
public String showProfile(Model model) {
String imageUrl = "/images/users/user1.png";
model.addAttribute("imageUrl", imageUrl);
return "profile";
}
}
このように記述すると、ビュー側でimageUrlという変数を使えるようになります。Spring MVCとThymeleafを組み合わせることで、Javaのコードから動的に値を渡してHTMLの中に埋め込むことが可能になります。
ここで重要なのは、@Controllerを使ってページ遷移を行うという点です。今回の前提条件では@RestControllerは使わないため、必ず@Controllerを利用してModelに値を格納するようにしてください。
5. ThymeleafのHTMLテンプレートでth:srcを使って動的に画像を表示する例
次に、コントローラから渡されたimageUrlをテンプレート内で利用して実際に画像を表示してみましょう。Thymeleafのテンプレートではth:src属性を利用して、Modelから渡された値を動的に反映させます。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>プロフィールページ</title>
</head>
<body>
<h3>プロフィール画像</h3>
<img th:src="@{${imageUrl}}" alt="プロフィール画像">
</body>
</html>
このテンプレートでは、imageUrlに格納されたパスが自動的にsrc属性に反映されます。結果として、コントローラで設定したユーザーごとの画像が表示されるようになります。
例えば、先ほどの例で/images/users/user1.pngを渡していた場合、ブラウザにはその画像が表示されます。Spring MVCの仕組みによって、テンプレートの中にJavaの変数が埋め込まれる形になるため、非常に柔軟な表現が可能です。
ここで重要なのは、Thymeleafのth:srcは通常のHTML属性を上書きするという点です。そのため、同じタグにsrcとth:srcを両方書いた場合、最終的に使われるのはth:srcです。動的な画像表示をしたいときには必ずth:srcを使うようにしましょう。
6. パラメータや変数を使った動的URLの設定例
さらに応用として、パラメータや変数を利用して動的にURLを組み立てる方法を紹介します。例えば、ユーザーIDを受け取って、そのユーザーに対応する画像ファイル名をURLとして生成するケースです。
コントローラ側では、リクエストパラメータからユーザーIDを取得し、そのIDをもとに画像URLを組み立てます。
@Controller
public class ProfileController {
@GetMapping("/profile/{userId}")
public String showProfile(@PathVariable String userId, Model model) {
String imageUrl = "/images/users/" + userId + ".png";
model.addAttribute("imageUrl", imageUrl);
return "profile";
}
}
この場合、URLとして/profile/user1にアクセスすれば、/images/users/user1.pngが自動的に指定されます。同様に/profile/user2にアクセスすれば、/images/users/user2.pngが表示されます。
ビュー側のテンプレートは先ほどと同じ記述で問題ありません。
<img th:src="@{${imageUrl}}" alt="動的プロフィール画像">
この仕組みによって、ひとつのテンプレートで複数のユーザー画像を切り替えて表示することができます。Spring MVCとThymeleafを組み合わせることで、URLの一部を動的に変更する処理が可能になるのです。
また、場合によってはクエリパラメータを利用してURLを作成することもできます。例えば、次のように書くとクエリ文字列を追加した形で画像を指定できます。
<img th:src="@{'/images/users/' + ${userId} + '.png'}" alt="ユーザー画像">
このように、変数を使ってURLを組み立てることで、より柔軟に画面を構築できるようになります。商品一覧や記事のサムネイルなど、さまざまな用途で応用が可能です。初心者の方でも、まずは固定的な画像URLから始め、徐々に動的な仕組みに挑戦してみると理解が深まります。
7. 実際のプロジェクトで使われる具体例(商品画像やプロフィール画像など)
商品画像を動的に切り替える例
商品一覧や詳細の画面では、商品ごとに異なる画像を素早く表示する必要があります。ここでは商品識別子を受け取り、画像の格納場所から対応する画像を選び、テンプレートで動的に表示する流れを示します。ひとつの画面で多数の画像を扱う場合でも、テンプレートの変数を活用することで、表示の切り替えが自然に行われます。たとえば商品識別子に合わせて画像の相対位置を決め、必要に応じて差し替えを行うと、一覧と詳細の両方に同じ方針を適用できます。これにより管理と保守の手間が下がり、変更にも強い構成になります。さらに画像の種類が増えても、同じ規則に従って格納する設計にしておけば、テンプレート側の記述を増やさずに流用できます。
@Controller
public class ProductController {
@GetMapping("/products/{code}")
public String detail(@PathVariable String code, Model model) {
String imageUrl = "/images/products/" + code + "/main.png";
model.addAttribute("imageUrl", imageUrl);
model.addAttribute("name", "サンプル商品");
return "product/detail";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h3>商品画像の表示</h3>
<img th:src="@{${imageUrl}}" alt="商品画像">
<p th:text="${name}">商品名</p>
</body>
</html>
プロフィール画像を動的に差し替える例
会員向けの画面では、利用者ごとのプロフィール画像を安全に読み出す仕組みが大切になります。識別子の指定だけでなく、未設定のときに代替画像を提示する工夫も重要です。未設定のまま画像要素が空になると表示の欠落が目立ち、体験の質が下がります。代替画像を決めておけば、どの利用者にも常にわかりやすい見た目を提供できます。さらに画像の切り替えに合わせて説明文も差し替えると、検索に役立つ語句を自然に含めることができ、案内も親切になります。
@Controller
public class AccountController {
@GetMapping("/account/{id}")
public String view(@PathVariable String id, Model model) {
boolean hasCustom = !"guest".equals(id);
String imageUrl = hasCustom ? "/images/profiles/" + id + ".png" : "/images/profiles/default.png";
model.addAttribute("imageUrl", imageUrl);
model.addAttribute("displayName", "メンバー");
return "account/view";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h3>プロフィール画像の表示</h3>
<img th:src="@{${imageUrl}}" alt="プロフィール画像">
<p th:text="${displayName}">利用者名</p>
</body>
</html>
外部配信基盤を前提にした設計の例
大規模な構成では画像を外部配信の基盤に置くことが多くなります。配信基盤の基底となる位置を変数として用意し、相対位置を結合して表示に使うと、配信先の切り替えも容易になります。基底の位置は設定から読み出し、画面には完全な位置を渡すようにすると、表現の側には規則だけが伝わり、環境差が透過的になります。
@Controller
public class CdnController {
private final String base = "https://cdn.example.com/assets";
@GetMapping("/banner/{key}")
public String banner(@PathVariable String key, Model model) {
String imageUrl = base + "/banners/" + key + ".png";
model.addAttribute("imageUrl", imageUrl);
return "banner/view";
}
}
<img th:src="@{${imageUrl}}" alt="バナー画像">
このように基底の位置をひとつにまとめておけば、移行や拡張の際にも変更点が少なくなり、管理の負担が軽くなります。さらに画像の規則が定まっていれば、一覧の画面でも詳細の画面でも同じ方式を共有でき、構成の単純さが保たれます。
8. 動的URL設定でよくあるエラーとその解決方法
変数の値が空のままになる不具合
変数の値が空のままだと表示が消え、要素が占める広さが想定と異なってしまいます。値が空でも代替の画像を示す設計にしておくと、崩れを防げます。表示の側に分岐を置くと記述が増えるため、できるだけ値を用意する段階で代替の値を決めておくのが素直です。
属性の指定を間違える記述
属性の書き方の勘違いもよく起こります。特に要素の属性と拡張属性を混在させると意図が伝わらず、値が反映されません。記述の比較を次に示します。
誤った記述の例
<!-- 誤り:変数を文字列として扱っているため置換されない -->
<img th:src="'${imageUrl}'" alt="誤り">
<!-- 誤り:通常の属性に変数を入れても評価されない -->
<img src="${imageUrl}" alt="誤り">
正しい記述の例
<!-- 正しい:変数の値を属性に反映する -->
<img th:src="@{${imageUrl}}" alt="正しい">
位置の指定に余分な区切りが入る不具合
位置の先頭の区切りと基底の末尾の区切りが重なると、位置に余分な区切りが紛れ、正しく参照できないことがあります。変数を組み合わせる前に末尾と先頭の区切りをそろえる工夫をすると安定します。基底の側は末尾を空にしておき、相対位置の側の先頭のみを使うのが単純です。
構成に関する準備不足による見えない不具合
位置の指定が合っていても、静的な資源の配布の規則が整っていなければ表示は成功しません。静的な資源の配布の規則を見直し、位置の範囲と出力の位置を一致させることが大切です。配布の規則を設定の側に書き、位置の範囲を画面の側の規則と合わせると、開発の環境と稼働の環境の差が少なくなります。
@Controller
public class StaticController {
@GetMapping("/sample")
public String sample(Model model) {
model.addAttribute("imageUrl", "/images/sample.png");
return "sample/view";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<img th:src="@{${imageUrl}}" alt="静的資源の確認">
</body>
</html>
このように画面と配布の規則の一致を検証する手順を用意しておくと、表示の不具合の切り分けが速くなります。値の受け渡しと配布の規則を別々に確認できるため、原因の特定が容易になります。
差し替えの直後に表示が更新されない不具合
差し替えの直後は記憶の仕組みにより以前の画像が残ることがあります。位置に軽い印を加え、表示の側に新しい画像を示すと、直後の確認が楽になります。印の値は生成の側で決め、画面の側では受け取った値をそのまま結合します。
@Controller
public class CacheController {
@GetMapping("/thumb/{code}")
public String thumb(@PathVariable String code, Model model) {
String version = String.valueOf(System.currentTimeMillis());
String imageUrl = "/images/thumbs/" + code + ".png?v=" + version;
model.addAttribute("imageUrl", imageUrl);
return "thumb/view";
}
}
<img th:src="@{${imageUrl}}" alt="差し替え直後の確認">
印の値は後から変更しやすいように用意し、必要がなくなれば取り除けるようにしておくと安全です。表示の側は受け取った値を素直に使うのみとし、複雑な結合を避けると読みやすさが保たれます。
9. 初心者がつまずかないためのポイントやベストプラクティス
値を渡す段階で代替の値を決めておく
表示の側で分岐を増やすと見通しが悪くなります。受け渡しの段階で代替の値を決め、画面の側ではひとつの変数だけを使うと、記述が短く安定します。代替の値は開発の環境でも稼働の環境でも同じ規則で決められるようにし、規則を一か所に保管しておくのが安全です。説明の文もあわせて代替の文言を用意しておくと、視覚の補助が届かない場合にも意味が通ります。
位置の結合は画面の外で行う
画面の側で結合を増やすと記述が長くなります。結合は受け渡しの側で行い、画面の側は完成した位置をそのまま使うと読みやすくなります。画面の側での結合が完全に不要になるため、保守の作業の負担が減ります。規則が増えたときにも受け渡しの側だけを直せばよく、画面の側はそのまま流用できます。
表現の属性は拡張の属性に統一する
表現の属性と拡張の属性が混在すると理解が難しくなります。動的な表示に関係する要素は拡張の属性に統一し、通常の属性は固定の要素に限ると、読み手が迷いません。統一した方針は画面全体で再利用でき、複数の人が作業を分けても見通しが保たれます。
画像の説明文を丁寧に用意する
説明文は意味の伝達だけでなく、検索でも重要な役目を担います。表示の文言と説明文の両方に対象の語句を含めると、内容の理解が深まり、探しやすくなります。動的な画像でも同じ規則を適用できるように、説明文の選び方も受け渡しの側に用意しておき、画面の側は受け取った文言をそのまま使うようにします。これにより表現の一貫性が増し、変更にも強くなります。
差し替えの確認の手順を共通化する
差し替えの作業は頻繁に発生します。確認の手順が共通化されていないと、誤りの切り分けに時間がかかります。画面の場面を少数に絞り、差し替え直後の確認の方法を定めておくと、判断の速さが保たれます。印の値の付与も手順の一部として定着させ、後から取り除きやすいように設計しておくと安全です。
一覧と詳細の画面で同じ規則を共有する
一覧と詳細の画面で表現の規則が異なると、表示のずれが積み重なります。同じ規則を共有し、画像の格納場所と命名の規則を両方の場面で再利用すると、統一された見た目が保たれます。規則が統一されていれば、表示の負荷が高くても基盤の見通しが良くなり、拡張に強い構成が実現します。
誤った記述の再発を防ぐための比較例
最後に記述の比較をまとめます。誤りでは変数を文字列の中に閉じ込めてしまい、値が置き換わりません。正しい記述では拡張の属性を使い、変数の値をそのまま反映します。作業のはじめに正しい記述を手元に置いておくと、迷いが少なくなります。
<!-- 誤りの例 -->
<img th:src="'/images/'+${code}+'.png'" alt="誤り">
<!-- 正しい例 -->
<img th:src="@{'/images/' + ${code} + '.png'}" alt="正しい">
規則の違いを意識し、表現の側は常に拡張の属性に統一する習慣を付けると、表現の失敗が減り、保守が楽になります。受け渡しの側は位置の結合を引き受け、画面の側は反映に専念するという役割分担も明確になります。こうした工夫の積み重ねが、実装の品質と読みやすさを高め、動的な画像の設定を安定させます。
まとめ
今回は、Thymeleafのth:srcを使って画像URLを動的に設定する方法について、基礎から実践的な使い方まで幅広く振り返りました。Spring MVCやSpring Bootで画面を構築する際、画像URLを固定的に書くよりも、コントローラから渡した変数を使って動的に切り替える仕組みは、商品一覧、プロフィール画面、投稿の一覧表示など、多くの場面で役立ちます。動的な画像URLを扱うことで、ユーザーごとに異なる画像を柔軟に表示でき、画面の汎用性が高まります。
また、th:srcの基本は「HTMLのsrc属性を置き換える」というシンプルな動きですが、URLの構築方法や、@{ }の扱い方、変数の受け渡し方、Spring側のModelとの関連性など、理解しておくべきポイントがたくさんあります。特に、ThymeleafはHTMLテンプレートとしても表示できる特性があるため、フロントエンドとバックエンドの両方が見やすく、開発効率の向上にもつながります。
さらに、静的画像と動的画像の違いを明確に比較したことで、用途ごとのメリットや実践的な使い分けが理解しやすくなります。「固定パスで表示する簡単な使い方」と「コントローラから変数を受け取って動的に表示する高度な使い方」を押さえておくことで、Thymeleafでのフロント構築が一段とスムーズになります。
以下に、記事で扱った内容と同じようなスタイルで、確認用のサンプルコードをまとめます。
動的画像URLのサンプルプログラム
<div class="card p-3">
<img th:src="@{${imageUrl}}" class="img-fluid rounded" alt="動的画像サンプル">
</div>
このように、ThymeleafではHTMLの構造を崩さず自然な記述で動的処理を組み込めるため、Springアプリケーションの中でも非常に重要な役割を持ちます。画像パスの扱いは小さな部分に見えますが、実際の現場では表示の切り替えや管理が頻繁に発生するため、th:srcを使いこなせると大きな効果を発揮します。テンプレートエンジンとしての柔軟性と、動的処理の仕組みを理解することで、より質の高い画面構築が実現できます。
生徒:「今日学んだ、Thymeleafのth:srcって本当に便利ですね。画像パスを変数で受け取れるから、どんな画面でも応用できそうです。」
先生:「その通りです。特にSpring MVCやSpring Bootでは、サーバーから渡すデータとテンプレートを組み合わせる場面が多いので、動的な画像切り替えは欠かせません。」
生徒:「静的なパスを書くだけだと、一つの画像しか表示できませんけど、動的だとユーザーごとに画像を変えられるところがとても魅力的です。」
先生:「実際の開発でもよく使われますよ。たとえば商品一覧や投稿のサムネイル、ユーザーのプロフィール画像など、用途は多岐にわたります。今日学んだ@{${imageUrl}}の基本的な構文を覚えておくと、さまざまな機能に応用できます。」
生徒:「実装するとき、コントローラ側のModelに値を入れて渡すのも理解しやすかったです。テンプレート側で自然な形で利用できるのがいいですね。」
先生:「そうですね。テンプレートが見やすくなることで、フロント側の人も編集しやすくなります。Thymeleafの利点を活かして、効率よく画面を作れるようにしていきましょう。」
生徒:「はい!今日の内容を活かして、もっとThymeleafを使いこなせるようになりたいです。」
先生:「その意欲が大切ですよ。これからも一緒に頑張っていきましょう。」