今回は、Spring BootとJavaScriptを連携する方法を、新人エンジニア向けに解説します。

特に、Thymeleafのth:inline="javascript"を使って、Controllerから渡した値をJavaScriptで使う方法を扱います。

Spring Bootを学び始めると、最初はController、Model、ThymeleafでHTML画面を作りますよね。

そのあと、JavaScriptを使って画面に動きを付けたくなります。

Controllerから渡した商品価格をJavaScriptで計算したい
検索結果の件数をJavaScriptで表示したい
DTOのリストをJavaScriptの配列として使いたい
ボタンを押したときに、Spring Bootから渡した値をもとに画面を変えたい

ログイン中のユーザー名をJavaScriptで使いたい

このような場面で使えるのが、ThymeleafのJavaScript inlineです。

Thymeleaf公式ドキュメントでも、JavaScript inliningはHTMLテンプレート内のscriptブロックとJavaScriptをよりよく統合するための仕組みであり、th:inline="javascript"を明示的に指定して使うと説明されています。

まず結論:inlineは「初期表示時にJavaScriptへ値を渡す仕組み」

最初に、inlineの役割をはっきりさせましょう。

Thymeleafのinlineは、Spring BootのControllerからModelに入れた値を、HTML内のJavaScriptへ埋め込む仕組みです。

Controller
        ↓
Modelに値を入れる
        ↓
ThymeleafがHTMLを作る
        ↓
th:inline="javascript"でJavaScript内に値を埋め込む
        ↓
ブラウザでJavaScriptが使う




たとえるなら、Controllerはお弁当を作る人です。

Modelはお弁当箱です。

Thymeleafはお弁当をきれいに盛り付ける人です。

JavaScript inlineは、そのお弁当の中身を「ブラウザ側のJavaScriptにも分けて渡す小皿」のようなものです。

つまり、inlineは「あとからAPIで取りに行く」のではなく、「画面を作るタイミングでJavaScriptに値を埋め込む」方法です。

Spring BootとJavaScriptの連携方法は大きく2種類ある

Spring BootとJavaScriptを連携するとき、よく使う方法は大きく2つあります。

方法使うもの向いている場面
Thymeleaf inlineth:inline="javascript"画面表示時に必要な初期値をJavaScriptへ渡す
API通信fetch()、@RestController画面表示後にデータを取得・登録・更新する

今回のテーマは、前者のThymeleaf inlineです。

ただし、inlineとAPI通信は競合するものではありません。

初期表示ではinlineを使い、ボタン押下後の最新データ取得ではfetch()を使う、という組み合わせもよくあります。

新人エンジニアは、まず次のように覚えてください。

画面を表示する時点で値が決まっている
        ↓
inlineが向いている

画面を表示したあとに追加で通信したい
        ↓
fetch()とAPIが向いている




今回作るサンプル

今回は、ユーザー詳細画面を例にします。

Spring BootのControllerからUserDtoをModelへ入れます。

ThymeleafのHTMLでユーザー名を表示します。

さらに、th:inline="javascript"を使って、JavaScriptでもユーザーID、名前、メールアドレスを使えるようにします。

ControllerでUserDtoを作る
        ↓
model.addAttribute("user", user)
        ↓
ThymeleafでHTML表示
        ↓
th:inline="javascript"でJS変数へ代入
        ↓
ボタンを押すとJavaScriptでユーザー情報を表示




この流れが理解できると、Spring BootとJavaScriptのつながりがかなり見えやすくなります。

Spring Bootのテンプレート配置場所

Spring BootでThymeleafを使う場合、通常テンプレートHTMLはsrc/main/resources/templatesに配置します。Spring Boot公式ドキュメントでも、テンプレートエンジンをデフォルト設定で使う場合、テンプレートはsrc/main/resources/templatesから自動的に見つけられると説明されています。

今回のファイル構成は、次のようにします。

src/main/java
    com.example.demo
        controller
            UserController.java
        model
            UserDto.java

src/main/resources
    templates
        users
            detail.html




外部JavaScriptファイルを使う場合は、src/main/resources/static/jsに配置します。

Spring Bootはデフォルトで、classpath上の/static、/public、/resources、/META-INF/resourcesなどから静的コンテンツを提供します。

