- ทีม Hardcover ย้ายระบบไปเป็น Ruby on Rails + Inertia.js เนื่องจากประสบปัญหา ประสิทธิภาพของโครงสร้างที่อิงกับ Next.js ลดลง, ต้นทุนสูง, และความเร็วในการพัฒนาที่ช้าลง
- เลือก Inertia.js เพื่อให้ตอบโจทย์เรื่อง SSR ที่รองรับ SEO, การเชื่อมต่อฐานข้อมูลโดยตรง, และ ยังคงใช้ React ต่อไปได้
- ค่าใช้จ่ายที่พุ่งขึ้นอย่างไม่คาดคิด บน Vercel และ Cloud Run รวมถึง ความไม่แน่นอนของระบบแคชใน Next.js เป็นปัจจัยชี้ขาดที่ทำให้ตัดสินใจเปลี่ยน
- Inertia.js เป็น วิธีที่เหมาะสมที่สุดในการเชื่อม Rails backend กับ React frontend ทำให้จัดการ SSR และแคชได้ง่ายขึ้น
- หลังการเปลี่ยนผ่าน คะแนน Google Pagespeed และ SEO ดีขึ้น พร้อมกับ เวลาเข้าชมเว็บไซต์และการแสดงผลในการค้นหาเพิ่มขึ้น
เบื้องหลังการเปลี่ยนผ่าน
- ในช่วงแรกเลือกใช้ Next.js ที่รองรับ SEO และ SSR และสร้างระบบด้วยสถาปัตยกรรมที่อิงกับ GraphQL API
- ข้อมูลส่วนใหญ่ถูกเรียกจากฝั่งไคลเอนต์ในเบราว์เซอร์ ขณะที่ข้อมูลแบบคงที่ถูกแคชไว้ที่เซิร์ฟเวอร์
- เมื่อเวลาผ่านไป เกิด คำขอ API เพิ่มขึ้นจากการขาดแคช, ประสิทธิภาพลดลง และ ความเร็วของสภาพแวดล้อมการพัฒนาก็ช้าลง
ปัญหาที่เกิดขึ้นใน Next.js
- แม้จะ ย้ายไปใช้ App Router แล้ว ความเร็วก็แทบไม่ดีขึ้น และคำขอ Apollo แบบ POST ก็ไม่ถูกแคช จึงไม่ได้ผลตามที่คาดหวัง
- นโยบายราคาของ Vercel เปลี่ยนจนค่าบริการรายเดือนพุ่งจาก $30 เป็น $354
- Cloud Run ก็เริ่มต้นราคาถูก แต่สุดท้าย เพิ่มขึ้นไปถึง $524
- โครงสร้างการแคชของ Next.js ทำความเข้าใจได้ยาก จึงไม่สามารถจัดการได้อย่างมีประสิทธิภาพ
- ความเร็วในการพัฒนาลดลงอย่างชัดเจน ทำให้การออนบอร์ดสมาชิกใหม่เป็นเรื่องยาก
เหตุผลที่เลือก Rails + Inertia.js
- ต้องการคง SSR ไว้ พร้อมกับ ดึงข้อมูลจากฐานข้อมูลโดยตรง
- ต้องการใช้ React ต่อไป และแม้จะพิจารณา Remix, react-rails, react_on_rails ด้วย แต่สุดท้ายเลือก inertia-rails
- Inertia.js ทำให้ ใช้ระบบ routing ของ Rails ได้โดยไม่ต้องมี frontend routing และทำ SSR ได้ไม่ยาก
- ที่คอนโทรลเลอร์สามารถเรนเดอร์ด้วย
inertia: '페이지명' และทำแคชด้วย Rails.cache.fetch
- ในคอมโพเนนต์ React รับ props ได้ผ่าน
usePage()
โครงสร้าง SSR และการบิลด์
- สำหรับ SSR มีการแยกการทำงาน
hydrateRoot / createRoot ใน application.tsx
- รัน Vite เป็นเซิร์ฟเวอร์แยกต่างหาก และรองรับ hot reload ระหว่างพัฒนา
- ทำ deployment automation ของ Rails + Vite ผ่าน Docker และ Kamal พร้อมแยก staging กับ production
- ตอน deploy ใช้คำสั่ง
make deploy และใช้ CloudFlare เป็น asset host เพื่อเพิ่มประสิทธิภาพการแคช
ผลลัพธ์หลังการเปลี่ยนผ่าน
- หลังปล่อยการย้ายระบบเมื่อวันที่ 18 มีนาคม 2025 การแสดงผลบน Google Search เพิ่มขึ้น และ ความเร็วของหน้าเว็บดีขึ้น
- Total Blocking Time ดีขึ้นอย่างมาก ส่งผลให้คะแนน Pagespeed สูงขึ้น
- เวลาเฉลี่ยที่ผู้เข้าชมอยู่บนเว็บไซต์มีแนวโน้มเพิ่มจาก 3 นาที → 6 นาที
- ทราฟฟิกยังคงที่ ขณะที่ จำนวนการสมัครสมาชิกยังคงเสถียร
งานต่อไปและจุดที่ต้องปรับปรุง
- นำเลย์เอาต์ร่วมกลับมาใช้ซ้ำได้ยาก และยังมีปัญหาการเรนเดอร์ใหม่ทั้งหน้าของแต่ละเพจ
- การดีบัก SSR ทำได้ยาก และการตั้งค่าสภาพแวดล้อมซับซ้อน
- เอกสารเกี่ยวกับการใช้ Inertia.js คู่กับ Rails มีไม่มาก จึงต้องอาศัยชุมชน Discord ในการแก้ปัญหา
- จำเป็นต้องปรับตัวจาก Suspense มาใช้แนวทางของ Inertia
- ตอนนี้ยังคงใช้ Hasura อยู่ ทำให้ยังไม่ได้ใช้ความสามารถบางส่วนของ Inertia เช่น form, flash
บทสรุปและสิ่งที่คาดหวัง
- เป็นโครงสร้างที่ผสาน React + Rails เข้าด้วยกันอย่างเป็นธรรมชาติ ทำให้ ประสิทธิภาพการพัฒนาและการดูแลรักษาดีขึ้น
- การเลือก Inertia.js ช่วยให้ได้ทั้ง ความเร็ว, SSR, และความปลอดภัยด้านชนิดข้อมูล พร้อมกัน
- จากนี้ไปมีแผน โอเพนซอร์สและหาผู้ร่วมพัฒนาเพิ่มเติม
2 ความคิดเห็น
มีประเด็นถกเถียงกันว่าเมื่อใช้
Linkใน next.js เพื่อใช้งาน React Server Components จะมีการสร้างและประมวลผล URL ในลักษณะอย่าง?_rsc=1ip3iต่อท้ายออกมา ได้ยินมาด้วยว่าค่าใช้จ่ายในการใช้ CDN พุ่งสูงขึ้นอย่างมาก และว่าทีมพัฒนา next.js เองก็รับทราบปัญหานี้อยู่แล้ว แต่ยังไม่แน่ชัดว่าจะมีการแก้ไขด้วยวิธีไหนและเมื่อไรความคิดเห็นจาก Hacker News
การเรนเดอร์ฝั่งเซิร์ฟเวอร์ (SSR) ไม่เคยหายไปไหน และตอนนี้เว็บเพิ่งจะนึกออกว่าทำไมมันถึงเคยเป็นค่าเริ่มต้น การเรนเดอร์ครั้งแรกและ SEO ยังดีกว่าเมื่อมาร์กอัปมาจากเซิร์ฟเวอร์โดยตรง เฟรมเวิร์กหลายตัวอย่าง Rails + Turbo, HTMX, Phoenix LiveView, React Server Components ต่างก็ยึด SSR เป็นพื้นฐาน แดชบอร์ดและแอป CRUD ส่วนใหญ่ไม่จำเป็นต้องมี client router, global state หรือ hydration bundle ขนาด 200kB แค่ต้องการการสลับ HTML บางส่วนเท่านั้น
แรงผลักดันที่แท้จริงคือราคาของความซับซ้อน ทุกบรรทัดของ client JS นำมาซึ่ง build tool, เสียงรบกวนจาก npm audit และความเสี่ยงด้าน supply chain การลด payload นี้ช่วยทั้งด้านประสิทธิภาพและความปลอดภัยพร้อมกัน แน่นอนว่าแอปอย่าง Figma หรือ Gmail ยังได้ประโยชน์จาก client logic ที่หนักอยู่ ดังนั้นจึงเริ่มเห็นรูปแบบ "HTML เป็นค่าเริ่มต้น และใช้ JS เท่าที่จำเป็น" ควรคิดเป็น islands มากกว่าจะเป็น SPA ทั้งก้อน
ดังนั้นกำลังเกิดการหวนกลับไปหาเซิร์ฟเวอร์ แต่ไม่ใช่ความโหยหา PHP แบบปี 2004 มันคือการปรับการใช้ JavaScript ให้เหมาะสม และปล่อยให้ HTML ทำงานน่าเบื่อ 90% ที่มันทำได้ดีมาโดยตลอด
เราใช้ NextJS กับบางโปรเจกต์ แต่ตอนนี้กำลังทยอยเลิกใช้แล้ว มีหลายเหตุผล แต่ปัจจัยหลัก ๆ มีดังนี้
เรื่อง authentication ยุ่งยาก next-auth มีข้อจำกัดบางอย่างจนเราต้องไปใช้ iron-session ตัวอย่างเช่น มันใช้โดเมนของผู้ให้บริการ ID แบบไดนามิกไม่ได้ ทำให้เราต้องถือครอง flow ของ openid ทั้งหมดเอง ทำได้ก็จริง แต่เป็นการเสียเวลาที่ไม่คาดคิดสำหรับเฟรมเวิร์กที่ดูเป็นผู้ใหญ่แล้ว
เพราะเซิร์ฟเวอร์ NextJS ไม่ใช่ API gateway หลักของเรา เราจึงต้อง proxy ทุก request เอกสารไม่ชัดเจน และยังเพิ่มปัญหาแปลก ๆ อย่าง request timeout / max header size เป็นต้น
เฟรมเวิร์กนี้ผลักดันไปทางคลาวด์อย่างหนักมาก ซึ่งขัดกับเป้าหมายของเรา
ผู้ดูแลโปรเจกต์ก็ไม่ได้ช่วยเหลือเท่าไรนัก เครื่องมือ/เฟรมเวิร์กอื่นบางตัวถึงจะมีข้อบกพร่อง แต่เราก็ยังใช้เพราะผู้ดูแลเข้าถึงง่ายและช่วยเหลือดีมาก (ขอบคุณ Chillicream/HotChocolate)
จำได้ว่าเมื่อปีที่แล้วเคยอ่านบล็อกโพสต์ที่บอกว่าการย้ายจาก page router ไปเป็น app router ของ Next.js ช่วยเรื่อง SEO ได้ ตอนนี้เรากำลังย้ายจาก Next ไปเป็น React+Inertia.js เพราะค่าใช้จ่ายของ Vercel เพิ่มขึ้น ถ้านำแอปเดียวกันไป deploy บน VPS ของตัวเองแทนผู้ให้บริการคลาวด์ ปัญหาก็น่าจะแก้ได้ แต่ผมไม่เข้าใจว่าทำไมถึงอยากได้ความซับซ้อนขนาดนั้น แอปติดตามหนังสือต้องใช้ GraphQL, เฟรมเวิร์กฝั่งฟรอนต์เอนด์แยกต่างหาก และกระบวนการ build ที่ซับซ้อนจริงหรือ หรือว่าจริง ๆ แค่ deploy แอป RoR เดียวพร้อม HTML templates ลง VPS ตั้งแต่แรกก็น่าจะแก้ปัญหาได้แล้ว
ทุกครั้งที่เห็นบทความและการถกเถียงเกี่ยวกับเว็บกับสแตก ผมมักจะถามว่า "จริง ๆ แล้วกำลังแก้ปัญหาอะไรอยู่" และคำตอบก็มักเป็น "แสดงข้อความบนหน้าจอ"
เวลาต้องการ JS full stack โดยเฉพาะเมื่อมี DB รวมอยู่ด้วย ผมสงสัยว่าคนส่วนใหญ่ทำอะไรกัน สถานการณ์ ORM ค่อนข้างแตกเป็นเสี่ยง ๆ ไม่ก็ต้องเขียน SQL ตรง ๆ และสุดท้ายก็ยังต้องตัดสินใจเรื่องแบ็กเอนด์อยู่ดี จะใช้ express ไหม? Next.js ก็เป็นที่รู้จักดีแต่มีวาระที่น่าสงสัย Remix, Astro, TanStack ฯลฯ มันสับสนเพราะต้องคอยปรับใหม่และประเมินใหม่ตลอดว่าจะใช้อะไร
สำหรับโปรเจกต์ส่วนตัว ผมมักกลับไปใช้ Ruby on Rails เสมอ และมันก็ยังสนุกเสมอ ในทางกลับกัน นักพัฒนา Rails ที่หาได้มีน้อยเกินไป (เมื่อเทียบกับ JS) เลยไม่ค่อยเหมาะกับโปรเจกต์ระดับมืออาชีพ การเลือก JS และบ่อยครั้งก็ Java สำหรับแบ็กเอนด์จึงไม่ใช่เรื่องไร้ความรับผิดชอบ
สงสัยว่ามีใครรู้สึกคล้ายกันไหม
นักพัฒนาฟรอนต์เอนด์และแบ็กเอนด์สื่อสารกันไม่ค่อยลงตัวมานานแล้ว
ในทางประวัติศาสตร์ ผมในฐานะนักพัฒนาแบ็กเอนด์เกลียด Html/JS/CSS มันเป็นกระบวนทัศน์ที่ต่างอย่างมีนัยสำคัญจาก Swing/Awt, WinForms, Android UX ฯลฯ แค่นั้นก็ทำให้ผมหงุดหงิดและเลือกอยู่กับแบ็กเอนด์ต่อไป ถ้าจะเรียนฟรอนต์เอนด์ก็ต้องเรียนทั้งสามอย่าง ตอนนี้เพิ่งเริ่มคุ้นมือ
แต่ฝั่งนักพัฒนาฟรอนต์เอนด์ก็ต้องเรียน "อีกหนึ่งภาษา" เช่นกัน หลายภาษามีระบบ build ที่ต่างออกไปหรือชวนปวดหัวเมื่อเทียบกับ nvm และอย่างที่ใครก็ตามที่เคยย้ายภาษาเคยรู้ ต้องเรียนเฟรมเวิร์กใหม่ กระบวนทัศน์ใหม่ ฯลฯ ด้วย
ดังนั้นบางคนเลยตระหนักว่าพวกเขาสามารถผลัก JavaScript เข้าไปถึงฝั่งแบ็กเอนด์ได้ มันมีข้อเสียมากมาย แต่สำหรับคนที่ "ทำงานให้เสร็จ" โดยเฉพาะในโลกของ "ก็เพิ่มเซิร์ฟเวอร์เข้าไปอีกสิ" และ "เงิน VC ฟรี! เผามันไปกับอินฟราฯ!" ข้อเสียเหล่านั้นไม่ใช่เรื่องที่ต้องกังวล
แต่เหล่านักพัฒนาฟรอนต์เอนด์ ซึ่งตอนนี้กลายเป็น "ฟูลสแตก" แต่จริง ๆ คือ "ทำทุกอย่างด้วย JavaScript" ยังคงสร้างสรรค์งานออกมาอย่างโดดเด่น และสิ่งนี้ก็สะท้อนอยู่ในประกาศรับงานบน LinkedIn ตอนนี้ ที่ต้องการคนสาย Next.JS/Node.JS/อื่น ๆ มันคือภาษาเดียวที่ปกครองทุกสิ่ง
เป็นแค่ข้อคิดบางอย่าง แต่ผมคิดว่ามันเกี่ยวข้องอย่างมากกับเหตุผลที่คนเลือก Next.JS
ผมพูดในเชิงเทคนิคไม่ได้มากนัก (เพราะคุ้นแค่ Next.js และไม่คุ้นกับ Rails จึงไม่ชัดเจนว่าบทความนี้สะท้อนความคุ้นเคยของผู้เขียนกับ Rails หรือสะท้อนสถาปัตยกรรมที่เหมาะสมกว่าในเชิงเทคนิคจริง ๆ) แต่ผมคิดว่ามันแปลกที่บริษัทซึ่งมีวิศวกรซอฟต์แวร์หลายคนจะกังวลกับค่าอินฟราฯ ที่ต่ำกว่า 1,000 ดอลลาร์ต่อเดือน การกังวลเรื่องค่าโฮสติ้งไม่ใช่เรื่องฉลาดนัก
ถ้า Rails มุ่งเน้นไปที่การรองรับการทำงานร่วมกับเฟรมเวิร์กฟรอนต์เอนด์แบบ first-class จริง ๆ ตั้งแต่แรก ตอนนี้มันน่าจะใหญ่กว่านี้มาก มันทุ่มแรงไปกับ Hotwire เยอะมาก แต่ผมอยากใช้ React และคนอื่น ๆ ก็น่าจะอยากใช้สิ่งที่ตัวเองคุ้นเคย
ผมสงสัยว่าทำไมถึงมีการถกกันเรื่อง Next.js vs. SSR ด้วย Next.js เป็นแบบไฮบริดและทำได้ค่อนข้างดี ต่างจากเฟรมเวิร์ก SPA อื่น ๆ Next.js สร้างผลลัพธ์ HTML ที่ pre-rendered สำหรับ first load ที่รวดเร็ว มี efficient JS chunks, มีตัวเลือกให้ preload ตอนเอาเมาส์ไปวางบนลิงก์ หรือ preload ลิงก์ n+1 ทั้งหมดหลังจากหน้า render แล้ว และมีการโหลดภาพแบบมีประสิทธิภาพ (ล่วงหน้า) ตาม breakpoint ด้วย (ซึ่งมักเป็นจุดอ่อนของโซลูชัน SSR ล้วน)
ผมเคยเขียน Rails มาบ้าง แต่ไม่ค่อยเข้าใจว่าทำไมหลายคนถึงคลั่งไคล้มันนัก มันก็โอเคดีสมบูรณ์แบบ แต่ผมหาอะไรที่พิเศษมากไม่เจอ