ข้ามไปยังเนื้อหา

วิธีอ่านโค้ด Next.js ที่คุณไม่ได้เขียนเอง

ทุกโมดูลก่อนหน้านี้สอนให้คุณ เขียน Next.js โมดูลนี้สอนอีกครึ่งหนึ่งของงาน คือการอ่านโค้ด Next.js ที่คนอื่น — หรือ AI — เขียนไว้ แล้วตัดสินว่ามันถูกต้องหรือไม่ AI assistant จะส่งโค้ดที่ compile ผ่าน, next build ยอมรับ, และหน้าตาเหมือนตัวอย่าง App Router ที่คุณเรียนมาทุกประการ แต่ไม่มีข้อไหนพิสูจน์ว่ามันถูกต้อง ความล้มเหลวของ Next.js เองมักจะเงียบผิดปกติ: หน้าที่ควรเป็น static กลายเป็น dynamic แบบเงียบๆ, prop ที่ควรพังดังๆ กลับ serialize เป็น undefined, action ที่ไม่มีการตรวจ auth เลย build และ deploy ได้โดยไม่มี warning สักคำ ห้าขั้นตอนด้านล่างคือลำดับที่ทำให้ความล้มเหลวเหล่านี้ปรากฏออกมาจริงๆ และทุกบทถัดไปในโมดูลนี้จะเจาะลึกทีละส่วน

flowchart TD
  A["1. Boundaries<br/>'use client' / 'use server' placement"] --> B["2. Caching model<br/>segment config vs cacheComponents"]
  B --> C["3. Auth<br/>inside every action and route handler"]
  C --> D["4. Error and redirect path<br/>try/catch around redirect()/notFound()"]
  D --> E["5. Config<br/>next.config.ts, env vars"]
ลำดับการอ่าน route ที่คุณไม่ได้เขียนเอง

ก่อนจะอ่านว่า component ทำ อะไร ให้อ่านก่อนว่ามันรันอยู่ฝั่งไหนของ network "use client" ไม่ได้ทำเครื่องหมายแค่ component เดียว — มันทำเครื่องหมาย entry point ของทั้ง subtree ใน module graph และทุกอย่างที่ import ไปถึงจากจุดนั้นจะถูกส่งไปที่ browser ส่วน "use server" ทำเครื่องหมายฟังก์ชันที่ compile ออกมาเป็น public HTTP endpoint ที่ใครก็เรียกได้ถ้ามี action ID ไม่ใช่แค่ form ของคุณเท่านั้น

  • สิ่งที่ต้องมองหา: ไฟล์ (หรือ parent ที่ใกล้ที่สุด) ที่มี 'use client' หรือ 'use server' เป็นบรรทัดแรก; ทุก prop ที่ส่งจาก Server Component ไปยัง Client Component; ทุก hook (useState, useRouter, useFormStatus) และตรวจว่าไฟล์ที่เรียกมันเป็น Client Component จริงหรือไม่
  • คำถามที่ต้องถาม: directive นี้อยู่ต่ำในต้นไม้เท่าที่ควรหรือเปล่า หรือมันถูกวางไว้ที่ layout/page ใกล้ราก เพราะเป็นวิธีที่ทำให้ error หายไปง่ายที่สุด
  • บทที่อธิบายเรื่องนี้: Server and Client Components สำหรับกฎเรื่อง subtree และ serialization boundary; Server Actions คืออะไร สำหรับเหตุผลที่ทุก action เป็น public endpoint ทันทีที่มันถูก export

Next.js มี caching model สองแบบตลอดช่วงอายุของมัน และ project หนึ่งจะรันแค่แบบเดียวในเวลาหนึ่ง: Route Segment Config (dynamic, revalidate, fetchCache, dynamicParams, runtime — model ที่ tutorial ก่อน 16 ทุกอันสอน) หรือ Cache Components (cacheComponents: true ใน next.config.ts, 'use cache', cacheLife, cacheTag) ทั้งสองแบบใช้ร่วมกันไม่ได้ — ทุก export ของ Route Segment Config จะทำให้ next build พังทันทีที่ cacheComponents เปิดอยู่ โค้ดที่ AI เขียนมักผสมคำศัพท์จากทั้งสอง model เพราะทั้งคู่ปรากฏใน training data ของมัน

  • สิ่งที่ต้องมองหา: เปิด next.config.ts ก่อนอ่าน route ไหนเลย และเช็คว่ามี cacheComponents: true หรือไม่ จากนั้นเช็คว่า route ใช้ export const dynamic = ... / export const revalidate = ... (model เก่า) หรือ 'use cache' / cacheLife(...) / cacheTag(...) (model ใหม่)
  • คำถามที่ต้องถาม: ทุกไฟล์สอดคล้องกับสิ่งที่ next.config.ts บอกไว้หรือเปล่า หรือไฟล์นี้สมมติว่าใช้ model อีกแบบ
  • บทที่อธิบายเรื่องนี้: Route Segment Config and Runtimes สำหรับ model เก่าและ error ที่บอกว่าใช้ร่วมกันไม่ได้; use cache และ Cache Components และ Partial Prerendering สำหรับ model ใหม่