ただし、ここで重要な注意があります。

th:inline="javascript"は、Thymeleafで処理されるHTMLテンプレート内のscriptで使います。

src/main/resources/static/jsのような静的JavaScriptファイルは、通常Thymeleafで処理されません。

つまり、外部JSファイルに次のように書いても、基本的にはThymeleafの式として処理されません。

const userName = [[${user.name}]];




inlineを使うなら、まずHTML内のscriptタグで値を受け取る、と覚えてください。

UserDtoを作る

まず、Controllerから画面へ渡すUserDtoを作ります。

package com.example.demo.model;

public class UserDto {

    private long userId;
    private String name;
    private String email;

    public UserDto(long userId, String name, String email) {
        this.userId = userId;
        this.name = name;
        this.email = email;
    }

    public long getUserId() {
        return userId;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}




DTOとは、Data Transfer Objectの略です。

データを運ぶためのクラスです。

今回のUserDtoは、ユーザーID、名前、メールアドレスを画面へ渡すための箱です。

Spring BootとThymeleafでは、getterが重要です。

たとえば、Thymeleafで${user.name}と書くと、Java側ではgetName()を使って値を取り出します。

ThymeleafJavaのgetter
${user.userId}getUserId()
${user.name}getName()
${user.email}getEmail()

ControllerでModelに値を入れる

次に、Controllerを作ります。

package com.example.demo.controller;

import com.example.demo.model.UserDto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UserController {

    @GetMapping("/users/detail")
    public String detail(Model model) {

        UserDto user = new UserDto(
                1,
                "山田太郎",
                "yamada@example.com"
        );

        model.addAttribute("user", user);

        return "users/detail";
    }
}




このControllerは、/users/detailへアクセスされたときに動きます。

Modelにuserという名前でUserDtoを入れています。

model.addAttribute("user", user);




このuserという名前が、Thymeleaf側で使う${user}に対応します。

ControllerThymeleaf
model.addAttribute("user", user)${user}
user.getName()${user.name}

ControllerからHTMLへ値を渡す基本は、Modelです。

JavaScript inlineも、まずModelに値が入っていることが前提です。

HTMLで普通に値を表示する

まず、JavaScriptの前に、Thymeleafで普通に値を表示してみましょう。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ユーザー詳細</title>
</head>
<body>

<h1>ユーザー詳細</h1>

<p>
    ユーザーID:
    <span th:text="${user.userId}"></span>
</p>

<p>
    名前:
    <span th:text="${user.name}"></span>
</p>

<p>
    メールアドレス:
    <span th:text="${user.email}"></span>
</p>

</body>
</html>




この時点では、JavaScriptは使っていません。

ControllerからModelへ渡したUserDtoを、ThymeleafがHTMLへ埋め込んでいます。

ここが動かない状態でJavaScript inlineへ進むと、混乱します。

まずはth:textで値が表示されることを確認してください。

th:inline="javascript"を使う

次に、JavaScriptでuserの値を使えるようにします。

<script th:inline="javascript">
    const userId = [[${user.userId}]];
    const userName = [[${user.name}]];
    const userEmail = [[${user.email}]];

