{"id":1163,"date":"2025-03-24T08:52:43","date_gmt":"2025-03-23T23:52:43","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=1163"},"modified":"2025-03-24T08:52:43","modified_gmt":"2025-03-23T23:52:43","slug":"thymeleaf-%e3%81%a8%e3%81%af%ef%bc%9f%e6%9c%80%e6%96%b0%e3%81%ae%e3%83%86%e3%83%b3%e3%83%97%e3%83%ac%e3%83%bc%e3%83%88%e3%82%a8%e3%83%b3%e3%82%b8%e3%83%b3%e3%82%92%e5%be%b9%e5%ba%95%e8%a7%a3%e8%aa%ac","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=1163","title":{"rendered":"Thymeleaf \u3068\u306f\uff1f\u6700\u65b0\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30a8\u30f3\u30b8\u30f3\u3092\u5fb9\u5e95\u89e3\u8aac"},"content":{"rendered":"\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\">    <span><\/span>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-0\">\u5f93\u6765\u306eJSP\u3068\u6bd4\u8f03\u3057\u305fThymeleaf\u306e\u9769\u65b0\u7684\u306a\u7279\u5fb4<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-4\">Spring Boot\u3068\u306e\u89aa\u548c\u6027\u304c\u9ad8\u3044\u7406\u7531<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-9\">Thymeleaf\u306e\u57fa\u672c\u6a5f\u80fd\u3068\u5b9f\u88c5\u624b\u9806<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-10\">\u958b\u767a\u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3068\u4f9d\u5b58\u95a2\u4fc2\u306e\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-13\">\u57fa\u672c\u7684\u306a\u69cb\u6587\u3068\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u4f5c\u6210\u306e\u6d41\u308c<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-16\">\u30c7\u30fc\u30bf\u30d0\u30a4\u30f3\u30c7\u30a3\u30f3\u30b0\u3068\u30e2\u30c7\u30eb\u64cd\u4f5c\u306e\u57fa\u790e<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-20\">\u5b9f\u8df5Thymeleaf\u30c6\u30af\u30cb\u30c3\u30af\u96c6<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-21\">\u6761\u4ef6\u5206\u5c90\u3068\u30eb\u30fc\u30d7\u51e6\u7406\u306e\u52b9\u7387\u7684\u306a\u5b9f\u88c5\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-24\">\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u3092\u6d3b\u7528\u3057\u305f\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u8a2d\u8a08<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-27\">\u30d5\u30a9\u30fc\u30e0\u51e6\u7406\u3068\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u5b9f\u88c5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-30\">Thymeleaf\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-31\">XSS\u5bfe\u7b56\u653b\u6483\u3068\u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406\u306e\u91cd\u8981\u6027<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-35\">CSRF\u5bfe\u7b56\u306e\u5b9f\u88c5\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-40\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-41\">\u30ad\u30e3\u30c3\u30b7\u30e5\u6a5f\u80fd\u306e\u52b9\u679c\u7684\u306a\u6d3b\u7528\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-44\">\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u51e6\u7406\u306e\u9ad8\u901f\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-50\">\u5b9f\u8df5\u7684\u306a\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u3068\u30b5\u30f3\u30d7\u30eb\u30b3\u30fc\u30c9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-51\">\u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u306a\u30ec\u30a4\u30a2\u30a6\u30c8\u306e\u5b9f\u88c5\u4f8b<\/a>      <\/li>      <li>        <a href=\"#i-54\">REST API\u3068\u306e\u9023\u643a\u30d1\u30bf\u30fc\u30f3<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-56\">\u975e\u540c\u671f\u51e6\u7406\u306e\u5b9f\u88c5\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-59\">Thymeleaf\u306e\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-60\">\u3088\u304f\u3042\u308b\u30a8\u30e9\u30fc\u3068\u305d\u306e\u89e3\u6c7a\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-66\">\u30c7\u30d0\u30c3\u30b0\u3068\u30ed\u30b0\u51fa\u529b\u306e\u30c6\u30af\u30cb\u30c3\u30af<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-72\">\u30d7\u30ed\u30c0\u30af\u30b7\u30e7\u30f3\u74b0\u5883\u3067\u306e\u904b\u7528\u30dd\u30a4\u30f3\u30c8<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-73\">\u672c\u756a\u74b0\u5883\u3067\u306e\u8a2d\u5b9a\u6700\u9069\u5316<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-77\">\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u4fdd\u5b88\u306e\u91cd\u8981\u30dd\u30a4\u30f3\u30c8<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-83\">Thymeleaf\u306e\u4eca\u5f8c\u306e\u5c55\u671b<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-84\">\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u65b0\u6a5f\u80fd\u3068\u6539\u5584\u70b9<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-87\">\u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u3067\u306e\u6d3b\u7528\u65b9\u6cd5<\/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-0\">\u5f93\u6765\u306eJSP\u3068\u6bd4\u8f03\u3057\u305fThymeleaf\u306e\u9769\u65b0\u7684\u306a\u7279\u5fb4<\/h2>\n\n\n\n<p>Thymeleaf\u306f\u3001\u30e2\u30c0\u30f3\u306aJava\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u306b\u304a\u3044\u3066\u6700\u3082\u4eba\u6c17\u306e\u3042\u308b\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30a8\u30f3\u30b8\u30f3\u306e1\u3064\u3067\u3059\u3002\u5f93\u6765\u306eJSP\u3068\u6bd4\u8f03\u3057\u3066\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u9769\u65b0\u7684\u306a\u7279\u5fb4\u3092\u6301\u3063\u3066\u3044\u307e\u3059\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-1\">1. \u30ca\u30c1\u30e5\u30e9\u30eb\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u6a5f\u80fd<\/h3>\n\n\n\n<p>Thymeleaf\u306e\u6700\u5927\u306e\u7279\u5fb4\u306f\u3001HTML\u30d5\u30a1\u30a4\u30eb\u3092\u305d\u306e\u307e\u307e\u9759\u7684\u30d7\u30ed\u30c8\u30bf\u30a4\u30d7\u3068\u3057\u3066\u4f7f\u7528\u3067\u304d\u308b\u300c\u30ca\u30c1\u30e5\u30e9\u30eb\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u300d\u6a5f\u80fd\u3067\u3059\u3002<\/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;!-- JSP\u306e\u5834\u5408 --&gt;\n&lt;span&gt;&lt;%= user.getName() %&gt;&lt;\/span&gt;\n\n&lt;!-- Thymeleaf\u306e\u5834\u5408 --&gt;\n&lt;span th:text=\"${user.name}\"&gt;John Doe&lt;\/span&gt;<\/pre>\n\n\n\n<p>\u3053\u306e\u4f8b\u3067\u306f\u3001Thymeleaf\u3092\u4f7f\u7528\u3057\u305f\u5834\u5408\u3001\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u30d6\u30e9\u30a6\u30b6\u3067\u76f4\u63a5\u958b\u3044\u3066\u3082\u300cJohn Doe\u300d\u3068\u8868\u793a\u3055\u308c\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u5b9f\u884c\u6642\u306b\u306f\u52d5\u7684\u306b\u30e6\u30fc\u30b6\u30fc\u540d\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">2. \u578b\u5b89\u5168\u6027\u306e\u5411\u4e0a<\/h3>\n\n\n\n<p>Thymeleaf\u306f\u3001Spring Framework\u3068\u306e\u7d71\u5408\u306b\u3088\u308a\u3001\u5f37\u529b\u306a\u578b\u5b89\u5168\u6027\u3092\u63d0\u4f9b\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=\"\">\/\/ Controller\u3067\u306e\u30e2\u30c7\u30eb\u8a2d\u5b9a\n@GetMapping(\"\/user\")\npublic String showUser(Model model) {\n    User user = userService.getCurrentUser();\n    model.addAttribute(\"user\", user);\n    return \"user\/profile\";\n}<\/pre>\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;!-- \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3067\u306e\u578b\u5b89\u5168\u306a\u30a2\u30af\u30bb\u30b9 --&gt;\n&lt;div th:object=\"${user}\"&gt;\n    &lt;p th:text=\"*{name}\"&gt;\u30e6\u30fc\u30b6\u30fc\u540d&lt;\/p&gt;\n    &lt;p th:text=\"*{email}\"&gt;\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9&lt;\/p&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-3\">3. \u30e2\u30b8\u30e5\u30fc\u30eb\u5316\u3068\u30ec\u30a4\u30a2\u30a6\u30c8\u7ba1\u7406<\/h3>\n\n\n\n<p>Thymeleaf\u306f\u3001\u52b9\u7387\u7684\u306a\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u518d\u5229\u7528\u3068\u30ec\u30a4\u30a2\u30a6\u30c8\u7ba1\u7406\u3092\u53ef\u80fd\u306b\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;!-- fragments\/header.html --&gt;\n&lt;header th:fragment=\"pageHeader\"&gt;\n    &lt;h1&gt;\u30b5\u30a4\u30c8\u30d8\u30c3\u30c0\u30fc&lt;\/h1&gt;\n    &lt;nav&gt;...&lt;\/nav&gt;\n&lt;\/header&gt;\n\n&lt;!-- main.html --&gt;\n&lt;div th:replace=\"fragments\/header :: pageHeader\"&gt;&lt;\/div&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-4\">Spring Boot\u3068\u306e\u89aa\u548c\u6027\u304c\u9ad8\u3044\u7406\u7531<\/h2>\n\n\n\n<p>Thymeleaf\u306f\u3001Spring Boot\u3068\u7279\u306b\u76f8\u6027\u304c\u826f\u304f\u3001\u4ee5\u4e0b\u306e\u7406\u7531\u3067\u591a\u304f\u306e\u958b\u767a\u8005\u306b\u9078\u3070\u308c\u3066\u3044\u307e\u3059\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-5\">1. \u81ea\u52d5\u8a2d\u5b9a\u6a5f\u80fd<\/h3>\n\n\n\n<p>Spring Boot\u306eauto-configuration\u6a5f\u80fd\u306b\u3088\u308a\u3001\u6700\u5c0f\u9650\u306e\u8a2d\u5b9a\u3067\u958b\u59cb\u3067\u304d\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;!-- pom.xml --&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;\/artifactId&gt;\n&lt;\/dependency&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-6\">2. Spring Security \u3068\u306e\u7d71\u5408<\/h3>\n\n\n\n<p>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u6a5f\u80fd\u3068\u306e\u9023\u643a\u304c\u5bb9\u6613\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;!-- \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u7d71\u5408\u306e\u4f8b --&gt;\n&lt;div th:if=\"${#authorization.expression('hasRole(''ADMIN'')')}\"&gt;\n    \u7ba1\u7406\u8005\u5411\u3051\u30b3\u30f3\u30c6\u30f3\u30c4\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-7\">3. Spring MVC\u3068\u306e\u5b8c\u5168\u306a\u4e92\u63db\u6027<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3068\u306e\u9023\u643a\u304c\u76f4\u611f\u7684<\/li>\n\n\n\n<li>\u30d5\u30a9\u30fc\u30e0\u30d0\u30a4\u30f3\u30c7\u30a3\u30f3\u30b0\u304c\u7c21\u5358<\/li>\n\n\n\n<li>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u6a5f\u80fd\u3068\u306e\u7d71\u5408\u304c\u5bb9\u6613<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-8\">4. \u958b\u767a\u8005\u4f53\u9a13\u306e\u5411\u4e0a<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30db\u30c3\u30c8\u30ea\u30ed\u30fc\u30c9\u5bfe\u5fdc\n<ul class=\"wp-block-list\">\n<li><code>spring-boot-devtools<\/code>\u3068\u306e\u9023\u643a\u3067\u30e9\u30a4\u30d6\u30ea\u30ed\u30fc\u30c9\u304c\u53ef\u80fd<\/li>\n\n\n\n<li>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u304c\u958b\u767a\u30e2\u30fc\u30c9\u3067\u81ea\u52d5\u7684\u306b\u7121\u52b9\u5316<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>IDE\u30b5\u30dd\u30fc\u30c8\n<ul class=\"wp-block-list\">\n<li>Spring\u30c4\u30fc\u30eb\u30b9\u30a4\u30fc\u30c8\u3067\u306e\u5b8c\u5168\u306a\u30b5\u30dd\u30fc\u30c8<\/li>\n\n\n\n<li>\u30b3\u30fc\u30c9\u88dc\u5b8c\u3084\u69cb\u6587\u30cf\u30a4\u30e9\u30a4\u30c8\u304c\u5229\u7528\u53ef\u80fd<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30c7\u30d0\u30c3\u30b0\u306e\u3057\u3084\u3059\u3055\n<ul class=\"wp-block-list\">\n<li>\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8\u304c\u5206\u304b\u308a\u3084\u3059\u3044<\/li>\n\n\n\n<li>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u51e6\u7406\u306e\u30c8\u30ec\u30fc\u30b9\u304c\u53ef\u80fd<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u7279\u5fb4\u306b\u3088\u308a\u3001Thymeleaf\u306f\u7279\u306bSpring Boot\u3092\u4f7f\u7528\u3059\u308b\u73fe\u4ee3\u306eJavaWeb\u958b\u767a\u306b\u304a\u3044\u3066\u3001\u6700\u9069\u306a\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30a8\u30f3\u30b8\u30f3\u306e\u9078\u629e\u80a2\u3068\u306a\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-9\">Thymeleaf\u306e\u57fa\u672c\u6a5f\u80fd\u3068\u5b9f\u88c5\u624b\u9806<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-10\">\u958b\u767a\u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3068\u4f9d\u5b58\u95a2\u4fc2\u306e\u8a2d\u5b9a<\/h2>\n\n\n\n<p>Spring Boot\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u306ethymeleaf\u306e\u5c0e\u5165\u306f\u975e\u5e38\u306b\u7c21\u5358\u3067\u3059\u3002\u4ee5\u4e0b\u306e\u624b\u9806\u3067\u74b0\u5883\u3092\u69cb\u7bc9\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-11\">1. Maven\u4f9d\u5b58\u95a2\u4fc2\u306e\u8ffd\u52a0<\/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;dependencies&gt;\n    &lt;!-- Spring Boot Starter for Thymeleaf --&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;\/artifactId&gt;\n    &lt;\/dependency&gt;\n\n    &lt;!-- \u958b\u767a\u8005\u30c4\u30fc\u30eb\uff08\u30aa\u30d7\u30b7\u30e7\u30f3\uff1a\u30db\u30c3\u30c8\u30ea\u30ed\u30fc\u30c9\u7528\uff09 --&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-boot-devtools&lt;\/artifactId&gt;\n        &lt;scope&gt;runtime&lt;\/scope&gt;\n        &lt;optional&gt;true&lt;\/optional&gt;\n    &lt;\/dependency&gt;\n&lt;\/dependencies&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-12\">2. \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d7\u30ed\u30d1\u30c6\u30a3\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=\"\"># application.properties\nspring.thymeleaf.cache=false        # \u958b\u767a\u6642\u306f\u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u7121\u52b9\u5316\nspring.thymeleaf.mode=HTML          # \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30e2\u30fc\u30c9\u306e\u8a2d\u5b9a\nspring.thymeleaf.encoding=UTF-8     # \u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u8a2d\u5b9a\nspring.thymeleaf.prefix=classpath:\/templates\/    # \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u914d\u7f6e\u5834\u6240\nspring.thymeleaf.suffix=.html       # \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u62e1\u5f35\u5b50<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-13\">\u57fa\u672c\u7684\u306a\u69cb\u6587\u3068\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u4f5c\u6210\u306e\u6d41\u308c<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-14\">1. \u57fa\u672c\u7684\u306a\u5f0f\u306e\u7a2e\u985e<\/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;!-- \u5909\u6570\u5f0f --&gt;\n&lt;p th:text=\"${message}\"&gt;\u30c7\u30d5\u30a9\u30eb\u30c8\u30e1\u30c3\u30bb\u30fc\u30b8&lt;\/p&gt;\n\n&lt;!-- \u9078\u629e\u5909\u6570\u5f0f --&gt;\n&lt;div th:object=\"${user}\"&gt;\n    &lt;p th:text=\"*{name}\"&gt;\u30e6\u30fc\u30b6\u30fc\u540d&lt;\/p&gt;\n    &lt;p th:text=\"*{email}\"&gt;\u30e1\u30fc\u30eb&lt;\/p&gt;\n&lt;\/div&gt;\n\n&lt;!-- \u30ea\u30f3\u30af\u5f0f --&gt;\n&lt;a th:href=\"@{\/user\/{id}(id=${user.id})}\"&gt;\u30e6\u30fc\u30b6\u30fc\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb&lt;\/a&gt;\n\n&lt;!-- \u30e1\u30c3\u30bb\u30fc\u30b8\u5f0f\uff08i18n\uff09 --&gt;\n&lt;p th:text=\"#{welcome.message}\"&gt;\u3088\u3046\u3053\u305d&lt;\/p&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-15\">2. \u4e3b\u8981\u306a\u5c5e\u6027\u306e\u4f7f\u7528\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=\"\">&lt;!-- \u30c6\u30ad\u30b9\u30c8\u8a2d\u5b9a --&gt;\n&lt;p th:text=\"${message}\"&gt;\u30e1\u30c3\u30bb\u30fc\u30b8&lt;\/p&gt;\n&lt;p th:utext=\"${htmlContent}\"&gt;HTML\u30b3\u30f3\u30c6\u30f3\u30c4&lt;\/p&gt;\n\n&lt;!-- \u6761\u4ef6\u5206\u5c90 --&gt;\n&lt;div th:if=\"${isAdmin}\"&gt;\u7ba1\u7406\u8005\u30e1\u30cb\u30e5\u30fc&lt;\/div&gt;\n&lt;div th:unless=\"${isAdmin}\"&gt;\u4e00\u822c\u30e6\u30fc\u30b6\u30fc\u30e1\u30cb\u30e5\u30fc&lt;\/div&gt;\n\n&lt;!-- \u7e70\u308a\u8fd4\u3057 --&gt;\n&lt;ul&gt;\n    &lt;li th:each=\"item : ${items}\" th:text=\"${item.name}\"&gt;\u5546\u54c1\u540d&lt;\/li&gt;\n&lt;\/ul&gt;\n\n&lt;!-- \u5c5e\u6027\u8a2d\u5b9a --&gt;\n&lt;input th:value=\"${user.name}\" th:readonly=\"${readonly}\"&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-16\">\u30c7\u30fc\u30bf\u30d0\u30a4\u30f3\u30c7\u30a3\u30f3\u30b0\u3068\u30e2\u30c7\u30eb\u64cd\u4f5c\u306e\u57fa\u790e<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-17\">1. \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3067\u306e\u30e2\u30c7\u30eb\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=\"\">@Controller\n@RequestMapping(\"\/users\")\npublic class UserController {\n\n    @GetMapping(\"\/profile\")\n    public String showProfile(Model model) {\n        \/\/ \u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u306e\u8a2d\u5b9a\n        User user = new User(\"\u5c71\u7530\u592a\u90ce\", \"yamada@example.com\");\n        model.addAttribute(\"user\", user);\n\n        \/\/ \u30ea\u30b9\u30c8\u30c7\u30fc\u30bf\u306e\u8a2d\u5b9a\n        List&lt;String&gt; roles = Arrays.asList(\"USER\", \"ADMIN\");\n        model.addAttribute(\"roles\", roles);\n\n        return \"user\/profile\";\n    }\n\n    @PostMapping(\"\/update\")\n    public String updateProfile(@ModelAttribute User user, \n                              BindingResult result) {\n        if (result.hasErrors()) {\n            return \"user\/profile\";\n        }\n        \/\/ \u30e6\u30fc\u30b6\u30fc\u66f4\u65b0\u51e6\u7406\n        return \"redirect:\/users\/profile\";\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-18\">2. \u30d5\u30a9\u30fc\u30e0\u30d0\u30a4\u30f3\u30c7\u30a3\u30f3\u30b0\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=\"\">&lt;form th:action=\"@{\/users\/update}\" th:object=\"${user}\" method=\"post\"&gt;\n    &lt;div&gt;\n        &lt;label&gt;\u540d\u524d\uff1a&lt;\/label&gt;\n        &lt;input type=\"text\" th:field=\"*{name}\"&gt;\n        &lt;span th:if=\"${#fields.hasErrors('name')}\" \n              th:errors=\"*{name}\" \n              class=\"error\"&gt;\n            \u540d\u524d\u30a8\u30e9\u30fc\n        &lt;\/span&gt;\n    &lt;\/div&gt;\n\n    &lt;div&gt;\n        &lt;label&gt;\u30e1\u30fc\u30eb\uff1a&lt;\/label&gt;\n        &lt;input type=\"email\" th:field=\"*{email}\"&gt;\n        &lt;span th:if=\"${#fields.hasErrors('email')}\" \n              th:errors=\"*{email}\" \n              class=\"error\"&gt;\n            \u30e1\u30fc\u30eb\u30a8\u30e9\u30fc\n        &lt;\/span&gt;\n    &lt;\/div&gt;\n\n    &lt;button type=\"submit\"&gt;\u66f4\u65b0&lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-19\">3. \u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u6d3b\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;!-- \u65e5\u4ed8\u30d5\u30a9\u30fc\u30de\u30c3\u30c8 --&gt;\n&lt;p th:text=\"${#temporals.format(localDate, 'yyyy\/MM\/dd')}\"&gt;2024\/01\/01&lt;\/p&gt;\n\n&lt;!-- \u6570\u5024\u30d5\u30a9\u30fc\u30de\u30c3\u30c8 --&gt;\n&lt;p th:text=\"${#numbers.formatDecimal(price, 1, 'COMMA', 2, 'POINT')}\"&gt;1,234.56&lt;\/p&gt;\n\n&lt;!-- \u6587\u5b57\u5217\u64cd\u4f5c --&gt;\n&lt;p th:text=\"${#strings.toUpperCase(text)}\"&gt;TEXT&lt;\/p&gt;\n\n&lt;!-- \u30ea\u30b9\u30c8\u64cd\u4f5c --&gt;\n&lt;p th:text=\"${#lists.size(items)}\"&gt;\u30a2\u30a4\u30c6\u30e0\u6570&lt;\/p&gt;<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u57fa\u672c\u6a5f\u80fd\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u3067\u3001Thymeleaf\u3092\u4f7f\u7528\u3057\u305f\u52b9\u679c\u7684\u306a\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u958b\u767a\u304c\u53ef\u80fd\u306b\u306a\u308a\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u3088\u308a\u9ad8\u5ea6\u306a\u30c6\u30af\u30cb\u30c3\u30af\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-20\">\u5b9f\u8df5Thymeleaf\u30c6\u30af\u30cb\u30c3\u30af\u96c6<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-21\">\u6761\u4ef6\u5206\u5c90\u3068\u30eb\u30fc\u30d7\u51e6\u7406\u306e\u52b9\u7387\u7684\u306a\u5b9f\u88c5\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-22\">1. \u9ad8\u5ea6\u306a\u6761\u4ef6\u5206\u5c90<\/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;!-- switch\u6587\u306b\u3088\u308b\u8907\u6570\u6761\u4ef6\u306e\u5236\u5fa1 --&gt;\n&lt;div th:switch=\"${user.role}\"&gt;\n    &lt;p th:case=\"'ADMIN'\"&gt;\u7ba1\u7406\u8005\u5411\u3051\u30b3\u30f3\u30c6\u30f3\u30c4&lt;\/p&gt;\n    &lt;p th:case=\"'MANAGER'\"&gt;\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\u5411\u3051\u30b3\u30f3\u30c6\u30f3\u30c4&lt;\/p&gt;\n    &lt;p th:case=\"*\"&gt;\u4e00\u822c\u30e6\u30fc\u30b6\u30fc\u5411\u3051\u30b3\u30f3\u30c6\u30f3\u30c4&lt;\/p&gt;\n&lt;\/div&gt;\n\n&lt;!-- \u8907\u5408\u6761\u4ef6\u306e\u4f7f\u7528 --&gt;\n&lt;div th:if=\"${user.age &gt;= 20 and user.verified}\"&gt;\n    \u6210\u4eba\u6e08\u307f\u8a8d\u8a3c\u30e6\u30fc\u30b6\u30fc\u5411\u3051\u30b3\u30f3\u30c6\u30f3\u30c4\n&lt;\/div&gt;\n\n&lt;!-- Elvis\u6f14\u7b97\u5b50\u306e\u6d3b\u7528 --&gt;\n&lt;span th:text=\"${user.nickname ?: user.fullName}\"&gt;\u30c7\u30d5\u30a9\u30eb\u30c8\u540d&lt;\/span&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-23\">2. \u52b9\u7387\u7684\u306a\u30eb\u30fc\u30d7\u51e6\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;!-- \u30b9\u30c6\u30fc\u30bf\u30b9\u5909\u6570\u306e\u6d3b\u7528 --&gt;\n&lt;tr th:each=\"item, stat : ${items}\"&gt;\n    &lt;td th:text=\"${stat.index + 1}\"&gt;1&lt;\/td&gt;\n    &lt;td th:text=\"${item.name}\"&gt;\u5546\u54c1\u540d&lt;\/td&gt;\n    &lt;td th:class=\"${stat.odd}? 'odd' : 'even'\"&gt;\n        &lt;span th:text=\"${item.price}\"&gt;1000&lt;\/span&gt;\u5186\n    &lt;\/td&gt;\n    &lt;!-- \u6700\u521d\u3068\u6700\u5f8c\u306e\u8981\u7d20\u306e\u7279\u5225\u51e6\u7406 --&gt;\n    &lt;td th:if=\"${stat.first}\"&gt;\u6700\u65b0\u5546\u54c1!&lt;\/td&gt;\n    &lt;td th:if=\"${stat.last}\"&gt;\u6700\u7d42\u5546\u54c1&lt;\/td&gt;\n&lt;\/tr&gt;\n\n&lt;!-- \u30cd\u30b9\u30c8\u3055\u308c\u305f\u30eb\u30fc\u30d7\u306e\u5b9f\u88c5 --&gt;\n&lt;div th:each=\"category : ${categories}\"&gt;\n    &lt;h3 th:text=\"${category.name}\"&gt;\u30ab\u30c6\u30b4\u30ea\u540d&lt;\/h3&gt;\n    &lt;ul&gt;\n        &lt;li th:each=\"product : ${category.products}\"\n            th:text=\"${product.name}\"&gt;\u5546\u54c1\u540d&lt;\/li&gt;\n    &lt;\/ul&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-24\">\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u3092\u6d3b\u7528\u3057\u305f\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u8a2d\u8a08<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-25\">1. \u518d\u5229\u7528\u53ef\u80fd\u306a\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u5b9a\u7fa9<\/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;!-- fragments\/common.html --&gt;\n&lt;!-- \u30d8\u30c3\u30c0\u30fc\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8 --&gt;\n&lt;header th:fragment=\"pageHeader(title)\"&gt;\n    &lt;h1 th:text=\"${title}\"&gt;\u30da\u30fc\u30b8\u30bf\u30a4\u30c8\u30eb&lt;\/h1&gt;\n    &lt;nav th:replace=\"fragments\/navigation :: mainNav\"&gt;\n        &lt;ul&gt;\n            &lt;li&gt;&lt;a href=\"#\"&gt;Home&lt;\/a&gt;&lt;\/li&gt;\n            &lt;li&gt;&lt;a href=\"#\"&gt;About&lt;\/a&gt;&lt;\/li&gt;\n        &lt;\/ul&gt;\n    &lt;\/nav&gt;\n&lt;\/header&gt;\n\n&lt;!-- \u30d5\u30c3\u30bf\u30fc\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\uff08\u30d1\u30e9\u30e1\u30fc\u30bf\u4ed8\u304d\uff09 --&gt;\n&lt;footer th:fragment=\"pageFooter(year)\"&gt;\n    &lt;p th:text=\"'\u00a9 ' + ${year} + ' My Company'\"&gt;\u00a9 2024 My Company&lt;\/p&gt;\n&lt;\/footer&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-26\">2. \u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u9ad8\u5ea6\u306a\u4f7f\u7528\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=\"\">&lt;!-- \u30e1\u30a4\u30f3\u30da\u30fc\u30b8\u3067\u306e\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u4f7f\u7528 --&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"&gt;\n&lt;head&gt;\n    &lt;!-- \u5171\u901aCSS\u3068JS\u306e\u8aad\u307f\u8fbc\u307f --&gt;\n    &lt;th:block th:replace=\"fragments\/common :: headerResources\"&gt;\n        &lt;link rel=\"stylesheet\" href=\"default.css\"&gt;\n        &lt;script src=\"default.js\"&gt;&lt;\/script&gt;\n    &lt;\/th:block&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;!-- \u30d1\u30e9\u30e1\u30fc\u30bf\u4ed8\u304d\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u633f\u5165 --&gt;\n    &lt;div th:replace=\"fragments\/common :: pageHeader('\u3088\u3046\u3053\u305d')\"&gt;\n        \u30d8\u30c3\u30c0\u30fc\u90e8\u5206\n    &lt;\/div&gt;\n\n    &lt;!-- \u6761\u4ef6\u4ed8\u304d\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u633f\u5165 --&gt;\n    &lt;div th:replace=\"fragments\/common :: ${user.premium ? 'premiumContent' : 'basicContent'}\"&gt;\n        \u30b3\u30f3\u30c6\u30f3\u30c4\u90e8\u5206\n    &lt;\/div&gt;\n\n    &lt;!-- \u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u5185\u5bb9\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba --&gt;\n    &lt;div th:replace=\"fragments\/common :: contentBlock(~{::customContent})\"&gt;\n        &lt;div th:fragment=\"customContent\"&gt;\n            &lt;p&gt;\u30ab\u30b9\u30bf\u30e0\u30b3\u30f3\u30c6\u30f3\u30c4&lt;\/p&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-27\">\u30d5\u30a9\u30fc\u30e0\u51e6\u7406\u3068\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u5b9f\u88c5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-28\">1. \u9ad8\u5ea6\u306a\u30d5\u30a9\u30fc\u30e0\u51e6\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=\"\">@Controller\n@RequestMapping(\"\/products\")\npublic class ProductController {\n\n    @GetMapping(\"\/create\")\n    public String showForm(Model model) {\n        ProductForm form = new ProductForm();\n        model.addAttribute(\"productForm\", form);\n        return \"products\/form\";\n    }\n\n    @PostMapping(\"\/create\")\n    public String createProduct(\n            @Valid @ModelAttribute(\"productForm\") ProductForm form,\n            BindingResult result,\n            RedirectAttributes redirectAttributes) {\n\n        if (result.hasErrors()) {\n            return \"products\/form\";\n        }\n\n        \/\/ \u6210\u529f\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u8a2d\u5b9a\n        redirectAttributes.addFlashAttribute(\n            \"message\", \"\u5546\u54c1\u304c\u6b63\u5e38\u306b\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f\u3002\");\n        return \"redirect:\/products\";\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-29\">2. \u9ad8\u5ea6\u306a\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\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=\"\">&lt;!-- products\/form.html --&gt;\n&lt;form th:action=\"@{\/products\/create}\" \n      th:object=\"${productForm}\" \n      method=\"post\"\n      class=\"product-form\"&gt;\n\n    &lt;!-- \u30b0\u30ed\u30fc\u30d0\u30eb\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8 --&gt;\n    &lt;div th:if=\"${#fields.hasGlobalErrors()}\" \n         class=\"alert alert-danger\"&gt;\n        &lt;p th:each=\"err : ${#fields.globalErrors()}\" \n           th:text=\"${err}\"&gt;\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8&lt;\/p&gt;\n    &lt;\/div&gt;\n\n    &lt;!-- \u5546\u54c1\u540d\u5165\u529b\u30d5\u30a3\u30fc\u30eb\u30c9 --&gt;\n    &lt;div class=\"form-group\" \n         th:classappend=\"${#fields.hasErrors('name')}? 'has-error'\"&gt;\n        &lt;label th:for=\"name\"&gt;\u5546\u54c1\u540d&lt;\/label&gt;\n        &lt;input type=\"text\" \n               th:field=\"*{name}\"\n               class=\"form-control\"\n               th:errorclass=\"is-invalid\"&gt;\n        &lt;div class=\"invalid-feedback\" \n             th:if=\"${#fields.hasErrors('name')}\"\n             th:errors=\"*{name}\"&gt;\n            \u5546\u54c1\u540d\u30a8\u30e9\u30fc\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n\n    &lt;!-- \u4fa1\u683c\u5165\u529b\u30d5\u30a3\u30fc\u30eb\u30c9\uff08\u30ab\u30b9\u30bf\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\uff09 --&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label th:for=\"price\"&gt;\u4fa1\u683c&lt;\/label&gt;\n        &lt;input type=\"number\" \n               th:field=\"*{price}\"\n               class=\"form-control\"\n               th:errorclass=\"is-invalid\"\n               min=\"0\"&gt;\n        &lt;div class=\"invalid-feedback\" \n             th:if=\"${#fields.hasErrors('price')}\"\n             th:errors=\"*{price}\"&gt;\n            \u4fa1\u683c\u30a8\u30e9\u30fc\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n\n    &lt;!-- \u52d5\u7684\u306a\u30ab\u30c6\u30b4\u30ea\u30fc\u9078\u629e --&gt;\n    &lt;div class=\"form-group\"&gt;\n        &lt;label&gt;\u30ab\u30c6\u30b4\u30ea\u30fc&lt;\/label&gt;\n        &lt;select th:field=\"*{categoryId}\" class=\"form-control\"&gt;\n            &lt;option value=\"\"&gt;\u30ab\u30c6\u30b4\u30ea\u30fc\u3092\u9078\u629e&lt;\/option&gt;\n            &lt;option th:each=\"category : ${categories}\"\n                    th:value=\"${category.id}\"\n                    th:text=\"${category.name}\"&gt;\n                \u30ab\u30c6\u30b4\u30ea\u30fc\u540d\n            &lt;\/option&gt;\n        &lt;\/select&gt;\n    &lt;\/div&gt;\n\n    &lt;!-- \u9001\u4fe1\u30dc\u30bf\u30f3 --&gt;\n    &lt;button type=\"submit\" \n            class=\"btn btn-primary\"\n            th:disabled=\"${#fields.hasErrors()}\"&gt;\n        \u767b\u9332\n    &lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u8df5\u7684\u306a\u30c6\u30af\u30cb\u30c3\u30af\u3092\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u4fdd\u5b88\u6027\u304c\u9ad8\u304f\u3001\u518d\u5229\u7528\u53ef\u80fd\u306aThymeleaf\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f5c\u6210\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u306b\u3064\u3044\u3066\u8a73\u3057\u304f\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-30\">Thymeleaf\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-31\">XSS\u5bfe\u7b56\u653b\u6483\u3068\u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406\u306e\u91cd\u8981\u6027<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-32\">1. XSS\u653b\u6483\u306e\u30ea\u30b9\u30af<\/h3>\n\n\n\n<p>Thymeleaf\u306f\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u3067HTML\u30a8\u30b9\u30b1\u30fc\u30d7\u3092\u63d0\u4f9b\u3057\u3066\u3044\u307e\u3059\u304c\u3001\u9069\u5207\u306a\u4f7f\u7528\u65b9\u6cd5\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002<\/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;!-- \u60aa\u610f\u306e\u3042\u308b\u30b9\u30af\u30ea\u30d7\u30c8\u304c\u542b\u307e\u308c\u308b\u53ef\u80fd\u6027\u306e\u3042\u308b\u5909\u6570 --&gt;\n&lt;!-- NG: \u30a8\u30b9\u30b1\u30fc\u30d7\u306a\u3057\u3067\u306e\u51fa\u529b --&gt;\n&lt;div th:utext=\"${userInput}\"&gt;\u5371\u967a\u306a\u51fa\u529b&lt;\/div&gt;\n\n&lt;!-- OK: \u9069\u5207\u306a\u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406 --&gt;\n&lt;div th:text=\"${userInput}\"&gt;\u5b89\u5168\u306a\u51fa\u529b&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-33\">2. \u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406\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=\"\">@Controller\npublic class ContentController {\n\n    @GetMapping(\"\/content\")\n    public String showContent(Model model) {\n        \/\/ HTML\u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406\u306e\u5b9f\u88c5\u4f8b\n        String userInput = \"&lt;script&gt;alert('\u5371\u967a!')&lt;\/script&gt;\";\n        String escapedContent = HtmlUtils.htmlEscape(userInput);\n        model.addAttribute(\"content\", escapedContent);\n\n        return \"content\/view\";\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-34\">3. \u30bb\u30ad\u30e5\u30a2\u306a\u51fa\u529b\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=\"\">&lt;!-- 1. \u5909\u6570\u51fa\u529b\u6642\u306e\u9069\u5207\u306a\u30a8\u30b9\u30b1\u30fc\u30d7 --&gt;\n&lt;div&gt;\n    &lt;!-- \u901a\u5e38\u306e\u30c6\u30ad\u30b9\u30c8\u51fa\u529b\uff08\u81ea\u52d5\u30a8\u30b9\u30b1\u30fc\u30d7\uff09 --&gt;\n    &lt;p th:text=\"${content}\"&gt;\u30b3\u30f3\u30c6\u30f3\u30c4&lt;\/p&gt;\n\n    &lt;!-- JavaScript\u5185\u3067\u306e\u5b89\u5168\u306a\u51fa\u529b --&gt;\n    &lt;script th:inline=\"javascript\"&gt;\n        const userContent = \/*[[${content}]]*\/ '\u30c7\u30d5\u30a9\u30eb\u30c8\u5024';\n    &lt;\/script&gt;\n&lt;\/div&gt;\n\n&lt;!-- 2. URL\u751f\u6210\u6642\u306e\u5b89\u5168\u306a\u51e6\u7406 --&gt;\n&lt;a th:href=\"@{\/user\/{id}(id=${userId})}\"&gt;\u30e6\u30fc\u30b6\u30fc\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb&lt;\/a&gt;\n\n&lt;!-- 3. \u6761\u4ef6\u4ed8\u304d\u30b3\u30f3\u30c6\u30f3\u30c4\u8868\u793a --&gt;\n&lt;div th:remove=\"${isUnsafe} ? 'all' : 'none'\"&gt;\n    \u4fdd\u8b77\u3055\u308c\u305f\u30b3\u30f3\u30c6\u30f3\u30c4\n&lt;\/div&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-35\">CSRF\u5bfe\u7b56\u306e\u5b9f\u88c5\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-36\">1. Spring Security\u3068\u306e\u9023\u643a<\/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=\"\">@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http\n            .csrf()\n                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())\n            .and()\n            .authorizeRequests()\n                .antMatchers(\"\/public\/**\").permitAll()\n                .anyRequest().authenticated();\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-37\">2. \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3067\u306eCSRF\u30c8\u30fc\u30af\u30f3\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=\"\">&lt;!-- \u30d5\u30a9\u30fc\u30e0\u3067\u306eCSRF\u30c8\u30fc\u30af\u30f3\u5b9f\u88c5 --&gt;\n&lt;form th:action=\"@{\/user\/update}\" method=\"post\"&gt;\n    &lt;!-- CSRF\u30c8\u30fc\u30af\u30f3\u306e\u81ea\u52d5\u8ffd\u52a0 --&gt;\n    &lt;input type=\"hidden\" th:name=\"${_csrf.parameterName}\" th:value=\"${_csrf.token}\" \/&gt;\n\n    &lt;input type=\"text\" name=\"username\" \/&gt;\n    &lt;button type=\"submit\"&gt;\u66f4\u65b0&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;!-- Ajax\u547c\u3073\u51fa\u3057\u3067\u306eCSRF\u5bfe\u7b56 --&gt;\n&lt;script th:inline=\"javascript\"&gt;\n    const csrfToken = \/*[[${_csrf.token}]]*\/ '';\n    const csrfHeader = \/*[[${_csrf.headerName}]]*\/ '';\n\n    \/\/ Ajax\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u8a2d\u5b9a\n    axios.defaults.headers.common[csrfHeader] = csrfToken;\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-38\">3. \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d8\u30c3\u30c0\u30fc\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=\"\">@Configuration\npublic class SecurityHeaderConfig {\n\n    @Bean\n    public WebMvcConfigurer webMvcConfigurer() {\n        return new WebMvcConfigurer() {\n            @Override\n            public void addInterceptors(InterceptorRegistry registry) {\n                registry.addInterceptor(new HandlerInterceptor() {\n                    @Override\n                    public boolean preHandle(HttpServletRequest request, \n                                          HttpServletResponse response, \n                                          Object handler) {\n                        \/\/ \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d8\u30c3\u30c0\u30fc\u306e\u8a2d\u5b9a\n                        response.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n                        response.setHeader(\"X-Frame-Options\", \"DENY\");\n                        response.setHeader(\"X-XSS-Protection\", \"1; mode=block\");\n                        response.setHeader(\"Content-Security-Policy\", \n                            \"default-src 'self'; script-src 'self' 'unsafe-inline'\");\n                        return true;\n                    }\n                });\n            }\n        };\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-39\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5165\u529b\u5024\u306e\u691c\u8a3c<\/li>\n<\/ol>\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=\"\">@RestController\npublic class InputValidationExample {\n\n    @PostMapping(\"\/api\/data\")\n    public ResponseEntity&lt;?&gt; processInput(\n            @RequestBody @Valid UserInput input, \n            BindingResult result) {\n\n        if (result.hasErrors()) {\n            return ResponseEntity.badRequest()\n                .body(result.getAllErrors());\n        }\n\n        \/\/ \u5165\u529b\u5024\u306e\u8ffd\u52a0\u691c\u8a3c\n        if (!InputValidator.isValidInput(input.getContent())) {\n            return ResponseEntity.badRequest()\n                .body(\"Invalid input format\");\n        }\n\n        \/\/ \u5b89\u5168\u306a\u51e6\u7406\u306e\u5b9f\u884c\n        return ResponseEntity.ok()\n            .body(\"\u51e6\u7406\u6210\u529f\");\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>\u51fa\u529b\u306e\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0<\/li>\n<\/ol>\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;!-- \u7279\u6b8a\u6587\u5b57\u3092\u542b\u3080\u53ef\u80fd\u6027\u306e\u3042\u308b\u30c7\u30fc\u30bf\u306e\u51fa\u529b --&gt;\n&lt;table&gt;\n    &lt;tr th:each=\"item : ${items}\"&gt;\n        &lt;!-- HTML\u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406 --&gt;\n        &lt;td th:text=\"${item.name}\"&gt;\u5546\u54c1\u540d&lt;\/td&gt;\n\n        &lt;!-- URL\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0 --&gt;\n        &lt;td&gt;\n            &lt;a th:href=\"@{\/items\/{id}(id=${#uris.escapePathSegment(item.id)})}\"&gt;\n                \u8a73\u7d30\n            &lt;\/a&gt;\n        &lt;\/td&gt;\n\n        &lt;!-- JavaScript\u7528\u30a8\u30b9\u30b1\u30fc\u30d7 --&gt;\n        &lt;td th:onclick=\"|showDetails('${#strings.escapeJavaScript(item.description)}')|\"&gt;\n            \u8aac\u660e\u3092\u8868\u793a\n        &lt;\/td&gt;\n    &lt;\/tr&gt;\n&lt;\/table&gt;<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3092\u9069\u5207\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001\u5b89\u5168\u306aWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u958b\u767a\u304c\u53ef\u80fd\u306b\u306a\u308a\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-40\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-41\">\u30ad\u30e3\u30c3\u30b7\u30e5\u6a5f\u80fd\u306e\u52b9\u679c\u7684\u306a\u6d3b\u7528\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-42\">1. \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ad\u30e3\u30c3\u30b7\u30e5\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=\"\"># application.properties\n# \u30d7\u30ed\u30c0\u30af\u30b7\u30e7\u30f3\u74b0\u5883\u3067\u306e\u8a2d\u5b9a\nspring.thymeleaf.cache=true\nspring.thymeleaf.cache-period=3600\nspring.thymeleaf.cache-ttl=3600\n\n# \u958b\u767a\u74b0\u5883\u3067\u306e\u8a2d\u5b9a\nspring.thymeleaf.cache=false<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-43\">2. \u30ab\u30b9\u30bf\u30e0\u30ad\u30e3\u30c3\u30b7\u30e5\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=\"\">@Configuration\npublic class ThymeleafConfig {\n\n    @Bean\n    public SpringResourceTemplateResolver templateResolver() {\n        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();\n        resolver.setPrefix(\"classpath:\/templates\/\");\n        resolver.setSuffix(\".html\");\n        resolver.setTemplateMode(TemplateMode.HTML);\n\n        \/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u8a2d\u5b9a\n        resolver.setCacheable(true);\n        resolver.setCacheTTLMs(3600000L); \/\/ 1\u6642\u9593\n\n        \/\/ \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u89e3\u6c7a\u306e\u6700\u9069\u5316\n        resolver.setCharacterEncoding(\"UTF-8\");\n        resolver.setCheckExistence(true);\n\n        return resolver;\n    }\n\n    @Bean\n    public ISpringTemplateEngine templateEngine() {\n        SpringTemplateEngine engine = new SpringTemplateEngine();\n        engine.setTemplateResolver(templateResolver());\n\n        \/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u30b5\u30a4\u30ba\u306e\u8a2d\u5b9a\n        engine.setCacheManager(new StandardCacheManager() {\n            @Override\n            protected void initializeCaches() {\n                super.initializeCaches();\n                getTemplateCache().setMaxSize(200);\n            }\n        });\n\n        return engine;\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-44\">\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u51e6\u7406\u306e\u9ad8\u901f\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-45\">1. \u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u6700\u9069\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=\"\">&lt;!-- \u52b9\u7387\u7684\u306a\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u5b9a\u7fa9 --&gt;\n&lt;!-- fragments\/common.html --&gt;\n&lt;div th:fragment=\"userInfo(user)\" th:remove=\"tag\"&gt;\n    &lt;span th:text=\"${user.name}\"&gt;\u30e6\u30fc\u30b6\u30fc\u540d&lt;\/span&gt;\n    &lt;span th:text=\"${user.email}\"&gt;\u30e1\u30fc\u30eb&lt;\/span&gt;\n&lt;\/div&gt;\n\n&lt;!-- \u30e1\u30a4\u30f3\u30da\u30fc\u30b8\u3067\u306e\u52b9\u7387\u7684\u306a\u4f7f\u7528 --&gt;\n&lt;div&gt;\n    &lt;!-- \u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u30a4\u30f3\u30e9\u30a4\u30f3\u5316 --&gt;\n    &lt;th:block th:replace=\"fragments\/common :: userInfo(${currentUser})\"\/&gt;\n\n    &lt;!-- \u8907\u6570\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u4e00\u62ec\u8aad\u307f\u8fbc\u307f --&gt;\n    &lt;th:block th:replace=\"fragments\/common :: common-resources\"\/&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-46\">2. \u30c7\u30fc\u30bf\u51e6\u7406\u306e\u6700\u9069\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=\"\">@Controller\npublic class OptimizedController {\n\n    @GetMapping(\"\/users\")\n    public String listUsers(Model model) {\n        \/\/ \u30c7\u30fc\u30bf\u306e\u4e8b\u524d\u51e6\u7406\u3068\u30ad\u30e3\u30c3\u30b7\u30e5\n        List&lt;UserDTO&gt; users = userService.getCachedUsers();\n\n        \/\/ \u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u306e\u6700\u9069\u5316\n        Page&lt;UserDTO&gt; userPage = new PageImpl&lt;&gt;(users, \n            PageRequest.of(0, 10), users.size());\n\n        model.addAttribute(\"userPage\", userPage);\n        return \"users\/list\";\n    }\n}\n\n\/\/ \u30d3\u30e5\u30fc\u3067\u306e\u52b9\u7387\u7684\u306a\u30c7\u30fc\u30bf\u8868\u793a\n&lt;table&gt;\n    &lt;tr th:each=\"user, stat : ${userPage.content}\" \n        th:if=\"${stat.index &lt; 10}\"&gt;\n        &lt;td th:text=\"${user.name}\"&gt;\u540d\u524d&lt;\/td&gt;\n    &lt;\/tr&gt;\n&lt;\/table&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-47\">3. \u30ea\u30bd\u30fc\u30b9\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/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;!-- \u30ea\u30bd\u30fc\u30b9\u306e\u6700\u9069\u5316 --&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"&gt;\n&lt;head&gt;\n    &lt;!-- CSS\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f --&gt;\n    &lt;link rel=\"preload\" \n          th:href=\"@{\/css\/styles.css}\" \n          as=\"style\" \n          onload=\"this.onload=null;this.rel='stylesheet'\"&gt;\n\n    &lt;!-- \u91cd\u8981\u306a\u30b9\u30af\u30ea\u30d7\u30c8\u306e\u5373\u6642\u8aad\u307f\u8fbc\u307f --&gt;\n    &lt;script th:src=\"@{\/js\/critical.js}\" \n            defer&gt;&lt;\/script&gt;\n\n    &lt;!-- \u975e\u91cd\u8981\u306a\u30b9\u30af\u30ea\u30d7\u30c8\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f --&gt;\n    &lt;script th:src=\"@{\/js\/non-critical.js}\" \n            async&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;!-- \u753b\u50cf\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f --&gt;\n    &lt;img th:src=\"@{\/images\/large-image.jpg}\" \n         loading=\"lazy\" \n         alt=\"\u9045\u5ef6\u8aad\u307f\u8fbc\u307f\u753b\u50cf\"&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-48\">4. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u76e3\u8996\u3068\u6e2c\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=\"\">@Configuration\npublic class PerformanceMonitorConfig {\n\n    @Bean\n    public FilterRegistrationBean&lt;PerformanceMonitorFilter&gt; performanceMonitorFilter() {\n        FilterRegistrationBean&lt;PerformanceMonitorFilter&gt; registrationBean \n            = new FilterRegistrationBean&lt;&gt;();\n\n        registrationBean.setFilter(new PerformanceMonitorFilter());\n        registrationBean.addUrlPatterns(\"\/*\");\n        return registrationBean;\n    }\n}\n\n@Component\npublic class PerformanceMonitorFilter implements Filter {\n\n    private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorFilter.class);\n\n    @Override\n    public void doFilter(ServletRequest request, \n                        ServletResponse response, \n                        FilterChain chain) throws IOException, ServletException {\n        long startTime = System.currentTimeMillis();\n\n        try {\n            chain.doFilter(request, response);\n        } finally {\n            long endTime = System.currentTimeMillis();\n            long processingTime = endTime - startTime;\n\n            if (processingTime &gt; 1000) { \/\/ 1\u79d2\u4ee5\u4e0a\u304b\u304b\u3063\u305f\u51e6\u7406\u3092\u8a18\u9332\n                logger.warn(\"Slow processing detected: {} ms for URI: {}\", \n                    processingTime, \n                    ((HttpServletRequest) request).getRequestURI());\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-49\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u6700\u9069\u5316<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u306e\u9069\u5207\u306a\u4f7f\u7528<\/li>\n\n\n\n<li>\u4e0d\u8981\u306a\u30cd\u30b9\u30c8\u306e\u524a\u9664<\/li>\n\n\n\n<li>\u6761\u4ef6\u5206\u5c90\u306e\u6700\u9069\u5316<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30ea\u30bd\u30fc\u30b9\u7ba1\u7406<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9759\u7684\u30ea\u30bd\u30fc\u30b9\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u30d8\u30c3\u30c0\u30fc\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30ea\u30bd\u30fc\u30b9\u306e\u5727\u7e2e<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u30a2\u30af\u30bb\u30b9\u306e\u6700\u9069\u5316<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>N+1\u554f\u984c\u306e\u56de\u907f<\/li>\n\n\n\n<li>\u5fc5\u8981\u306a\u30c7\u30fc\u30bf\u306e\u307f\u306e\u53d6\u5f97<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af\u3092\u9069\u5207\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001Thymeleaf\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u5927\u5e45\u306b\u6539\u5584\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5b9f\u8df5\u7684\u306a\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-50\">\u5b9f\u8df5\u7684\u306a\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u3068\u30b5\u30f3\u30d7\u30eb\u30b3\u30fc\u30c9<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-51\">\u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u306a\u30ec\u30a4\u30a2\u30a6\u30c8\u306e\u5b9f\u88c5\u4f8b<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-52\">1. \u30e2\u30d0\u30a4\u30eb\u30d5\u30a1\u30fc\u30b9\u30c8\u306e\u30ec\u30a4\u30a2\u30a6\u30c8\u8a2d\u8a08<\/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;!DOCTYPE html&gt;\n&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"&gt;\n&lt;head&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"&gt;\n    &lt;title&gt;\u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u30c0\u30c3\u30b7\u30e5\u30dc\u30fc\u30c9&lt;\/title&gt;\n\n    &lt;style th:inline=\"css\"&gt;\n        \/* \u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u30b0\u30ea\u30c3\u30c9\u306e\u5b9a\u7fa9 *\/\n        .grid-container {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n            gap: 1rem;\n            padding: 1rem;\n        }\n\n        \/* \u30ab\u30fc\u30c9\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8 *\/\n        .card {\n            background: #fff;\n            border-radius: 8px;\n            padding: 1rem;\n            box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n        }\n\n        \/* \u30e1\u30c7\u30a3\u30a2\u30af\u30a8\u30ea *\/\n        @media (max-width: 768px) {\n            .grid-container {\n                grid-template-columns: 1fr;\n            }\n        }\n    &lt;\/style&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;!-- \u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u306a\u30c0\u30c3\u30b7\u30e5\u30dc\u30fc\u30c9\u5b9f\u88c5 --&gt;\n    &lt;div class=\"grid-container\"&gt;\n        &lt;!-- \u7d71\u8a08\u30ab\u30fc\u30c9 --&gt;\n        &lt;div class=\"card\" th:each=\"stat : ${statistics}\"&gt;\n            &lt;h3 th:text=\"${stat.title}\"&gt;\u7d71\u8a08\u30bf\u30a4\u30c8\u30eb&lt;\/h3&gt;\n            &lt;p th:text=\"${stat.value}\"&gt;\u5024&lt;\/p&gt;\n            &lt;div th:replace=\"fragments\/charts :: ${stat.chartType}(${stat.data})\"&gt;\n                \u30c1\u30e3\u30fc\u30c8\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n\n    &lt;!-- \u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u306a\u30ca\u30d3\u30b2\u30fc\u30b7\u30e7\u30f3 --&gt;\n    &lt;nav&gt;\n        &lt;button class=\"menu-toggle\" th:onclick=\"'toggleMenu()'\"&gt;\n            \u30e1\u30cb\u30e5\u30fc\n        &lt;\/button&gt;\n        &lt;ul class=\"nav-items\" th:classappend=\"${isMobile} ? 'mobile' : ''\"&gt;\n            &lt;li th:each=\"item : ${menuItems}\"&gt;\n                &lt;a th:href=\"@{${item.url}}\" th:text=\"${item.name}\"&gt;\n                    \u30e1\u30cb\u30e5\u30fc\u9805\u76ee\n                &lt;\/a&gt;\n            &lt;\/li&gt;\n        &lt;\/ul&gt;\n    &lt;\/nav&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-53\">2. \u30c7\u30fc\u30bf\u30c6\u30fc\u30d6\u30eb\u306e\u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u5bfe\u5fdc<\/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\u30b9\u30dd\u30f3\u30b7\u30d6\u30c6\u30fc\u30d6\u30eb\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8 --&gt;\n&lt;div class=\"table-container\"&gt;\n    &lt;table class=\"responsive-table\"&gt;\n        &lt;thead&gt;\n            &lt;tr&gt;\n                &lt;th th:each=\"header : ${headers}\" \n                    th:text=\"${header}\"&gt;\u30d8\u30c3\u30c0\u30fc&lt;\/th&gt;\n            &lt;\/tr&gt;\n        &lt;\/thead&gt;\n        &lt;tbody&gt;\n            &lt;tr th:each=\"row : ${data}\"&gt;\n                &lt;td th:each=\"cell, stat : ${row}\"\n                    th:data-label=\"${headers[stat.index]}\"\n                    th:text=\"${cell}\"&gt;\n                    \u30bb\u30eb\u30c7\u30fc\u30bf\n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n        &lt;\/tbody&gt;\n    &lt;\/table&gt;\n&lt;\/div&gt;\n\n&lt;style&gt;\n@media (max-width: 768px) {\n    .responsive-table td {\n        display: block;\n    }\n    .responsive-table td::before {\n        content: attr(data-label);\n        font-weight: bold;\n    }\n}\n&lt;\/style&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-54\">REST API\u3068\u306e\u9023\u643a\u30d1\u30bf\u30fc\u30f3<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-55\">1. API\u547c\u3073\u51fa\u3057\u3068\u7d50\u679c\u8868\u793a<\/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=\"\">@Controller\n@RequestMapping(\"\/api-demo\")\npublic class ApiDemoController {\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"\/users\")\n    public String getUserData(Model model) {\n        \/\/ API\u547c\u3073\u51fa\u3057\n        ResponseEntity&lt;List&lt;UserDTO&gt;&gt; response = restTemplate.exchange(\n            \"https:\/\/api.example.com\/users\",\n            HttpMethod.GET,\n            null,\n            new ParameterizedTypeReference&lt;List&lt;UserDTO&gt;&gt;() {}\n        );\n\n        model.addAttribute(\"users\", response.getBody());\n        return \"users\/list\";\n    }\n}<\/pre>\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;!-- users\/list.html --&gt;\n&lt;div class=\"user-grid\"&gt;\n    &lt;!-- API\u30c7\u30fc\u30bf\u306e\u8868\u793a --&gt;\n    &lt;div th:each=\"user : ${users}\" class=\"user-card\"&gt;\n        &lt;img th:src=\"${user.avatarUrl}\" alt=\"\u30e6\u30fc\u30b6\u30fc\u30a2\u30d0\u30bf\u30fc\"&gt;\n        &lt;h3 th:text=\"${user.name}\"&gt;\u30e6\u30fc\u30b6\u30fc\u540d&lt;\/h3&gt;\n        &lt;p th:text=\"${user.email}\"&gt;\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9&lt;\/p&gt;\n\n        &lt;!-- \u975e\u540c\u671f\u30a2\u30af\u30b7\u30e7\u30f3 --&gt;\n        &lt;button th:onclick=\"'loadUserDetails(' + ${user.id} + ')'\"&gt;\n            \u8a73\u7d30\u3092\u8868\u793a\n        &lt;\/button&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;!-- \u975e\u540c\u671f\u30c7\u30fc\u30bf\u8aad\u307f\u8fbc\u307f\u306e\u30b9\u30af\u30ea\u30d7\u30c8 --&gt;\n&lt;script th:inline=\"javascript\"&gt;\nasync function loadUserDetails(userId) {\n    try {\n        const response = await fetch(`\/api\/users\/${userId}`);\n        const data = await response.json();\n        updateUserDetails(data);\n    } catch (error) {\n        console.error('Error:', error);\n    }\n}\n&lt;\/script&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-56\">\u975e\u540c\u671f\u51e6\u7406\u306e\u5b9f\u88c5\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-57\">1. WebSocket\u3092\u4f7f\u7528\u3057\u305f\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u66f4\u65b0<\/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=\"\">@Configuration\n@EnableWebSocket\npublic class WebSocketConfig implements WebSocketConfigurer {\n\n    @Override\n    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {\n        registry.addHandler(new RealTimeUpdateHandler(), \"\/websocket\")\n               .setAllowedOrigins(\"*\");\n    }\n}\n\n@Component\npublic class RealTimeUpdateHandler extends TextWebSocketHandler {\n\n    @Override\n    protected void handleTextMessage(WebSocketSession session, \n                                   TextMessage message) throws Exception {\n        \/\/ \u30e1\u30c3\u30bb\u30fc\u30b8\u51e6\u7406\u30ed\u30b8\u30c3\u30af\n        String payload = message.getPayload();\n        \/\/ \u51e6\u7406\u7d50\u679c\u3092\u9001\u4fe1\n        session.sendMessage(new TextMessage(\"\u66f4\u65b0\u5b8c\u4e86: \" + payload));\n    }\n}<\/pre>\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;!-- \u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u66f4\u65b0UI --&gt;\n&lt;div id=\"real-time-updates\"&gt;\n    &lt;div th:each=\"update : ${updates}\" \n         th:id=\"'update-' + ${update.id}\"\n         class=\"update-item\"&gt;\n        &lt;span th:text=\"${update.content}\"&gt;\u66f4\u65b0\u5185\u5bb9&lt;\/span&gt;\n        &lt;span th:text=\"${update.timestamp}\" \n              class=\"timestamp\"&gt;\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7&lt;\/span&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;script th:inline=\"javascript\"&gt;\n    const socket = new WebSocket('ws:\/\/localhost:8080\/websocket');\n\n    socket.onmessage = function(event) {\n        const update = JSON.parse(event.data);\n        addUpdateToUI(update);\n    };\n\n    function addUpdateToUI(update) {\n        const container = document.getElementById('real-time-updates');\n        const updateElement = document.createElement('div');\n        updateElement.className = 'update-item';\n        updateElement.innerHTML = `\n            &lt;span&gt;${update.content}&lt;\/span&gt;\n            &lt;span class=\"timestamp\"&gt;${update.timestamp}&lt;\/span&gt;\n        `;\n        container.prepend(updateElement);\n    }\n&lt;\/script&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-58\">2. \u975e\u540c\u671f\u30bf\u30b9\u30af\u51e6\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=\"\">@Controller\npublic class AsyncTaskController {\n\n    @Autowired\n    private AsyncTaskService asyncTaskService;\n\n    @PostMapping(\"\/process\")\n    public String startProcess(@RequestParam(\"taskId\") String taskId, \n                             RedirectAttributes attributes) {\n        \/\/ \u975e\u540c\u671f\u30bf\u30b9\u30af\u306e\u958b\u59cb\n        CompletableFuture&lt;ProcessResult&gt; future = \n            asyncTaskService.processTask(taskId);\n\n        attributes.addFlashAttribute(\"taskId\", taskId);\n        return \"redirect:\/task\/status\";\n    }\n\n    @GetMapping(\"\/task\/status\")\n    public String checkStatus(@ModelAttribute(\"taskId\") String taskId, \n                            Model model) {\n        model.addAttribute(\"taskId\", taskId);\n        return \"task\/status\";\n    }\n}<\/pre>\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;!-- task\/status.html --&gt;\n&lt;div class=\"task-status-container\"&gt;\n    &lt;h2&gt;\u30bf\u30b9\u30af\u72b6\u614b\u76e3\u8996&lt;\/h2&gt;\n\n    &lt;div id=\"status-display\"\n         th:data-task-id=\"${taskId}\"\n         class=\"status-panel\"&gt;\n        &lt;div class=\"progress-bar\"&gt;\n            &lt;div class=\"progress\" \n                 style=\"width: 0%\"\n                 id=\"task-progress\"&gt;&lt;\/div&gt;\n        &lt;\/div&gt;\n        &lt;p id=\"status-message\"&gt;\u51e6\u7406\u4e2d...&lt;\/p&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;script th:inline=\"javascript\"&gt;\n    const taskId = \/*[[${taskId}]]*\/ '';\n\n    async function pollTaskStatus() {\n        try {\n            const response = await fetch(`\/api\/task\/${taskId}\/status`);\n            const status = await response.json();\n\n            updateProgressUI(status);\n\n            if (!status.completed) {\n                setTimeout(pollTaskStatus, 1000);\n            }\n        } catch (error) {\n            console.error('Error:', error);\n        }\n    }\n\n    function updateProgressUI(status) {\n        document.getElementById('task-progress').style.width = \n            `${status.progress}%`;\n        document.getElementById('status-message').textContent = \n            status.message;\n    }\n\n    \/\/ \u30dd\u30fc\u30ea\u30f3\u30b0\u958b\u59cb\n    pollTaskStatus();\n&lt;\/script&gt;<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u4f8b\u306f\u3001Thymeleaf\u3092\u4f7f\u7528\u3057\u305f\u73fe\u4ee3\u7684\u306aWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u306e\u5b9f\u8df5\u7684\u306a\u30a2\u30d7\u30ed\u30fc\u30c1\u3092\u793a\u3057\u3066\u3044\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-59\">Thymeleaf\u306e\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-60\">\u3088\u304f\u3042\u308b\u30a8\u30e9\u30fc\u3068\u305d\u306e\u89e3\u6c7a\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-61\">1. \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u89e3\u6c7a\u30a8\u30e9\u30fc<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-62\">Template might not exist or might not be accessible<\/h4>\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\u306e\u4f8b\norg.thymeleaf.exceptions.TemplateInputException: Error resolving template \"users\/list\"\n\n\/\/ \u89e3\u6c7a\u7b561\uff1a\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30d1\u30b9\u306e\u8a2d\u5b9a\u78ba\u8a8d\n@Configuration\npublic class ThymeleafConfig {\n    @Bean\n    public SpringResourceTemplateResolver templateResolver() {\n        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();\n        resolver.setPrefix(\"classpath:\/templates\/\");  \/\/ \u30d1\u30b9\u306e\u78ba\u8a8d\n        resolver.setSuffix(\".html\");                 \/\/ \u62e1\u5f35\u5b50\u306e\u78ba\u8a8d\n        resolver.setTemplateMode(TemplateMode.HTML);\n        return resolver;\n    }\n}\n\n\/\/ \u89e3\u6c7a\u7b562\uff1a\u30d5\u30a1\u30a4\u30eb\u914d\u7f6e\u306e\u78ba\u8a8d\n\/\/ src\/main\/resources\/templates\/users\/list.html \u306e\u5b58\u5728\u78ba\u8a8d<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-63\">2. \u5909\u6570\u30a2\u30af\u30bb\u30b9\u30a8\u30e9\u30fc<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-64\">Property or field not found<\/h4>\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;!-- \u30a8\u30e9\u30fc\u306e\u4f8b --&gt;\norg.thymeleaf.exceptions.TemplateProcessingException: \nException evaluating SpringEL expression: \"user.firstName\"\n\n&lt;!-- \u89e3\u6c7a\u7b561\uff1anull\u30c1\u30a7\u30c3\u30af\u306e\u8ffd\u52a0 --&gt;\n&lt;span th:if=\"${user != null}\" th:text=\"${user.firstName}\"&gt;\u540d\u524d&lt;\/span&gt;\n\n&lt;!-- \u89e3\u6c7a\u7b562\uff1a\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u306e\u8a2d\u5b9a --&gt;\n&lt;span th:text=\"${user?.firstName ?: '\u672a\u8a2d\u5b9a'}\"&gt;\u540d\u524d&lt;\/span&gt;\n\n&lt;!-- \u89e3\u6c7a\u7b563\uff1a\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u69cb\u9020\u306e\u78ba\u8a8d --&gt;\n&lt;div th:object=\"${user}\"&gt;\n    &lt;span th:text=\"*{firstName}\"&gt;\u540d\u524d&lt;\/span&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-65\">3. \u5f0f\u306e\u69cb\u6587\u30a8\u30e9\u30fc<\/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;!-- \u30a8\u30e9\u30fc\u306e\u4f8b --&gt;\n&lt;!-- \u8aa4\u3063\u305f\u69cb\u6587 --&gt;\n&lt;div th:if=\"user.isAdmin\"&gt;\u7ba1\u7406\u8005\u30e1\u30cb\u30e5\u30fc&lt;\/div&gt;\n\n&lt;!-- \u6b63\u3057\u3044\u69cb\u6587 --&gt;\n&lt;div th:if=\"${user.isAdmin()}\"&gt;\u7ba1\u7406\u8005\u30e1\u30cb\u30e5\u30fc&lt;\/div&gt;\n&lt;div th:if=\"${user.admin}\"&gt;\u7ba1\u7406\u8005\u30e1\u30cb\u30e5\u30fc&lt;\/div&gt;\n\n&lt;!-- \u8907\u96d1\u306a\u6761\u4ef6\u5f0f\u306e\u5834\u5408 --&gt;\n&lt;div th:if=\"${user.role == 'ADMIN' and user.enabled}\"&gt;\n    \u7ba1\u7406\u8005\u30e1\u30cb\u30e5\u30fc\n&lt;\/div&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-66\">\u30c7\u30d0\u30c3\u30b0\u3068\u30ed\u30b0\u51fa\u529b\u306e\u30c6\u30af\u30cb\u30c3\u30af<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-67\">1. \u30c7\u30d0\u30c3\u30b0\u30e2\u30fc\u30c9\u306e\u6d3b\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=\"\"># application.properties\n# \u30c7\u30d0\u30c3\u30b0\u30e2\u30fc\u30c9\u306e\u6709\u52b9\u5316\nspring.thymeleaf.cache=false\nlogging.level.org.thymeleaf=DEBUG\nlogging.level.org.springframework.web=DEBUG<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-68\">2. \u30a8\u30e9\u30fc\u30c8\u30ec\u30fc\u30b9\u8868\u793a\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=\"\">@Configuration\npublic class ThymeleafDebugConfig {\n\n    @Bean\n    public ThymeleafViewResolver viewResolver() {\n        ThymeleafViewResolver resolver = new ThymeleafViewResolver();\n        resolver.setTemplateEngine(templateEngine());\n        resolver.setCharacterEncoding(\"UTF-8\");\n\n        \/\/ \u30c7\u30d0\u30c3\u30b0\u60c5\u5831\u306e\u8868\u793a\u8a2d\u5b9a\n        Map&lt;String, Object&gt; variables = new HashMap&lt;&gt;();\n        variables.put(\"debug\", true);  \/\/ \u30c7\u30d0\u30c3\u30b0\u30d5\u30e9\u30b0\n        resolver.setStaticVariables(variables);\n\n        return resolver;\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-69\">3. \u30ab\u30b9\u30bf\u30e0\u30a8\u30e9\u30fc\u30da\u30fc\u30b8\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=\"\">&lt;!-- error\/404.html --&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html xmlns:th=\"http:\/\/www.thymeleaf.org\"&gt;\n&lt;head&gt;\n    &lt;title&gt;\u30da\u30fc\u30b8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"error-container\"&gt;\n        &lt;h1&gt;404 - \u30da\u30fc\u30b8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093&lt;\/h1&gt;\n\n        &lt;!-- \u30a8\u30e9\u30fc\u8a73\u7d30\uff08\u958b\u767a\u74b0\u5883\u306e\u307f\uff09 --&gt;\n        &lt;div th:if=\"${debug}\" class=\"error-details\"&gt;\n            &lt;p&gt;\u30ea\u30af\u30a8\u30b9\u30c8URL: &lt;span th:text=\"${path}\"&gt;URL&lt;\/span&gt;&lt;\/p&gt;\n            &lt;p&gt;\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8: &lt;span th:text=\"${message}\"&gt;\u30e1\u30c3\u30bb\u30fc\u30b8&lt;\/p&gt;\n\n            &lt;!-- \u30b9\u30bf\u30c3\u30af\u30c8\u30ec\u30fc\u30b9 --&gt;\n            &lt;div th:if=\"${trace}\" class=\"stack-trace\"&gt;\n                &lt;pre th:text=\"${trace}\"&gt;\u30b9\u30bf\u30c3\u30af\u30c8\u30ec\u30fc\u30b9&lt;\/pre&gt;\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n\n        &lt;a th:href=\"@{\/}\" class=\"btn-home\"&gt;\u30db\u30fc\u30e0\u306b\u623b\u308b&lt;\/a&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-70\">4. \u30ed\u30b0\u51fa\u529b\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=\"\">@Controller\npublic class DebugDemoController {\n\n    private static final Logger logger = \n        LoggerFactory.getLogger(DebugDemoController.class);\n\n    @GetMapping(\"\/debug-demo\")\n    public String debugDemo(Model model) {\n        try {\n            \/\/ \u51e6\u7406\u306e\u958b\u59cb\u3092\u30ed\u30b0\n            logger.debug(\"\u30c7\u30d0\u30c3\u30b0\u30c7\u30e2\u306e\u51e6\u7406\u3092\u958b\u59cb\");\n\n            \/\/ \u30e2\u30c7\u30eb\u306e\u72b6\u614b\u3092\u30ed\u30b0\n            Map&lt;String, Object&gt; modelMap = new HashMap&lt;&gt;();\n            model.asMap().forEach((k, v) -&gt; \n                modelMap.put(k, v != null ? v.toString() : \"null\"));\n            logger.debug(\"\u73fe\u5728\u306e\u30e2\u30c7\u30eb\u72b6\u614b: {}\", modelMap);\n\n            \/\/ \u51e6\u7406\u306e\u5b9f\u884c\n            SomeBusinessLogic logic = new SomeBusinessLogic();\n            Result result = logic.process();\n\n            \/\/ \u7d50\u679c\u3092\u30ed\u30b0\n            logger.info(\"\u51e6\u7406\u7d50\u679c: {}\", result);\n            model.addAttribute(\"result\", result);\n\n        } catch (Exception e) {\n            \/\/ \u30a8\u30e9\u30fc\u30ed\u30b0\n            logger.error(\"\u51e6\u7406\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f: {}\", e.getMessage(), e);\n            throw e;\n        }\n\n        return \"debug\/demo\";\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-71\">\u30c7\u30d0\u30c3\u30b0\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u6bb5\u968e\u7684\u306a\u30c7\u30d0\u30c3\u30b0\u30a2\u30d7\u30ed\u30fc\u30c1<\/li>\n<\/ol>\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=\"\">   \/\/ 1. \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3067\u306e\u5024\u306e\u78ba\u8a8d\n   logger.debug(\"\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306b\u6e21\u3055\u308c\u305f\u5024: {}\", value);\n\n   \/\/ 2. \u30b5\u30fc\u30d3\u30b9\u30ec\u30a4\u30e4\u30fc\u3067\u306e\u51e6\u7406\u78ba\u8a8d\n   logger.debug(\"\u30b5\u30fc\u30d3\u30b9\u51e6\u7406\u958b\u59cb: \u30d1\u30e9\u30e1\u30fc\u30bf = {}\", params);\n\n   \/\/ 3. \u30e2\u30c7\u30eb\u72b6\u614b\u306e\u78ba\u8a8d\n   logger.debug(\"\u30e2\u30c7\u30eb\u306e\u72b6\u614b: {}\", model.asMap());<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3067\u306e\u30c7\u30d0\u30c3\u30b0\u60c5\u5831\u8868\u793a<\/li>\n<\/ol>\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;!-- \u958b\u767a\u74b0\u5883\u3067\u306e\u307f\u8868\u793a\u3055\u308c\u308b\u30c7\u30d0\u30c3\u30b0\u60c5\u5831 --&gt;\n   &lt;div th:if=\"${@environment.getActiveProfiles().contains('dev')}\"&gt;\n       &lt;h4&gt;\u30c7\u30d0\u30c3\u30b0\u60c5\u5831&lt;\/h4&gt;\n       &lt;pre th:text=\"${#vars}\"&gt;\u5909\u6570\u4e00\u89a7&lt;\/pre&gt;\n   &lt;\/div&gt;<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u5b9f\u88c5<\/li>\n<\/ol>\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=\"\">   @ControllerAdvice\n   public class GlobalErrorHandler {\n\n       private static final Logger logger = \n           LoggerFactory.getLogger(GlobalErrorHandler.class);\n\n       @ExceptionHandler(Exception.class)\n       public String handleError(Exception e, Model model) {\n           logger.error(\"\u4e88\u671f\u305b\u306c\u30a8\u30e9\u30fc\u304c\u767a\u751f: {}\", e.getMessage(), e);\n           model.addAttribute(\"error\", e);\n           return \"error\/general\";\n       }\n   }<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0\u624b\u6cd5\u3092\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001Thymeleaf\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u554f\u984c\u3092\u52b9\u7387\u7684\u306b\u7279\u5b9a\u3057\u89e3\u6c7a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30d7\u30ed\u30c0\u30af\u30b7\u30e7\u30f3\u74b0\u5883\u3067\u306e\u904b\u7528\u30dd\u30a4\u30f3\u30c8\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-72\">\u30d7\u30ed\u30c0\u30af\u30b7\u30e7\u30f3\u74b0\u5883\u3067\u306e\u904b\u7528\u30dd\u30a4\u30f3\u30c8<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-73\">\u672c\u756a\u74b0\u5883\u3067\u306e\u8a2d\u5b9a\u6700\u9069\u5316<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-74\">1. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\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=\"\"># application-prod.properties\n# \u30ad\u30e3\u30c3\u30b7\u30e5\u8a2d\u5b9a\nspring.thymeleaf.cache=true\nspring.thymeleaf.cache-period=3600\nspring.resources.cache.period=3600\nspring.resources.chain.strategy.content.enabled=true\nspring.resources.chain.strategy.content.paths=\/**\n\n# \u5727\u7e2e\u8a2d\u5b9a\nserver.compression.enabled=true\nserver.compression.mime-types=text\/html,text\/css,application\/javascript\nserver.compression.min-response-size=1024\n\n# \u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb\u8a2d\u5b9a\nspring.datasource.hikari.maximum-pool-size=10\nspring.datasource.hikari.minimum-idle=5\nspring.datasource.hikari.idle-timeout=300000<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-75\">2. \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\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=\"\">@Configuration\n@Profile(\"prod\")\npublic class ProductionSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http\n            .requiresChannel()\n                .anyRequest().requiresSecure()  \/\/ HTTPS\u5f37\u5236\n            .and()\n            .headers()\n                .contentSecurityPolicy(\"default-src 'self'\")\n                .frameOptions().deny()\n                .xssProtection().block(true)\n            .and()\n            .sessionManagement()\n                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)\n                .maximumSessions(1)  \/\/ \u540c\u6642\u30bb\u30c3\u30b7\u30e7\u30f3\u6570\u5236\u9650\n            .and()\n            .csrf()\n                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-76\">3. \u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\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=\"\">@ControllerAdvice\n@Profile(\"prod\")\npublic class ProductionErrorHandler {\n\n    @ExceptionHandler(Exception.class)\n    public String handleError(Exception e, Model model) {\n        \/\/ \u30a8\u30e9\u30fc\u30da\u30fc\u30b8\u3078\u306e\u6700\u5c0f\u9650\u306e\u60c5\u5831\u63d0\u4f9b\n        model.addAttribute(\"errorCode\", \"E-SYSTEM\");\n        model.addAttribute(\"errorMessage\", \"\u30b7\u30b9\u30c6\u30e0\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\");\n\n        return \"error\/production\";\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-77\">\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u4fdd\u5b88\u306e\u91cd\u8981\u30dd\u30a4\u30f3\u30c8<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-78\">1. \u30e1\u30c8\u30ea\u30af\u30b9\u53ce\u96c6\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=\"\">@Configuration\n@Profile(\"prod\")\npublic class MonitoringConfig {\n\n    @Bean\n    public MeterRegistry meterRegistry() {\n        return new SimpleMeterRegistry();\n    }\n\n    @Bean\n    public TimedAspect timedAspect(MeterRegistry registry) {\n        return new TimedAspect(registry);\n    }\n}\n\n@Component\npublic class ThymeleafMetricsAspect {\n\n    private final MeterRegistry registry;\n\n    public ThymeleafMetricsAspect(MeterRegistry registry) {\n        this.registry = registry;\n    }\n\n    @Around(\"execution(* org.thymeleaf.TemplateEngine.process(..))\")\n    public Object measureTemplateProcessing(ProceedingJoinPoint joinPoint) throws Throwable {\n        Timer.Sample sample = Timer.start(registry);\n        try {\n            return joinPoint.proceed();\n        } finally {\n            sample.stop(registry.timer(\"thymeleaf.template.processing\"));\n        }\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-79\">2. \u30d8\u30eb\u30b9\u30c1\u30a7\u30c3\u30af\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=\"\">@Component\npublic class ThymeleafHealthIndicator implements HealthIndicator {\n\n    private final TemplateEngine templateEngine;\n\n    public ThymeleafHealthIndicator(TemplateEngine templateEngine) {\n        this.templateEngine = templateEngine;\n    }\n\n    @Override\n    public Health health() {\n        try {\n            \/\/ \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30a8\u30f3\u30b8\u30f3\u306e\u72b6\u614b\u78ba\u8a8d\n            Context context = new Context();\n            context.setVariable(\"test\", \"health-check\");\n            templateEngine.process(\"fragments\/health-check\", context);\n\n            return Health.up()\n                .withDetail(\"status\", \"Template engine is working\")\n                .build();\n\n        } catch (Exception e) {\n            return Health.down()\n                .withException(e)\n                .build();\n        }\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-80\">3. \u30ed\u30b0\u7ba1\u7406\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=\"\">&lt;!-- logback-spring.xml --&gt;\n&lt;configuration&gt;\n    &lt;springProfile name=\"prod\"&gt;\n        &lt;appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\"&gt;\n            &lt;file&gt;logs\/application.log&lt;\/file&gt;\n            &lt;rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\"&gt;\n                &lt;fileNamePattern&gt;logs\/application.%d{yyyy-MM-dd}.log&lt;\/fileNamePattern&gt;\n                &lt;maxHistory&gt;30&lt;\/maxHistory&gt;\n                &lt;totalSizeCap&gt;3GB&lt;\/totalSizeCap&gt;\n            &lt;\/rollingPolicy&gt;\n            &lt;encoder&gt;\n                &lt;pattern&gt;%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n&lt;\/pattern&gt;\n            &lt;\/encoder&gt;\n        &lt;\/appender&gt;\n\n        &lt;root level=\"INFO\"&gt;\n            &lt;appender-ref ref=\"FILE\" \/&gt;\n        &lt;\/root&gt;\n    &lt;\/springProfile&gt;\n&lt;\/configuration&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-81\">4. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\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=\"\">@Component\n@Profile(\"prod\")\npublic class PerformanceMonitor {\n\n    private final MeterRegistry registry;\n    private final Logger logger = LoggerFactory.getLogger(PerformanceMonitor.class);\n\n    public PerformanceMonitor(MeterRegistry registry) {\n        this.registry = registry;\n\n        \/\/ \u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u76e3\u8996\n        Gauge.builder(\"jvm.memory.used\", Runtime.getRuntime(), \n            runtime -&gt; runtime.totalMemory() - runtime.freeMemory())\n            .register(registry);\n\n        \/\/ \u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u51e6\u7406\u6642\u9593\u306e\u76e3\u8996\n        Timer.builder(\"template.processing\")\n            .publishPercentiles(0.5, 0.95, 0.99)\n            .register(registry);\n    }\n\n    @Scheduled(fixedRate = 60000)  \/\/ 1\u5206\u3054\u3068\u306b\u5b9f\u884c\n    public void reportMetrics() {\n        \/\/ \u30e1\u30c8\u30ea\u30af\u30b9\u306e\u30ed\u30b0\u51fa\u529b\n        registry.getMeters().forEach(meter -&gt; {\n            logger.info(\"Metric: {} = {}\", \n                meter.getId(), \n                meter.measure().iterator().next().getValue());\n        });\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-82\">\u904b\u7528\u7ba1\u7406\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5b9a\u671f\u7684\u306a\u30e1\u30f3\u30c6\u30ca\u30f3\u30b9\u8a08\u753b<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5b9a\u671f\u7684\u306a\u30af\u30ea\u30a2<\/li>\n\n\n\n<li>\u4e0d\u8981\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u524a\u9664<\/li>\n\n\n\n<li>\u30ed\u30b0\u30ed\u30fc\u30c6\u30fc\u30b7\u30e7\u30f3<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u6226\u7565<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8ca0\u8377\u306b\u5fdc\u3058\u305f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u6570\u306e\u8abf\u6574<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u306e\u5206\u6563\u5316<\/li>\n\n\n\n<li>\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5206\u6563\u5316<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u6226\u7565<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30d5\u30a1\u30a4\u30eb\u306e\u5b9a\u671f\u30d0\u30c3\u30af\u30a2\u30c3\u30d7<\/li>\n\n\n\n<li>\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u7ba1\u7406<\/li>\n\n\n\n<li>\u30ed\u30b0\u30d5\u30a1\u30a4\u30eb\u306e\u30a2\u30fc\u30ab\u30a4\u30d6<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u904b\u7528\u30dd\u30a4\u30f3\u30c8\u3092\u9069\u5207\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001\u5b89\u5b9a\u3057\u305f\u30d7\u30ed\u30c0\u30af\u30b7\u30e7\u30f3\u74b0\u5883\u3092\u7dad\u6301\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001Thymeleaf\u306e\u4eca\u5f8c\u306e\u5c55\u671b\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"i-83\">Thymeleaf\u306e\u4eca\u5f8c\u306e\u5c55\u671b<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-84\">\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u65b0\u6a5f\u80fd\u3068\u6539\u5584\u70b9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-85\">1. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306e\u5411\u4e0a<\/h3>\n\n\n\n<p>Thymeleaf 3.x\u30b7\u30ea\u30fc\u30ba\u3067\u306f\u3001\u4ee5\u4e0b\u306e\u6539\u5584\u304c\u5b9f\u88c5\u3055\u308c\u3066\u3044\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=\"\">\/\/ \u65b0\u3057\u3044\u30d1\u30fc\u30b5\u30fc\u30a8\u30f3\u30b8\u30f3\u306e\u6d3b\u7528\u4f8b\n@Configuration\npublic class ThymeleafConfig {\n\n    @Bean\n    public SpringTemplateEngine templateEngine() {\n        SpringTemplateEngine engine = new SpringTemplateEngine();\n        \/\/ \u65b0\u30d1\u30fc\u30b5\u30fc\u306e\u8a2d\u5b9a\n        engine.setEnableSpringELCompiler(true);\n\n        \/\/ \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u8a2d\u5b9a\n        Set&lt;IDialect&gt; dialects = new HashSet&lt;&gt;();\n        dialects.add(new SpringStandardDialect());\n        engine.setDialects(dialects);\n\n        return engine;\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-86\">2. \u30e2\u30c0\u30f3\u306a\u6a5f\u80fd\u306e\u7d71\u5408<\/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;!-- \u30ea\u30a2\u30af\u30c6\u30a3\u30d6\u306a\u8981\u7d20\u306e\u7d71\u5408 --&gt;\n&lt;div th:fragment=\"reactive-content\" th:async-supported=\"true\"&gt;\n    &lt;div th:each=\"item : ${reactiveData}\" \n         th:with=\"result=${item.block()}\"&gt;\n        &lt;span th:text=\"${result}\"&gt;\u30c7\u30fc\u30bf&lt;\/span&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;!-- \u30e2\u30c0\u30f3\u306aWeb\u6a5f\u80fd\u306e\u30b5\u30dd\u30fc\u30c8 --&gt;\n&lt;template th:fragment=\"dynamic-import\"&gt;\n    &lt;script type=\"module\" th:src=\"@{\/js\/module.js}\"&gt;&lt;\/script&gt;\n    &lt;link rel=\"modulepreload\" th:href=\"@{\/js\/dependency.js}\"&gt;\n&lt;\/template&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-87\">\u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u3067\u306e\u6d3b\u7528\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-88\">1. \u30de\u30a4\u30af\u30ed\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u7d71\u5408<\/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=\"\">@Configuration\npublic class MicroFrontendConfig {\n\n    @Bean\n    public FragmentRegistry fragmentRegistry() {\n        FragmentRegistry registry = new FragmentRegistry();\n\n        \/\/ \u30de\u30a4\u30af\u30ed\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u306e\u767b\u9332\n        registry.register(\"header\", \"http:\/\/ui-service\/fragments\/header\");\n        registry.register(\"footer\", \"http:\/\/ui-service\/fragments\/footer\");\n\n        return registry;\n    }\n}<\/pre>\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;!-- \u30de\u30a4\u30af\u30ed\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u306e\u7d71\u5408\u4f8b --&gt;\n&lt;div th:replace=\"${@fragmentRegistry.resolve('header')}\"&gt;\n    \u30d8\u30c3\u30c0\u30fc\u9818\u57df\n&lt;\/div&gt;\n\n&lt;main&gt;\n    &lt;!-- \u30e1\u30a4\u30f3\u30b3\u30f3\u30c6\u30f3\u30c4 --&gt;\n    &lt;div th:replace=\"local\/content :: content\"&gt;\n        \u30b3\u30f3\u30c6\u30f3\u30c4\u9818\u57df\n    &lt;\/div&gt;\n&lt;\/main&gt;\n\n&lt;div th:replace=\"${@fragmentRegistry.resolve('footer')}\"&gt;\n    \u30d5\u30c3\u30bf\u30fc\u9818\u57df\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-89\">2. API\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3068\u306e\u9023\u643a<\/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=\"\">@Service\npublic class ApiGatewayService {\n\n    private final WebClient webClient;\n\n    public ApiGatewayService(WebClient.Builder webClientBuilder) {\n        this.webClient = webClientBuilder\n            .baseUrl(\"http:\/\/api-gateway\")\n            .build();\n    }\n\n    public Mono&lt;UserData&gt; getUserData(String userId) {\n        return webClient.get()\n            .uri(\"\/users\/{id}\", userId)\n            .retrieve()\n            .bodyToMono(UserData.class);\n    }\n}<\/pre>\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;!-- API\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u304b\u3089\u306e\u30c7\u30fc\u30bf\u8868\u793a --&gt;\n&lt;div th:with=\"userData=${@apiGatewayService.getUserData(userId).block()}\"&gt;\n    &lt;h2 th:text=\"${userData.name}\"&gt;\u30e6\u30fc\u30b6\u30fc\u540d&lt;\/h2&gt;\n    &lt;div th:each=\"service : ${userData.services}\"&gt;\n        &lt;span th:text=\"${service.name}\"&gt;\u30b5\u30fc\u30d3\u30b9\u540d&lt;\/span&gt;\n    &lt;\/div&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-90\">3. \u5c06\u6765\u7684\u306a\u5c55\u671b<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Web\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3068\u306e\u7d71\u5408<\/li>\n<\/ol>\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;!-- \u30ab\u30b9\u30bf\u30e0\u30a8\u30ec\u30e1\u30f3\u30c8\u306e\u30b5\u30dd\u30fc\u30c8 --&gt;\n&lt;custom-element th:data=\"${someData}\"&gt;\n    &lt;div slot=\"content\"&gt;\n        &lt;span th:text=\"${someData.value}\"&gt;\u5024&lt;\/span&gt;\n    &lt;\/div&gt;\n&lt;\/custom-element&gt;\n\n&lt;!-- \u30b7\u30e3\u30c9\u30a6DOM\u30b5\u30dd\u30fc\u30c8 --&gt;\n&lt;template id=\"shadow-template\" th:fragment=\"shadow-content\"&gt;\n    &lt;style&gt;\n        :host { display: block; }\n    &lt;\/style&gt;\n    &lt;div&gt;\n        &lt;slot name=\"content\"&gt;&lt;\/slot&gt;\n    &lt;\/div&gt;\n&lt;\/template&gt;<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>\u30b5\u30fc\u30d0\u30fc\u30b5\u30a4\u30c9\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u306e\u9032\u5316<\/li>\n<\/ol>\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=\"\">@Configuration\npublic class ModernRenderingConfig {\n\n    @Bean\n    public HybridTemplateEngine templateEngine() {\n        HybridTemplateEngine engine = new HybridTemplateEngine();\n\n        \/\/ \u30cf\u30a4\u30d6\u30ea\u30c3\u30c9\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u306e\u8a2d\u5b9a\n        engine.setStreamingEnabled(true);\n        engine.setAsyncSupported(true);\n\n        return engine;\n    }\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>AI\u3068\u6a5f\u68b0\u5b66\u7fd2\u306e\u7d71\u5408<\/li>\n<\/ol>\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;!-- AI\u652f\u63f4\u578b\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u751f\u6210\u306e\u4f8b --&gt;\n&lt;div th:fragment=\"ai-assisted\" \n     th:with=\"suggestion=${@aiService.getSuggestion(content)}\"&gt;\n\n    &lt;!-- AI\u304c\u63d0\u6848\u3059\u308b\u30ec\u30a4\u30a2\u30a6\u30c8 --&gt;\n    &lt;div th:replace=\"${suggestion.layout}\"&gt;\n        \u30ec\u30a4\u30a2\u30a6\u30c8\u63d0\u6848\n    &lt;\/div&gt;\n\n    &lt;!-- AI\u751f\u6210\u30b3\u30f3\u30c6\u30f3\u30c4 --&gt;\n    &lt;div th:text=\"${suggestion.content}\"&gt;\n        \u6700\u9069\u5316\u3055\u308c\u305f\u30b3\u30f3\u30c6\u30f3\u30c4\n    &lt;\/div&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-91\">\u79fb\u884c\u6226\u7565\u3068\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u6bb5\u968e\u7684\u306a\u6a5f\u80fd\u66f4\u65b0<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u65e2\u5b58\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306f\u7dad\u6301\u3057\u306a\u304c\u3089\u3001\u65b0\u6a5f\u80fd\u3092\u6bb5\u968e\u7684\u306b\u5c0e\u5165<\/li>\n\n\n\n<li>\u4e92\u63db\u6027\u3092\u4fdd\u3061\u306a\u304c\u3089\u306e\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6539\u5584\u306e\u7d99\u7d9a\u7684\u306a\u9069\u7528<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30e2\u30c0\u30f3\u5316\u3078\u306e\u5bfe\u5fdc<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Web Components\u3084Shadow DOM\u3078\u306e\u5bfe\u5fdc\u6e96\u5099<\/li>\n\n\n\n<li>\u30de\u30a4\u30af\u30ed\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u3078\u306e\u79fb\u884c\u8a08\u753b<\/li>\n\n\n\n<li>\u30ea\u30a2\u30af\u30c6\u30a3\u30d6\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u30e2\u30c7\u30eb\u306e\u63a1\u7528<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5c06\u6765\u3078\u306e\u5099\u3048<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>AI\u3068\u6a5f\u68b0\u5b66\u7fd2\u306e\u6d3b\u7528\u6e96\u5099<\/li>\n\n\n\n<li>\u30af\u30e9\u30a6\u30c9\u30cd\u30a4\u30c6\u30a3\u30d6\u74b0\u5883\u3078\u306e\u9069\u5fdc<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3068\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u306e\u5411\u4e0a<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5c55\u671b\u3068\u6226\u7565\u3092\u610f\u8b58\u3059\u308b\u3053\u3068\u3067\u3001Thymeleaf\u3092\u4f7f\u7528\u3057\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5c06\u6765\u306b\u308f\u305f\u3063\u3066\u6301\u7d9a\u53ef\u80fd\u306a\u5f62\u3067\u767a\u5c55\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Warning: Undefined array key &#8220;is_admin&#8221; in \/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/ &#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-1163","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\/1163","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=1163"}],"version-history":[{"count":1,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1163\/revisions"}],"predecessor-version":[{"id":1164,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1163\/revisions\/1164"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1163"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1163"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1163"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}