Server Action คือ POST endpoint; Route Handler คือ HTTP method อะไรก็ตามที่คุณ export ทั้งคู่ไม่ได้รับการป้องกันสืบทอดมาจากหน้าที่ render ปุ่มชี้ไปหามัน — การซ่อน UI ตอน render (โชว์ปุ่มลบให้แค่ admin) ควบคุมแค่ว่า UI แสดงอะไร ไม่ได้ควบคุมว่าใคร curl เข้า endpoint ตรงๆ ได้

  • สิ่งที่ต้องมองหา: บรรทัดแรกๆ ของทุกฟังก์ชันใน app/actions.ts, ไฟล์ 'use server' ใดๆ, และทุก app/**/route.ts มองหาการเช็ค session (pattern DAL ของคอร์สนี้คือ verifySession()) ก่อนอ่านหรือเขียนใดๆ และมองหาว่าการเขียนกรองด้วย ID ของ user ปัจจุบันด้วย ไม่ใช่แค่เช็คว่า มีใครสักคน login อยู่
  • คำถามที่ต้องถาม: ถ้าฉันส่ง request แบบนี้ด้วย curl พร้อม session cookie ของคนอื่น ข้อมูลของใครจะถูกแก้ไข
  • บทที่อธิบายเรื่องนี้: Server Actions คืออะไร และ Security and Patterns; Authentication Patterns สำหรับตัว DAL เอง

redirect() กับ notFound() ไม่ได้ return — มัน throw error ควบคุม flow พิเศษของ Next.js (NEXT_REDIRECT / NEXT_HTTP_ERROR_FALLBACK) ที่ framework จะดักจับสูงขึ้นไปในต้นไม้ try/catch ที่ครอบโค้ดที่เรียกอันใดอันหนึ่งจะดักจับ throw ภายในนี้ไปด้วย ถ้ามันไม่ตั้งใจ re-throw ออกไป — และความผิดพลาดนี้มองไม่เห็นตอน development เพราะหน้ามักจะ render อะไรบางอย่างออกมาอยู่ดี

  • สิ่งที่ต้องมองหา: ทุก try { … } catch { … } block; มันมีการเรียก redirect() หรือ notFound() อยู่ข้างในไหม ไม่ว่าจะตรงๆ หรือผ่าน helper อย่าง verifySession(); catch block log แล้วกลืนไปเลย หรือเรียก unstable_rethrow(err) / re-throw
  • คำถามที่ต้องถาม: ถ้าฉันยิงเข้า code path นี้กับ server ที่รันจริง มันตอบกลับด้วย redirect / 404 จริงไหม — หรือตอบ 200 พร้อม body ที่ผิด
  • บทที่อธิบายเรื่องนี้: Mutations and Revalidation ซึ่งพิสูจน์ความล้มเหลวแบบนี้กับ server จริง