    console.log(userId);
    console.log(userName);
    console.log(userEmail);
</script>




ポイントは、scriptタグにth:inline="javascript"を付けることです。

<script th:inline="javascript">

この指定により、scriptタグの中でThymeleafのインライン式が使えるようになります。

Thymeleaf公式ドキュメントでは、JavaScript inlineを有効にするにはth:inline="javascript"を明示的に指定すると説明されています。また、[[...]]で出力すると、文字列は引用符で囲まれ、JavaScript向けにエスケープされ、正しいJavaScriptリテラルとして出力されます。

Thymeleaf処理後のJavaScriptはどうなるのか

テンプレート側では、次のように書きます。

<script th:inline="javascript">
    const userId = [[${user.userId}]];
    const userName = [[${user.name}]];
    const userEmail = [[${user.email}]];
</script>




ブラウザへ届くときには、次のようなJavaScriptになります。

<script>
    const userId = 1;
    const userName = "山田太郎";
    const userEmail = "yamada@example.com";
</script>




つまり、Thymeleafがサーバー側でJavaScriptの中に値を埋め込んでから、ブラウザへHTMLを返しています。

ここが重要です。

JavaScriptがSpring Bootへ直接アクセスして値を取りに行っているわけではありません。

画面を作る段階で、Spring Bootが値を埋め込んでいるのです。

完成版のHTML

ボタンを押すと、Controllerから渡されたユーザー情報をJavaScriptで画面に表示する完成例です。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ユーザー詳細</title>
</head>
<body>

<h1>ユーザー詳細</h1>

<p>
    ユーザーID:
    <span th:text="${user.userId}"></span>
</p>

<p>
    名前:
    <span th:text="${user.name}"></span>
</p>

<p>
    メールアドレス:
    <span th:text="${user.email}"></span>
</p>

<button type="button" onclick="showUserInfo()">
    JavaScriptでユーザー情報を表示
</button>

<p id="messageArea"></p>

<script th:inline="javascript">
    const userId = [[${user.userId}]];
    const userName = [[${user.name}]];
    const userEmail = [[${user.email}]];

    function showUserInfo() {
        const message =
            "ユーザーID: " + userId
            + " / 名前: " + userName
            + " / メール: " + userEmail;

        document.getElementById("messageArea").textContent = message;
    }
</script>

</body>
</html>




このコードでは、Spring Bootから渡されたUserDtoを、HTML表示にもJavaScriptにも使っています。

HTML表示ではth:textを使います。

JavaScriptではth:inline="javascript"と[[...]]を使います。

使う場所書き方役割
HTML本文th:text="${user.name}"画面に文字として表示する
JavaScript[[${user.name}]]JavaScript変数へ値を渡す

コメント形式で書く自然なテンプレート

ThymeleafのJavaScript inlineでは、次のようなコメント形式もよく使われます。

<script th:inline="javascript">
    const userName = /*[[${user.name}]]*/ "サンプルユーザー";
</script>




Thymeleafで処理されると、次のようになります。

<script>
    const userName = "山田太郎";
</script>




この書き方の良いところは、HTMLファイルを静的に開いてもJavaScriptとして成立しやすい点です。

Thymeleaf公式ドキュメントでも、JavaScript inlineの式をコメントで包むと、Thymeleaf処理前でも有効なJavaScriptとして動かせる「JavaScript natural templates」として使えると説明されています。

新人エンジニア向けには、まず次のように覚えるとよいです。

書き方特徴
const userName = [[${user.name}]];シンプルで分かりやすい
const userName = /*[[${user.name}]]*/ "サンプル";静的HTMLとして開いたときも見やすい

研修では、最初はシンプルな[[...]]から入り、慣れてきたらコメント形式を学ぶと理解しやすいです。

DTOまるごとをJavaScriptオブジェクトにする

Thymeleaf inlineでは、文字列や数値だけでなく、JavaのオブジェクトもJavaScriptのオブジェクトとして出力できます。

Thymeleaf公式ドキュメントでは、JavaScript inlineの評価は文字列だけに限定されず、数値、真偽値、配列、コレクション、Map、getter/setterを持つBeanなどをJavaScript構文へ正しく書き出せると説明されています。

たとえば、次のように書けます。

<script th:inline="javascript">
    const user = /*[[${user}]]*/ null;

    console.log(user.userId);
    console.log(user.name);
    console.log(user.email);
</script>




ブラウザへ届くJavaScriptのイメージです。

<script>
    const user = {
        "userId": 1,
        "name": "山田太郎",
        "email": "yamada@example.com"
    };

