เหตุใดจึงเลือกโครงสร้าง Spring Boot Backend + Vue 3 Frontend แยกกัน และวิธีเชื่อมต่อจริงๆ
บทนำ
เมื่อสร้างแอพ DVD rental สำหรับลูกค้า มีโครงสร้างที่เราตัดสินใจตั้งแต่แรก
- Backend: Spring Boot (REST API) → Port 8082
- Frontend: Vue 3 + Vite → Port 5173 (development)
คุณอาจสงสัยว่า “ทำไมไม่ใช้ Thymeleaf?” — ขอตอบตรงๆ ก่อน
เราสร้าง admin panel ด้วย Thymeleaf นั่นเป็นการเลือกที่ถูกต้องสำหรับ admin panel
อย่างไรก็ตาม แอพฝั่งลูกค้ามีข้อกำหนด UX ที่ยากจะทำได้ด้วย Thymeleaf
บทความนี้ไม่ใช่เรื่อง “อันไหนถูกต้อง” แต่เป็นเรื่อง “เราตัดสินใจอย่างไร”
กรณีที่ Thymeleaf เพียงพอ vs. ไม่เพียงพอ
กรณีที่ Thymeleaf เก่ง
สำหรับ admin panel ที่มีวงจร “ส่งฟอร์ม → แสดงผล” ซ้ำๆ Thymeleaf ยอดเยี่ยมมาก
- Return HTML จากเซิร์ฟเวอร์ในทุก page transition
- Integration ที่แน่นกับ Spring Security มี CSRF protection อัตโนมัติ
- Server-side validation และการแสดงข้อผิดพลาดง่าย
Thymeleaf เพียงพอสำหรับ admin panel
ทำไมจึงเลือก Vue 3
มี UX ที่เราต้องการในแอพฝั่งลูกค้า
- Feedback การค้นหาและกรองแบบ instant — ประสบการณ์ “ผลลัพธ์เปลี่ยนโดยไม่ต้องกดปุ่มค้นหา”
- Cart drawer เปิด-ปิดได้ smooth — Interactions โดยไม่มี page transition
- Layout ที่ต่างกันสำหรับ mobile และ desktop — การสลับการแสดงผลแบบ dynamic ตาม viewport
- การแชร์ API กับ native app ในอนาคต — REST API เดียวกันสามารถเรียกจากแอพ iOS/Android ได้
สิ่งเหล่านี้ไม่ใช่ “เป็นไปไม่ได้” กับ Thymeleaf แต่ต้องเขียน JavaScript มาก
ถ้าต้องเขียน JS อยู่แล้ว การทำ componentize ด้วย Vue 3 maintainable มากกว่า
โครงสร้างจริง
dvd-rental-customer-app/
backend/ ← Spring Boot (Port 8082)
src/main/java/
api/ ← REST API controllers
service/ ← Business logic
frontend/ ← Vue 3 + Vite (Port 5173 ใน development)
src/
api/ ← HTTP client สำหรับ backend
components/ ← Vue components
การตั้งค่า CORS และ Proxy สำหรับ Development
อุปสรรคแรกกับสถาปัตยกรรมแยกคือ CORS
การเรียกจาก frontend (port 5173) ไปยัง backend (port 8082) ทำให้ browser แสดง CORS error
วิธีแก้ไขที่ 1: Dev Proxy ของ Vite (Development)
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8082',
changeOrigin: true,
}
}
}
})
ใน development Vite forward requests ไปยัง /api ให้ backend ดังนั้น CORS ไม่เกิดขึ้น
วิธีแก้ไขที่ 2: การตั้งค่า CORS ของ Spring Boot (Production)
ใน production frontend และ backend อาจอยู่ต่าง domain หรือ port
ในกรณีนั้น อนุญาต CORS ฝั่ง Spring Boot
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://your-frontend-domain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true);
}
}
การรวม Production Build
สำหรับ production เรา build Vue ด้วย npm run build และ serve output เป็น static resources ของ Spring Boot
<!-- pom.xml (ตัดมา) -->
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<executions>
<execution>
<id>npm build</id>
<goals><goal>npm</goal></goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
mvn package คำสั่งเดียวสร้าง jar ที่รวม frontend build ไว้ด้วย
Pain Points จริงกับโครงสร้างแยก
① ต้องเริ่ม 2 กระบวนการระหว่าง Development
เริ่ม Vite แล้วเริ่ม Spring Boot
เมื่อคุ้นเคยแล้วใช้เวลา 10 วินาที แต่ตอนแรกมักลืมอันหนึ่งแล้วสงสัย “ทำไมไม่ทำงาน?”
② Deploy โดยไม่ Build Frontend
อาจเกิดกรณีที่ mvn package รันขณะที่ frontend ยังมี build เก่า
สำคัญมากที่จะกำหนดลำดับอย่างชัดเจนใน CI/CD ให้รัน npm run build ก่อนเสมอ
③ Type ไม่ตรงกัน
Response types จาก backend และ TypeScript types ใน frontend มีแนวโน้มแยกออกจากกัน
ต้องแชร์คำจำกัดความ schema ด้วย OpenAPI หรือมีนิสัยในการจัดแนวให้ตรงสม่ำเสมอ
สรุป
| แง่มุม | Thymeleaf | Vue 3 + REST API |
|---|---|---|
| ความเร็ว development เริ่มต้น | เร็ว | ค่อนข้างช้า (ต้องตั้งค่า) |
| UI แบบ interactive | ยาก | จุดแข็ง |
| แชร์ API (mobile ฯลฯ) | ไม่ได้ | ได้ |
| Spring Security integration | ง่าย | ต้องทำ implementation เอง |
| ความซับซ้อนในการ deploy | ง่าย | ค่อนข้างซับซ้อน |
Thymeleaf สำหรับ admin panel, Vue 3 สำหรับ customer-facing app
การแบ่งนี้เป็นตัวเลือกที่เหมาะสมที่สุดสำหรับโปรเจกต์นี้
สิ่งสำคัญไม่ใช่ “อันไหนดีกว่า” แต่คือ “เลือกตามสิ่งที่คุณต้องการสร้าง”
แผนที่บทความของ Series นี้
→ การสร้างแอพ DVD Rental สำหรับผู้ใช้ควบคู่กับหน้าจัดการ — ภาพรวมโครงสร้าง Vue 3 + Spring Boot