สิ่งสุดท้ายที่ควรอ่าน เพราะมันเปลี่ยนความหมายของทุกอย่างข้างบน next.config.ts ตัดสินว่า caching model ไหนกำลังทำงาน (ขั้นที่ 2) และ flag ทดลองอะไรเปิดอยู่; ไฟล์ .env* ตัดสินว่าตัวแปรไหนใช้ได้ที่ไหน และมีแค่ตัวแปรที่ขึ้นต้นด้วย NEXT_PUBLIC_ — ที่เข้าถึงแบบ static literal process.env.NEXT_PUBLIC_X — เท่านั้นที่ปลอดภัยพอจะให้โค้ดฝั่ง browser เข้าถึงได้

  • สิ่งที่ต้องมองหา: cacheComponents, reactCompiler, turbopack.* ใน next.config.ts; ทุกการอ่าน process.env.* ในไฟล์ที่มี (หรือ import เข้า) 'use client'; ทุกตัวแปรที่ขึ้นต้น NEXT_PUBLIC_ และมันเก็บอะไรอยู่จริงๆ
  • คำถามที่ต้องถาม: ตัวแปรนี้จำเป็นต้องไปถึง browser จริงไหม — และถ้าจำเป็น ฉันยังสบายใจไหมถ้ามันโผล่เป็น plain text ในไฟล์ .next/static/chunks/*.js ที่ใครก็ดาวน์โหลดได้
  • บทที่อธิบายเรื่องนี้: Environment and Configuration; Middleware and the Request Lifecycle สำหรับ proxy.ts และ config ระดับ request-time ที่รันก่อนทุกอย่างข้างบน
ลำดับการอ่าน route ของ Next.js ที่คุณไม่ได้เขียนเอง:
1. Boundaries → 'use client' / 'use server' อยู่ตรงไหน และดึงอะไรเข้ามาด้วย
2. Caching model → segment config หรือ cacheComponents — project นี้ใช้อันไหน?
3. Auth → ทุก action/route handler เช็ค session แล้วกรองเจ้าของ row ด้วยไหม?
4. Error/redirect→ มี try/catch ที่ไหนกลืน redirect()/notFound() ไปหรือเปล่า?
5. Config/env → flag ใน next.config.ts และตัวแปรไหนไปถึง browser
การข้ามไปดูแค่ "build ผ่านไหม" ตอบคำถามเหล่านี้ไม่ได้เลย — ทั้งห้าข้อ compile ผ่านหมด
  • “ถ้า next build ผ่าน แปลว่าโค้ดถูกต้อง” ทุกรูปแบบความล้มเหลวในแคตตาล็อกของโมดูลนี้ build ผ่านทั้งหมด next build พิสูจน์แค่ว่าโค้ดเป็น Next.js ที่ syntax ถูกต้อง ไม่ได้พิสูจน์ว่ามันปลอดภัยหรือทำงานตามที่ตั้งใจ
  • “แค่โค้ดในฟอร์มเท่านั้นที่สำคัญสำหรับ Server Action” action ถูกเรียกได้จาก ID ของมันจากที่ไหนก็ได้ ไม่ขึ้นกับฟอร์มที่บังเอิญเรียกมัน — ดูขั้นที่ 3
  • “try/catch ครอบโค้ดเสี่ยงๆ ปลอดภัยกว่าเสมอ” รอบการเรียก redirect() หรือ notFound() การ catch แบบไม่มีเงื่อนไข อันตรายกว่า: มันเปลี่ยน redirect/404 ที่สะอาดให้กลายเป็น response 200 ที่ผิดแบบเงียบๆ
  • “ถ้าคนเขียนเอง ต้องการความระมัดระวังน้อยกว่า AI” ทุก pattern ในแคตตาล็อกของโมดูลนี้ก็ปรากฏในโค้ด Next.js ที่คนเขียนเองเหมือนกัน AI แค่ผลิตมันด้วยความมั่นใจที่สูงกว่าและปริมาณที่มากกว่า
ทำไมบทนี้ถึงบอกให้เช็ค next.config.ts หา cacheComponents *ก่อน* อ่านไฟล์ route ใดๆ
server ที่รันจริงจะตอบอะไรเมื่อ post หาไม่เจอ
try {
  const post = await getPost(id)
  if (!post) notFound()
  return post
} catch (err) {
  console.error(err)
  return null
}
Server Action ไม่เช็คอะไรเลยก่อนเขียนฐานข้อมูล แต่ปุ่มที่เรียกมัน render ให้แค่ admin ที่ login แล้วเห็นใน UI action นี้ปลอดภัยไหม
ข้อมูลที่ขึ้นต้นด้วย NEXT_PUBLIC_ จะไปจบที่ไหนถ้าถูกอ่านใน Client Component
เกิดอะไรขึ้นเมื่อ project นี้รัน next build
// next.config.ts
const nextConfig = { cacheComponents: true }

// app/page.tsx
export const dynamic = 'force-dynamic'
ลำดับไหนที่บทนี้แนะนำสำหรับการอ่าน route ของ Next.js ที่คุณไม่ได้เขียนเอง