    console.log(user.userId);
    console.log(user.name);
    console.log(user.email);
</script>




この書き方を使うと、JavaScript側でuser.nameやuser.emailのように扱えます。

ただし、DTOをまるごとJavaScriptに渡すときは注意してください。

DTOに外へ出してはいけない情報が含まれていると、ブラウザ側に見えてしまいます。

JavaScriptへ渡してよい情報、渡してはいけない情報

inlineでJavaScriptへ渡した値は、ブラウザのDevToolsで確認できます。

つまり、ユーザーに見えていると考えてください。

渡してよい例理由
画面表示に使う名前もともと画面に表示する情報だから
商品名公開表示する情報だから
検索条件ユーザーが入力した条件だから
画面制御用の表示フラグ見えても問題ない範囲なら使える
渡してはいけない例理由
パスワード絶対に外へ出してはいけない
passwordHashハッシュでも外へ出さない
APIキー不正利用される可能性がある
管理者だけが知る内部メモ情報漏えいになる
本当の権限制御情報ブラウザ側で改ざんされる可能性がある
割引率や価格計算の秘密ロジック業務上の情報漏えいになる可能性がある

ここは強く意識してください。

JavaScriptへ渡した情報は、ユーザーが見られる情報です。

画面に表示していなくても、HTMLソースやDevToolsを見れば確認できます。

「JavaScriptの変数に入れただけだから安全」という考え方は危険です。

リストをJavaScriptへ渡す

次に、Controllerからユーザー一覧を渡し、JavaScriptで配列として扱う例を見てみましょう。

Controllerです。

package com.example.demo.controller;

import com.example.demo.model.UserDto;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UserListController {

    @GetMapping("/users")
    public String list(Model model) {

        List<UserDto> userList = List.of(
                new UserDto(1, "山田太郎", "yamada@example.com"),
                new UserDto(2, "鈴木花子", "suzuki@example.com"),
                new UserDto(3, "佐藤一郎", "sato@example.com")
        );

        model.addAttribute("userList", userList);

        return "users/list";
    }
}




Thymeleafです。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ユーザー一覧</title>
</head>
<body>

<h1>ユーザー一覧</h1>

<button type="button" onclick="showUserCount()">
    ユーザー件数を表示
</button>

<p id="countArea"></p>

<ul id="userListArea"></ul>

<script th:inline="javascript">
    const userList = /*[[${userList}]]*/ [];

