{"id":469,"date":"2025-03-24T08:53:27","date_gmt":"2025-03-23T23:53:27","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=469"},"modified":"2025-03-24T08:53:27","modified_gmt":"2025-03-23T23:53:27","slug":"%e3%80%90%e4%bf%9d%e5%ad%98%e7%89%88%e3%80%91thymeleaf%e3%81%a8javascript%e3%81%ae%e9%80%a3%e6%90%ba%e5%ae%8c%e5%85%a8%e3%82%ac%e3%82%a4%e3%83%89%ef%bc%9a7%e3%81%a4%e3%81%ae%e5%ae%9f%e8%a3%85%e3%83%91","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=469","title":{"rendered":"\u3010\u4fdd\u5b58\u7248\u3011Thymeleaf\u3068JavaScript\u306e\u9023\u643a\u5b8c\u5168\u30ac\u30a4\u30c9\uff1a7\u3064\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3068\u5b9f\u8df5\u30c6\u30af\u30cb\u30c3\u30af"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\" id=\"i-0\">Thymeleaf\u3068JavaScript\u306e\u57fa\u672c\u7684\u306a\u9023\u643a\u65b9\u6cd5<\/h1>\n\n\n\n<div class=\"toc\"><br \/>\n<b>Warning<\/b>:  Undefined array key \"is_admin\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>116<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_category_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>121<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>128<\/b><br \/>\n    <div id=\"toc_container\" class=\"sgb-toc--bullets js-smooth-scroll\" data-dialog-title=\"\u76ee\u6b21\">\n      <p class=\"toc_title\">\u76ee\u6b21 <\/p>\n      <ul class=\"toc_list\">  <li class=\"first\">    <a href=\"#i-0\">Thymeleaf\u3068JavaScript\u306e\u57fa\u672c\u7684\u306a\u9023\u643a\u65b9\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-1\">Thymeleaf\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3067\u306e\u30b9\u30af\u30ea\u30d7\u30c8\u8aad\u307f\u8fbc\u307f\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-4\">Thymeleaf\u304b\u3089JavaScript\u3078\u306e\u30c7\u30fc\u30bf\u53d7\u3051\u6e21\u3057\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-8\">JavaScript\u5074\u3067\u306eThymeleaf\u5909\u6570\u306e\u53d6\u308a\u6271\u3044\u65b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-12\">7\u3064\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3068\u4f7f\u3044\u5206\u3051<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-13\">\u30d5\u30a9\u30fc\u30e0\u9001\u4fe1\u3068\u52d5\u7684\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/a>      <\/li>      <li>        <a href=\"#i-14\">\u975e\u540c\u671f\u30c7\u30fc\u30bf\u66f4\u65b0\u3068DOM\u64cd\u4f5c<\/a>      <\/li>      <li>        <a href=\"#i-15\">\u30e2\u30fc\u30c0\u30eb\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u306e\u30c7\u30fc\u30bf\u8868\u793a<\/a>      <\/li>      <li>        <a href=\"#i-16\">\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u3068\u52d5\u7684\u30c7\u30fc\u30bf\u8aad\u307f\u8fbc\u307f<\/a>      <\/li>      <li>        <a href=\"#i-17\">\u52d5\u7684\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u3068\u691c\u7d22\u6a5f\u80fd<\/a>      <\/li>      <li>        <a href=\"#i-18\">\u30b0\u30e9\u30d5\u30fb\u30c1\u30e3\u30fc\u30c8\u306e\u52d5\u7684\u66f4\u65b0<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-19\">\u30d5\u30a1\u30a4\u30eb\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3068\u9032\u6357\u8868\u793a<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-22\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3068\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-23\">XSS\u653b\u6483\u306e\u9632\u6b62\u7b56<\/a>      <\/li>      <li>        <a href=\"#i-26\">CSRF\u30c8\u30fc\u30af\u30f3\u306e\u9069\u5207\u306a\u6271\u3044\u65b9<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-29\">\u6a5f\u5bc6\u30c7\u30fc\u30bf\u306e\u5b89\u5168\u306a\u53d7\u3051\u6e21\u3057\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-33\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-34\">JavaScript\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-37\">\u30ad\u30e3\u30c3\u30b7\u30e5\u5236\u5fa1\u306e\u6700\u9069\u5316\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-40\">\u30d0\u30f3\u30c9\u30eb\u30b5\u30a4\u30ba\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-44\">\u30c7\u30d0\u30c3\u30b0\u3068\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-45\">\u958b\u767a\u8005\u30c4\u30fc\u30eb\u3092\u4f7f\u3063\u305f\u30c7\u30d0\u30c3\u30b0\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-48\">\u3088\u304f\u3042\u308b\u30a8\u30e9\u30fc\u3068\u89e3\u6c7a\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-51\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306e\u7279\u5b9a\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-54\">\u307e\u3068\u3081\uff1aThymeleaf\u3068JavaScript\u306e\u52b9\u679c\u7684\u306a\u9023\u643a\u306b\u5411\u3051\u3066<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-55\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8<\/a>      <\/li>      <li>        <a href=\"#i-56\">\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u306e\u4f7f\u3044\u5206\u3051<\/a>      <\/li>      <li>        <a href=\"#i-57\">\u4eca\u5f8c\u306e\u767a\u5c55\u306b\u5411\u3051\u3066<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-58\">\u7d50\u8ad6<\/a>      <\/li>    <\/ul>  <\/li><\/ul>\n      <a href=\"#\" class=\"sgb-toc-button js-toc-button\" rel=\"nofollow\" data-open-dialog=\"true\"><i class=\"fa fa-list\"><\/i><span class=\"sgb-toc-button__text\">\u76ee\u6b21\u3078<\/span><\/a>\n    <\/div><\/div><h2 class=\"wp-block-heading\" id=\"i-1\">Thymeleaf\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3067\u306e\u30b9\u30af\u30ea\u30d7\u30c8\u8aad\u307f\u8fbc\u307f\u65b9\u6cd5<\/h2>\n\n\n\n<p>Thymeleaf\u3067\u306f\u3001JavaScript\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080\u969b\u306b\u7279\u5225\u306a\u69cb\u6587\u3092\u4f7f\u7528\u3057\u3066\u3001\u3088\u308a\u67d4\u8edf\u3067\u5b89\u5168\u306a\u5b9f\u88c5\u304c\u53ef\u80fd\u3067\u3059\u3002\u4ee5\u4e0b\u306b\u4e3b\u8981\u306a\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3092\u793a\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">1. \u57fa\u672c\u7684\u306a\u30b9\u30af\u30ea\u30d7\u30c8\u8aad\u307f\u8fbc\u307f<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u901a\u5e38\u306e\u8aad\u307f\u8fbc\u307f --&gt;\n&lt;script th:src=\"@{\/js\/main.js}\"&gt;&lt;\/script&gt;\n\n&lt;!-- \u30d0\u30fc\u30b8\u30e7\u30f3\u7ba1\u7406\u4ed8\u304d\u306e\u8aad\u307f\u8fbc\u307f --&gt;\n&lt;script th:src=\"@{\/js\/main.js(v=${version})}\"&gt;&lt;\/script&gt;\n\n&lt;!-- \u6761\u4ef6\u4ed8\u304d\u8aad\u307f\u8fbc\u307f --&gt;\n&lt;script th:if=\"${isDevelopment}\" th:src=\"@{\/js\/debug.js}\"&gt;&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-3\">2. \u8907\u6570\u30b9\u30af\u30ea\u30d7\u30c8\u306e\u52b9\u7387\u7684\u306a\u7ba1\u7406<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30ec\u30a4\u30a2\u30a6\u30c8\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\uff08layout.html\uff09--&gt;\n&lt;head&gt;\n    &lt;!-- \u5171\u901a\u30b9\u30af\u30ea\u30d7\u30c8 --&gt;\n    &lt;script th:src=\"@{\/js\/common.js}\"&gt;&lt;\/script&gt;\n\n    &lt;!-- \u500b\u5225\u30da\u30fc\u30b8\u306e\u30b9\u30af\u30ea\u30d7\u30c8\u30d6\u30ed\u30c3\u30af --&gt;\n    &lt;th:block layout:fragment=\"scripts\"&gt;\n    &lt;\/th:block&gt;\n&lt;\/head&gt;\n\n&lt;!-- \u500b\u5225\u30da\u30fc\u30b8 --&gt;\n&lt;th:block layout:fragment=\"scripts\"&gt;\n    &lt;script th:src=\"@{\/js\/specific-page.js}\"&gt;&lt;\/script&gt;\n&lt;\/th:block&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-4\">Thymeleaf\u304b\u3089JavaScript\u3078\u306e\u30c7\u30fc\u30bf\u53d7\u3051\u6e21\u3057\u65b9\u6cd5<\/h2>\n\n\n\n<p>Thymeleaf\u304b\u3089\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u306ejava\u30b9\u30af\u30ea\u30d7\u30c8\u306b\u30c7\u30fc\u30bf\u3092\u6e21\u3059\u65b9\u6cd5\u306f\u4e3b\u306b3\u3064\u3042\u308a\u307e\u3059\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-5\">1. \u30a4\u30f3\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u5c5e\u6027\u306e\u4f7f\u7528<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- Thymeleaf\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;div id=\"userInfo\" \n     th:data-user-id=\"${user.id}\"\n     th:data-user-name=\"${user.name}\"&gt;\n&lt;\/div&gt;\n\n&lt;!-- JavaScript --&gt;\n&lt;script&gt;\nconst userInfo = document.getElementById('userInfo');\nconst userId = userInfo.dataset.userId;\nconst userName = userInfo.dataset.userName;\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-6\">2. JavaScript\u30b0\u30ed\u30fc\u30d0\u30eb\u5909\u6570\u3068\u3057\u3066\u306e\u53d7\u3051\u6e21\u3057<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- Thymeleaf\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;script th:inline=\"javascript\"&gt;\n    const user = \/*[[${user}]]*\/ {};\n    const settings = \/*[[${settings}]]*\/ {};\n&lt;\/script&gt;\n\n&lt;!-- \u5225\u30d5\u30a1\u30a4\u30eb\u306eJavaScript --&gt;\n&lt;script&gt;\nconsole.log(user.name);    \/\/ \u30e6\u30fc\u30b6\u30fc\u540d\u306b\u30a2\u30af\u30bb\u30b9\u53ef\u80fd\nconsole.log(settings.theme); \/\/ \u8a2d\u5b9a\u306b\u30a2\u30af\u30bb\u30b9\u53ef\u80fd\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-7\">3. JSON\u5f62\u5f0f\u3067\u306e\u53d7\u3051\u6e21\u3057<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- Thymeleaf\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;script th:inline=\"javascript\"&gt;\n    const userData = JSON.parse([[${userJson}]]);\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-8\">JavaScript\u5074\u3067\u306eThymeleaf\u5909\u6570\u306e\u53d6\u308a\u6271\u3044\u65b9<\/h2>\n\n\n\n<p>JavaScript\u3067Thymeleaf\u304b\u3089\u53d7\u3051\u53d6\u3063\u305f\u30c7\u30fc\u30bf\u3092\u5b89\u5168\u304b\u3064\u52b9\u7387\u7684\u306b\u6271\u3046\u305f\u3081\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3092\u7d39\u4ecb\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-9\">1. \u578b\u5b89\u5168\u306a\u5909\u6570\u30a2\u30af\u30bb\u30b9<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u5909\u6570\u306e\u5b58\u5728\u78ba\u8a8d\u3068\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u306e\u8a2d\u5b9a\nconst getUserData = () =&gt; {\n    try {\n        return typeof userData !== 'undefined' ? userData : {};\n    } catch (e) {\n        console.error('User data not available');\n        return {};\n    }\n};\n\n\/\/ \u5b89\u5168\u306a\u30d7\u30ed\u30d1\u30c6\u30a3\u30a2\u30af\u30bb\u30b9\nconst getUserName = () =&gt; {\n    const data = getUserData();\n    return data?.name ?? 'Unknown User';\n};<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-10\">2. \u30c7\u30fc\u30bf\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u53d7\u3051\u53d6\u3063\u305f\u30c7\u30fc\u30bf\u306e\u691c\u8a3c\nconst validateUserData = (data) =&gt; {\n    const required = ['id', 'name', 'email'];\n    return required.every(prop =&gt; \n        Object.prototype.hasOwnProperty.call(data, prop) &amp;&amp; \n        data[prop] !== null &amp;&amp; \n        data[prop] !== undefined\n    );\n};\n\n\/\/ \u4f7f\u7528\u4f8b\nconst userData = getUserData();\nif (validateUserData(userData)) {\n    \/\/ \u30c7\u30fc\u30bf\u3092\u4f7f\u7528\u3057\u305f\u51e6\u7406\n} else {\n    console.error('Invalid user data received');\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-11\">3. \u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Thymeleaf\u304b\u3089\u53d7\u3051\u53d6\u3063\u305f\u30c7\u30fc\u30bf\u3092\u4f7f\u7528\u3057\u305f\u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30e9\ndocument.addEventListener('DOMContentLoaded', () =&gt; {\n    const userElement = document.getElementById('userInfo');\n    if (!userElement) return;\n\n    const userId = userElement.dataset.userId;\n    if (!userId) {\n        console.error('User ID not found');\n        return;\n    }\n\n    userElement.addEventListener('click', async () =&gt; {\n        try {\n            const response = await fetch(`\/api\/users\/${userId}`);\n            const data = await response.json();\n            updateUserDisplay(data);\n        } catch (error) {\n            console.error('Error fetching user data:', error);\n        }\n    });\n});<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3092\u9069\u5207\u306b\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001Thymeleaf\u3068JavaScript\u306e\u9023\u643a\u3092\u52b9\u7387\u7684\u304b\u3064\u5b89\u5168\u306b\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"wp-block-sgb-message\"><div class=\"memo sng-shadow-0\" style=\"border-radius:0px;background-color:#b4e0fa;color:#009EF3\"><div class=\"memo_ttl dfont\"><span class=\"sng-box-msg__icon\" style=\"background:#009EF3\"><i class=\"far fa-lightbulb\"><\/i><\/span><div class=\"sng-box-msg__title\">\u91cd\u8981\u70b9<\/div><\/div><div class=\"sng-box-msg__contents\">\n<ul class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u5c5e\u6027\u3092\u4f7f\u7528\u3057\u305f\u5ba3\u8a00\u7684\u306a\u30c7\u30fc\u30bf\u53d7\u3051\u6e21\u3057<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u3068\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/li>\n\n\n\n<li>\u578b\u5b89\u5168\u6027\u3092\u8003\u616e\u3057\u305f\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u8003\u616e\u3057\u305f\u30a4\u30d9\u30f3\u30c8\u51e6\u7406<\/li>\n<\/ul>\n<\/div><\/div><\/div>\n\n\n\n<p>\u5b9f\u969b\u306e\u5b9f\u88c5\u3067\u306f\u3001\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u8981\u4ef6\u3084\u898f\u6a21\u306b\u5fdc\u3058\u3066\u3001\u3053\u308c\u3089\u306e\u30d1\u30bf\u30fc\u30f3\u3092\u9069\u5207\u306b\u9078\u629e\u30fb\u7d44\u307f\u5408\u308f\u305b\u3066\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-12\">7\u3064\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3068\u4f7f\u3044\u5206\u3051<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-13\">\u30d5\u30a9\u30fc\u30e0\u9001\u4fe1\u3068\u52d5\u7684\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/h2>\n\n\n\n<p>\u30d5\u30a9\u30fc\u30e0\u306e\u52d5\u7684\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306f\u3001\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\u3092\u5411\u4e0a\u3055\u305b\u308b\u91cd\u8981\u306a\u6a5f\u80fd\u3067\u3059\u3002\u4ee5\u4e0b\u306b\u5b9f\u88c5\u4f8b\u3092\u793a\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30d5\u30a9\u30fc\u30e0\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;form id=\"userForm\" th:action=\"@{\/users\/create}\" th:object=\"${userForm}\" method=\"post\"&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;input type=\"text\" th:field=\"*{username}\" \n               class=\"form-control\" \n               th:data-validation-rules=\"${validationRules.username}\"&gt;\n        &lt;span class=\"error-message\"&gt;&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;\u9001\u4fe1&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;!-- \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u7528JavaScript --&gt;\n&lt;script th:inline=\"javascript\"&gt;\nconst validationRules = \/*[[${validationRules}]]*\/ {};\n\ndocument.getElementById('userForm').addEventListener('submit', async (e) =&gt; {\n    e.preventDefault();\n    const form = e.target;\n\n    \/\/ \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u5b9f\u884c\n    if (!validateForm(form)) return;\n\n    try {\n        const response = await submitForm(form);\n        if (response.ok) {\n            showSuccess('\u767b\u9332\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f');\n        } else {\n            showError('\u767b\u9332\u306b\u5931\u6557\u3057\u307e\u3057\u305f');\n        }\n    } catch (error) {\n        console.error('Error:', error);\n    }\n});\n\nfunction validateForm(form) {\n    let isValid = true;\n    const inputs = form.querySelectorAll('input[data-validation-rules]');\n\n    inputs.forEach(input =&gt; {\n        const rules = JSON.parse(input.dataset.validationRules);\n        const value = input.value;\n        const errorElement = input.nextElementSibling;\n\n        if (rules.required &amp;&amp; !value) {\n            showInputError(input, errorElement, '\u5fc5\u9808\u9805\u76ee\u3067\u3059');\n            isValid = false;\n        } else if (rules.minLength &amp;&amp; value.length &lt; rules.minLength) {\n            showInputError(input, errorElement, `${rules.minLength}\u6587\u5b57\u4ee5\u4e0a\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044`);\n            isValid = false;\n        }\n    });\n\n    return isValid;\n}\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-14\">\u975e\u540c\u671f\u30c7\u30fc\u30bf\u66f4\u65b0\u3068DOM\u64cd\u4f5c<\/h2>\n\n\n\n<p>API\u3092\u4f7f\u7528\u3057\u305f\u975e\u540c\u671f\u30c7\u30fc\u30bf\u66f4\u65b0\u3068DOM\u64cd\u4f5c\u306e\u5b9f\u88c5\u4f8b\u3067\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30c7\u30fc\u30bf\u8868\u793a\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;div id=\"userList\" \n     th:data-api-url=\"@{\/api\/users}\"\n     th:data-csrf=\"${_csrf.token}\"&gt;\n    &lt;div th:each=\"user : ${users}\" th:id=\"'user-' + ${user.id}\"&gt;\n        &lt;span th:text=\"${user.name}\"&gt;&lt;\/span&gt;\n        &lt;button class=\"update-user\" th:data-user-id=\"${user.id}\"&gt;\u66f4\u65b0&lt;\/button&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;script&gt;\n\/\/ \u975e\u540c\u671f\u66f4\u65b0\u306e\u5b9f\u88c5\nclass UserUpdater {\n    constructor(element) {\n        this.element = element;\n        this.apiUrl = element.dataset.apiUrl;\n        this.csrfToken = element.dataset.csrf;\n        this.initializeEventListeners();\n    }\n\n    initializeEventListeners() {\n        this.element.addEventListener('click', e =&gt; {\n            if (e.target.classList.contains('update-user')) {\n                this.handleUserUpdate(e.target.dataset.userId);\n            }\n        });\n    }\n\n    async handleUserUpdate(userId) {\n        try {\n            const response = await fetch(`${this.apiUrl}\/${userId}`, {\n                method: 'PUT',\n                headers: {\n                    'Content-Type': 'application\/json',\n                    'X-CSRF-TOKEN': this.csrfToken\n                },\n                body: JSON.stringify({ \/* \u66f4\u65b0\u30c7\u30fc\u30bf *\/ })\n            });\n\n            if (response.ok) {\n                const updatedUser = await response.json();\n                this.updateUserDisplay(userId, updatedUser);\n            }\n        } catch (error) {\n            console.error('Update failed:', error);\n        }\n    }\n\n    updateUserDisplay(userId, userData) {\n        const userElement = document.getElementById(`user-${userId}`);\n        if (userElement) {\n            userElement.querySelector('span').textContent = userData.name;\n        }\n    }\n}\n\n\/\/ \u521d\u671f\u5316\nnew UserUpdater(document.getElementById('userList'));\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-15\">\u30e2\u30fc\u30c0\u30eb\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u306e\u30c7\u30fc\u30bf\u8868\u793a<\/h2>\n\n\n\n<p>\u30e2\u30fc\u30c0\u30eb\u30a6\u30a3\u30f3\u30c9\u30a6\u3092\u4f7f\u7528\u3057\u305f\u30c7\u30fc\u30bf\u8868\u793a\u30d1\u30bf\u30fc\u30f3\u3067\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30e2\u30fc\u30c0\u30eb\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;div id=\"userModal\" class=\"modal\" th:fragment=\"userModal\"&gt;\n    &lt;div class=\"modal-content\" th:data-loading-url=\"@{\/api\/users}\"&gt;\n        &lt;span class=\"close\"&gt;&amp;times;&lt;\/span&gt;\n        &lt;div id=\"modalContent\"&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;script&gt;\nclass ModalHandler {\n    constructor() {\n        this.modal = document.getElementById('userModal');\n        this.modalContent = document.getElementById('modalContent');\n        this.loadingUrl = this.modal.querySelector('.modal-content').dataset.loadingUrl;\n        this.initializeModal();\n    }\n\n    initializeModal() {\n        \/\/ \u30e2\u30fc\u30c0\u30eb\u3092\u9589\u3058\u308b\u51e6\u7406\n        this.modal.querySelector('.close').onclick = () =&gt; {\n            this.hideModal();\n        };\n\n        \/\/ \u30e2\u30fc\u30c0\u30eb\u5916\u30af\u30ea\u30c3\u30af\u3067\u9589\u3058\u308b\n        window.onclick = (event) =&gt; {\n            if (event.target === this.modal) {\n                this.hideModal();\n            }\n        };\n    }\n\n    async showUserDetails(userId) {\n        try {\n            const response = await fetch(`${this.loadingUrl}\/${userId}`);\n            if (response.ok) {\n                const userData = await response.json();\n                this.modalContent.innerHTML = this.createUserContent(userData);\n                this.showModal();\n            }\n        } catch (error) {\n            console.error('Failed to load user details:', error);\n        }\n    }\n\n    createUserContent(userData) {\n        return `\n            &lt;h2&gt;${userData.name}&lt;\/h2&gt;\n            &lt;div class=\"user-details\"&gt;\n                &lt;p&gt;Email: ${userData.email}&lt;\/p&gt;\n                &lt;p&gt;\u767b\u9332\u65e5: ${new Date(userData.createdAt).toLocaleDateString()}&lt;\/p&gt;\n            &lt;\/div&gt;\n        `;\n    }\n\n    showModal() {\n        this.modal.style.display = 'block';\n    }\n\n    hideModal() {\n        this.modal.style.display = 'none';\n    }\n}\n\n\/\/ \u30e2\u30fc\u30c0\u30eb\u30cf\u30f3\u30c9\u30e9\u30fc\u306e\u521d\u671f\u5316\nconst modalHandler = new ModalHandler();\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-16\">\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u3068\u52d5\u7684\u30c7\u30fc\u30bf\u8aad\u307f\u8fbc\u307f<\/h2>\n\n\n\n<p>\u7121\u9650\u30b9\u30af\u30ed\u30fc\u30eb\u3092\u542b\u3080\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u5b9f\u88c5\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;div id=\"infiniteScroll\" \n     th:data-api-url=\"@{\/api\/items}\"\n     th:data-page-size=\"${pageSize}\"\n     th:data-total-pages=\"${totalPages}\"&gt;\n    &lt;div class=\"items-container\"&gt;\n        &lt;div th:each=\"item : ${items}\" class=\"item\" th:text=\"${item.name}\"&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"loading-indicator\" style=\"display: none;\"&gt;\u8aad\u307f\u8fbc\u307f\u4e2d...&lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;script&gt;\nclass InfiniteScroll {\n    constructor(element) {\n        this.element = element;\n        this.apiUrl = element.dataset.apiUrl;\n        this.pageSize = parseInt(element.dataset.pageSize);\n        this.totalPages = parseInt(element.dataset.totalPages);\n        this.currentPage = 1;\n        this.loading = false;\n\n        this.initializeScroll();\n    }\n\n    initializeScroll() {\n        window.addEventListener('scroll', () =&gt; {\n            if (this.shouldLoadMore()) {\n                this.loadMoreItems();\n            }\n        });\n    }\n\n    shouldLoadMore() {\n        if (this.loading || this.currentPage &gt;= this.totalPages) return false;\n\n        const rect = this.element.getBoundingClientRect();\n        return rect.bottom &lt;= window.innerHeight + 100;\n    }\n\n    async loadMoreItems() {\n        this.loading = true;\n        this.showLoading();\n\n        try {\n            const response = await fetch(\n                `${this.apiUrl}?page=${this.currentPage + 1}&amp;size=${this.pageSize}`\n            );\n\n            if (response.ok) {\n                const data = await response.json();\n                this.appendItems(data.items);\n                this.currentPage++;\n            }\n        } catch (error) {\n            console.error('Failed to load more items:', error);\n        } finally {\n            this.loading = false;\n            this.hideLoading();\n        }\n    }\n\n    appendItems(items) {\n        const container = this.element.querySelector('.items-container');\n        items.forEach(item =&gt; {\n            const div = document.createElement('div');\n            div.className = 'item';\n            div.textContent = item.name;\n            container.appendChild(div);\n        });\n    }\n\n    showLoading() {\n        this.element.querySelector('.loading-indicator').style.display = 'block';\n    }\n\n    hideLoading() {\n        this.element.querySelector('.loading-indicator').style.display = 'none';\n    }\n}\n\n\/\/ \u521d\u671f\u5316\nnew InfiniteScroll(document.getElementById('infiniteScroll'));\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-17\">\u52d5\u7684\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u3068\u691c\u7d22\u6a5f\u80fd<\/h2>\n\n\n\n<p>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u691c\u7d22\u6a5f\u80fd\u306e\u5b9f\u88c5\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u691c\u7d22\u30d5\u30a9\u30fc\u30e0\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;div id=\"searchContainer\" \n     th:data-search-url=\"@{\/api\/search}\"\n     th:data-debounce-time=\"300\"&gt;\n    &lt;input type=\"text\" id=\"searchInput\" placeholder=\"\u691c\u7d22...\"&gt;\n    &lt;div id=\"searchResults\"&gt;&lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;script&gt;\nclass SearchHandler {\n    constructor(element) {\n        this.element = element;\n        this.searchUrl = element.dataset.searchUrl;\n        this.debounceTime = parseInt(element.dataset.debounceTime);\n        this.searchInput = element.querySelector('#searchInput');\n        this.resultsContainer = element.querySelector('#searchResults');\n\n        this.initializeSearch();\n    }\n\n    initializeSearch() {\n        let debounceTimer;\n\n        this.searchInput.addEventListener('input', (e) =&gt; {\n            clearTimeout(debounceTimer);\n            debounceTimer = setTimeout(() =&gt; {\n                this.performSearch(e.target.value);\n            }, this.debounceTime);\n        });\n    }\n\n    async performSearch(query) {\n        if (!query.trim()) {\n            this.clearResults();\n            return;\n        }\n\n        try {\n            const response = await fetch(\n                `${this.searchUrl}?q=${encodeURIComponent(query)}`\n            );\n\n            if (response.ok) {\n                const results = await response.json();\n                this.displayResults(results);\n            }\n        } catch (error) {\n            console.error('Search failed:', error);\n        }\n    }\n\n    displayResults(results) {\n        this.resultsContainer.innerHTML = '';\n\n        if (results.length === 0) {\n            this.resultsContainer.innerHTML = '&lt;p&gt;\u7d50\u679c\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f&lt;\/p&gt;';\n            return;\n        }\n\n        const ul = document.createElement('ul');\n        results.forEach(result =&gt; {\n            const li = document.createElement('li');\n            li.textContent = result.name;\n            ul.appendChild(li);\n        });\n\n        this.resultsContainer.appendChild(ul);\n    }\n\n    clearResults() {\n        this.resultsContainer.innerHTML = '';\n    }\n}\n\n\/\/ \u521d\u671f\u5316\nnew SearchHandler(document.getElementById('searchContainer'));\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-18\">\u30b0\u30e9\u30d5\u30fb\u30c1\u30e3\u30fc\u30c8\u306e\u52d5\u7684\u66f4\u65b0<\/h2>\n\n\n\n<p>Chart.js\u3092\u4f7f\u7528\u3057\u305f\u30b0\u30e9\u30d5\u306e\u52d5\u7684\u66f4\u65b0\u5b9f\u88c5\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30b0\u30e9\u30d5\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;div id=\"chartContainer\" \n     th:data-chart-data=\"${chartData}\"\n     th:data-update-url=\"@{\/api\/chart-data}\"&gt;\n    &lt;canvas id=\"myChart\"&gt;&lt;\/canvas&gt;\n&lt;\/div&gt;\n\n&lt;script th:src=\"@{\/js\/chart.js}\"&gt;&lt;\/script&gt;\n&lt;script&gt;\nclass ChartUpdater {\n    constructor(element) {\n        this.element = element;\n        this.updateUrl = element.dataset.updateUrl;\n        this.initialData = JSON.parse(element.dataset.chartData);\n        this.chart = null;\n\n        this.initializeChart();\n        this.startAutoUpdate();\n    }\n\n    initializeChart() {\n        const ctx = document.getElementById('myChart').getContext('2d');\n        this.chart = new Chart(ctx, {\n            type: 'line',\n            data: this.initialData,\n            options: {\n                responsive: true,\n                animation: {\n                    duration: 1000\n                }\n            }\n        });\n    }\n\n    startAutoUpdate() {\n        setInterval(() =&gt; {\n            this.updateChartData();\n        }, 5000); \/\/ 5\u79d2\u3054\u3068\u306b\u66f4\u65b0\n    }\n\n    async updateChartData() {\n        try {\n            const response = await fetch(this.updateUrl);\n            if (response.ok) {\n                const newData = await response.json();\n                this.updateChart(newData);\n            }\n        } catch (error) {\n            console.error('Failed to update chart:', error);\n        }\n    }\n\n    updateChart(newData) {\n        this.chart.data = newData;\n        this.chart.update();\n    }\n}\n\n\/\/ \u521d\u671f\u5316\nnew ChartUpdater(document.getElementById('chartContainer'));\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-19\">\u30d5\u30a1\u30a4\u30eb\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3068\u9032\u6357\u8868\u793a<\/h2>\n\n\n\n<p>\u30d5\u30a1\u30a4\u30eb\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3068\u9032\u6357\u30d0\u30fc\u306e\u5b9f\u88c5\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u30d5\u30a9\u30fc\u30e0\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 --&gt;\n&lt;div id=\"uploadContainer\" \n     th:data-upload-url=\"@{\/api\/upload}\"\n     th:data-max-size=\"${maxFileSize}\"&gt;\n    &lt;input type=\"file\" id=\"fileInput\" multiple&gt;\n    &lt;div class=\"progress-bar\" style=\"display: none;\"&gt;\n        &lt;div class=\"progress\"&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n    &lt;div class=\"upload-status\"&gt;&lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;script&gt;\nclass FileUploader {\n    constructor(element) {\n        this.element = element;\n        this.uploadUrl = element.dataset.uploadUrl;\n        this.maxSize = parseInt(element.dataset.maxSize);\n        this.fileInput = element.querySelector('#fileInput');\n        this.progressBar = element.querySelector('.progress-bar');\n        this.progress = element.querySelector('.progress');\n        this.status = element.querySelector('.upload-status');\n        \n        this.initializeUploader();\n    }\n    \n    initializeUploader() {\n        this.fileInput.addEventListener('change', (e) =&gt; {\n            const files = Array.from(e.target.files);\n            \n            if (!this.validateFiles(files)) return;\n            \n            this.uploadFiles(files);\n        });\n    }\n    \n    validateFiles(files) {\n        for (const file of files) {\n            if (file.size &gt; this.maxSize) {\n                this.showError(`${file.name}\u306e\u30b5\u30a4\u30ba\u304c\u4e0a\u9650\u3092\u8d85\u3048\u3066\u3044\u307e\u3059`);\n                return false;\n            }\n        }\n        return true;\n    }\n    \n    async uploadFiles(files) {\n        this.showProgressBar();\n        const formData = new FormData();\n        files.forEach(file =&gt; formData.append('files', file));\n        \n        try {\n            const response = await fetch(this.uploadUrl, {\n                method: 'POST',\n                body: formData,\n                onUploadProgress: this.handleProgress.bind(this)\n            });\n            \n            if (response.ok) {\n                this.showSuccess('\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u5b8c\u4e86');\n            } else {\n                this.showError('\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f');\n            }\n        } catch (error) {\n            console.error('Upload failed:', error);\n            this.showError('\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u30a8\u30e9\u30fc');\n        } finally {\n            this.hideProgressBar();\n        }\n    }\n    \n    handleProgress(event) {\n        if (event.lengthComputable) {\n            const percentComplete = (event.loaded \/ event.total) * 100;\n            this.updateProgress(percentComplete);\n        }\n    }\n    \n    showProgressBar() {\n        this.progressBar.style.display = 'block';\n        this.updateProgress(0);\n    }\n    \n    hideProgressBar() {\n        this.progressBar.style.display = 'none';\n    }\n    \n    updateProgress(percent) {\n        this.progress.style.width = `${percent}%`;\n    }\n    \n    showSuccess(message) {\n        this.status.textContent = message;\n        this.status.className = 'upload-status success';\n    }\n    \n    showError(message) {\n        this.status.textContent = message;\n        this.status.className = 'upload-status error';\n    }\n}\n\n\/\/ \u521d\u671f\u5316\nnew FileUploader(document.getElementById('uploadContainer'));\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-20\">\u5404\u30d1\u30bf\u30fc\u30f3\u306e\u4f7f\u3044\u5206\u3051<\/h3>\n\n\n\n<p>\u4ee5\u4e0a\u3067\u7d39\u4ecb\u3057\u305f7\u3064\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u306e\u9069\u5207\u306a\u4f7f\u3044\u5206\u3051\u306b\u3064\u3044\u3066\u3001\u4ee5\u4e0b\u306e\u8868\u306b\u307e\u3068\u3081\u307e\u3059\uff1a<\/p>\n\n\n<div id=\"id-10a04368-6320-49c1-b0ac-a018a9424db6\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3<\/th><th>\u6700\u9069\u306a\u4f7f\u7528\u30b7\u30fc\u30f3<\/th><th><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-sango-blue-color\">\u4e3b\u306a\u5229\u70b9<\/mark><\/th><th><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-sango-red-color\">\u6ce8\u610f\u70b9<\/mark><\/th><\/tr><\/thead><tbody><tr><td>\u30d5\u30a9\u30fc\u30e0\u9001\u4fe1\u3068\u52d5\u7684\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/td><td>\u30fb\u30e6\u30fc\u30b6\u30fc\u5165\u529b\u304c\u591a\u3044\u30d5\u30a9\u30fc\u30e0<br>\u30fb\u5373\u6642\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u304c\u5fc5\u8981\u306a\u5834\u9762<\/td><td>\u30fbUX\u306e\u5411\u4e0a<br>\u30fb\u30b5\u30fc\u30d0\u30fc\u8ca0\u8377\u306e\u8efd\u6e1b<\/td><td>\u30fb\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u5074\u3068\u30b5\u30fc\u30d0\u30fc\u5074\u306e\u4e21\u65b9\u3067\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u304c\u5fc5\u8981<\/td><\/tr><tr><td>\u975e\u540c\u671f\u30c7\u30fc\u30bf\u66f4\u65b0\u3068DOM\u64cd\u4f5c<\/td><td>\u30fb\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u30c7\u30fc\u30bf\u66f4\u65b0<br>\u30fb\u90e8\u5206\u7684\u306a\u753b\u9762\u66f4\u65b0<\/td><td>\u30fb\u30b9\u30e0\u30fc\u30ba\u306a\u753b\u9762\u9077\u79fb<br>\u30fb\u30b5\u30fc\u30d0\u30fc\u8ca0\u8377\u306e\u5206\u6563<\/td><td>\u30fb\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u8003\u616e<br>\u30fb\u72b6\u614b\u7ba1\u7406\u306e\u8907\u96d1\u5316<\/td><\/tr><tr><td>\u30e2\u30fc\u30c0\u30eb\u30a6\u30a3\u30f3\u30c9\u30a6<\/td><td>\u30fb\u8a73\u7d30\u60c5\u5831\u306e\u8868\u793a<br>\u30fb\u8efd\u91cf\u306a\u5165\u529b\u30d5\u30a9\u30fc\u30e0<\/td><td>\u30fb\u753b\u9762\u9077\u79fb\u304c\u5c11\u306a\u3044<br>\u30fb\u30d5\u30a9\u30fc\u30ab\u30b9\u3055\u308c\u305f\u8868\u793a<\/td><td>\u30fb\u30e2\u30d0\u30a4\u30eb\u5bfe\u5fdc<br>\u30fb\u30a2\u30af\u30bb\u30b7\u30d3\u30ea\u30c6\u30a3\u3078\u306e\u914d\u616e<\/td><\/tr><tr><td>\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3<\/td><td>\u30fb\u5927\u91cf\u30c7\u30fc\u30bf\u306e\u8868\u793a<br>\u30fb\u7121\u9650\u30b9\u30af\u30ed\u30fc\u30eb\u5b9f\u88c5<\/td><td>\u30fb\u30e1\u30e2\u30ea\u52b9\u7387\u306e\u5411\u4e0a<br>\u30fbUX\u306e\u5411\u4e0a<\/td><td>\u30fb\u30b9\u30af\u30ed\u30fc\u30eb\u4f4d\u7f6e\u306e\u7ba1\u7406<br>\u30fb\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306e\u8003\u616e<\/td><\/tr><tr><td>\u52d5\u7684\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0<\/td><td>\u30fb\u691c\u7d22\u6a5f\u80fd<br>\u30fb\u30c7\u30fc\u30bf\u306e\u7d5e\u308a\u8fbc\u307f<\/td><td>\u30fb\u5373\u6642\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af<br>\u30fb\u4f7f\u3044\u3084\u3059\u3055\u306e\u5411\u4e0a<\/td><td>\u30fb\u30c7\u30d0\u30a6\u30f3\u30b9\u51e6\u7406<br>\u30fb\u691c\u7d22\u6027\u80fd\u306e\u6700\u9069\u5316<\/td><\/tr><tr><td>\u30b0\u30e9\u30d5\u30fb\u30c1\u30e3\u30fc\u30c8<\/td><td>\u30fb\u30c7\u30fc\u30bf\u306e\u53ef\u8996\u5316<br>\u30fb\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u66f4\u65b0<\/td><td>\u30fb\u30c7\u30fc\u30bf\u306e\u7406\u89e3\u4fc3\u9032<br>\u30fb\u30a4\u30f3\u30bf\u30e9\u30af\u30c6\u30a3\u30d6\u6027<\/td><td>\u30fb\u66f4\u65b0\u983b\u5ea6\u306e\u9069\u6b63\u5316<br>\u30fb\u30e2\u30d0\u30a4\u30eb\u5bfe\u5fdc<\/td><\/tr><tr><td>\u30d5\u30a1\u30a4\u30eb\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9<\/td><td>\u30fb\u5927\u5bb9\u91cf\u30d5\u30a1\u30a4\u30eb<br>\u30fb\u8907\u6570\u30d5\u30a1\u30a4\u30eb\u51e6\u7406<\/td><td>\u30fb\u9032\u6357\u8868\u793a<br>\u30fb\u30e6\u30fc\u30b6\u30fc\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af<\/td><td>\u30fb\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<br>\u30fb\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-21\">\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u9078\u629e\u6642\u306e\u8003\u616e\u70b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8981\u4ef6\n<ul class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u91cf\u3068\u66f4\u65b0\u983b\u5ea6<\/li>\n\n\n\n<li>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u5074\u306e\u51e6\u7406\u8ca0\u8377<\/li>\n\n\n\n<li>\u30b5\u30fc\u30d0\u30fc\u5074\u306e\u30ea\u30bd\u30fc\u30b9\u5236\u7d04<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\uff08UX\uff09\n<ul class=\"wp-block-list\">\n<li>\u64cd\u4f5c\u306e\u76f4\u611f\u6027<\/li>\n\n\n\n<li>\u30ec\u30b9\u30dd\u30f3\u30b9\u6642\u9593<\/li>\n\n\n\n<li>\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u306e\u9069\u5207\u3055<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u4fdd\u5b88\u6027\n<ul class=\"wp-block-list\">\n<li>\u30b3\u30fc\u30c9\u306e\u53ef\u8aad\u6027<\/li>\n\n\n\n<li>\u518d\u5229\u7528\u6027<\/li>\n\n\n\n<li>\u30c7\u30d0\u30c3\u30b0\u306e\u3057\u3084\u3059\u3055<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\n<ul class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u306e\u691c\u8a3c<\/li>\n\n\n\n<li>XSS\u5bfe\u7b56<\/li>\n\n\n\n<li>CSRF\u5bfe\u7b56<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u306f\u3001\u8981\u4ef6\u306b\u5fdc\u3058\u3066\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u3088\u308a\u52b9\u679c\u7684\u306aWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-22\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3068\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-23\">XSS\u653b\u6483\u306e\u9632\u6b62\u7b56<\/h2>\n\n\n\n<p>Thymeleaf\u3068JavaScript\u306e\u9023\u643a\u6642\u306b\u304a\u3051\u308bXSS\uff08\u30af\u30ed\u30b9\u30b5\u30a4\u30c8\u30b9\u30af\u30ea\u30d7\u30c6\u30a3\u30f3\u30b0\uff09\u653b\u6483\u306e\u9632\u6b62\u7b56\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-24\">1. Thymeleaf\u5074\u3067\u306e\u5bfe\u7b56<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u60aa\u3044\u4f8b --&gt;\n&lt;div th:utext=\"${userInput}\"&gt;...&lt;\/div&gt;\n\n&lt;!-- \u826f\u3044\u4f8b --&gt;\n&lt;div th:text=\"${userInput}\"&gt;...&lt;\/div&gt;\n\n&lt;!-- \u5c5e\u6027\u5024\u306e\u30a8\u30b9\u30b1\u30fc\u30d7 --&gt;\n&lt;div th:attr=\"data-user-input=${userInput}\"&gt;...&lt;\/div&gt;\n\n&lt;!-- JavaScript\u3067\u306e\u5909\u6570\u5b9a\u7fa9 --&gt;\n&lt;script th:inline=\"javascript\"&gt;\n    \/\/ \u60aa\u3044\u4f8b\n    const userInput = [[${userInput}]];\n\n    \/\/ \u826f\u3044\u4f8b\n    const userInput = \/*[[${userInput}]]*\/ '';\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-25\">2. JavaScript\u5074\u3067\u306e\u5bfe\u7b56<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ XSS\u5bfe\u7b56\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30af\u30e9\u30b9\nclass XSSPrevention {\n    static escapeHTML(str) {\n        return str\n            .replace(\/&amp;\/g, '&amp;amp;')\n            .replace(\/&lt;\/g, '&amp;lt;')\n            .replace(\/&gt;\/g, '&amp;gt;')\n            .replace(\/\"\/g, '&amp;quot;')\n            .replace(\/'\/g, '&amp;#039;');\n    }\n\n    static sanitizeInput(input) {\n        const div = document.createElement('div');\n        div.textContent = input;\n        return div.innerHTML;\n    }\n\n    static createSafeHTML(content) {\n        const template = document.createElement('template');\n        template.innerHTML = this.escapeHTML(content);\n        return template.content.cloneNode(true);\n    }\n}\n\n\/\/ \u5b9f\u88c5\u4f8b\nclass SafeDOM {\n    static updateContent(elementId, content) {\n        const element = document.getElementById(elementId);\n        if (!element) return;\n\n        element.textContent = content; \/\/ textContent\u3092\u4f7f\u7528\u3057\u3066\u81ea\u52d5\u30a8\u30b9\u30b1\u30fc\u30d7\n    }\n\n    static appendSafeHTML(elementId, htmlContent) {\n        const element = document.getElementById(elementId);\n        if (!element) return;\n\n        const safeContent = XSSPrevention.createSafeHTML(htmlContent);\n        element.appendChild(safeContent);\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-26\">CSRF\u30c8\u30fc\u30af\u30f3\u306e\u9069\u5207\u306a\u6271\u3044\u65b9<\/h2>\n\n\n\n<p>CSRF\uff08\u30af\u30ed\u30b9\u30b5\u30a4\u30c8\u30ea\u30af\u30a8\u30b9\u30c8\u30d5\u30a9\u30fc\u30b8\u30a7\u30ea\uff09\u653b\u6483\u304b\u3089\u306e\u4fdd\u8b77\u65b9\u6cd5\u3092\u8aac\u660e\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-27\">1. \u30c8\u30fc\u30af\u30f3\u306e\u8a2d\u5b9a<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- CSRF\u30c8\u30fc\u30af\u30f3\u306e\u8a2d\u5b9a --&gt;\n&lt;meta name=\"_csrf\" th:content=\"${_csrf.token}\"\/&gt;\n&lt;meta name=\"_csrf_header\" th:content=\"${_csrf.headerName}\"\/&gt;\n\n&lt;script th:inline=\"javascript\"&gt;\n\/\/ CSRF\u30c8\u30fc\u30af\u30f3\u7ba1\u7406\u30af\u30e9\u30b9\nclass CSRFManager {\n    static getToken() {\n        return document.querySelector('meta[name=\"_csrf\"]')?.content;\n    }\n\n    static getHeaderName() {\n        return document.querySelector('meta[name=\"_csrf_header\"]')?.content;\n    }\n\n    static addTokenToHeaders(headers = {}) {\n        const token = this.getToken();\n        const headerName = this.getHeaderName();\n\n        if (token &amp;&amp; headerName) {\n            return {\n                ...headers,\n                [headerName]: token\n            };\n        }\n        return headers;\n    }\n}\n\n\/\/ \u4f7f\u7528\u4f8b\nclass APIClient {\n    static async post(url, data) {\n        try {\n            const response = await fetch(url, {\n                method: 'POST',\n                headers: CSRFManager.addTokenToHeaders({\n                    'Content-Type': 'application\/json'\n                }),\n                body: JSON.stringify(data)\n            });\n            return response.json();\n        } catch (error) {\n            console.error('API error:', error);\n            throw error;\n        }\n    }\n}\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-28\">2. Ajax\u30ea\u30af\u30a8\u30b9\u30c8\u3067\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Ajax\u30ea\u30af\u30a8\u30b9\u30c8\u30e9\u30c3\u30d1\u30fc\u30af\u30e9\u30b9\nclass SecureAjax {\n    constructor() {\n        this.csrfToken = CSRFManager.getToken();\n        this.csrfHeader = CSRFManager.getHeaderName();\n    }\n\n    async request(url, options = {}) {\n        const defaultHeaders = {\n            'Content-Type': 'application\/json',\n            [this.csrfHeader]: this.csrfToken\n        };\n\n        const config = {\n            ...options,\n            headers: {\n                ...defaultHeaders,\n                ...options.headers\n            }\n        };\n\n        try {\n            const response = await fetch(url, config);\n            if (!response.ok) {\n                throw new Error(`HTTP error! status: ${response.status}`);\n            }\n            return response.json();\n        } catch (error) {\n            console.error('Request failed:', error);\n            throw error;\n        }\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-29\">\u6a5f\u5bc6\u30c7\u30fc\u30bf\u306e\u5b89\u5168\u306a\u53d7\u3051\u6e21\u3057\u65b9\u6cd5<\/h2>\n\n\n\n<p>\u6a5f\u5bc6\u30c7\u30fc\u30bf\u3092\u5b89\u5168\u306b\u6271\u3046\u305f\u3081\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-30\">1. \u30c7\u30fc\u30bf\u306e\u6697\u53f7\u5316<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u6697\u53f7\u5316\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\nclass CryptoUtil {\n    static async encrypt(text, key) {\n        const encoder = new TextEncoder();\n        const data = encoder.encode(text);\n\n        const cryptoKey = await crypto.subtle.importKey(\n            'raw',\n            encoder.encode(key),\n            { name: 'AES-GCM' },\n            false,\n            ['encrypt']\n        );\n\n        const iv = crypto.getRandomValues(new Uint8Array(12));\n        const encrypted = await crypto.subtle.encrypt(\n            { name: 'AES-GCM', iv },\n            cryptoKey,\n            data\n        );\n\n        return {\n            encrypted: Array.from(new Uint8Array(encrypted)),\n            iv: Array.from(iv)\n        };\n    }\n}\n\n\/\/ \u5b9f\u88c5\u4f8b\nclass SecureDataTransfer {\n    static async sendSecureData(url, data, encryptionKey) {\n        try {\n            const encrypted = await CryptoUtil.encrypt(\n                JSON.stringify(data),\n                encryptionKey\n            );\n\n            return await SecureAjax.request(url, {\n                method: 'POST',\n                body: JSON.stringify(encrypted)\n            });\n        } catch (error) {\n            console.error('Secure data transfer failed:', error);\n            throw error;\n        }\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-31\">2. \u30bb\u30ad\u30e5\u30a2\u306a\u4fdd\u5b58\u65b9\u6cd5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30bb\u30ad\u30e5\u30a2\u30b9\u30c8\u30ec\u30fc\u30b8\u30af\u30e9\u30b9\nclass SecureStorage {\n    static setSecureItem(key, value) {\n        if (typeof value === 'object') {\n            value = JSON.stringify(value);\n        }\n        sessionStorage.setItem(key, value);\n    }\n\n    static getSecureItem(key) {\n        const value = sessionStorage.getItem(key);\n        try {\n            return JSON.parse(value);\n        } catch {\n            return value;\n        }\n    }\n\n    static removeSecureItem(key) {\n        sessionStorage.removeItem(key);\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-32\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u306e\u307e\u3068\u3081<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5165\u529b\u30c7\u30fc\u30bf\u306e\u691c\u8a3c\n<ul class=\"wp-block-list\">\n<li>\u3059\u3079\u3066\u306e\u30e6\u30fc\u30b6\u30fc\u5165\u529b\u3092\u7591\u3046<\/li>\n\n\n\n<li>\u30b5\u30fc\u30d0\u30fc\u5074\u3068\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u5074\u306e\u4e21\u65b9\u3067\u691c\u8a3c<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406\u306e\u5b9f\u65bd<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u901a\u4fe1\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\n<ul class=\"wp-block-list\">\n<li>\u5e38\u306bHTTPS\u901a\u4fe1\u306e\u4f7f\u7528<\/li>\n\n\n\n<li>CSRF\u30c8\u30fc\u30af\u30f3\u306e\u9069\u5207\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u6a5f\u5bc6\u30c7\u30fc\u30bf\u306e\u6697\u53f7\u5316<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u30c8\u30fc\u30af\u30f3\u306e\u9069\u5207\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30a2\u306a\u30af\u30c3\u30ad\u30fc\u8a2d\u5b9a<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u51e6\u7406\n<ul class=\"wp-block-list\">\n<li>\u8a73\u7d30\u306a\u30a8\u30e9\u30fc\u60c5\u5831\u306e\u975e\u516c\u958b<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30ed\u30b0\u8a18\u9332<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30fc\u30d5\u30ec\u30f3\u30c9\u30ea\u30fc\u306a\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30b3\u30fc\u30c9\u54c1\u8cea\n<ul class=\"wp-block-list\">\n<li>\u5b9a\u671f\u7684\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ec\u30d3\u30e5\u30fc<\/li>\n\n\n\n<li>\u4f9d\u5b58\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u6700\u65b0\u5316<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30c6\u30b9\u30c8\u306e\u5b9f\u65bd<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5bfe\u7b56\u3092\u9069\u5207\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001Thymeleaf\u3068JavaScript\u306e\u9023\u643a\u306b\u304a\u3051\u308b\u4e3b\u8981\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ea\u30b9\u30af\u3092\u8efd\u6e1b\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-33\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-34\">JavaScript\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f\u5b9f\u88c5<\/h2>\n\n\n\n<p>\u30da\u30fc\u30b8\u306e\u521d\u671f\u8868\u793a\u3092\u9ad8\u901f\u5316\u3059\u308b\u305f\u3081\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f\uff08Lazy Loading\uff09\u5b9f\u88c5\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-35\">1. \u57fa\u672c\u7684\u306a\u9045\u5ef6\u8aad\u307f\u8fbc\u307f<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30e2\u30b8\u30e5\u30fc\u30eb\u578b\u30b9\u30af\u30ea\u30d7\u30c8\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f --&gt;\n&lt;script type=\"module\" th:src=\"@{\/js\/main.js}\" defer&gt;&lt;\/script&gt;\n\n&lt;!-- \u6761\u4ef6\u4ed8\u304d\u9045\u5ef6\u8aad\u307f\u8fbc\u307f --&gt;\n&lt;script th:inline=\"javascript\"&gt;\n    \/\/ \u5fc5\u8981\u306b\u306a\u3063\u305f\u6642\u70b9\u3067\u30b9\u30af\u30ea\u30d7\u30c8\u3092\u8aad\u307f\u8fbc\u3080\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\n    class ScriptLoader {\n        static loadedScripts = new Set();\n\n        static async load(src) {\n            if (this.loadedScripts.has(src)) {\n                return Promise.resolve();\n            }\n\n            return new Promise((resolve, reject) =&gt; {\n                const script = document.createElement('script');\n                script.src = src;\n                script.async = true;\n\n                script.onload = () =&gt; {\n                    this.loadedScripts.add(src);\n                    resolve();\n                };\n                script.onerror = reject;\n\n                document.head.appendChild(script);\n            });\n        }\n\n        static async loadMultiple(scripts) {\n            return Promise.all(scripts.map(src =&gt; this.load(src)));\n        }\n    }\n\n    \/\/ \u4f7f\u7528\u4f8b\uff1a\u5fc5\u8981\u306a\u6642\u70b9\u3067\u30b9\u30af\u30ea\u30d7\u30c8\u3092\u8aad\u307f\u8fbc\u3080\n    class FeatureManager {\n        static async initializeFeature(featureName) {\n            switch (featureName) {\n                case 'chart':\n                    await ScriptLoader.loadMultiple([\n                        '\/js\/chart.min.js',\n                        '\/js\/chart-config.js'\n                    ]);\n                    this.initializeChart();\n                    break;\n                \/\/ \u4ed6\u306e\u6a5f\u80fd\u3082\u540c\u69d8\u306b\n            }\n        }\n    }\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-36\">2. \u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u306e\u9045\u5ef6\u521d\u671f\u5316<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Intersection Observer\u3092\u4f7f\u7528\u3057\u305f\u9045\u5ef6\u521d\u671f\u5316\nclass LazyComponent {\n    constructor(element, options = {}) {\n        this.element = element;\n        this.options = {\n            threshold: 0.1,\n            rootMargin: '50px',\n            ...options\n        };\n        this.initialized = false;\n        this.observer = null;\n\n        this.init();\n    }\n\n    init() {\n        this.observer = new IntersectionObserver(\n            this.handleIntersection.bind(this),\n            this.options\n        );\n        this.observer.observe(this.element);\n    }\n\n    async handleIntersection(entries) {\n        const entry = entries[0];\n        if (entry.isIntersecting &amp;&amp; !this.initialized) {\n            await this.initializeComponent();\n            this.initialized = true;\n            this.observer.disconnect();\n        }\n    }\n\n    async initializeComponent() {\n        \/\/ \u5177\u4f53\u7684\u306a\u521d\u671f\u5316\u51e6\u7406\u3092\u5b9f\u88c5\n        throw new Error('Must be implemented by subclass');\n    }\n}\n\n\/\/ \u4f7f\u7528\u4f8b\uff1a\u30c7\u30fc\u30bf\u30c6\u30fc\u30d6\u30eb\u306e\u9045\u5ef6\u521d\u671f\u5316\nclass LazyDataTable extends LazyComponent {\n    async initializeComponent() {\n        await ScriptLoader.load('\/js\/data-table.js');\n        \/\/ \u30c7\u30fc\u30bf\u30c6\u30fc\u30d6\u30eb\u306e\u521d\u671f\u5316\u51e6\u7406\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-37\">\u30ad\u30e3\u30c3\u30b7\u30e5\u5236\u5fa1\u306e\u6700\u9069\u5316\u65b9\u6cd5<\/h2>\n\n\n\n<p>\u30d6\u30e9\u30a6\u30b6\u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u52b9\u679c\u7684\u306b\u6d3b\u7528\u3059\u308b\u305f\u3081\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u8aac\u660e\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-38\">1. \u9759\u7684\u30ea\u30bd\u30fc\u30b9\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u5236\u5fa1<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30d0\u30fc\u30b8\u30e7\u30f3\u7ba1\u7406\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u306e\u8aad\u307f\u8fbc\u307f --&gt;\n&lt;script th:src=\"@{\/js\/app.js(v=${appVersion})}\" \n        th:data-cache-version=\"${cacheVersion}\"&gt;&lt;\/script&gt;\n\n&lt;script th:inline=\"javascript\"&gt;\n\/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\nclass CacheManager {\n    static VERSION_KEY = 'app_cache_version';\n\n    static async initialize() {\n        const currentVersion = document.querySelector('script[data-cache-version]')\n            ?.dataset.cacheVersion;\n\n        if (currentVersion !== localStorage.getItem(this.VERSION_KEY)) {\n            await this.clearCache();\n            localStorage.setItem(this.VERSION_KEY, currentVersion);\n        }\n    }\n\n    static async clearCache() {\n        if ('caches' in window) {\n            const cacheNames = await caches.keys();\n            await Promise.all(\n                cacheNames.map(name =&gt; caches.delete(name))\n            );\n        }\n    }\n\n    static async cacheResponse(url, response) {\n        if ('caches' in window) {\n            const cache = await caches.open('app-cache');\n            await cache.put(url, response.clone());\n        }\n    }\n\n    static async getCachedResponse(url) {\n        if ('caches' in window) {\n            const cache = await caches.open('app-cache');\n            return await cache.match(url);\n        }\n        return null;\n    }\n}\n\n\/\/ API\u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\nclass CachedAPIClient {\n    static async fetch(url, options = {}) {\n        const cachedResponse = await CacheManager.getCachedResponse(url);\n        if (cachedResponse &amp;&amp; !options.noCache) {\n            return cachedResponse.json();\n        }\n\n        const response = await fetch(url, options);\n        if (response.ok &amp;&amp; !options.noCache) {\n            await CacheManager.cacheResponse(url, response);\n        }\n        return response.json();\n    }\n}\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-39\">2. \u30c7\u30fc\u30bf\u306e\u30e1\u30e2\u30ea\u30ad\u30e3\u30c3\u30b7\u30e5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30e1\u30e2\u30ea\u30ad\u30e3\u30c3\u30b7\u30e5\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\nclass MemoryCache {\n    constructor(options = {}) {\n        this.cache = new Map();\n        this.maxAge = options.maxAge || 5 * 60 * 1000; \/\/ \u30c7\u30d5\u30a9\u30eb\u30c85\u5206\n    }\n\n    set(key, value) {\n        this.cache.set(key, {\n            value,\n            timestamp: Date.now()\n        });\n    }\n\n    get(key) {\n        const item = this.cache.get(key);\n        if (!item) return null;\n\n        if (Date.now() - item.timestamp &gt; this.maxAge) {\n            this.cache.delete(key);\n            return null;\n        }\n\n        return item.value;\n    }\n\n    clear() {\n        this.cache.clear();\n    }\n}\n\n\/\/ \u4f7f\u7528\u4f8b\uff1aAPI\u5fdc\u7b54\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\nclass CachedService {\n    constructor() {\n        this.cache = new MemoryCache({\n            maxAge: 10 * 60 * 1000 \/\/ 10\u5206\n        });\n    }\n\n    async getData(url) {\n        const cachedData = this.cache.get(url);\n        if (cachedData) {\n            return cachedData;\n        }\n\n        const response = await fetch(url);\n        const data = await response.json();\n        this.cache.set(url, data);\n        return data;\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-40\">\u30d0\u30f3\u30c9\u30eb\u30b5\u30a4\u30ba\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/h2>\n\n\n\n<p>JavaScript\u30b3\u30fc\u30c9\u306e\u30d0\u30f3\u30c9\u30eb\u30b5\u30a4\u30ba\u3092\u6700\u9069\u5316\u3059\u308b\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-41\">1. \u52d5\u7684\u30a4\u30f3\u30dd\u30fc\u30c8<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u6a5f\u80fd\u5358\u4f4d\u3067\u306e\u52d5\u7684\u30a4\u30f3\u30dd\u30fc\u30c8\nclass FeatureLoader {\n    static async loadFeature(featureName) {\n        try {\n            const module = await import(`\/js\/features\/${featureName}.js`);\n            return module.default;\n        } catch (error) {\n            console.error(`Failed to load feature: ${featureName}`, error);\n            throw error;\n        }\n    }\n}\n\n\/\/ \u4f7f\u7528\u4f8b\ndocument.getElementById('loadFeature').addEventListener('click', async () =&gt; {\n    const feature = await FeatureLoader.loadFeature('specificFeature');\n    feature.initialize();\n});<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-42\">2. \u30b3\u30fc\u30c9\u5206\u5272\u3068\u30d7\u30ea\u30ed\u30fc\u30c9<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u91cd\u8981\u306a\u30ea\u30bd\u30fc\u30b9\u306e\u30d7\u30ea\u30ed\u30fc\u30c9 --&gt;\n&lt;link rel=\"preload\" th:href=\"@{\/js\/critical.js}\" as=\"script\"&gt;\n\n&lt;script th:inline=\"javascript\"&gt;\n\/\/ \u30d7\u30ea\u30ed\u30fc\u30c9\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\nclass PreloadManager {\n    static preloadedModules = new Set();\n\n    static preloadModule(modulePath) {\n        if (this.preloadedModules.has(modulePath)) return;\n\n        const link = document.createElement('link');\n        link.rel = 'modulepreload';\n        link.href = modulePath;\n        document.head.appendChild(link);\n\n        this.preloadedModules.add(modulePath);\n    }\n\n    static preloadMultiple(modules) {\n        modules.forEach(module =&gt; this.preloadModule(module));\n    }\n}\n\n\/\/ \u30eb\u30fc\u30c8\u30d9\u30fc\u30b9\u306e\u30d7\u30ea\u30ed\u30fc\u30c9\nclass RoutePreloader {\n    static routes = {\n        '\/dashboard': [\n            '\/js\/dashboard\/charts.js',\n            '\/js\/dashboard\/widgets.js'\n        ],\n        '\/profile': [\n            '\/js\/profile\/editor.js'\n        ]\n    };\n\n    static preloadRoute(path) {\n        const modules = this.routes[path];\n        if (modules) {\n            PreloadManager.preloadMultiple(modules);\n        }\n    }\n}\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-43\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30ea\u30bd\u30fc\u30b9\u306e\u8aad\u307f\u8fbc\u307f\u6700\u9069\u5316\n<ul class=\"wp-block-list\">\n<li>\u91cd\u8981\u306a\u30ea\u30bd\u30fc\u30b9\u306e\u512a\u5148\u8aad\u307f\u8fbc\u307f<\/li>\n\n\n\n<li>\u975e\u91cd\u8981\u30ea\u30bd\u30fc\u30b9\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f<\/li>\n\n\n\n<li>\u30e2\u30b8\u30e5\u30fc\u30eb\u5206\u5272\u3068\u30d0\u30f3\u30c9\u30eb\u6700\u9069\u5316<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565\n<ul class=\"wp-block-list\">\n<li>\u9069\u5207\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u30d8\u30c3\u30c0\u30fc\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30d0\u30fc\u30b8\u30e7\u30f3\u7ba1\u7406\u306b\u3088\u308b\u30ad\u30e3\u30c3\u30b7\u30e5\u5236\u5fa1<\/li>\n\n\n\n<li>\u30e1\u30e2\u30ea\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u6d3b\u7528<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u5b9f\u884c\u6642\u6700\u9069\u5316\n<ul class=\"wp-block-list\">\n<li>\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u30fc\u306e\u9069\u5207\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u306e\u9632\u6b62<\/li>\n\n\n\n<li>\u91cd\u3044\u51e6\u7406\u306e\u975e\u540c\u671f\u5b9f\u884c<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u30e1\u30c8\u30ea\u30af\u30b9\n<ul class=\"wp-block-list\">\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6307\u6a19\u306e\u8a08\u6e2c<\/li>\n\n\n\n<li>\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306e\u7279\u5b9a<\/li>\n\n\n\n<li>\u7d99\u7d9a\u7684\u306a\u6539\u5584<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af\u3092\u9069\u5207\u306b\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001Thymeleaf\u3068JavaScript\u3092\u4f7f\u7528\u3057\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u5927\u5e45\u306b\u5411\u4e0a\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-44\">\u30c7\u30d0\u30c3\u30b0\u3068\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-45\">\u958b\u767a\u8005\u30c4\u30fc\u30eb\u3092\u4f7f\u3063\u305f\u30c7\u30d0\u30c3\u30b0\u65b9\u6cd5<\/h2>\n\n\n\n<p>Thymeleaf\u3068JavaScript\u306e\u9023\u643a\u6642\u306b\u304a\u3051\u308b\u52b9\u679c\u7684\u306a\u30c7\u30d0\u30c3\u30b0\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-46\">1. \u30c7\u30d0\u30c3\u30b0\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30c7\u30d0\u30c3\u30b0\u30e2\u30fc\u30c9\u7ba1\u7406\nclass DebugManager {\n    static isDebugMode = false;\n\n    static initialize() {\n        this.isDebugMode = document.querySelector('meta[name=\"debug-mode\"]')\n            ?.content === 'true';\n\n        if (this.isDebugMode) {\n            this.setupDebugTools();\n        }\n    }\n\n    static setupDebugTools() {\n        window.debugUtils = {\n            logThymeleafData: this.logThymeleafData.bind(this),\n            inspectElement: this.inspectElement.bind(this),\n            traceEvents: this.traceEvents.bind(this)\n        };\n\n        console.info('Debug utilities loaded. Access via window.debugUtils');\n    }\n\n    static logThymeleafData(element) {\n        const data = {};\n        for (const attr of element.attributes) {\n            if (attr.name.startsWith('th:') || attr.name.startsWith('data-')) {\n                data[attr.name] = attr.value;\n            }\n        }\n        console.table(data);\n    }\n\n    static inspectElement(selector) {\n        const element = document.querySelector(selector);\n        if (!element) {\n            console.warn(`Element not found: ${selector}`);\n            return;\n        }\n\n        console.group(`Element Inspection: ${selector}`);\n        console.log('Element:', element);\n        this.logThymeleafData(element);\n        console.log('Computed Style:', window.getComputedStyle(element));\n        console.groupEnd();\n    }\n\n    static traceEvents(selector) {\n        const element = document.querySelector(selector);\n        if (!element) return;\n\n        const eventTypes = ['click', 'input', 'change', 'submit'];\n        eventTypes.forEach(type =&gt; {\n            element.addEventListener(type, (e) =&gt; {\n                console.log(`Event ${type}:`, {\n                    target: e.target,\n                    currentTarget: e.currentTarget,\n                    value: e.target.value,\n                    timestamp: new Date().toISOString()\n                });\n            });\n        });\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-47\">2. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30d7\u30ed\u30d5\u30a1\u30a4\u30ea\u30f3\u30b0<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\nclass PerformanceMonitor {\n    static measures = new Map();\n\n    static startMeasure(label) {\n        performance.mark(`${label}-start`);\n    }\n\n    static endMeasure(label) {\n        performance.mark(`${label}-end`);\n        performance.measure(label, \n            `${label}-start`, \n            `${label}-end`\n        );\n\n        const measures = performance.getEntriesByName(label);\n        const latestMeasure = measures[measures.length - 1];\n\n        this.measures.set(label, {\n            duration: latestMeasure.duration,\n            timestamp: Date.now()\n        });\n\n        console.log(`${label}: ${latestMeasure.duration.toFixed(2)}ms`);\n    }\n\n    static generateReport() {\n        console.group('Performance Report');\n\n        for (const [label, data] of this.measures) {\n            console.log(`${label}:`, {\n                duration: `${data.duration.toFixed(2)}ms`,\n                timestamp: new Date(data.timestamp).toLocaleTimeString()\n            });\n        }\n\n        console.groupEnd();\n    }\n}\n\n\/\/ \u4f7f\u7528\u4f8b\nasync function loadData() {\n    PerformanceMonitor.startMeasure('data-loading');\n    \/\/ \u30c7\u30fc\u30bf\u8aad\u307f\u8fbc\u307f\u51e6\u7406\n    PerformanceMonitor.endMeasure('data-loading');\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-48\">\u3088\u304f\u3042\u308b\u30a8\u30e9\u30fc\u3068\u89e3\u6c7a\u65b9\u6cd5<\/h2>\n\n\n\n<p>\u4e3b\u8981\u306a\u30a8\u30e9\u30fc\u30d1\u30bf\u30fc\u30f3\u3068\u305d\u306e\u89e3\u6c7a\u65b9\u6cd5\u3092\u4f53\u7cfb\u7684\u306b\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-49\">1. \u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30a8\u30e9\u30fc\u76e3\u8996\u3068\u5831\u544a\nclass ErrorTracker {\n    static errorLog = [];\n    static maxLogSize = 100;\n\n    static initialize() {\n        window.addEventListener('error', this.handleError.bind(this));\n        window.addEventListener('unhandledrejection', this.handlePromiseError.bind(this));\n    }\n\n    static handleError(event) {\n        const error = {\n            type: 'runtime',\n            message: event.message,\n            filename: event.filename,\n            line: event.lineno,\n            column: event.colno,\n            stack: event.error?.stack,\n            timestamp: new Date().toISOString()\n        };\n\n        this.logError(error);\n    }\n\n    static handlePromiseError(event) {\n        const error = {\n            type: 'promise',\n            message: event.reason?.message || 'Promise rejected',\n            stack: event.reason?.stack,\n            timestamp: new Date().toISOString()\n        };\n\n        this.logError(error);\n    }\n\n    static logError(error) {\n        this.errorLog.unshift(error);\n        if (this.errorLog.length &gt; this.maxLogSize) {\n            this.errorLog.pop();\n        }\n\n        if (this.shouldReportError(error)) {\n            this.reportError(error);\n        }\n\n        console.error('Error tracked:', error);\n    }\n\n    static shouldReportError(error) {\n        \/\/ \u30a8\u30e9\u30fc\u5831\u544a\u306e\u6761\u4ef6\u3092\u5b9a\u7fa9\n        return error.type === 'runtime' &amp;&amp; error.message !== 'Script error.';\n    }\n\n    static async reportError(error) {\n        try {\n            await fetch('\/api\/error-report', {\n                method: 'POST',\n                headers: { 'Content-Type': 'application\/json' },\n                body: JSON.stringify(error)\n            });\n        } catch (e) {\n            console.error('Failed to report error:', e);\n        }\n    }\n\n    static getErrorSummary() {\n        const summary = {\n            total: this.errorLog.length,\n            byType: {},\n            recentErrors: this.errorLog.slice(0, 5)\n        };\n\n        this.errorLog.forEach(error =&gt; {\n            summary.byType[error.type] = (summary.byType[error.type] || 0) + 1;\n        });\n\n        return summary;\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-50\">2. \u4e00\u822c\u7684\u306a\u30a8\u30e9\u30fc\u3068\u89e3\u6c7a\u65b9\u6cd5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30a8\u30e9\u30fc\u89e3\u6c7a\u30ac\u30a4\u30c9\nclass TroubleshootingGuide {\n    static commonErrors = {\n        'th:text not working': {\n            symptoms: [\n                '\u30c6\u30ad\u30b9\u30c8\u304c\u66f4\u65b0\u3055\u308c\u306a\u3044',\n                '\u30d7\u30ec\u30fc\u30b9\u30db\u30eb\u30c0\u30fc\u304c\u8868\u793a\u3055\u308c\u305f\u307e\u307e'\n            ],\n            causes: [\n                'Thymeleaf\u306e\u69cb\u6587\u30a8\u30e9\u30fc',\n                '\u30e2\u30c7\u30eb\u5c5e\u6027\u306e\u6b20\u843d',\n                'JavaScript\u306b\u3088\u308b\u8981\u7d20\u306e\u4e0a\u66f8\u304d'\n            ],\n            solutions: [\n                'Thymeleaf\u5f0f\u306e\u69cb\u6587\u3092\u78ba\u8a8d',\n                '\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u3067\u306e\u30e2\u30c7\u30eb\u5c5e\u6027\u8a2d\u5b9a\u3092\u78ba\u8a8d',\n                'JavaScript\u306e\u5b9f\u884c\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u8abf\u6574'\n            ],\n            codeExample: `\n                \/\/ \u6b63\u3057\u3044\u5b9f\u88c5\n                &lt;div th:text=\"${message}\"&gt;&lt;\/div&gt;\n\n                \/\/ JavaScript\u3067\u306e\u5bfe\u5fdc\n                document.addEventListener('DOMContentLoaded', () =&gt; {\n                    \/\/ DOM\u306e\u64cd\u4f5c\n                });\n            `\n        },\n        'CSRF token missing': {\n            symptoms: [\n                '403 Forbidden \u30a8\u30e9\u30fc',\n                '\u30a2\u30af\u30bb\u30b9\u62d2\u5426\u30e1\u30c3\u30bb\u30fc\u30b8'\n            ],\n            causes: [\n                'CSRF\u30c8\u30fc\u30af\u30f3\u306e\u672a\u8a2d\u5b9a',\n                '\u30c8\u30fc\u30af\u30f3\u306e\u8aa4\u3063\u305f\u9001\u4fe1\u65b9\u6cd5'\n            ],\n            solutions: [\n                '\u30e1\u30bf\u30bf\u30b0\u3067\u306e\u30c8\u30fc\u30af\u30f3\u8a2d\u5b9a\u78ba\u8a8d',\n                'Ajax\u30ea\u30af\u30a8\u30b9\u30c8\u3078\u306e\u30c8\u30fc\u30af\u30f3\u8ffd\u52a0'\n            ],\n            codeExample: `\n                \/\/ CSRF\u30c8\u30fc\u30af\u30f3\u306e\u8a2d\u5b9a\n                &lt;meta name=\"_csrf\" th:content=\"${_csrf.token}\"\/&gt;\n\n                \/\/ Ajax\u3067\u306e\u9001\u4fe1\n                fetch(url, {\n                    headers: {\n                        'X-CSRF-TOKEN': document.querySelector('meta[name=\"_csrf\"]').content\n                    }\n                });\n            `\n        }\n    };\n\n    static getDiagnosis(errorType) {\n        const error = this.commonErrors[errorType];\n        if (!error) {\n            return '\u672a\u77e5\u306e\u30a8\u30e9\u30fc\u3067\u3059\u3002\u8a73\u7d30\u306a\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002';\n        }\n\n        return {\n            ...error,\n            timestamp: new Date().toISOString()\n        };\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-51\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306e\u7279\u5b9a\u65b9\u6cd5<\/h2>\n\n\n\n<p>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u554f\u984c\u3092\u7279\u5b9a\u3057\u89e3\u6c7a\u3059\u308b\u305f\u3081\u306e\u624b\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-52\">1. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5206\u6790\u30c4\u30fc\u30eb<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30a2\u30ca\u30e9\u30a4\u30b6\u30fc\nclass PerformanceAnalyzer {\n    static metrics = {\n        domLoaded: 0,\n        firstPaint: 0,\n        firstContentfulPaint: 0,\n        interactions: []\n    };\n\n    static initialize() {\n        this.measurePageLoad();\n        this.measurePaintTiming();\n        this.measureInteractions();\n    }\n\n    static measurePageLoad() {\n        window.addEventListener('DOMContentLoaded', () =&gt; {\n            this.metrics.domLoaded = performance.now();\n        });\n    }\n\n    static measurePaintTiming() {\n        const observer = new PerformanceObserver((list) =&gt; {\n            for (const entry of list.getEntries()) {\n                if (entry.name === 'first-paint') {\n                    this.metrics.firstPaint = entry.startTime;\n                }\n                if (entry.name === 'first-contentful-paint') {\n                    this.metrics.firstContentfulPaint = entry.startTime;\n                }\n            }\n        });\n\n        observer.observe({ entryTypes: ['paint'] });\n    }\n\n    static measureInteractions() {\n        const interactionEvents = ['click', 'input', 'scroll'];\n\n        interactionEvents.forEach(eventType =&gt; {\n            document.addEventListener(eventType, (e) =&gt; {\n                const timing = {\n                    type: eventType,\n                    target: e.target.tagName,\n                    timestamp: performance.now()\n                };\n                this.metrics.interactions.push(timing);\n            });\n        });\n    }\n\n    static generateReport() {\n        return {\n            pageLoad: {\n                domLoaded: this.metrics.domLoaded,\n                firstPaint: this.metrics.firstPaint,\n                firstContentfulPaint: this.metrics.firstContentfulPaint\n            },\n            interactions: this.metrics.interactions.slice(-10),\n            summary: this.analyzeTrends()\n        };\n    }\n\n    static analyzeTrends() {\n        \/\/ \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c8\u30ec\u30f3\u30c9\u306e\u5206\u6790\n        const interactionTimes = this.metrics.interactions.map(i =&gt; i.timestamp);\n        return {\n            averageInteractionTime: this.calculateAverage(interactionTimes),\n            maxInteractionTime: Math.max(...interactionTimes),\n            totalInteractions: this.metrics.interactions.length\n        };\n    }\n\n    static calculateAverage(numbers) {\n        return numbers.length ? \n            numbers.reduce((a, b) =&gt; a + b) \/ numbers.length : 0;\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-53\">\u30c7\u30d0\u30c3\u30b0\u3068\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u4f53\u7cfb\u7684\u306a\u30c7\u30d0\u30c3\u30b0\u624b\u9806\n<ul class=\"wp-block-list\">\n<li>\u30a8\u30e9\u30fc\u306e\u518d\u73fe\u3068\u5206\u985e<\/li>\n\n\n\n<li>\u6839\u672c\u539f\u56e0\u306e\u7279\u5b9a<\/li>\n\n\n\n<li>\u89e3\u6c7a\u7b56\u306e\u5b9f\u88c5\u3068\u691c\u8a3c<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u52b9\u679c\u7684\u306a\u30ed\u30b0\u7ba1\u7406\n<ul class=\"wp-block-list\">\n<li>\u9069\u5207\u306a\u30ed\u30b0\u30ec\u30d9\u30eb\u306e\u4f7f\u7528<\/li>\n\n\n\n<li>\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u60c5\u5831\u306e\u8a18\u9332<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u30b9\u30bf\u30c3\u30af\u306e\u4fdd\u6301<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\n<ul class=\"wp-block-list\">\n<li>\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306e\u7279\u5b9a<\/li>\n\n\n\n<li>\u6e2c\u5b9a\u53ef\u80fd\u306a\u6539\u5584\u76ee\u6a19<\/li>\n\n\n\n<li>\u7d99\u7d9a\u7684\u306a\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u4e88\u9632\u7684\u5bfe\u7b56\n<ul class=\"wp-block-list\">\n<li>\u30a8\u30e9\u30fc\u30d1\u30bf\u30fc\u30f3\u306e\u6587\u66f8\u5316<\/li>\n\n\n\n<li>\u81ea\u52d5\u30c6\u30b9\u30c8\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30b3\u30fc\u30c9\u30ec\u30d3\u30e5\u30fc\u306e\u5b9f\u65bd<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u30c4\u30fc\u30eb\u3068\u624b\u6cd5\u3092\u9069\u5207\u306b\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001Thymeleaf\u3068JavaScript\u306e\u9023\u643a\u306b\u304a\u3051\u308b\u554f\u984c\u3092\u52b9\u7387\u7684\u306b\u7279\u5b9a\u3057\u89e3\u6c7a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-54\">\u307e\u3068\u3081\uff1aThymeleaf\u3068JavaScript\u306e\u52b9\u679c\u7684\u306a\u9023\u643a\u306b\u5411\u3051\u3066<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-55\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u57fa\u672c\u8a2d\u8a08\u306e\u91cd\u8981\u6027\n<ul class=\"wp-block-list\">\n<li>\u9069\u5207\u306a\u8cac\u4efb\u5206\u62c5\n<ul class=\"wp-block-list\">\n<li>Thymeleaf: \u30b5\u30fc\u30d0\u30fc\u30b5\u30a4\u30c9\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u51e6\u7406<\/li>\n\n\n\n<li>JavaScript: \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u30b5\u30a4\u30c9\u306e\u30a4\u30f3\u30bf\u30e9\u30af\u30b7\u30e7\u30f3<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u660e\u78ba\u306a\u30c7\u30fc\u30bf\u30d5\u30ed\u30fc\n<ul class=\"wp-block-list\">\n<li>\u30b5\u30fc\u30d0\u30fc\u304b\u3089\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3078\u306e\u5b89\u5168\u306a\u30c7\u30fc\u30bf\u53d7\u3051\u6e21\u3057<\/li>\n\n\n\n<li>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3067\u306e\u9069\u5207\u306a\u30c7\u30fc\u30bf\u52a0\u5de5\u3068\u8868\u793a<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d5\u30a1\u30fc\u30b9\u30c8\n<ul class=\"wp-block-list\">\n<li>XSS\u5bfe\u7b56\u306e\u5fb9\u5e95<\/li>\n\n\n\n<li>CSRF\u30c8\u30fc\u30af\u30f3\u306e\u9069\u5207\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u6a5f\u5bc6\u30c7\u30fc\u30bf\u306e\u614e\u91cd\u306a\u53d6\u308a\u6271\u3044<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\n<ul class=\"wp-block-list\">\n<li>\u30ea\u30bd\u30fc\u30b9\u306e\u52b9\u7387\u7684\u306a\u8aad\u307f\u8fbc\u307f<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565<\/li>\n\n\n\n<li>\u30d0\u30f3\u30c9\u30eb\u30b5\u30a4\u30ba\u306e\u6700\u9069\u5316<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-56\">\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u306e\u4f7f\u3044\u5206\u3051<\/h2>\n\n\n\n<p>\u72b6\u6cc1\u306b\u5fdc\u3058\u305f\u6700\u9069\u306a\u30d1\u30bf\u30fc\u30f3\u306e\u9078\u629e\u6307\u91dd\uff1a<\/p>\n\n\n<div id=\"id-8ca60b7e-c0ff-483c-bc89-9a1e8ef56075\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3<\/th><th>\u6700\u9069\u306a\u4f7f\u7528\u30b7\u30fc\u30f3<\/th><th>\u4e3b\u306a\u5229\u70b9<\/th><th>\u5b9f\u88c5\u306e\u512a\u5148\u5ea6<\/th><\/tr><\/thead><tbody><tr><td>\u30d5\u30a9\u30fc\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/td><td>\u30c7\u30fc\u30bf\u5165\u529b\u753b\u9762<\/td><td>\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\u306e\u5411\u4e0a<\/td><td>\u9ad8<\/td><\/tr><tr><td>\u975e\u540c\u671f\u66f4\u65b0<\/td><td>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u30c7\u30fc\u30bf\u8868\u793a<\/td><td>\u30b5\u30fc\u30d0\u30fc\u8ca0\u8377\u306e\u5206\u6563<\/td><td>\u9ad8<\/td><\/tr><tr><td>\u30e2\u30fc\u30c0\u30eb\u8868\u793a<\/td><td>\u8a73\u7d30\u60c5\u5831\u306e\u8868\u793a<\/td><td>\u753b\u9762\u9077\u79fb\u306e\u524a\u6e1b<\/td><td>\u4e2d<\/td><\/tr><tr><td>\u52d5\u7684\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3<\/td><td>\u5927\u91cf\u30c7\u30fc\u30bf\u306e\u8868\u793a<\/td><td>\u30e1\u30e2\u30ea\u52b9\u7387\u306e\u5411\u4e0a<\/td><td>\u4e2d<\/td><\/tr><tr><td>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u691c\u7d22<\/td><td>\u691c\u7d22\u6a5f\u80fd<\/td><td>\u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u5411\u4e0a<\/td><td>\u4e2d<\/td><\/tr><tr><td>\u30b0\u30e9\u30d5\u66f4\u65b0<\/td><td>\u30c7\u30fc\u30bf\u53ef\u8996\u5316<\/td><td>\u60c5\u5831\u306e\u76f4\u611f\u7684\u7406\u89e3<\/td><td>\u4f4e<\/td><\/tr><tr><td>\u30d5\u30a1\u30a4\u30eb\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9<\/td><td>\u30d5\u30a1\u30a4\u30eb\u51e6\u7406<\/td><td>\u9032\u6357\u306e\u53ef\u8996\u5316<\/td><td>\u4f4e<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"i-57\">\u4eca\u5f8c\u306e\u767a\u5c55\u306b\u5411\u3051\u3066<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u7d99\u7d9a\u7684\u306a\u6539\u5584\n<ul class=\"wp-block-list\">\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30fc\u30d5\u30a3\u30fc\u30c9\u30d0\u30c3\u30af\u306e\u53ce\u96c6<\/li>\n\n\n\n<li>\u30b3\u30fc\u30c9\u54c1\u8cea\u306e\u7dad\u6301<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u65b0\u6280\u8853\u3078\u306e\u5bfe\u5fdc\n<ul class=\"wp-block-list\">\n<li>\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u5bfe\u5fdc<\/li>\n\n\n\n<li>\u65b0\u3057\u3044JavaScript\u6a5f\u80fd\u306e\u6d3b\u7528<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u306e\u66f4\u65b0<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u306e\u78ba\u4fdd\n<ul class=\"wp-block-list\">\n<li>\u30e2\u30b8\u30e5\u30fc\u30eb\u5316\u306e\u63a8\u9032<\/li>\n\n\n\n<li>\u518d\u5229\u7528\u53ef\u80fd\u306a\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u5316<\/li>\n\n\n\n<li>\u30c6\u30b9\u30c8\u5bb9\u6613\u6027\u306e\u7dad\u6301<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-58\">\u7d50\u8ad6<\/h2>\n\n\n\n<p>Thymeleaf\u3068JavaScript\u306e\u9023\u643a\u306f\u3001\u9069\u5207\u306a\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3068\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u306e\u9069\u7528\u306b\u3088\u308a\u3001\u5805\u7262\u3067\u4fdd\u5b88\u6027\u306e\u9ad8\u3044Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u69cb\u7bc9\u3092\u53ef\u80fd\u306b\u3057\u307e\u3059\u3002\u672c\u30ac\u30a4\u30c9\u3067\u7d39\u4ecb\u3057\u305f\u5b9f\u88c5\u624b\u6cd5\u306f\u3001\u958b\u767a\u73fe\u5834\u3067\u306e\u5b9f\u8df5\u7684\u306a\u8ab2\u984c\u89e3\u6c7a\u306b\u76f4\u63a5\u6d3b\u7528\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3001\u4fdd\u5b88\u6027\u306e\u30d0\u30e9\u30f3\u30b9\u3092\u53d6\u308a\u306a\u304c\u3089\u3001\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\u306e\u5411\u4e0a\u3092\u76ee\u6307\u3057\u305f\u5b9f\u88c5\u3092\u5fc3\u304c\u3051\u308b\u3053\u3068\u3067\u3001\u3088\u308a\u8cea\u306e\u9ad8\u3044\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u304c\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u7d99\u7d9a\u7684\u306a\u5b66\u7fd2\u3068\u6539\u5584\u3092\u901a\u3058\u3066\u3001\u3088\u308a\u52b9\u679c\u7684\u306aThymeleaf\u3068JavaScript\u306e\u9023\u643a\u3092\u5b9f\u73fe\u3057\u3066\u3044\u304f\u3053\u3068\u304c\u3001\u4eca\u5f8c\u306e\u958b\u767asuccess story\u306b\u3064\u306a\u304c\u308b\u3067\u3057\u3087\u3046\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Thymeleaf\u3068JavaScript\u306e\u57fa\u672c\u7684\u306a\u9023\u643a\u65b9\u6cd5 Warning: Undefined array key &#8220;is_admin&#8221; in \/home\/xs392991\/dexall.co.jp\/public_ht &#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":{"0":"post-469","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-java","7":"nothumb"},"_links":{"self":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/469","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=469"}],"version-history":[{"count":2,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/469\/revisions"}],"predecessor-version":[{"id":472,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/469\/revisions\/472"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=469"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=469"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=469"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}