สาเหตุที่การแก้ไข Thymeleaf + TypeScript + CSS ไม่ปรากฏบนหน้าจอ และวิธีแก้ไข
บทนำ
ระหว่างพัฒนาด้วย Spring Boot + Thymeleaf + TypeScript + TailwindCSS ผมมีประสบการณ์นี้:
- แก้ไข TypeScript code
- Refresh browser
- ไม่มีการเปลี่ยนแปลง
สงสัย “cache หรือเปล่า?” จึงลอง hard refresh (Ctrl+Shift+R)
ก็ยังไม่มีการเปลี่ยนแปลง
หลังจากงงอยู่สักพัก ในที่สุดก็รู้ว่าสาเหตุคืออะไร ผมไม่ได้ build มัน
บทความนี้อาจดูเหมือน “ก็ชัดเจนอยู่แล้วไม่ใช่เหรอ?” — แต่ใน setup แบบผสม Spring Boot + frontend assets นี่เป็นจุดสับสนที่เกิดขึ้นบ่อยอย่างน่าแปลกใจ
คำอธิบายโครงสร้าง
โครงสร้างของโปรเจกต์นี้:
src/
main/
resources/
templates/ ← Thymeleaf templates (.html)
static/
css/
tailwind.css ← TailwindCSS source
js/
app.ts ← TypeScript source
dist/
tailwind.min.css ← Built CSS (นี่คือที่ serve)
app.js ← Built JS (นี่คือที่ serve)
สิ่งที่ serve ให้ browser คือ built files ภายใต้ dist/
แม้จะแก้ไข source files (app.ts, tailwind.css) โดยตรง dist/ จะไม่ถูกอัปเดตจนกว่าจะ build
ทำไมจึงเข้าใจผิดง่าย
Thymeleaf Templates แสดงผลทันที
ไฟล์ .html ของ Thymeleaf ด้วยการตั้งค่า spring.thymeleaf.cache=false จะแสดงเนื้อหาล่าสุดทุก browser refresh โดยไม่ต้อง build
# application.yml (development)
spring:
thymeleaf:
cache: false
คุ้นเคยกับสิ่งนี้แล้ว จึงแก้ไข TypeScript และ CSS ด้วยความคาดหวังเดียวกัน
Routine Save → Browser Refresh พัง
สำหรับการแก้ไข Thymeleaf: save → browser refresh เพียงพอ
สำหรับ frontend assets: ต้องมี save → build → browser refresh คุณลืมขั้นตอน “build” นี้
Build Commands
// package.json
{
"scripts": {
"build:css": "npx tailwindcss -i ./src/main/resources/static/css/tailwind.css -o ./src/main/resources/static/dist/tailwind.min.css --minify",
"build:ts": "npx tsc",
"build": "npm run build:css && npm run build:ts",
"watch": "npm run build:css -- --watch & npx tsc --watch"
}
}
ด้วย npm run watch ใน watch mode จะ build อัตโนมัติเมื่อบันทึกไฟล์
การตั้งค่า Watch Mode สำหรับ Development
# Terminal 1: Spring Boot
./mvnw spring-boot:run
# Terminal 2: Frontend assets watch build
npm run watch
รัน 2 กระบวนการพร้อมกัน
ใน VS Code การใช้ terminal tab แยก หรือกำหนดเป็น task สะดวกกว่า
กำหนดเป็น VS Code Task
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "watch-frontend",
"type": "shell",
"command": "npm run watch",
"isBackground": true,
"problemMatcher": []
}
]
}
Checklist เมื่อการเปลี่ยนแปลงไม่ปรากฏ
เมื่อการเปลี่ยนแปลงยังไม่ปรากฏ ตรวจสอบตามลำดับนี้:
-
คุณ build หรือยัง?
- ตรวจสอบว่า
npm run buildหรือnpm run watchกำลังรันอยู่ - ตรวจสอบ build errors (ดูที่ terminal)
- ตรวจสอบว่า
-
คุณแก้ไขไฟล์ที่ถูกต้องหรือเปล่า?
- คุณแก้ไข source file (
app.ts) หรือแก้ไข built file (app.js) ผิดพลาด?
- คุณแก้ไข source file (
-
Thymeleaf cache ถูก disable หรือยัง?
- ตรวจสอบการตั้งค่า
spring.thymeleaf.cache=false
- ตรวจสอบการตั้งค่า
-
Browser cache ยังค้างอยู่ไหม?
- Hard refresh (Ctrl+Shift+R หรือ Ctrl+F5)
- Developer tools → Network tab → เลือก “Disable cache”
- เพิ่ม query parameter
?v=timestampใน HTML สำหรับ cache busting
-
Spring Boot cache ถูก disable หรือยัง?
- ตั้งค่า
spring.web.resources.cache.period=0
- ตั้งค่า
Cache Busting ใน Production Builds
ใน production environments browser มักถูก configure ให้ cache CSS และ JS นาน
การเพิ่ม hash ให้ชื่อไฟล์ทำให้ cache ไม่ valid
<!-- Thymeleaf template -->
<!-- Simple version (เปลี่ยนทุก deployment) -->
<link rel="stylesheet" th:href="@{/dist/tailwind.min.css(v=${buildVersion})}">
<script th:src="@{/dist/app.js(v=${buildVersion})}"></script>
// ส่ง build version ใน controller
@ModelAttribute("buildVersion")
public String buildVersion() {
return System.getenv("BUILD_VERSION"); // ตั้งค่าเป็น environment variable ใน CI
}
สรุป
กฎทองสำหรับ setup แบบผสม Thymeleaf + frontend assets:
- Frontend assets ต้องการ build (ต่างจาก HTML)
- ใช้ watch mode สำหรับ automatic build ระหว่าง development
- เมื่อการเปลี่ยนแปลงไม่ปรากฏ ตรวจสอบ build → แล้ว hard refresh
- ใช้ cache busting ใน production (version query หรือ file name hash)
“ทำไมไฟล์ Thymeleaf แสดงผลทันทีแต่อันนี้ไม่?” — คำตอบคือ “เพราะ files ที่ไม่ต้อง build และ files ที่ต้อง build ผสมกันอยู่”
การเข้าใจสิ่งนี้ตั้งแต่แรกช่วยลดเวลา debug ที่เสียไปได้อย่างมาก