    function showUserCount() {
        document.getElementById("countArea").textContent =
            "ユーザー件数: " + userList.length + "件";

        const userListArea = document.getElementById("userListArea");
        userListArea.innerHTML = "";

        userList.forEach(function (user) {
            const li = document.createElement("li");
            li.textContent = user.userId + ": " + user.name + " / " + user.email;
            userListArea.appendChild(li);
        });
    }
</script>

</body>
</html>




この例では、JavaのList<UserDto>がJavaScriptの配列のように扱えます。

userList.length
userList.forEach(function (user) {
    ...
});

JavaのListとJavaScriptの配列がつながると、画面上での動的表示がしやすくなります。

Java側JavaScript側
List<UserDto>配列
UserDtoオブジェクト
getName()user.name

inlineで渡した値を外部JavaScriptで使いたい場合

実務では、JavaScriptをHTML内に長く書くより、外部ファイルに分けたいことがあります。

たとえば、次のような構成です。

src/main/resources/templates/users/detail.html
src/main/resources/static/js/user-detail.js




ただし、外部JSファイルは通常Thymeleafで処理されません。

そのため、外部JSファイルに直接[[${user.name}]]を書いてはいけません。

よくない例です。

const userName = [[${user.name}]];




このコードをstatic/js/user-detail.jsに書いても、Thymeleafの式として処理されません。

ではどうするのかというと、HTML内のinline scriptでグローバル変数へ渡し、外部JSがその変数を読む方法があります。

detail.htmlです。

<script th:inline="javascript">
    window.userDetail = /*[[${user}]]*/ null;
</script>

<script th:src="@{/js/user-detail.js}"></script>

user-detail.jsです。

function showUserInfo() {
    const user = window.userDetail;

    if (!user) {
        console.log("ユーザー情報がありません。");
        return;
    }

    document.getElementById("messageArea").textContent =
        user.userId + ": " + user.name + " / " + user.email;
}


このとき、HTML側のinline scriptを先に読み込み、その後で外部JSを読み込む順番が大切です。

window.userDetailを作る
        ↓
外部JSを読み込む
        ↓
外部JSがwindow.userDetailを使う




順番が逆になると、外部JSがwindow.userDetailを読もうとした時点で、まだ値が存在しない可能性があります。

data属性を使う方法

もう1つの方法として、HTMLのdata属性に値を埋め込み、JavaScriptで読む方法もあります。

<div id="userData"
     th:data-user-id="${user.userId}"
     th:data-user-name="${user.name}"
     th:data-user-email="${user.email}">
</div>




JavaScript側です。

const userData = document.getElementById("userData");

const userId = userData.dataset.userId;
const userName = userData.dataset.userName;
const userEmail = userData.dataset.userEmail;

console.log(userId);
console.log(userName);
console.log(userEmail);




data属性は、HTML要素に追加情報を持たせるための属性です。

少量の値をJavaScriptへ渡す場合に向いています。

方法向いている場面
th:inline="javascript"オブジェクトやリストをJavaScript変数にしたい
data属性少量の値をHTML要素に持たせたい
fetch()画面表示後に最新データをAPIから取得したい

inlineとfetch()の使い分け

ここで、inlineとfetch()の使い分けを整理します。

比較inlinefetch()
通信タイミング画面表示時JavaScript実行時
Spring Boot側@Controller + Model@RestController
戻り値HTMLテンプレートJSONなど
向いている用途初期表示データあとから取得・登録・更新するデータ
ログインユーザー名、初期検索条件一覧の再取得、登録、削除、検索

inlineは、画面を開いた時点で分かっている値をJavaScriptへ渡す方法です。

fetch()は、画面を開いたあとに、JavaScriptからSpring Boot APIへ通信する方法です。

たとえるなら、inlineは「出発前に地図を印刷して持っていく」方法です。

fetch()は「移動中にスマホで最新情報を取りに行く」方法です。

どちらも便利ですが、使う場面が違います。

inlineを使うべき場面

場面理由
初期表示のユーザー情報画面表示時にすでに分かっているから
検索条件の初期値JavaScriptで入力欄や表示を調整しやすいから
画面表示用のDTOHTML表示とJavaScript操作の両方で使えるから
一覧データの初期描画画面読み込み直後に使えるから
画面制御用のフラグボタン表示やメッセージ切り替えに使えるから

fetch()を使うべき場面

場面理由
ボタン押下後に検索する画面表示後に条件が決まるから
登録処理を非同期で行う画面遷移せずに結果を表示したいから
最新の一覧を再取得する初期表示時点の情報では古くなる可能性があるから
削除処理を行うユーザー操作後にサーバーへ依頼するから
外部APIと連携する必要なタイミングで通信するから

新人エンジニアは、inlineだけで何でもやろうとしないでください。

初期値はinline。

あとから変わるデータはfetch()。

この分け方が大切です。

よくあるミス

scriptタグにth:inlineを付け忘れる

悪い例です。

<script>
    const userName = [[${user.name}]];
</script>




良い例です。

<script th:inline="javascript">
    const userName = [[${user.name}]];
</script>




th:inline="javascript"を付け忘れると、JavaScript内のThymeleaf式が期待どおり処理されません。

外部JSファイルにThymeleaf式を書く

悪い例です。

src/main/resources/static/js/user.js

const userName = [[${user.name}]];

外部JSファイルは通常Thymeleafで処理されないため、この書き方は避けてください。

HTML内でwindow.userDetailに渡し、外部JSから読む形にしましょう。

文字列をクォーテーションで囲んでしまう

次のように書きたくなるかもしれません。

const userName = "[[${user.name}]]";




この書き方が常に間違いとは限りませんが、th:inline="javascript"では[[...]]がJavaScriptリテラルとして適切に出力してくれます。

まずは次の形を基本にしてください。

const userName = [[${user.name}]];




Thymeleafは文字列をJavaScript向けに引用符付きで出力します。

秘密情報を渡してしまう

悪い例です。

<script th:inline="javascript">
    const loginUser = /*[[${loginUser}]]*/ null;
</script>




loginUserの中にpasswordHashやroleの内部情報が入っていると危険です。

JavaScriptへ渡す専用DTOを作り、必要な項目だけに絞りましょう。

public class LoginUserViewDto {

    private long userId;
    private String name;

    public LoginUserViewDto(long userId, String name) {
        this.userId = userId;
        this.name = name;
    }

    public long getUserId() {
        return userId;
    }

