EngineeringSenior DeveloperInterview Sessions

Interview Answers - Phase 2: Frontend & Full-Stack Integration

Trọng tâm trả lời đòi hỏi kiến thức cả hai thái cực API (BE) và Architecture (FE).


1. Web Frameworks (Next.js & React)

Câu 1: SSR vs SSG vs ISR vs CSR (E-commerce case)

  • Trả lời:
    • CSR: Browser down source trắng, tự vẽ HTML. Tệ SEO, Load initial chậm. Dùng cho Dashboard nội bộ Admin.
    • SSR: Đòi API ở Server lúc user F5/Enter url, sinh HTML trả về ngay. SEO tốt, nhưng Time-To-First-Byte (TTFB) bị chậm phụ thuộc Backend.
    • SSG: Build ra HTML chết ở màn hình máy build. Cực nhanh, free hosting CDN, nhưng ko dynamic được dữ liệu realtime.
    • ISR: SSG nhưng cho phép re-build lại trang ở Background ngầm theo Interval định kỳ, không block luồng user.
  • Architect Case (E-Commerce Catalog): Sử dụng ISR (Incremental Static Regeneration) (Hoặc NextJS 14+ Data fetching caching { next: { revalidate: 3600 } }).
    • Cache TTL 1 tiếng. User nào vào cũng view bản HTML cực nhanh (tốc độ SSG). Hết 1 tiếng, Next.js render một bản cập nhật mới ở background ngầm đổi trạng thái giá tiền. E-commerce không cần giá tiền cập nhật theo millisecond, SEO + Performance đánh tan độ trễ.

Câu 2: Hydration Mismatch trong SSR NextJS

  • Trả lời: Là tình trạng HTML mà Node server render trả xuống trình duyệt KHÁC MỘT CHÚT so với HTML mà môi trường Trình duyệt (Browser) tự render lần đầu ở Client lúc gắn React instance vào.
  • Ví dụ kinh điển: Header hiện Text "Thời gian lúc load trang: {new Date().toTimeString()}". Sever lúc sinh HTML là 10h00, nhưng Browser nhận xuống là 10h01, hai text khác nhau khiến React la làng "Warning: Text content did not match".
  • Cách Fix: Check if useEffect (isMounted boolean flag). Những gì lệ thuộc môi trường Browser (window context, datetime now) chỉ render return khi isMounted === true, ngược lại default Null ở SSR.

Câu 3: useMemo & useCallback lạm dụng phản tác dụng

  • Trả lời: Các hook này sinh ra để memoize (nhớ Cache) array/object lớn hoặc function để React không coi reference (con trỏ địa chỉ) của tham số lọt vào Dependency Array của useEffect / React.memo(Component) bị đổi thay, nhằm né Child Rerender thừa thãi.
  • Hệ quả Lạm dụng: React phải allocate (cấp phát RAM) 1 mảng dependency để tự tra soát mỗi lần render xem deps có đổi không (Deep Comparison ngầm). Việc chạy code tra soát này ở Parent còn tốn tài nguyên gấp mười lần việc để Component Child chạy lại thuật toán Virtual DOM Diffing đơn giản một text bình thường.

2. Vite & Architecture Frontend

Câu 4: Bí mật tốc độ của Vite

  • Trả lời: Vite lợi dụng ES Modules (ESM) Native trong các browser đời mới. Theo cấu trúc cổ Webpack (Bundle Pattern), nó crawl đống source từ index.js ra hàng nghìn file phụ để đóng gói nguyên 1 cục JS lớn rồi mới quăng ra DOM preview cho dev. Chỉnh sửa 1 dòng $\implies$ bundle lại toàn bộ cục JS.
  • Vite xé nhỏ làm 2 cỗ máy:
    1. Dependencies (React, Lodash): Vite dùng esbuild pre-bundle cực nhanh chúng thành ESM dạng Cache (vì thư viện NPM hiếm update thực thụ bởi dev).
    2. Source code của Dev: Vite chạy Serve Node gửi nguyên thủy 100% ESM theo native HTTP route. Trình duyệt tự fetch modules nào trang web cần. Khi sửa file A, Dev Server báo Websocket trigger trình duyệt kéo lại ĐÚNG DUY NHẤT file A đó. HMR $O(1)$ constant time.

