Tech Blog

สาเหตุที่การแก้ไข Thymeleaf + TypeScript + CSS ไม่ปรากฏบนหน้าจอ และวิธีแก้ไข

by Tech Writer
Thymeleaf TypeScript CSS Spring Boot

บทนำ

ระหว่างพัฒนาด้วย 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 เมื่อการเปลี่ยนแปลงไม่ปรากฏ

เมื่อการเปลี่ยนแปลงยังไม่ปรากฏ ตรวจสอบตามลำดับนี้:

  1. คุณ build หรือยัง?

    • ตรวจสอบว่า npm run build หรือ npm run watch กำลังรันอยู่
    • ตรวจสอบ build errors (ดูที่ terminal)
  2. คุณแก้ไขไฟล์ที่ถูกต้องหรือเปล่า?

    • คุณแก้ไข source file (app.ts) หรือแก้ไข built file (app.js) ผิดพลาด?
  3. Thymeleaf cache ถูก disable หรือยัง?

    • ตรวจสอบการตั้งค่า spring.thymeleaf.cache=false
  4. Browser cache ยังค้างอยู่ไหม?

    • Hard refresh (Ctrl+Shift+R หรือ Ctrl+F5)
    • Developer tools → Network tab → เลือก “Disable cache”
    • เพิ่ม query parameter ?v=timestamp ใน HTML สำหรับ cache busting
  5. 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:

  1. Frontend assets ต้องการ build (ต่างจาก HTML)
  2. ใช้ watch mode สำหรับ automatic build ระหว่าง development
  3. เมื่อการเปลี่ยนแปลงไม่ปรากฏ ตรวจสอบ build → แล้ว hard refresh
  4. ใช้ cache busting ใน production (version query หรือ file name hash)

“ทำไมไฟล์ Thymeleaf แสดงผลทันทีแต่อันนี้ไม่?” — คำตอบคือ “เพราะ files ที่ไม่ต้อง build และ files ที่ต้อง build ผสมกันอยู่”
การเข้าใจสิ่งนี้ตั้งแต่แรกช่วยลดเวลา debug ที่เสียไปได้อย่างมาก

ส่งข้อความได้ตามสบาย

กรุณาส่งข้อความ หากมีคำปรึกษาด้านเทคนิค ความคิดเห็น หรือคำถาม