โหมดมืด
บทที่ 0 — ภาพรวม Full-Stack Architecture
ทำไมต้องเรียน full-stack แบบ "ต่อกัน"
ภาพรวมก่อนเริ่ม: Frontend = หน้าจอที่ผู้ใช้เห็นและกด (React); Backend = หลังบ้านที่จัดการข้อมูล/business logic + คุยกับฐานข้อมูล (Spring Boot). ทั้งสองคุยกันผ่าน HTTP + JSON — Full-stack book = สอนให้สองฝั่งคุยกันได้สะอาด ปลอดภัย และ deploy ได้จริง
ถ้าคุณอ่าน Spring Boot กับ React Book จบแล้ว คุณรู้:
- Spring Boot = สร้าง REST API (อ่าน: เร็สท์-เอ-พี-ไอ; ย่อจาก Representational State Transfer Application Programming Interface — Representational อ่าน เร็พ-รี-เซน-เท-ชั่น-แนล = ช่องทางให้โปรแกรมอื่นเรียกข้อมูล/สั่งงานผ่าน HTTP) ที่ return JSON (อ่าน: เจ-สัน; JavaScript Object Notation = รูปแบบข้อความที่ใช้ส่งข้อมูลระหว่างเครื่อง) ได้
- React = สร้าง UI (อ่าน: ยู-ไอ; User Interface = หน้าจอที่ผู้ใช้เห็นและกด) ที่เรียก API แล้ว render (เรน-เดอร์ = วาด/แสดงผลออกหน้าจอ) ได้
แต่ตอนต่อจริง จะเจอปัญหาที่ไม่มีในหนังสือแต่ละเล่ม:
| ปัญหา | อธิบาย |
|---|---|
| CORS (อ่าน: คอร์ส; Cross-Origin Resource Sharing — Cross-Origin อ่าน ครอส-ออ-ริ-จิ้น = กฎความปลอดภัยของเบราว์เซอร์เรื่องเรียกข้าม origin) | browser (เบราว์เซอร์) block (บล็อก/ห้าม) request ข้าม domain (โดเมน คนละที่อยู่เว็บ) |
| Auth flow (auth = authentication การยืนยันตัวตน; flow = ลำดับขั้นตอน) | login อยู่ React แต่ JWT (อ่าน: จ๊อต หรือ เจ-ดับเบิ้ลยู-ที; JSON Web Token = บัตรผ่านดิจิทัลที่เซิร์ฟเวอร์ออกให้หลัง login) อยู่ Spring → เก็บ token (โทเคน บัตรผ่าน) ที่ไหน? ส่งยังไง? |
| Error format | backend ส่ง error มายังไง React ถึงจะแสดงได้สวย? |
| File upload | React ส่ง file → Spring เก็บ → React preview |
| Environment | dev ใช้ localhost:8080 แต่ prod ใช้ domain จริง → config ยังไง? |
| Deploy | frontend กับ backend ต้อง run ด้วยกันยังไง? |
บทนี้จะให้ภาพรวม → บทถัดไปจะลงมือทำทีละเรื่อง
1. Architecture ที่เราจะสร้าง
📦 เครื่องมือที่จะเห็นใน diagram (อธิบายเต็มในบทถัดไป — ตอนนี้รู้ชื่อพอ):
- TanStack Query (แทน-สแต็ก-คิวรี่) = ไลบรารีฝั่ง React ที่จัดการเรียก API + cache + retry ให้อัตโนมัติ
- JPA (เจ-พี-เอ; Java Persistence API) = มาตรฐาน Java สำหรับคุยกับฐานข้อมูลแบบ object (Spring Boot ใช้ Hibernate เป็น implementation)
- PostgreSQL (โพสต์-เกรส-คิว-แอล) = ฐานข้อมูล relational ที่นิยมมากในงาน production
- Spring Security Filter Chain = ชั้นกรอง request ของ Spring ที่ตรวจ token/permission ก่อนเข้าถึง endpoint
⚠️ JWT storage — 2 แบบ:
- MVP / เรียน: เก็บใน
localStorageหรือ memory (ง่าย, debug สบาย)- Production (2026 best practice): เก็บใน HttpOnly cookie + CSRF protection (กัน XSS อ่าน token ไม่ได้) — ดูบทที่ 2 §7
หนังสือเล่มนี้สอน localStorage ก่อนเพราะเห็นภาพชัด แล้วค่อยอัพเกรดเป็น HttpOnly cookie ในบทที่ 2
SPA (อ่าน: เอส-พี-เอ) = Single Page Application (เว็บหน้าเดียว) — โหลด HTML/JS ครั้งเดียวตอนแรก แล้วหลังจากนั้น React สลับหน้า/ดึงข้อมูลเองโดยไม่ refresh ทั้งหน้า
Request Flow (เมื่อ user กดปุ่ม "Load Users")
ฉบับย่อ (เห็นภาพรวมก่อน):
- React เรียก API → 2. Backend ตรวจ token → 3. Backend คุย database → 4. Return JSON → 5. React render
ฉบับเต็ม (ค่อยทำความเข้าใจในบทถัดไป):
- React component เรียก
useQuery→ TanStack Query เรียกfetchUsers() fetchUsers()→fetch('http://localhost:8080/api/users', { headers: { Authorization: 'Bearer xxx' } })- Browser ส่ง HTTP request ไป Spring Boot
- Spring Security Filter Chain:
JwtAuthFilterอ่าน header → validate token → ถ้า valid ใส่ user เข้า SecurityContext
UserController.getAll()ถูกเรียกUserService.findAll()→UserRepository.findAll()→ PostgreSQL- Return
List<UserDto>→ Spring แปลงเป็น JSON → ส่ง HTTP Response กลับ - TanStack Query รับ JSON → cache → trigger re-render
- React component ได้ data → render UI
🔜 ยังไม่ต้องเข้าใจ TanStack Query / Spring Security Filter Chain / JwtAuthFilter ตอนนี้ — บทที่ 1 + 2 จะอธิบายทีละขั้น
ถ้า token หมดอายุ?
- Spring Security ส่ง
401 Unauthorizedกลับ - React API layer detect 401 → redirect ไป
/login - User login ใหม่ → ได้ token ใหม่ → retry request เดิม
2. Project Structure
เรา แยก repo (หรือ folder) ระหว่าง frontend กับ backend
ขั้นที่ 1 — โครงสร้าง minimal (เริ่มจากตรงนี้)
my-fullstack-app/
├── backend/ ← Spring Boot project
│ ├── src/main/java/com/example/app/
│ │ ├── auth/ ← JWT, login, signup
│ │ ├── user/ ← User CRUD
│ │ └── config/ ← Security, CORS
│ ├── src/main/resources/application.yml
│ └── pom.xml
│
├── frontend/ ← React project
│ ├── src/
│ │ ├── api/ ← API layer (axios/fetch wrapper)
│ │ ├── auth/ ← login, token management
│ │ ├── components/ ← shared UI
│ │ ├── pages/ ← route-level components
│ │ ├── App.tsx
│ │ └── main.tsx
│ └── package.jsonขั้นที่ 2 — เพิ่ม Docker + Production (บทที่ 5)
my-fullstack-app/
├── backend/
│ ├── src/main/resources/db/migration/ ← Flyway migrations
│ ├── src/test/
│ ├── Dockerfile ← container image
│ └── pom.xml
├── frontend/
│ ├── src/features/ ← feature modules (users/, posts/)
│ ├── src/hooks/ ← custom hooks
│ ├── src/types/ ← TypeScript types
│ ├── Dockerfile
│ ├── nginx.conf ← production reverse proxy
│ ├── .env.development
│ └── .env.production
└── docker-compose.yml ← run ทั้ง stack🔜 Docker (ด็อก-เกอร์) = เครื่องมือบรรจุแอปลงในกล่อง (container) เพื่อ run ได้เหมือนกันทุกเครื่อง — ยังไม่ต้องเข้าใจตอนนี้ บทที่ 5 จะสอน
ทำไมแยก? เพราะ frontend กับ backend:
- Deploy (ดีพลอย = เอาขึ้นเซิร์ฟเวอร์ให้คนใช้จริง) ต่างที่ได้ (CDN อ่าน ซี-ดี-เอ็น = Content Delivery Network เครือข่ายเซิร์ฟเวอร์กระจายไฟล์ static อ่าน ส-แต-ติก = ไฟล์ที่ไม่ต้องประมวลผล เช่น HTML/CSS/JS/รูป — ให้โหลดเร็วใกล้ผู้ใช้ vs server ปกติ)
- Scale ต่างจังหวะ (frontend = static, backend = CPU/memory)
- Team คนละทีมดูแลได้
- CI/CD pipeline แยกกัน
CDN ทำงานยังไง (ภาพย่อ):
ผู้ใช้ในไทย ─┐ ├─→ CDN edge ไทย (cache ไฟล์ static) ─→ เร็ว ผู้ใช้ในสหรัฐ ─┐ ├─→ CDN edge สหรัฐ (cache ไฟล์เดียวกัน) ─→ เร็ว ↕ (sync จาก origin) Origin server (Vercel/Cloudflare/Netlify)2026 CDN ยอดนิยม: Cloudflare, Vercel Edge, Netlify, AWS CloudFront
3. เครื่องมือ + Port ที่ใช้ตอน Development
| เครื่องมือ | Port | หมายเหตุ |
|---|---|---|
| Vite dev server (React) | 5173 | hot reload |
| Spring Boot | 8080 | API server |
| PostgreSQL | 5432 | database |
| pgAdmin (optional) | 5050 | database GUI |
ปัญหาตอน dev — "Cross Origin"
React run ที่ localhost:5173
Spring Boot run ที่ localhost:8080
Browser มองว่าคนละ origin (port ต่างกัน) → browser block request ด้วย CORS policy
วิธีแก้ 2 ทาง (บทที่ 1 จะอธิบายละเอียด):
- Backend เปิด CORS — Spring Boot config ว่า "อนุญาตให้ 5173 เรียก"
- Frontend proxy — Vite ตั้ง proxy ให้
/api/*forward ไป 8080
4. Shared Contract — API Response Format
📖 API Contract คืออะไร? = ข้อตกลงระหว่าง backend กับ frontend ว่า "ฉันจะส่ง/รับข้อมูลรูปร่างแบบนี้นะ" — เหมือนสัญญา ถ้าฝั่งใดฝั่งหนึ่งเปลี่ยนรูปร่างโดยไม่บอก อีกฝั่งจะพัง ทำไมต้องมี? เพราะคนเขียน frontend กับ backend คนละคน (หรือคนเดียวกันแต่คนละเวลา) ถ้าไม่ตกลงล่วงหน้า → debug นาน, bug เยอะ, type ไม่ตรง
Backend กับ Frontend ต้อง ตกลงกัน ว่า response (คำตอบที่เซิร์ฟเวอร์ส่งกลับ) หน้าตาเป็นยังไง — หนังสือเล่มนี้เลือก convention เดียวใช้ทั้งเล่ม:
- Success = ส่ง object/array ตรง ๆ ไม่มี wrapper (เช่น login response =
{ accessToken, user, ... }ส่งออกมาเลย) - Error = ห่อในรูปแบบเดียวกันทุก endpoint (
{ error: { code, message, details }, timestamp }) เพื่อให้ frontend มีhandleApiError()ตัวเดียว
ดูตัวอย่างจริงที่ บทที่ 1 §9–10
💡 มี convention อีกแบบที่บางทีมใช้ — ห่อ success ใน
{ data, message, timestamp }ด้วย ดีตรง consistent กับ error format แต่ verbose กว่า หนังสือเล่มนี้เลือกแบบไม่ห่อเพราะใช้กับ TypeScript ได้ตรงไปตรงมากว่า (ไม่ต้อง unwrap.dataทุก request)
Error Response
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "field": "email", "message": "must be a valid email" },
{ "field": "name", "message": "must not be blank" }
]
},
"timestamp": "2026-05-18T10:00:00Z"
}ทำไมต้อง consistent?
ถ้า backend บาง endpoint ส่ง { "users": [...] } บาง endpoint ส่ง [...] ตรง ๆ
→ Frontend ต้อง handle case ต่าง → code ซับซ้อน → bug เยอะ
ถ้า error format เหมือนกันหมด → Frontend มี handleApiError() ตัวเดียว → สะอาด
5. TypeScript Types ที่ Share กัน
React ต้องมี Type ที่ตรงกับ DTO (อ่าน: ดี-ที-โอ; Data Transfer Object = คลาส/อ็อบเจกต์ที่ใช้ส่งข้อมูลเข้า-ออก API โดยเฉพาะ แยกจาก entity ในฐานข้อมูล) ของ Spring Boot:
typescript
// types/api.ts — error format ที่ใช้ทั้งระบบ (success ไม่มี wrapper)
interface ApiError {
error: {
code: string;
message: string;
details?: { field: string; message: string }[];
};
timestamp: string;
}
// types/user.ts — match กับ Spring Boot DTO
interface User {
id: number;
email: string;
name: string;
role: 'USER' | 'ADMIN';
createdAt: string; // ISO date string
}
interface CreateUserRequest {
email: string;
name: string;
password: string;
}
interface LoginRequest {
email: string;
password: string;
}
interface LoginResponse {
accessToken: string; // JWT ใช้แนบ Authorization header
refreshToken: string; // ใช้ขอ accessToken ใหม่เมื่อหมดอายุ
expiresIn: number; // อายุ accessToken (วินาที)
user: User;
}💡 Tip: เขียน type ตรงนี้ → Zod (ไลบรารีตรวจสอบความถูกต้องของข้อมูล/validate ในฝั่ง TypeScript) schema (สคีมา = แบบกำหนดรูปร่างข้อมูล) ที่ validate (ตรวจสอบความถูกต้อง) form → API layer (ชั้นรวมโค้ดเรียก API) ที่ส่ง request → ทุกอย่างเชื่อมกัน
🔜 ยังไม่ต้องเข้าใจ Zod/API layer ตอนนี้ — เดี๋ยวเจอเต็ม ๆ ใน บทที่ 1 และ บทที่ 2
6. Development Workflow
ก่อนเริ่ม — ต้องติดตั้งอะไรบ้าง
| เครื่องมือ | ใช้ทำอะไร | ลิงก์ติดตั้ง |
|---|---|---|
| Java 25 (LTS, แนะนำ; min Java 21) | run Spring Boot | Eclipse Temurin (แนะนำ) หรือ Oracle JDK |
| Node.js 22 LTS | run Vite / React | nodejs.org — เลือก LTS |
| Docker Desktop | run PostgreSQL ใน container | docker.com/products/docker-desktop |
| Git | version control | git-scm.com |
✅ verify ติดตั้งสำเร็จด้วยคำสั่ง:
java --version,node --version,docker --version,git --version
เปิดทำงาน (ทุกวัน)
bash
# Terminal 1 (หน้าต่างคำสั่งที่ 1): Database — ฐานข้อมูล
docker compose up db -d
# Terminal 2: Backend — เซิร์ฟเวอร์ฝั่งหลังบ้าน
cd backend
./mvnw spring-boot:run
# Terminal 3: Frontend — เว็บฝั่งผู้ใช้
cd frontend
npm run devเปิด browser ที่ http://localhost:5173
React app จะ render → เรียก API ไปที่ localhost:8080 (ผ่าน proxy หรือ direct)
Workflow เมื่อแก้ code
| แก้ที่ไหน | เกิดอะไร |
|---|---|
| React component | Vite HMR (Hot Module Replacement = สลับเฉพาะโค้ดที่แก้เข้าไปทันทีโดยไม่โหลดหน้าใหม่) → browser update ทันที (ไม่ reload) |
| Spring Boot controller | Spring DevTools → auto restart (~2 วิ) |
| Database schema | เพิ่ม Flyway migration → restart backend |
| API contract เปลี่ยน | แก้ทั้ง backend DTO + frontend type |
7. Production Architecture
ตอน production ไม่มี Vite dev server:
ข้อดี:
- Same domain → ไม่มีปัญหา CORS
- Nginx → serve static file เร็วมาก + caching + gzip
- Security → เปิดแค่ port 80/443 สู่ outside
8. เรื่องที่ต้องรู้ก่อนเริ่มบทถัดไป
HTTP Methods ที่ใช้บ่อย
Idempotent (อ่าน: ไอ-เดม-โพ-เทนต์) = "เรียกซ้ำกี่ครั้งผลก็เหมือนเรียกครั้งเดียว"
- ✅
DELETE /users/5ซ้ำ 3 ครั้ง → user id 5 ก็หายไป อันเดียว เท่าเดิม (idempotent)- ❌
POST /usersซ้ำ 3 ครั้ง → สร้าง user ใหม่ 3 ตัว (ไม่ idempotent)
| Method | ใช้ทำอะไร | Body | Idempotent |
|---|---|---|---|
| GET | อ่านข้อมูล | ไม่มี | ✅ |
| POST | สร้างข้อมูลใหม่ | มี | ❌ |
| PUT | แก้ไขทั้ง object | มี | ✅ |
| PATCH | แก้ไขบางส่วน | มี | ✅ |
| DELETE | ลบ | ไม่มี/มีก็ได้ | ✅ |
HTTP Status Code ที่ใช้บ่อย
| Code | ความหมาย | เมื่อไหร่ |
|---|---|---|
| 200 | OK | request สำเร็จ |
| 201 | Created | สร้าง resource ใหม่สำเร็จ |
| 204 | No Content | สำเร็จ แต่ไม่มี body (เช่น DELETE) |
| 400 | Bad Request | request ผิด format / validation fail |
| 401 | Unauthorized | ไม่มี token / token invalid |
| 403 | Forbidden | มี token แต่ไม่มีสิทธิ์ |
| 404 | Not Found | resource ไม่มี |
| 409 | Conflict | ข้อมูลซ้ำ (เช่น email ซ้ำ) |
| 500 | Internal Server Error | server พัง |
สิ่งที่ต้องมีบนเครื่องก่อนเริ่ม
- [ ] Java 25 LTS (แนะนำ; min 21) —
java --version· ดาวน์โหลด: Eclipse Temurin - [ ] Node.js 22 LTS —
node --version· ดาวน์โหลด: nodejs.org - [ ] Docker + Docker Compose —
docker --version· ดาวน์โหลด: docker.com - [ ] IDE: ตามถนัด — แนะนำ IntelliJ IDEA สำหรับ Java + VS Code/Cursor/Zed สำหรับ TS (จะใช้ IDE ตัวเดียวรวมทั้งคู่ก็ได้)
- [ ] Git —
git --version· ดาวน์โหลด: git-scm.com
9. Checkpoint
🛠️ Checkpoint 0.1 — Setup ทุกอย่าง
- สร้าง Spring Boot project ด้วย Spring Initializr (Web, JPA, Security, PostgreSQL, Validation)
- สร้าง React project ด้วย
npm create vite@latest frontend -- --template react-ts - สร้าง
docker-compose.ymlที่ run PostgreSQL - verify: backend start ได้ + frontend start ได้ + db connect ได้
🛠️ Checkpoint 0.2 — วาด architecture
เอากระดาษหรือ draw.io วาด architecture ของ app ที่คุณอยากทำ:
- กี่ entity?
- API endpoint อะไรบ้าง?
- หน้าไหนบ้าง?
- หน้าไหน public / protected?
ไม่ต้องสมบูรณ์ — วาดก่อน แก้ทีหลัง
คำย่อที่จะเจอบ่อยในเล่มนี้ (Acronym Cheatsheet)
| ย่อ | เต็ม | คำอ่าน | ความหมายสั้น |
|---|---|---|---|
| API | Application Programming Interface | เอ-พี-ไอ | ช่องทางให้โปรแกรมเรียกกัน |
| REST | Representational State Transfer | เร็สท์ | สถาปัตยกรรม API ผ่าน HTTP |
| JSON | JavaScript Object Notation | เจ-สัน | รูปแบบข้อความส่งข้อมูล |
| HTTP | HyperText Transfer Protocol | เอช-ที-ที-พี | โปรโตคอลคุยกันบนเว็บ |
| URL | Uniform Resource Locator | ยู-อาร์-แอล | ที่อยู่ของ resource |
| UI | User Interface | ยู-ไอ | หน้าจอผู้ใช้ |
| UX | User Experience | ยู-เอ็กซ์ | ประสบการณ์ผู้ใช้ |
| SPA | Single Page Application | เอส-พี-เอ | เว็บหน้าเดียว |
| CORS | Cross-Origin Resource Sharing | คอร์ส | กฎเรียก API ข้าม origin |
| JWT | JSON Web Token | จ๊อต / เจ-ดับเบิ้ลยู-ที | บัตรผ่านดิจิทัล |
| CRUD | Create, Read, Update, Delete | ครัด | สร้าง/อ่าน/แก้/ลบ |
| DTO | Data Transfer Object | ดี-ที-โอ | object ส่งข้อมูลเข้า-ออก API |
| JPA | Java Persistence API | เจ-พี-เอ | มาตรฐาน Java คุยฐานข้อมูล |
| ORM | Object-Relational Mapping | โอ-อาร์-เอ็ม | แปลง table ↔ object |
| CDN | Content Delivery Network | ซี-ดี-เอ็น | เครือข่ายกระจายไฟล์ static |
| HMR | Hot Module Replacement | เอช-เอ็ม-อาร์ | สลับโค้ดทันทีไม่ reload |
| LTS | Long Term Support | แอล-ที-เอส | เวอร์ชันซัพพอร์ตยาว |
| MVP | Minimum Viable Product | เอ็ม-วี-พี | เวอร์ชันแรกที่ใช้งานได้ |
| CI/CD | Continuous Integration / Continuous Deployment | ซี-ไอ / ซี-ดี | ระบบ build + deploy อัตโนมัติ |
สรุป
✅ Full-stack = React (frontend) + Spring Boot (backend) + Database
✅ แยก project → deploy + scale ได้อิสระ
✅ CORS + Auth + Error format = 3 เรื่องที่ต้องตกลงก่อน
✅ Dev ใช้ 2 port (5173 + 8080) + proxy/CORS → Production ใช้ Nginx same domain
✅ ตกลง API contract (response format) ก่อนเริ่มเขียน → ลด bug
บทถัดไป → เชื่อม React กับ Spring Boot
Glossary: ../glossary.md · Style guide: ../CONTRIBUTING.md last_verified: 2026-06-03 · review report: ../REVIEW-2026-06-03.md