    public String getName() {
        return name;
    }
}




DevToolsで確認する

inlineが正しく動いているかは、ブラウザのDevToolsで確認できます。

1. ブラウザで画面を開く
2. 右クリックして「検証」を開く
3. Elementsでscriptの中身を見る
4. Consoleで変数を確認する
5. エラーがあればConsoleを見る

たとえば、Consoleで次のように入力します。

userName

山田太郎のように表示されれば、JavaScript変数として使えています。

もし[[${user.name}]]のような文字がそのまま残っていたら、Thymeleafで処理されていない可能性があります。

その場合は、次を確認してください。

確認項目内容
HTMLの場所templates配下にあるか
Controllerの戻り値テンプレート名を返しているか
Model名model.addAttributeの名前と一致しているか
th:inlinescriptタグに付いているか
外部JSではないかstatic/jsにThymeleaf式を書いていないか

新人エンジニア向けの完成サンプル

最後に、Controller、DTO、HTMLをまとめた完成例を示します。

UserDtoです。

package com.example.demo.model;

public class UserDto {

    private long userId;
    private String name;
    private String email;

    public UserDto(long userId, String name, String email) {
        this.userId = userId;
        this.name = name;
        this.email = email;
    }

    public long getUserId() {
        return userId;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}




Controllerです。

package com.example.demo.controller;

import com.example.demo.model.UserDto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UserController {

    @GetMapping("/users/detail")
    public String detail(Model model) {

        UserDto user = new UserDto(
                1,
                "山田太郎",
                "yamada@example.com"
        );

        model.addAttribute("user", user);

        return "users/detail";
    }
}




detail.htmlです。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Spring BootとJavaScript inline連携</title>
</head>
<body>

<h1>Spring BootとJavaScript inline連携</h1>

<h2>HTMLとして表示</h2>

<p>
    名前:
    <span th:text="${user.name}"></span>
</p>

<p>
    メール:
    <span th:text="${user.email}"></span>
</p>

<h2>JavaScriptで表示</h2>

<button type="button" onclick="showUser()">
    JavaScriptで表示する
</button>

<p id="jsMessageArea"></p>

<script th:inline="javascript">
    const user = /*[[${user}]]*/ null;

    function showUser() {
        if (!user) {
            document.getElementById("jsMessageArea").textContent =
                "ユーザー情報がありません。";
            return;
        }

        document.getElementById("jsMessageArea").textContent =
            "JavaScriptで受け取ったユーザー: "
            + user.userId + " / "
            + user.name + " / "
            + user.email;
    }
</script>

</body>
</html>




このサンプルを動かすと、同じUserDtoをHTML表示にもJavaScriptにも使えます。

ここまで理解できれば、Spring BootとJavaScriptのinline連携の基本はかなり身につきます。

まとめ

Spring BootとJavaScriptを連携するとき、Thymeleafのth:inline="javascript"を使うと、ControllerからModelに入れた値をJavaScriptの変数として使えます。

用語意味
ModelControllerからThymeleafへ値を渡す箱
ThymeleafHTMLをサーバー側で生成するテンプレートエンジン
th:inline="javascript"JavaScript内でThymeleaf式を使うための指定
[[...]]値をJavaScript向けに安全に出力する書き方
/*[[...]]*/JavaScript natural templateとして使いやすい書き方
fetch()画面表示後にAPI通信するためのJavaScript機能

一言でまとめるなら、inlineは「Spring Bootが画面を作るタイミングで、JavaScriptへ初期データを渡す仕組み」です。

新人エンジニアは、まず次の流れを覚えてください。

ControllerでModelに値を入れる
        ↓
Thymeleafテンプレートを返す
        ↓
scriptタグにth:inline="javascript"を付ける
        ↓
[[${...}]]でJavaScript変数に入れる
        ↓
ブラウザ上のJavaScriptで使う




そして、次の使い分けも大切です。

初期表示に必要な値
        ↓
inline

画面表示後に取得・登録・更新したい値
        ↓
fetch() + API




今後の学習では、th:text、Model、DTO、th:inline="javascript"、[[...]]、JavaScriptのオブジェクト、DevTools、fetch()、@RestControllerの順番で学ぶとよいです。まずは小さなUserDtoをControllerから渡し、HTML表示とJavaScript表示の両方で使ってみましょう!

最後までお読みいただきありがとうございます。