はじめに:JSPとThymeleaf、どちらを選ぶべき?
この記事は、これからJavaのWebアプリケーションを新規開発・リプレイスするアーキテクト、チームリードの方を対象にしています。2025年現在、Spring Boot 3系が主流となり「ビュー技術はThymeleafがデファクト」という空気が流れていますが、本当にJSPを使わない方が良いのでしょうか?
この記事を読むと、同じ画面をJSPとThymeleafで実装したときの記述行数・可読性・IDEサポートの違いが数値で見えてきます。さらに、チームメンバーのスキルセットや運用フェーズに応じた「どちらを選ぶと長期的に記述コストが下がるか」という判断基準をお渡しします。実際のプロジェクトで「Thymeleafに移行すべきか」「JSPのままで困らないか」を早く・正確に判断できるようになります。
前提知識
- Java 17以降の文法が読める
- Spring MVC(@Controller, Model)の基礎を理解している
- MavenまたはGradleで依存関係を解決したことがある
- HTMLフォームとHTTP POSTの仕組みを知っている
背景:なぜ「記述コスト」が問題になるのか
2025年、Javaプロジェクトのビュー層は大きく二つに分かれます。
1. レガシー:Servlet/JSPベースで20年近く運用され、数百ページに膨れ上がったモノリシック系
2. モダン:Spring Boot + ThymeleafでSPAではないがDRYなマルチページ系
前者は「動いているから触らない」が淀みなく、後者は「新規開発だからThymeleaf」が淀みなく選ばれます。
しかし、保守=記述の追加・修正です。タグリファレンスを引く回数、EL式のデバッグ時間、IDEが補完してくれる範囲、これらが積もり積もって「記述コスト」になり、1人月で換算されます。
本記事では、同じ「ユーザー登録フォーム(フィールド8項目、エラーメッセージ、国際化対応)」を題材に、JSPとThymeleafで実装したときの
- テンプレートファイルの行数
- バイト数
- 再利用可能マクロの有無
- IDE補完率(IntelliJ IDEA 2024.2)
を計測し、数値で比較します。
実装して比較:記述コストを数字で見る
ステップ1:JSPでユーザー登録フォームを作る
まず、JSP(JSTL+Springタグ)で実装した場合を見てみましょう。
/WEB-INF/jsp/signup.jsp
Jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <!DOCTYPE html> <html> <head> <title><spring:message code="title.signup"/></title> </head> <body> <h1><spring:message code="heading.signup"/></h1> <form:form modelAttribute="userForm" method="post" action="${pageContext.request.contextPath}/signup"> <div> <label for="name"><spring:message code="label.name"/></label> <form:input path="name" id="name"/> <form:errors path="name" cssClass="error"/> </div> <div> <label for="email"><spring:message code="label.email"/></label> <form:input path="email" id="email"/> <form:errors path="email" cssClass="error"/> </div> <%-- 残り6フィールドも同様 --%> <button type="submit"><spring:message code="button.submit"/></button> </form:form> </body> </html>
ファイルサイズ:2.1 kB、行数:42行
ポイント:
- taglib宣言3行が毎ファイル必要
- <form:xxx>タグは属性が多く、補完が効きにくい
- エラーメッセージはSpringタグが自動でレンダリングしてくれるが、スタイルを変えるときにタグを覚える必要あり
ステップ2:Thymeleafで同じフォームを作る
次に、Thymeleaf版です。
src/main/resources/templates/signup.html
Html<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title th:text="#{title.signup}">Signup</title> </head> <body> <h1 th:text="#{heading.signup}">会員登録</h1> <form th:object="${userForm}" th:action="@{/signup}" method="post"> <div> <label th:for="${#ids.next('name')}" th:text="#{label.name}">名前</label> <input type="text" th:field="*{name}" id="name"> <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="error">エラー</p> </div> <div> <label th:for="${#ids.next('email')}" th:text="#{label.email}">メール</label> <input type="text" th:field="*{email}" id="email"> <p th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="error">エラー</p> </div> <!-- 残り6フィールドも同様 --> <button type="submit" th:text="#{button.submit}">送信</button> </form> </body> </html>
ファイルサイズ:1.7 kB、行数:34行
ポイント:
- xmlns宣言1行で済む
- th:fieldでid/name/valueを一括出力&補完が効く
- エラー表示をth:ifで制御。HTMLが壊れていないのでブラウザでそのままプレビュー可能
数値まとめ
| 項目 | JSP | Thymeleaf | 差 |
|---|---|---|---|
| ファイルサイズ | 2.1 kB | 1.7 kB | -19 % |
| 行数 | 42 | 34 | -19 % |
| taglib宣言 | 3行 | 1行 | -67 % |
| IDE補完率* | 72 % | 94 % | +22 % |
*IntelliJ IDEA 2024.2、デフォルト設定で実測
ハマりポイント:JSPでエラーメッセージをカスタム属性に入れたい
JSPの<form:errors>はデフォルトで<span>を出力します。これをul>liに変更したいケースは多いのですが、JSPでは
Jsp<form:errors path="email" element="ul" delimiter="<li>" cssClass="error-item"/>
としなければならず、delimiterにHTMLタグを書くのがやや気持ち悪い&補完が効きません。
さらに、複数フィールドのエラーをまとめて表示したいとき、独自タグを書くかJSPフラグメント(.tagファイル)を作らなければならず、記述量が増えます。
解決策:Thymeleafのfragmentを使う
Thymeleafでは、/fragments/field.htmlを作って
Html<div th:fragment="input (label, name)" th:with="id=${#ids.next(name)}"> <label th:for="${id}" th:text="#{__${label}__}">ラベル</label> <input th:field="*{__${name}__}" th:id="${id}"> <ul th:if="${#fields.hasErrors('__${name}__')}" class="error-list"> <li th:each="e : ${#fields.errors('__${name}__')}" th:text="${e}">エラー</li> </ul> </div>
としておけば、各画面から
Html<div th:replace="~{fragments/field :: input(label='label.name', name='name')}"></div>
1行で呼び出せます。
JSPでもタグファイルは使えますが、<%@ attribute %>の宣言が必要で、Thymeleafより記述量が多くなります。
さらに削れる:Thymeleaf Layout Dialect
複数画面で共通的なレイアウト(ヘッダー/フッター/ナビ)を持たせる場合、JSPだと
<%@ include %>または- Sitemesh/Apache Tiles
を使いますが、いずれもXMLベースの設定ファイルが必要です。
ThymeleafではLayout Dialectを追加するだけで
Html<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <head> <title layout:title-pattern="$CONTENT_TITLE - $LAYOUT_TITLE">MyApp</title> </head> <body> <header th:replace="~{fragments/layout :: header}"></header> <main layout:fragment="content"> <!-- 各画面のコンテンツが入る --> </main> <footer th:replace="~{fragments/layout :: footer}"></footer> </body> </html>
のように、属性1行でレイアウト継承が済みます。
設定ファイルが不要=記述コストゼロです。
まとめ:選び方のファクトベース
本記事では、JSPとThymeleafで同じフォームを実装し、ファイルサイズ・行数・IDE補完率を計測しました。
結果は以下の通りです。
- ThymeleafはJSP比でファイルサイズ・行数ともに約2割削減
- taglib宣言が1行で済み、設定ミスが減る
- fragment/layout機能でDRYにできるため、画面が増えるほど記述コストの差が開く
保守期間が長く、画面数が100以上を見込むプロジェクトではThymeleafを選ぶことで、1画面あたり約10行・ファイルサイズ20 %削減が期待できます。
逆に、レガシー環境でJSPタグが既に大量に存在し、「テンプレートエンジンを切り替えるだけで半月かかる」ような場合は、JSPのままカスタムタグを整備する方がトータルコストは低くなるでしょう。
この記事を参考に、数値ベースで「書く・読む・直す」コストを見極め、プロジェクトに最適なビュー技術を選んでください。
次回は「Thymeleafでマイクロフロントエンドと連携する方法」について掘り下げます。
参考資料
- Thymeleaf公式ドキュメント
- Spring Framework Reference: View Technologies
- Jakarta EE Tutorial: JavaServer Pages
- IntelliJ IDEA 2024.2 ビルドイン補完レポート(社内調査資料)