Câu 5: Tổ chức Monorepo lớn chia sẻ API Clients

  • Giải pháp Architect:
    1. Sử dụng Workspace Tool (Ví dụ Turborepo + pnpm workspaces). Phân tách app ra folder apps/ (admin, web), và chia packages logic ra packages/.
    2. Tạo 1 Package nội bộ shared-ui dùng Typescript để cấu hình React Components (Design System chung).
    3. Tạo 1 Package api-client (Tự gen Fetch/Axios thông qua NSwag/OpenAPI của Backend .NET).
    4. Turborepo cho phép cache artifact build. Các repo con import local workspace versions. Việc đóng gói sẽ deduplicate triệt để (các dependencies trùng version ở root level Node modules sẽ không nhân bản RAM), giảm thời gian CI pipeline hàng loạt bằng turbo cache.

3. Full-Stack Integration & Security

Câu 6: localStorage chứa JWT gây rủi ro

  • Trả lời: Bị hổng XSS (Cross Site Scripting). Nếu diễn đàn có Box chat, hacker nhập nội dung chat chứa <script>localStorage.getItem('token') và chuyển qua api của hacker</script>, tất cả thành viên vào Box chat đọc sẽ tự chạy JS bẩn cướp Token, bị hacker trộm tài khoản không dấu vết.
  • Giải pháp: Server Backend Response Set-Cookie gắn vào trình duyệt cờ HttpOnly, Secure, SameSite=Strict. JS trên browser tuyệt đối không đọc được Token này bằng mã lệnh $\implies$ Né XSS triệt để.
  • Trả lời: Có token trong HttpOnly Cookie thoát được XSS, nhưng nảy sinh CSRF (Cross Site Request Forgery).
  • Khi user lướt tới Forum bẩn của hacker, hacker gài mã ảnh ngầm <img src="domain-ngan-hang.com/transfer/money" />. Vì cơ chế Cookie là browser tự động mang đính kèm trên TỌA ĐỘ DOMAIN API đích bất kể trang nào gọi, lệnh này vô tình gửi kèm Cookie Token hợp lệ lên .NET Backend!
  • Kiến trúc phòng thủ Antiforgery ở .NET + React:
    1. Backend cấu hình AddAntiforgery và Middleware.
    2. Backend cấp phát GET /api/csrf-token trả về 1 string token tạm, token này để dưới LocalStorage React (Token này không có quyền gì cả, bị đánh cắp XSS bằng rỗng, nó chỉ là con tem xác thực xuất xứ HTML page).
    3. React gửi POST lên BackEnd, attach Token đó vào Header X-CSRF-TOKEN. Cùng lúc đó trình duyệt attach Automagic Cookie JWT vào Headers.
    4. Backend .NET duyệt 2 chốt: Một là Cookie JWT hợp lệ, hai là Custom header CSRF này nằm trong dải đang chờ khớp chìa. Trình duyệt ngoài Domain hacker ép gửi Cookie nhưng ko giả form lấy Header CSRF được.

Câu 8: Real-time SignalR ở Microservices Scale

  • Trả lời: User A Connect Websocket qua Frontend Load Balancer rơi vào Pod 01 (.NET). User B gửi tin nhắn, HTTP request của User B lọt vào Pod 02 (.NET). Pod 02 tìm danh sách Socket ID để đẩy, nhưng Socket của A đang lưu ở RAM Pod 01. Tin nhắn bặt vô âm tín (Scale out vỡ trận).
  • Kiến trúc khắc phục: Dùng Backplane (Redis) hoặc Azure SignalR Service.
    • Các Pod .NET giờ kết nối Pub/Sub vào Redis Message Bus chung.
    • Pod 02 publish message ra Channel toàn cục Redis. Pod 01 hứng trigger từ redis liền phát xuống cho Socket của A. Đồng bộ diện rộng giải quyết vấn đề sticky session hay node-affinity.