แสดง 136 จาก 136 รายการ

React

8 ข้อ

State กับ Props ต่างกันยังไง?

ReactKrungsri SecuritiesENetworksCIMB Thai#hook#พื้นฐาน
  • Props = ข้อมูลที่ส่งจาก parent → child ผ่าน attribute เป็น read-only child แก้เองไม่ได้
  • State = ข้อมูลภายใน component เอง เปลี่ยนผ่าน useState / setState แล้ว component re-render
  • อธิบายสั้น ๆ ในสัมภาษณ์: props เป็น "argument ของ component" ส่วน state เป็น "หน่วยความจำภายใน"
  • ถ้าลูกอยากแจ้งกลับ parent ให้ส่ง callback function ลงมาผ่าน props แล้วให้ลูกเรียก

อธิบาย Hooks ที่ใช้บ่อย และกติกาสำคัญของ Hooks

ReactKrungsri SecuritiesENetworksCIMB Thai#hooks
  • useState — เก็บค่าที่เปลี่ยนแล้วต้อง render ใหม่
  • useEffect — ทำ side effect (เรียก API, subscribe, จัดการ DOM ภายนอก)
  • useMemo / useCallback — จำผลคำนวณ / จำ reference ของฟังก์ชัน เพื่อลด re-render
  • useRef — เก็บค่าที่เปลี่ยนแต่ "ไม่ต้อง" render ใหม่ เช่น ชี้ DOM, เก็บ timer id
  • useContext — อ่านค่าจาก Context โดยไม่ต้อง drill props
  • กติกา: เรียก hooks เฉพาะ top level ของ component ห้ามอยู่ใน if/loop เพราะ React ใช้ ลำดับการเรียก ในการจับคู่ state ของแต่ละ render

useEffect ทำงานยังไง เมื่อไหร่ต้อง cleanup?

React🧪 แบบจำลองKrungsri SecuritiesENetworksCIMB Thai#useEffect
  • ทำงาน หลัง render เสร็จ (ไม่ใช่ระหว่าง render)
  • deps array ควบคุมการรัน:
  • ไม่ใส่ = รันทุกครั้งที่ render
  • ใส่ [] = รันครั้งเดียวหลัง mount
  • ใส่ [x] = รันใหม่เมื่อ x เปลี่ยน
  • Cleanup (return function) ใช้เมื่อ: subscribe/unsubscribe, ตั้ง timer, addEventListener — กัน memory leak และกัน effect ซ้อนกันตอน strict mode เรียก 2 รอบ
  • ตัวอย่าง: useEffect(() => { const t = setInterval(...); return () => clearInterval(t); }, [])
🧪 แบบจำลอง: useEffect + cleanup (React) — กดดูการทำงานทีละขั้น
⚛️Component
🌐ภายนอก (DOM/API)
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/7

useMemo กับ useCallback ต่างกันยังไง เมื่อไหร่ควรใช้?

ReactENetworksCIMB Thai#performance
  • useMemo(fn, deps) — จำ ค่าผลลัพธ์ ใช้เมื่อคำนวณหนัก เช่น filter/sort list ใหญ่
  • useCallback(fn, deps) — จำ ตัวฟังก์ชัน (reference) ใช้เมื่อส่ง callback ให้ child ที่ถูก React.memo ครอบ หรือเป็น dependency ของ hook อื่น
  • หลักคิด: อย่าใช้กับทุกอย่าง — มีต้นทุนการเก็บ cache เอง ใช้เมื่อมีปัญหาจริง (คำนวณหนัก / re-render ชัดเจน)

Virtual DOM คืออะไร และ key ใน list สำคัญเพราะอะไร?

ReactKrungsri SecuritiesENetworksCIMB Thai#virtual dom#key
  • Virtual DOM = object tree จำลอง DOM จริงใน memory เมื่อ state เปลี่ยน React สร้าง tree ใหม่แล้ว diff (reconciliation) หาส่วนที่ต่าง แล้วแก้ DOM จริงเฉพาะจุดนั้น → ลดการจัดการ DOM ที่แพง
  • key ช่วยให้ diff รู้ว่า item ไหน "ตัวเดิม" ตอนเพิ่ม/ลบ/เรียงใหม่
  • ใช้ id ที่ stable อย่าใช้ index เมื่อ list มีการเพิ่ม/ลด/เรียงใหม่ เพราะจะ match ผิดแล้ว state/animation พัง

Re-render เกิดเมื่อไหร่ และลด re-render ยังไง?

ReactENetworksCIMB Thai#performance#re-render
  • เกิดเมื่อ: state ของตัวเองเปลี่ยน / parent render ใหม่ (default ลูก render ตาม) / context ที่ใช้เปลี่ยน
  • วิธีลด:
  • React.memo ครอบ component ที่ render แพง + parent ส่ง props ที่ reference คงที่ (ใช้ useMemo/useCallback ช่วย)
  • ย้าย state ลงลึก ให้ state อยู่ใกล้ component ที่ใช้ ไม่ต้องเก็บกลางอยู่บนสุด
  • แยก component เพื่อตัดขอบเขตการ render
  • virtualized list (react-window) สำหรับ list ยาว ๆ

เมื่อไหร่ควรเก็บ state แบบ local vs Context vs Redux?

ReactKrungsri SecuritiesENetworksCIMB Thai#แนวคิด
  • Local state: ใช้เฉพาะ component เดียว/กลุ่มเล็ก → เก็บที่ component นั้น (default ที่ดีที่สุด)
  • Context: ข้อมูล "ค่อนข้างคงที่" ใช้หลายที่ เช่น theme, ภาษา, ข้อมูล user ที่ login — เปลี่ยนบ่อย ๆ จะทำให้ consumer render มาก
  • Redux: state ที่แชร์กันทั้งแอป เปลี่ยนบ่อย มี logic กลาง ต้องการ devtools/time-travel — งานหนักค่อยคุ้ม
  • เทรนด์ใหม่: server state ใช้ React Query/TanStack Query แยกจาก client state ไม่ต้องยัดข้อมูลจาก API ลง Redux ทุกอย่าง

Controlled vs Uncontrolled component (ฟอร์ม) ต่างกันยังไง?

ReactENetworksCIMB Thai#form
  • Controlled: ค่า input ผูกกับ state (value + onChange) — validate และจัดการได้ทันทีที่พิมพ์ เป็นวิธีหลักของ React
  • Uncontrolled: DOM เก็บค่าเอง อ่านตอนจำเป็นผ่าน ref.current.value — เขียนน้อยกว่า เหมาะฟอร์มง่าย ๆ หรือ integrate กับ library เก่า

Next.js

5 ข้อ

SSR vs SSG vs ISR vs CSR ต่างกันยังไง เลือกใช้เมื่อไหร่?

Next.js🧪 แบบจำลองCIMB Thai#ssr#ssg# isr
  • SSR (Server-Side Rendering): render ใหม่ ทุก request — ข้อมูลสด/เป็นส่วนตัว เช่น หน้าที่ login แล้ว เหมาะระบบธนาคาร
  • SSG (Static Site Generation): สร้างครั้งเดียว ตอน build — หน้า landing, เอกสาร เน้นเร็วและ SEO
  • ISR (Incremental Static Regeneration): static แต่ regenerate ตามระยะเวลา (revalidate) — ได้ความเร็วของ static + ข้อมูลไม่เก่าเกินไป
  • CSR: render ที่เบราว์เซอร์หลังโหลด JS — แดชบอร์ดที่ไม่ต้องการ SEO
  • เลือกตาม: ข้อมูลเปลี่ยนไวไหม + ต้องการ SEO ไหม + เป็นข้อมูลส่วนตัวไหม
🧪 แบบจำลอง: SSG vs SSR vs ISR — ทำงานตอนไหน — กดดูการทำงานทีละขั้น
📦Build
🖥️Server
🧑‍💻Browser
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/8

App Router ต่างจาก Pages Router ยังไง?

Next.jsCIMB Thai#app router
  • Pages Router (รุ่นเก่า): ไฟล์ใน pages/ = หน้า, ดึงข้อมูลด้วย getServerSideProps / getStaticProps
  • App Router (รุ่นใหม่): โฟลเดอร์ app/, มี layout.tsx ที่คงอยู่ข้ามหน้า (ไม่ re-render ซ้ำ), nested layout, โหลดด้วย loading.tsx / error.tsx แบบสร้างเสร็จ
  • ใน App Router ทุกอย่างเป็น Server Component โดย default — จะใช้ state/hooks/browser API ต้องใส่ 'use client'

Server Components คืออะไร ดีตรงไหน?

Next.jsCIMB Thai#server components
  • Component ที่ render ที่ server แล้วส่ง result มาแสดง ไม่ถูกส่งมาเป็น JS ใน bundle
  • ข้อดี: ลดขนาด JS ฝั่ง client, await ดึงข้อมูลตรง ๆ ได้เลย, token/secret ไม่หลุดมาเบราว์เซอร์
  • ข้อจำกัด: ใช้ useState, useEffect, event onClick ไม่ได้ → ส่วนที่ต้อง interactive แยกเป็น Client Components ('use client')
  • แนวปฏิบัติ: คงเงื่อนไข server ให้มากที่สุด ดึง interactive ลงลึกเฉพาะปุ่ม/input ที่จำเป็น

ทำ API endpoint ใน Next.js ได้ไหม?

Next.jsCIMB Thai#api#bff
  • ได้ — เรียกว่า Route Handlers (App Router): app/api/xxx/route.ts export GET / POST ฯลฯ
  • ใช้เป็น BFF (Backend for Frontend) เช่น ซ่อน API หลังบ้าน, รวมหลาย API, เก็บ secret ไว้ฝั่ง server, แปลงข้อมูลให้พร้อมใช้
  • Pages Router ใช้ pages/api/*.ts (API Routes แบบเดิม)
  • ข้อควรระวัง: เหมาะกับ endpoint เบา ๆ ของหน้าเว็บ ไม่ใช่แทน backend ใหญ่

Next.js ช่วยเรื่อง image และ SEO ยังไง?

Next.jsCIMB Thai#seo#image
  • next/image: ย่อ/สร้างหลายขนาดอัตโนมัติ, lazy load, กัน layout shift (กำหนด width/height) → คะแนน LCP/CLS ดีขึ้น
  • Metadata API: ตั้ง title/description/OpenGraph ต่อหน้าได้จาก metadata export
  • SSR/SSG ทำให้ HTML มีเนื้อหาครบตั้งแต่แรก แมงมอมเห็นเลย

Redux

4 ข้อ

Redux ประกอบด้วยอะไร และ data flow เป็นยังไง?

Redux🧪 แบบจำลองKrungsri SecuritiesENetworks#แนวคิด
  • Store = ที่เก็บ state กลางอันเดียวของแอป
  • Action = object บอกว่า "เกิดอะไรขึ้น" เช่น { type: 'cart/addItem', payload }
  • Reducer = ฟังก์ชัน pure รับ (state เดิม, action) คืน state ใหม่ (ห้ามแก้ของเดิม)
  • dispatch = สั่ง action, selector = อ่าน state
  • Flow ทิศเดียว: UI dispatch → reducer คำนวณ state ใหม่ → store update → UI ที่ subscribe render ใหม่
🧪 แบบจำลอง: Redux data flow — กดดูการทำงานทีละขั้น
🖱️UI (React)
🔌Middleware
🧮Reducer
🗄️Store
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/5

Redux middleware ทำอะไร และ thunk กับ saga ต่างกันยังไง?

ReduxKrungsri SecuritiesENetworks#middleware
  • Middleware = ส่วนคั่นระหว่าง dispatch กับ reducer ใช้จัดงาน async/log
  • Redux Thunk: ให้ dispatch ฟังก์ชันได้ — เรียก API แล้ว dispatch action ผลลัพธ์ เขียนง่าย เหมาะงาน async ทั่วไป
  • Redux Saga: ใช้ generator function ฟัง action เหมือน background process — จัดการ takeLatest, debounce, cancel, ลำดับงานซับซ้อนได้ดี แต่ learning curve สูงกว่า
  • ปัจจุบันเริ่มใหม่ใช้ Redux Toolkit (RTK) ซึ่งมี RTK Query จัดการข้อมูลจาก server ให้

ทำไม Redux ต้อง immutable (ห้าม mutate state เดิม)?

ReduxKrungsri SecuritiesENetworks#immutability
  • Redux เทียบ reference ว่า state เปลี่ยนหรือยัง ถ้า mutate ของเดิม reference เดิม → component ไม่รู้ตัวว่าต้อง render ใหม่
  • การคืน object/array ใหม่เสมอ ทำให้ time-travel debugging และ devtools เปรียบเทียบ state ได้
  • RTK ใช้ Immer ให้เขียนแบบ "เหมือน mutate" ได้ แต่จริง ๆ สร้างใหม่ให้ตลอด

เมื่อไหร่ที่ควรเลี่ยง Redux (และใช้อะไรแทน)?

ReduxKrungsri SecuritiesENetworks#เลือกใช้
  • แอปเล็ก หรือ state ใช้แค่ไม่กี่ component → local state + props พอ
  • ข้อมูลจาก server (list, cache, pagination) → React Query / SWR จัดการ loading/caching/refetch ให้ดีกว่า
  • ข้อมูลค่อนข้างคงที่ → Context
  • Redux คุ้มเมื่อ: state กลางซับซ้อน หลายหน้าแชร์กัน, ต้องการ devtools ตรวจทุก action, ทีมใหญ่ต้องการรูปแบบเดียวกัน

Vue + Vuetify

5 ข้อ

Vue ต่างจาก React ยังไง (มุมมองคนใช้ทั้งคู่)?

Vue + VuetifyENetworks#เปรียบเทียบ
  • Syntax: Vue ใช้ template + directive (v-if, v-for, v-model) — React ใช้ JSX กับ JS ล้วน
  • Reactivity: Vue 3 ใช้ Proxy ติดตามการเปลี่ยน อัตโนมัติ แก้ state แล้วอัพเดตเอง — React ต้องผ่าน setState/useState แล้ว rerender ทั้งชิ้น
  • แนวทาง: Vue มีของครบในเฟรมเวิร์ก (router, pinia, style scoped) — React เป็น ecosystem เลือกประกอบเอง
  • สรุปสัมภาษณ์: แนวคิด component/state พ้องกัน ต่างที่ระดับ syntax และ reactivity — สลับใช้ได้ไม่ยาก

ref กับ reactive ต่างกันยังไง?

Vue + VuetifyENetworks#reactivity
  • ref(value): ใช้ได้ทุกชนิด (primitive/object) — เข้าถึงใน script ต้องมี .value แต่ใน template เรียกตรง ๆ ได้
  • reactive(obj): เฉพาะ object/array — เข้าถึง property ตรง ๆ ไม่ต้อง .value แต่ destructure แล้วเสีย reactivity
  • ทีมส่วนใหญ่จึงใช้ ref เป็นหลักเพื่อความสม่ำเสมอ

computed กับ watch กับ methods ต่างกันเมื่อไหร่ใช้อะไร?

Vue + VuetifyENetworks#computed#watch
  • computed: ค่าที่ "อนุพันธ์" จาก reactive อื่น — cache ไว้ คำนวณใหม่เมื่อ dependency เปลี่ยน → ใช้แสดงผล
  • watch: ฟังความเปลี่ยน แล้วทำ side effect (เรียก API, set timer) — ไม่คืนค่า
  • methods: เรียกทุกครั้งที่ render — ใช้กับ event handler / logic ที่ไม่ต้อง cache
  • จำง่าย ๆ: อยากได้ "ค่า" → computed, อยากให้ "เกิดอะไรบางอย่าง" → watch

v-if กับ v-show ต่างกันยังไง?

Vue + VuetifyENetworks#directive
  • v-if: สร้าง/ทำลาย element จริง ใน DOM — toggle บ่อยแพง (มีค่าใช้จ่ายสร้างใหม่) แต่ไม่แสดงก็ไม่กิน resource
  • v-show: element อยู่ใน DOM เสมอ แค่สลับ display: none — toggle ถี่ ๆ เบากว่า
  • เลือก: สลับบ่อย → v-show, ค่อยยใช้/เงื่อนไขเปลี่ยนนานครั้ง → v-if

Component ใน Vue สื่อสารกันยังไง?

Vue + VuetifyENetworks#component
  • props down / events up: พ่อส่ง props ลง, ลูก emit event ขึ้น (emit('submit', data))
  • v-model: two-way binding ทำฟอร์มสะดวก
  • provide/inject: ส่งข้อมูลข้ามหลายชั้นโดยไม่ต้อง drill props
  • ข้อมูลกลาง: Pinia (รุ่นใหม่) หรือ Vuex (รุ่นเก่า)
  • Vuetify: component library สไตล์ Material Design (ตาราง, ฟอร์ม, dialog ครบ) — งาน admin/e-commerce ที่ ENetworks ใช้ทำ UI ได้เร็วมาก

JavaScript พื้นฐาน

8 ข้อ

var, let, const ต่างกันยังไง?

JavaScript พื้นฐานKrungsri SecuritiesENetworksCIMB Thai#scope
  • var: function scope, มี hoisting (ได้ undefined), ประกาศซ้ำได้ → เลี่ยงใช้
  • let: block scope { }, hoisting แต่เข้า TDZ (Temporal Dead Zone — เรียกก่อนประกาศจะ error ทันที)
  • const: บล็อกสโคป + ห้าม assign ใหม่ (แต่ข้างใน object ยังแก้ได้ ถ้าไม่ freeze)
  • แนวปฏิบัติ: default ใช้ const, ต้อง reassign ค่อยใช้ let

Closure คืออะไร ใช้ทำอะไรได้?

JavaScript พื้นฐานKrungsri SecuritiesENetworksCIMB Thai#closure
  • ฟังก์ชันที่ "จำ" ตัวแปรรอบนอก (scope ที่มันถูกสร้าง) ได้ แม้ scope นั้นจะจบไปแล้ว
  • ตัวอย่าง: counter factory — function makeCounter(){ let c=0; return () => ++c; } แต่ละอันที่ได้มี c ของตัวเอง
  • ใช้จริง: private state, debounce/throttle, curry, hook ของ React ก็อาศัยแนวคิดนี้

Event loop ทำงานยังไง? setTimeout กับ Promise ใครก่อน?

JavaScript พื้นฐาน🧪 แบบจำลอง🧊 3DKrungsri SecuritiesENetworksCIMB Thai#event loop
  • JS เดินโค้ดบน call stack ทีละอย่าง (single thread); งาน async เกิดนอก stack (Web API / libuv)
  • เมื่อ stack ว่าง event loop เอา callback จาก queue ขึ้นมาทำ
  • Microtask (Promise .then, await ถัดไป) ทำก่อน macrotask (setTimeout, setInterval) เสมอ
  • คำถามคลาสสิก: setTimeout(()=>console.log(1),0); Promise.resolve().then(()=>console.log(2)); console.log(3); → ตอบ 3, 2, 1
🧪 แบบจำลอง: Event Loop — คำถามคลาสสิก 3, 2, 1 — กดดูการทำงานทีละขั้น
📚Call Stack
Microtask Queue
🐢Macrotask Queue
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/7
🧊 3D · Event Loop 3D — sync → microtask → macrotask🖱️ ลากเพื่อหมุน · scroll เพื่อซูม
console.log("1");
setTimeout(() => console.log("6"), 0);
Promise.resolve().then(() => console.log("4"));
(async () => {
  console.log("2");
  await null;          // พักที่นี่
  console.log("5");    // continuation = microtask
})();
console.log("3");
0/10

Promise / async-await ใช้ยังไง และจับ error ยังไง?

JavaScript พื้นฐานKrungsri SecuritiesENetworksCIMB Thai#async
  • await = ยุบ .then ให้อ่านเหมือน synchronous, ใช้ได้เฉพาะใน async function
  • จับ error ด้วย try/catch (เทียบเท่า .catch) — ถ้าไม่จับจะกลายเป็น unhandled rejection
  • Parallel: await Promise.all([a(), b()]) เร็วกว่า await ทีละอัน — ถ้าอยากให้ตัวไหนพังไม่ดึงทั้งกลุ่ม ใช้ Promise.allSettled
  • finally ใช้ปิด spinner ฯลฯ

== กับ === ต่างกันยังไง?

JavaScript พื้นฐานKrungsri SecuritiesENetworksCIMB Thai#เปรียบเทียบ
  • == แปลงชนิดข้อมูลก่อนเทียบ (coercion): '5' == 5 เป็น true, null == undefined เป็น true
  • === เทียบ ทั้งค่า + ชนิด ไม่แปลงอะไร — '5' === 5 เป็น false
  • ใช้ === เสมอเว้นแต่ตั้งใจเช็ค null/undefined พร้อมกัน (x == null)

this ใน JS เป็นยังไง และ bind/call/apply ต่างกันยังไง?

JavaScript พื้นฐานENetworks#this
  • this กำหนดตอน เรียกใช้ ไม่ใช่ตอนประกาศ: ใน method ของ obj → obj, เรียกเปล่า ๆ (non-strict) → window, arrow function → ยืม this จาก scope นอก
  • call(obj, a, b) / apply(obj, [a, b]) — เรียกทันที ต่างที่รูปแบบ argument
  • bind(obj) — สร้างฟังก์ชันใหม่ที่ผูก this ถาวร
  • ใน React สมัย hooks ปัญหา this แทบหายไป เพราะใช้ arrow function กับ function component

Debounce กับ Throttle ต่างกันยังไง ใช้เมื่อไหร่?

JavaScript พื้นฐานENetworksCIMB Thai#utility
  • Debounce: รอให้เลิกทำไปสักพักค่อยรัน (reset ทุกครั้งที่เกิด event) — เช่น ช่องค้นหา พิมพ์จบ 500ms ค่อยยิง API
  • Throttle: รันได้ ไม่เกิน 1 ครั้งต่อช่วงเวลา — เช่น scroll, resize, กดปุ่ม submit ซ้ำ
  • ทั้งคู่ลดจำนวนครั้งที่ฟังก์ชันทำงานจริง ประหยัด API/DOM

map, filter, reduce ต่างกันยังไง?

JavaScript พื้นฐานKrungsri SecuritiesENetworksCIMB Thai#array
  • map: แปลงทุกตัว → ได้ array เท่าเดิม (เช่นแปลง dto เป็น model สำหรับ UI)
  • filter: คัดที่ผ่านเงื่อนไข → array สั้นลงหรือเท่าเดิม
  • reduce: รวมทั้ง array เป็น ค่าเดียว (sum, group by, สร้าง object จาก array)
  • ทั้งสามตัว ไม่แก้ array เดิม (immutable) — เข้ากับ React ดี

Async / Promise

12 ข้อ

Promise ทำงาน 'ภายใน' จริง ๆ ยังไง? (state machine)

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#promise#internals#ลึกๆ

Promise = object ที่เก็บ สถานะ + ผลลัพธ์ + รายชื่อ callback ที่รออยู่

  • สถานะมี 3 อย่าง: pendingfulfilled (มี value) หรือ rejected (มี reason) — เปลี่ยนได้ ครั้งเดียว แล้ว lock ตลอดกาล (settled แล้วเปลี่ยนอีกไม่ได้)
  • executor รันแบบ synchronous ทันที ตอน new Promise(...) — ไม่รอแป๊บเดียว:
new Promise((resolve) => {
  console.log("A");      // รัน sync ทันที
  resolve(42);
});
console.log("B");        // A ก่อน B เสมอ
  • resolve(v) ไม่ได้ "เรียก callback" ทันที — แค่เปลี่ยนสถานะ แล้ว เข้าคิว microtask เพื่อเรียก .then ทีหลัง
  • then ที่ register กับ promise ที่ settled แล้ว ก็ยังรันเป็น microtask เสมอ (การันตี async)
  • resolve ซ้อน promise: resolve(p2) → รอ p2 settle ก่อน ("thenable unwrapping") นี่คือหัวใจของ chaining
  • ถ้า reject แล้วไม่มีใคร .catchunhandledrejection ใน browser / warning ใน Node

Microtask vs Macrotask — กติกาการรันเป๊ะ ๆ พร้อมโจทย์ลำดับ output

Async / Promise🧊 3DKrungsri SecuritiesENetworksCIMB Thai#event loop#microtask#คำถามยอดฮิต

กติกาทองของ Event Loop:

  1. รัน sync จน Call Stack ว่าง
  2. เคลียร์ microtask ทั้งกอง จนหมด (แม้ microtask คิด microtask เพิ่มระหว่าง drain — รันหมดเลย)
  3. ค่อยหยิบ macrotask 1 ตัว → แล้ววนกลับไปเช็ค microtask อีก
  • Microtask: .then/.catch/.finally, await resume, queueMicrotask, MutationObserver
  • Macrotask: setTimeout/setInterval, event (click/keydown), I/O, setImmediate (Node)
console.log("1");
setTimeout(() => console.log("6"), 0);
Promise.resolve().then(() => console.log("4"));
(async () => {
  console.log("2");
  await null;
  console.log("5");
})();
console.log("3");
// output: 1 2 3 4 5 6
  • ไล่ลำดับ: sync ทั้งหมดก่อน (1,2,3) → microtask ตามลำดับคิว (4 แล้ว continuation ของ await = 5) → ค่อย macrotask (6)
  • setTimeout(fn, 0) จริง ๆ คือ setTimeout(fn, 1ms) ตาม spec — และ ไม่เคย แซง microtask
🧊 3D · Event Loop 3D — sync → microtask → macrotask🖱️ ลากเพื่อหมุน · scroll เพื่อซูม
console.log("1");
setTimeout(() => console.log("6"), 0);
Promise.resolve().then(() => console.log("4"));
(async () => {
  console.log("2");
  await null;          // พักที่นี่
  console.log("5");    // continuation = microtask
})();
console.log("3");
0/10

async/await ถูกแปลง (desugar) เป็นอะไรตอน compile?

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#async await#compiler

async function = function ที่ return Promise เสมอ + await คือจุด "หยุด-ต่อ" ของ state machine

  • โค้ดหลัง await = continuation — engine แบ่งฟังก์ชันเป็นท่อน ๆ คล้าย generator (yield.next())
async function load() {
  const a = await step1();   // จุด split
  return step2(a);
}
// ~ เทียบเท่ากับ
function load() {
  return Promise.resolve()
    .then(() => step1())     // ท่อน 1
    .then((a) => step2(a));  // ท่อน 2 (continuation)
}
  • เจอ await → ฟังก์ชัน หยุดและ pop ออกจาก stack ทันที (ไม่ block thread!) คืน promise ให้คนเรียกไปก่อน
  • พอ promise ที่ await settle → continuation ถูก คิวเป็น microtask แล้วกลับมารันต่อ
  • return x ใน async fn = resolve(x), throw e = reject(e)
  • ข้อสอบชอบถาม: โค้ดก่อน await แรก คือ sync (รันบน stack ของคนเรียก) — โค้ดหลัง await แรกเป็น async เสมอ

Promise chaining + error เด้งข้ามท่อนยังไง (error propagation)?

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#chaining#error

ทุก .then คืน promise ใหม่เสมอ — เลยต่อกันเป็นท่องได้ และ error เด้งข้ามท่อนที่ไม่มี handler

getUser()
  .then((u) => getOrders(u.id))     // return promise → ท่อนต่อไปรองานนี้
  .then((orders) => save(orders))
  .catch((e) => log(e))             // จับ error จาก "ทุกท่อนข้างบน"
  .finally(() => hideSpinner());    // รันเสมอไม่สนผลลัพธ์ (ไม่เปลี่ยนค่าที่ส่งต่อ)
  • catch(fn) = then(null, fn) — จับได้ทั้ง rejection และ throw ในท่อนก่อนหน้า
  • ถ้า .catch ไม่ rethrow → ถือว่า "กู้error สำเร็จ" → ท่อนถัดไปกลับมาเป็น fulfilled
  • then(onF, onR) แบบ 2 ตัว: onF ไม่จับ error ของท่อนก่อนหน้า (ต่างจาก catch ที่จับทั้งท่อ)
  • อย่าใส่ .catch ระดับลึกเงียบ ๆ แล้วปล่อย UI คิดว่าสำเร็จ — error ควร bubble ถึงที่จัดการจริง
  • ทำนายผล: .catch คืนค่าปกติ → .then ถัดไปรัน; .catch ที่ throw → .then ข้ามไป catch ตัวถัดไป

Promise.all vs allSettled vs race vs any — ต่างกันยังไง เลือกใช้เมื่อไหร่?

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#combinators#เปรียบเทียบ
  • Promise.all([p1, p2]) — เก็บค่าตาม ลำดับ array ไม่ใช่ลำดับเสร็จ / ตัวไหน reject ตัวเดียว → ทั้งก้อน reject ทันที (fail-fast) → ใช้เมื่องาน "ต้องสำเร็จทุกอย่าง" เช่นโหลดข้อมูลหน้าจอ
  • Promise.allSettled — รอทุกตัวจบ ได้ [{status: "fulfilled", value}, {status: "rejected", reason}] → ใช้เมื่อ "อยากรู้ผลทุกงาน" เช่น bulk import รายงานว่าสำเร็จ/พังกี่รายการ
  • Promise.race — ตัวแรกที่ settle (ไม่ว่าจะสำเร็จ/พัง) ชนะ → ใช้ทำ timeout, cancel
  • Promise.any — ตัวแรกที่ fulfill ชนะ / พังทั้งหมด → AggregateError → ใช้เมื่อ "ขออันใดอันหนึ่งสำเร็จก็พอ" เช่นลองหลาย mirror
  • สังเกต: ตัวที่แพ้ race/any ยังรันต่อจนจบ (ไม่ได้ถูก cancel) — cancel ต้องใช้ AbortController
const results = await Promise.allSettled(urls.map(fetchOne));
const ok = results.filter((r) => r.status === "fulfilled").length;

โจทย์ลำดับ output ระดับสัมภาษณ์ — อธิบายเป๊ะ ๆ ทีละบรรทัด

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#puzzle#output#ข้อสอบ
console.log("start");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve()
  .then(() => { console.log("p1"); })
  .then(() => { console.log("p2"); });
const p = new Promise((res) => {
  console.log("executor");
  res();
}).then(() => console.log("p0"));
console.log("end");

Output: start → executor → end → p1 → p0 → p2 → timeout

  1. start — sync
  2. executor — executor ของ new Promise รัน sync ทันที (จุดที่คนพลาดบ่อยสุด)
  3. timeout เข้า macrotask queue / p1 + p0 เข้า microtask queue ตามลำดับการ register
  4. end — sync จบ → stack ว่าง
  5. microtask ตาม FIFO: p1 → ระหว่างรัน .then ตัวถัดไป (p2) ถูกคิว ต่อท้ายคิว หลัง p0p0p2
  6. หมด microtask ค่อย macrotask: timeout
  • สรุปเทคนิค: แยกให้ได้ว่าอะไร sync / อะไรเข้าคิวเมื่อไหร่ / then ที่ต่อกันคิว "ทีหลัง" คำที่ register ก่อนหน้า

จับ error ของ async ยังไงให้ถูกจุด? (try/catch + floating promise + unhandled rejection)

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#error handling#best practice
  • try/catch ครอบ await จับได้เพราะ rejection กลายเป็น exception ตรงจุดนั้น:
try {
  const res = await fetch(url);
  if (!res.ok) throw new Error(res.status);
} catch (e) {
  setError(e.message);       // จับทั้ง network error และ throw ของเรา
}
  • ลืม await (floating promise) = error หลุดมือ เพราะ fn() ไม่ throw — มันคืน promise ที่ reject ตอนหลัง → กลายเป็น unhandledrejection
  • เลือกได้ 2 แบบ: await fn().catch(handle) หรือ try/catch ครอบ await — อย่า mix จนซ้อนเยอะ
  • fetch ไม่ reject กับ HTTP 4xx/5xx (reject เฉพาะ network down) — ต้องเช็ค res.ok เอง อีกจุดที่ข้อสอบชอบถาม
  • ตั้ง safety net ระดับแอป: window.addEventListener("unhandledrejection", ...) ส่งเข้า logging
  • ใน React: จับ error ใน event handler/effect เอง — error boundary ไม่จับ error ที่เกิดใน async callback

ยกเลิกงาน async ยังไง? (AbortController + timeout + cleanup ใน useEffect)

Async / PromiseENetworksCIMB Thai#abortcontroller#cancel

Promise ธรรมดา cancel ไม่ได้ — แต่ AbortController เป็นสัญญาณกลางที่ฝั่ง producer เช็คแล้วเลิกทำ

const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 5000);
try {
  const res = await fetch(url, { signal: ctrl.signal });
} catch (e) {
  if (e.name === "AbortError") return;  // ถูกยกเลิก ≠ error จริง
  throw e;
} finally { clearTimeout(timer); }
  • ใน React ต้อง abort ใน cleanup กัน state update หลัง unmount / กัน request เก่าแซงใหม่ (stale response):
useEffect(() => {
  const ctrl = new AbortController();
  fetch(url, { signal: ctrl.signal }).then(...).catch(...);
  return () => ctrl.abort();   // cleanup
}, [url]);
  • หลาย request ใช้ signal ร่วมกันได้ / AbortSignal.timeout(5000) = ทางลัดสร้าง signal ที่หมดเวลาเอง
  • สำหรับงานที่ไม่ใช่ fetch: เขียน await ในลูปแล้วเช็ค signal.aborted ระหว่างทำ

ทำงานหลายงานพร้อมกันแบบจำกัดจำนวน (concurrency limit) ยังไง?

Async / PromiseENetworksCIMB Thai#concurrency#pattern

Promise.all ยิงทุกอันพร้อมกัน — งานเป็นพันอันจะกด server ตาย ต้องมี pool จำกัด

async function mapLimit(items, limit, worker) {
  const results = new Array(items.length);
  let next = 0;
  async function runner() {
    while (next < items.length) {
      const i = next++;                  // ควักงานถัดไป (atomic ใน JS เพราะ single-thread)
      results[i] = await worker(items[i], i);
    }
  }
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, runner));
  return results;                        // ครบตามลำดับเดิมเสมอ
}
  • ไอเดีย: สร้าง runner จำนวน = limit ตัว แต่ละตัววนควักงานจากคิวเดียวกันจนหมด
  • อีกทาง: p-limit (library) หรือใช้ Semaphore pattern
  • เลือกใช้: sequential (for + await) = ช้าแต่ลำดับชัวร์ / all = เร็วสุด / limit = สมดุล — ข้อสอบชอบถาม "ทำไมไม่ใช้ Promise.all ตอนข้อมูลเยอะ"

อะไรใหม่ในโลก Promise: top-level await, Promise.withResolvers, Promise.try?

Async / PromiseCIMB Thai#es2024#modern
  • top-level await (ES2022): await นอกฟังก์ชันได้ใน ES module — Next.js ใช้จุดนี้ทำ streaming SSR รอ data ก่อนเรนเดอร์เสร็จ
  • Promise.withResolvers() (ES2024) ลบ boilerplate รูปแบบ "เก็บ resolve ไว้ใช้ทีหลัง":
// เดิม: let resolve; const p = new Promise((r) => resolve = r);
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve("done"), 1000);
await promise;
  • Promise.try(fn) (ES2025): เรียกฟังก์ชัน sync แล้วได้ผลเป็น promise เสมอ — จับ throw แบบ sync ให้กลายเป็น rejection ทันที (สะดวกกับ .catch ท่อเดียวจบ)
  • ที่ควรรู้ต่อ: AbortSignal.any([...]) รวมหลาย signal, structuredClone คู่กับ web worker
  • อธิบายในสัมภาษณ์: ของใหม่พวกนี้แก้ pain point จริง — withResolvers สำหรับ event/callback bridge, try สำหรับ เริ่มท่อด้วยฟังก์ชัน sync ได้ลื่น

เขียน retry + exponential backoff ยังไงให้ใช้งานจริง?

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#retry#pattern

หลักการ: ลองใหม่เฉพาะ error ที่ "น่าจะหายเอง" (network/5xx/429) + รอด้วยเวลาเพิ่มขึ้น + มี jitter กัน thundering herd

async function retry(fn, { tries = 3, baseMs = 300 } = {}) {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (e) {
      const retriable = e.name === "TypeError" || e.status >= 500 || e.status === 429;
      if (i >= tries - 1 || !retriable) throw e;
      const wait = baseMs * 2 ** i + Math.random() * 100;   // 300, 600, 1200 + jitter
      await new Promise((r) => setTimeout(r, wait));
    }
  }
}
  • จุดที่ต้องเช็ค retriable — อย่า retry กับ 400/401/403 เพราะลองร้อยครั้งก็พังเหมือนเดิม
  • 429 ควรอ่าน header Retry-After มาใช้แทนสูตรถ้า server บอกมา
  • ใช้คู่กับ AbortController ได้: โดน abort ให้ throw ทันทีไม่ต้อง retry
  • ใน production: พวกนี้มักอยู่ใน layer ของ api client / react-query (retry option) ให้แล้ว

await ใน for loop กับ Promise.all — กับดักยอดฮิตข้อสอบ?

Async / PromiseKrungsri SecuritiesENetworksCIMB Thai#await in loop#trap#ข้อสอบ
// A: ลำดับ (sequential) — งานชิ้นหลังรอชิ้นก่อนเสร็จเสมอ
for (const id of ids) {
  await save(id);            // รวมเวลา = ผลรวมของทุกงาน
}
// B: ขนาน (parallel)
await Promise.all(ids.map((id) => save(id)));   // รวมเวลา ≈ งานที่ช้าที่สุด
  • A จำเป็นเมื่องานถัดไป "ต้องใช้ผลของก่อนหน้า" หรือ server รับ request ทีละอันเท่านั้น
  • B เร็วกว่ามากแต่ระวัง: ลำดับ "จบ" ไม่การันตี, งานเยอะจะกด server (ใช้ limit คุม), ตัวนึงพัง = throw ทันที
  • กับดักในข้อสอบ: ids.forEach(async (id) => await save(id)) — forEach ไม่รอ async callback! วนจบก่อนงานเสร็จ → ใช้ for...of หรือ map + Promise.all เท่านั้น
  • for await...of ใช้กับ async iterable (stream ทีละชิ้น) ไม่ใช่ array ธรรมดา
  • สรุปตอบสัมภาษณ์: "ถ้าอิสระต่อกันผมใช้ map + Promise.all, ถ้ามี dependency หรือต้องคุม rate ผมใช้ for + await หรือ pool"

TypeScript

4 ข้อ

ทำไมต้องใช้ TypeScript?

TypeScriptKrungsri SecuritiesENetworksCIMB Thai#ประโยชน์
  • จับ error ตั้งแต่ตอนเขียน/compile ไม่ต้องรอรันแล้วพังตอน production
  • type/interface ทำหน้าที่เป็น เอกสาร ของข้อมูล — ทีมอ่านรู้ทันทีว่า API ส่งอะไรมา
  • IDE ช่วย autocomplete แม่นขึ้น, refactor ปลอดภัยขึ้น
  • ค่าใช้จ่าย: ต้อง compile และมี learning curve เรื่อง type — แต่คุ้มเมื่อโปรเจกต์โตและคนหลายคนแก้พร้อมกัน

interface กับ type ต่างกันยังไง เลือกยังไง?

TypeScriptENetworksCIMB Thai#type
  • interface: อธิบายรูปร่าง object, extend/merge ได้ (declaration merging) เหมาะกำหนด public API/contract
  • type: ทำได้มากกว่า — union (A | B), intersection, tuple, mapped/conditional type
  • สรุปง่าย ๆ: ของที่เป็น object ที่อาจถูก extend → interface, ต้องการ union หรือ transform type → type อย่างเดียวเท่านั้น

Generics คืออะไร ยกตัวอย่างการใช้?

TypeScriptENetworksCIMB Thai#generics
  • คือ "ตัวแปรชนิดข้อมูล" ทำให้โค้ดใช้ซ้ำได้หลาย type โดยรักษาความแม่นของ type ตลอด
  • ตัวอย่างที่เจอทุกวัน: Array<T>, Promise<T>
  • เขียนเอง: function getField<T, K extends keyof T>(obj: T, key: K): T[K] หรือ API wrapper api.get<User>('/me') ได้ response เป็น User เต็ม ๆ

any, unknown, never ต่างกันยังไง + utility types ที่ใช้บ่อย?

TypeScriptCIMB Thai#utility types
  • any: ปิดตรวจทุกอย่าง (หลุดจากความปลอดภัย) — เลี่ยง
  • unknown: รับอะไรก็ได้แต่ต้องตรวจ/ยืนยัน type ก่อนใช้ — ปลอดภัยกว่า any สำหรับค่าจากภายนอก
  • never: ค่าที่เป็นไปไม่ได้ (เช่น return ของฟังก์ชันโยน error เสมอ)
  • Utility ที่ใช้บ่อย: Partial<T>, Pick<T, K>, Omit<T, K>, Record<K, V>, ReturnType<F>

HTML / CSS / Tailwind

4 ข้อ

Box model คืออะไร?

HTML / CSS / TailwindKrungsri SecuritiesENetworksCIMB Thai#box model
  • ทุก element ประกอบด้วย content → padding → border → margin (จากในออกนอก)
  • จำเป็นตอนคำนวณขนาดจริง: box-sizing: content-box (default) width = แค่ content แต่ border-box รวม padding+border แล้ว
  • ทุกงานจริงมัก reset เป็น * { box-sizing: border-box } เพราะคิดขนาดง่ายกว่า

position แต่ละแบบต่างกันยังไง?

HTML / CSS / TailwindKrungsri SecuritiesENetworksCIMB Thai#position
  • static: ปกติ (default) ไหลตามเอกสาร
  • relative: ขยับจากตำแหน่งเดิมได้ (top/left) และทำหน้าที่เป็น แกนอ้างอิง ให้ absolute ลูก
  • absolute: หลุดจากการไหล ชี้ตำแหน่งกับ ancestor ที่ position ไม่ใช่ static ที่ใกล้สุด
  • fixed: ติดกับ viewport (เช่นแถบเมนูบนสุด) เลื่อนหน้าก็ไม่ขยับ
  • sticky: อยู่กับที่จนถึงจุดที่กำหนดแล้วเกาะ (เช่น หัวตาราง)

Flexbox กับ Grid ต่างกันยังไง เลือกยังไง?

HTML / CSS / TailwindKrungsri SecuritiesENetworksCIMB Thai#layout
  • Flexbox: แกนเดียว (แถว หรือ คอลัมน์) — จัด ข้างใน component เช่น แถบ icon, form row, nav
  • Grid: สองมิติ (แถว × คอลัมน์ พร้อมกัน) — จัด โครงหน้าเว็บ, ตาราง layout, gallery
  • ใช้ร่วมกันได้: Grid วางโครงใหญ่ แล้วข้างในแต่ละช่องใช้ Flex จัดย่อย

ทำ responsive ยังไง และ Tailwind ช่วยอะไร?

HTML / CSS / TailwindKrungsri SecuritiesENetworksCIMB Thai#responsive
  • หลัก: viewport meta + mobile-first เขียน mobile ก่อนแล้วเพิ่ม @media (min-width: ...) ขยายขึ้น
  • อย่า fix ขนาดด้วย px ล้วน ใช้ %, rem, max-width, min-height
  • Tailwind = utility-first class สั้น ๆ (flex justify-between p-4 md:text-lg) — responsive ผ่าน prefix breakpoint (sm:, md:, lg:) ไม่ต้องเขียน media query เอง และไม่ตั้งชื่อ class ใหม่

Browser / Security / Performance

5 ข้อ

CORS คืออะไร เกิดเมื่อไหร่ แก้ได้ยังไง?

Browser / Security / Performance🧪 แบบจำลองKrungsri SecuritiesENetworksCIMB Thai#cors#คำถามยอดฮิต
  • = Cross-Origin Resource Sharing — กลไกความปลอดภัยของเบราว์เซอร์ (ไม่ใช่ของ server) กันเว็บ A เรียก API ของเว็บ B โดยไม่ได้รับอนุญาต
  • เกิดเมื่อ fetch ข้าม origin (โดเมน/port/protocol ต่างกัน) และ server ไม่ส่ง header อนุญาต
  • Request ที่ไม่ใช่ simple จะมี preflight OPTIONS ก่อนเสมอ
  • แก้ถูกวิธี: backend ใส่ Access-Control-Allow-Origin ฯลฯ ให้ถูก domain, หรือทำ proxy ให้ same-origin (เช่น Next.js rewrites) — ไม่ใช่ปิด security ของเบราว์เซอร์
🧪 แบบจำลอง: CORS + Preflight — กดดูการทำงานทีละขั้น
🧑‍💻เว็บ A (Browser)
🖥️API B (origin อื่น)
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/6

cookie vs localStorage vs sessionStorage และควรเก็บ token ที่ไหน?

Browser / Security / PerformanceKrungsri SecuritiesENetworksCIMB Thai#storage
  • cookie: ส่งไปกับทุก request อัตโนมัติ (เปลือง bandwidth), ตั้ง httpOnly ได้ → JS อ่านไม่ได้ กัน XSS, มี SameSite กัน CSRF
  • localStorage: เก็บถาวรในเบราว์เซอร์ เก็บ ~5-10MB, JS อ่านได้ → ถ้าโดน XSS โดนขโมย
  • sessionStorage: เหมือน localStorage แต่หายเมื่อปิดแท็บ
  • แนวทาง token: access token เก็บ ใน memory (state) + refresh token ใน httpOnly cookie สั้น ๆ — สละความสะดวกแลกความปลอดภัย

XSS กับ CSRF ต่างกันยังไง ป้องกันยังไง?

Browser / Security / PerformanceKrungsri SecuritiesENetworksCIMB Thai#security
  • XSS (Cross-Site Scripting): ฝัง JS ชั่วร้ายในหน้าเว็บ โดนทำงานบนเบราว์เซอร์ของเรา → ขโมย cookie/token
  • ป้องกัน: escape output (React ทำให้อยู่แล้ว ถ้าไม่ใช้ dangerouslySetInnerHTML), CSP header, httpOnly cookie
  • CSRF: ปลอมเป็นผู้ใช้ ส่ง request ไปยังเว็บที่เรา login ค้างไว้ (ผ่านแท็บอื่น/ฟอร์มซ่อน)
  • ป้องกัน: CSRF token, SameSite=Lax/Strict ของ cookie, ตรวจ Origin/Referer, ใช้ Authorization header แทน cookie

SPA routing แบบ hash กับ history ต่างกันยังไง?

Browser / Security / PerformanceENetworksCIMB Thai#spa
  • Hash routing (#/page): ส่วนหลัง # ไม่ถูกส่งไป server → deploy ง่าย ไม่ต้อง config อะไร แต่ URL ไม่สวยและ SEO แย่กว่า
  • History routing (/page): URL สะอาด ใช้ history.pushState — แต่ user กด refresh แล้ว server ต้อง fallback กลับมาที่ index.html ไม่งั้น 404
  • Next.js (โดยเฉพาะ SSR) แก้เรื่องนี้ให้อยู่แล้ว

Web Vitals คืออะไร และทำหน้าเว็บเร็วขึ้นยังไง?

Browser / Security / PerformanceENetworksCIMB Thai#performance
  • ตัวชี้วัดหลักของ Google: LCP (โหลดเนื้อหาหลักเสร็จ, ดี <2.5s), INP (ตอบสนองตอน interact), CLS (layout เลื่อน/กระตุก)
  • วิธีเร่ง:
  • ลด JS bundle: code splitting (React.lazy / dynamic import ของ Next), tree-shaking, ลด library หนัก
  • รูป: ย่อขนาด, ใช้ next/image, lazy load รูปนอกจอ
  • cache API ด้วย header/CDN, prefetch หน้าที่คาดว่าจะไป
  • วัดด้วย Lighthouse ใน Chrome DevTools

Go (Golang)

8 ข้อ

ทำไมระบบ API ยุคใหม่นิยมใช้ Go?

Go (Golang)CIMB Thai#ภาพรวม
  • Concurrency แบบ goroutine เบามาก (เริ่มต้น stack ~2KB) สร้างเป็นพัน ๆ ตัวได้สบาย เหมาะ API ที่ต้องจัดการ request พร้อมกันเยอะ
  • compile เป็น binary เดียว ไม่ต้องมี runtime/VM → deploy ง่าย, กิน memory น้อย, start ไว (ดีกับ container)
  • เรียบง่าย มี keyword น้อย, เน้นความชัดเจน, เครื่องมือมาตรฐาน (go fmt, go test) ครบ
  • ที่ CIMB ใช้ Go ทำ REST API รองรับระบบ LINE OA (Withholding Tax) คู่กับ React/Next.js

Goroutine กับ Channel คืออะไร ใช้ยังไง?

Go (Golang)🧪 แบบจำลอง🧊 3DCIMB Thai#concurrency#คำถามยอดฮิต
  • goroutine: ฟังก์ชันที่รันขนานกัน เขียน go doWork() พอ — เบากว่า OS thread มาก
  • channel: ท่อส่งข้อมูลระหว่าง goroutine — ปรัชญา Go คือ *"Don't communicate by sharing memory, share memory by communicating"* → ส่งค่าผ่าน channel แทนการล็อกตัวแปรร่วม
  • ch := make(chan int) ส่ง ch <- v รับ v := <-ch (blocking จนมีค่า — เป็นจุด sync ไปในตัว)
  • รอให้ทุก goroutine จบ: sync.WaitGroup (Add/Done/Wait)
  • select ใช้เลือกรับ/ส่งจากหลาย channel พร้อมกัน เช่น รอผล หรือ timeout
🧪 แบบจำลอง: Goroutine + Channel + WaitGroup — กดดูการทำงานทีละขั้น
🚪main()
🤖worker goroutine
📮channel
WaitGroup
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/6
🧊 3D · Goroutine + Channel 3D (Go)🖱️ ลากเพื่อหมุน · scroll เพื่อซูม
func main() {
    ch := make(chan int)        // unbuffered
    go worker(1, ch)            // goroutine ใหม่
    ch <- 1                     // ส่ง (บล็อกจนมีคนรับ)
    close(ch)
}
0/8

defer ทำงานยังไง ใช้เมื่อไหร่?

Go (Golang)CIMB Thai#defer
  • เลื่อนการทำงานไปทำ ก่อนฟังก์ชัน return พอดี และเป็นแบบ LIFO (ตัวหลังเรียกก่อน)
  • ใช้ปิด resource ให้แน่นอน: defer file.Close(), defer rows.Close(), defer mu.Unlock()
  • จับ panic กู้ระบบ: defer func(){ if r := recover(); r != nil {...} }()

Interface ของ Go ต่างจากภาษาอื่นยังไง ดีตรงไหน?

Go (Golang)CIMB Thai#interface
  • Go เป็น implicit: struct ไม่ต้องประกาศ "implements" — แค่มี method ตรง signature ก็ถือว่า implement interface นั้น
  • ทำให้ decouple ได้ดี: ฟังก์ชันรับ interface{ error }, io.Reader ไม่ผูกกับ implementation
  • ใช้ทำ mock ตอนเทสต์ ง่าย — define interface เล็ก ๆ ตรงที่ใช้ (กฎ: interface ควรประกาศที่ ฝั่งผู้ใช้)
  • interface เปล่า any (= interface{}) รับได้ทุกค่า แต่ต้อง type assert (v.(string)) ก่อนใช้

Go จัดการ error ยังไง?

Go (Golang)CIMB Thai#error handling
  • error เป็น ค่า (value) ไม่ใช่ exception — เขียน if err != nil { return err } เป็นประจำ
  • wrap บริบท ตอนส่งต่อ: fmt.Errorf("query user: %w", err) แล้วผู้เรียกเช็คด้วย errors.Is / ดึงรายละเอียดด้วย errors.As
  • สร้าง sentinel error เป็นตัวแปร (var ErrNotFound = errors.New("not found")) เพื่อเทียบด้วย errors.Is ได้
  • ข้อดีมุมสัมภาษณ์: flow การจัดการ error เห็นชัดในโค้ด ไม่มี exception โผล่กะทันหัน

Pointer receiver กับ Value receiver ต่างกันยังไง?

Go (Golang)CIMB Thai#struct
  • value receiver (s MyStruct) method(): ได้ สำเนา — แก้ field ข้างในไม่กระทบตัวเดิม
  • pointer receiver (s *MyStruct) method(): ทำงานกับตัวจริง — แก้ state ได้, ไม่ copy struct ใหญ่ (ประหยัด)
  • เลือก: method ไหนต้องแก้ค่า หรือ struct ใหญ่ → pointer; ถ้าต้องการ immutability → value
  • ในทีมนิยมใช้ pointer ให้เป็นกลางทั้ง struct เพื่อ consistency

context ใน Go ใช้ทำอะไร?

Go (Golang)CIMB Thai#context
  • พกข้อมูล + สัญญาณยกเลิก (cancel) / timeout ไปตาม call chain ของ request
  • ทุก handler เริ่มด้วย ctx := r.Context() แล้วส่งต่อเข้า service/DB call — เมื่อ client ยกเลิก/หมดเวลา ทุกชั้นรู้และหยุดทำงาน (query ก็ยกเลิก)
  • context.WithTimeout(ctx, 3*time.Second) ตั้งเวลา, context.WithValue ใส่ค่า เช่น request id (อย่าใช้เก็บพารามิเตอร์ธุรกิจ)

โครงสร้าง REST API ด้วย Go + Gin เป็นยังไง?

Go (Golang)CIMB Thai#gin#โครงสร้าง
  • Gin = web framework ยอดนิยม: routing เร็ว, middleware, binding JSON ให้ (c.ShouldBindJSON(&req))
  • Middleware คือฟังก์ชันคั่น request (c.Next() ส่งต่อ) — ใช้ทำ logging, auth, recover จาก panic, CORS
  • โครงมาตรฐาน: main.go → router → handlers (รับ/ตอบ HTTP) → services (business logic) → repositories (คุย DB) — ชั้นชัด เทสต์แยกได้
  • ต่อ SQL Server ด้วย database/sql + driver go-mssqldb หรือใช้ GORM เป็น ORM (migrate, struct↔table)

C# / .NET Core

7 ข้อ

ASP.NET Core pipeline / middleware ทำงานยังไง และลำดับสำคัญยังไง?

C# / .NET CoreKrungsri SecuritiesENetworks#middleware
  • ทุก request ไหลผ่าน ห่วงโซ่ middleware เข้าไปแล้วย้อนออกมา (จริง ๆ เป็นรูปตัว U)
  • Middleware ที่ควรมี: ExceptionHandler → HTTPS redirect → Static files → Routing → CORS → Authentication → Authorization → Controller endpoints
  • ลำดับผิดพังทันที เช่น ใส่ UseAuthentication หลัง UseAuthorization จะ authorize ไม่ได้, ใส่ UseCors หลัง UseRouting/Mvc จะ CORS ไม่ทำงาน
  • เขียนเองได้: app.Use(async (ctx, next) => { /*ก่อน*/ await next(); /*หลัง*/ })

DI lifetime 3 แบบ (Transient / Scoped / Singleton) ต่างกันยังไง?

C# / .NET CoreKrungsri SecuritiesENetworks#di#คำถามยอดฮิต
  • Transient: สร้างใหม่ทุกครั้งที่ขอ — เหมาะ service เบา ไร้ state
  • Scoped: สร้าง หนึ่งอันต่อหนึ่ง HTTP request — เหมาะ DbContext (EF Core ต้อง scoped)
  • Singleton: หนึ่งเดียวตลอดอายุแอป — เหมาะ cache, config, connection pool
  • ข้อควรระวัง (ถามบ่อย): Singleton ฉีด Scoped เข้าไป (captive dependency) → object ที่ควรอยู่แค่ request ถูกเก็บข้าม request แล้วพัง/ไม่ปลอดภัย (โดยเฉพาะ DbContext)
  • Scope validation ใน Development จะจับ error แบบนี้ให้ตอนรัน

Entity Framework Core ใช้ยังไง และ N+1 ป้องกันยังไง?

C# / .NET CoreKrungsri SecuritiesENetworks#ef core
  • DbContext = unit of work, DbSet<T> = ชุด entity; เปลี่ยนแปลงแล้ว SaveChanges() เป็น transaction
  • Eager loading ด้วย .Include(x => x.Items) ดึง relation มาใน query เดียว — ป้องกัน N+1 (query ลูกแยกทีละ parent รวมเป็นร้อย query)
  • อ่านอย่างเดียวใช้ .AsNoTracking() เร็วกว่า (ไม่เก็บ change tracking)
  • Migration: add-migration / update-database (หรือ script SQL สำหรับ production) คุม schema จากโค้ด
  • Debug query จริง: log ToSqlString หรือดู query ที่ SQL Server รับ

LINQ ทำงานยังไง IEnumerable กับ IQueryable ต่างกันยังไง?

C# / .NET CoreKrungsri SecuritiesENetworks#linq
  • LINQ = query ข้อมูลด้วย syntax ของ C# (Where, Select, OrderBy, GroupBy ...)
  • Deferred execution: query ไม่ทำงานจนกว่าจะ enumerate (ToList(), foreach) — ต่อเงื่อนไขต่อได้ก่อนยิงจริง
  • IEnumerable: ประมวลผล ใน memory (LINQ to Objects)
  • IQueryable: แปลงเป็น SQL จริง ส่งไป DB — Where ก่อน ToList() ให้ filter ที่ server ไม่ใช่ดึงมาทั้งตารางแล้วค่อยกรอง (พลาดจุดนี้ = ช้ามาก)
  • ToList() เร็ว ๆ = ปิด deferred แล้วทำงานต่อใน memory

async/await ทำไมต้องใช้ และข้อควรระวังคืออะไร?

C# / .NET CoreKrungsri SecuritiesENetworks#async
  • ให้ thread ปล่อยตัวไปรับงานอื่นระหว่างรอ I/O (DB, HTTP) → throughput สูงขึ้น ไม่ต้องเปิด thread เพิ่ม
  • กติกา: async ทั้ง chain — method ที่ต้องรอใช้ await และคืน Task / Task<T>
  • ห้าม block บน async: .Result / .Wait() เสี่ยง deadlock และเปลือง thread
  • async void ใช้เฉพาะ event handler — error จับไม่ได้
  • ConfigureAwait ไม่ค่อยจำเป็นใน ASP.NET Core (ไม่มี SynchronizationContext)

ASP.NET MVC vs Web API vs Minimal API ต่างกันยังไง?

C# / .NET CoreKrungsri SecuritiesENetworks#โครงสร้าง
  • ASP.NET MVC: ทำหน้าเว็บเต็มรูปแบบ (Razor view ผสมโค้ด) — ยุค Krungsri ใช้คู่กับ jQuery
  • Web API: ทำ REST API ให้ frontend/mobile กิน — Controller + attribute routing ([Route], [HttpGet])
  • Minimal API: เขียนสั้น ๆ ตรง ๆ app.MapGet("/users", ...) เหมาะ microservice/endpoint เล็ก
  • งานจริงมักผสม: .NET Core ทำ API + React เป็น frontend แยกกัน

จัดการ configuration, environment และ global error ยังไง?

C# / .NET CoreKrungsri SecuritiesENetworks#config#error
  • config เรียงตามลำดับ: appsettings.jsonappsettings.{Environment}.jsonenvironment variables/user-secrets (ทับกันได้) — secret ไม่ลง git
  • IOptions<T> ผูก section ของ config เป็น class แรง ๆ
  • Global error: app.UseExceptionHandler (หรือ middleware เอง) แปลง exception เป็น ProblemDetails + log เต็มรูปแบบ
  • Logging ใช้ built-in ILogger<T> เขียนเป็น structured log (level, message template) ส่งต่อเข้า ELK/cloud ได้

Node.js

2 ข้อ

Node.js ทำงานยังไง เหมาะกับงานแบบไหน?

Node.jsENetworks#event loop
  • single thread + event loop: รัน JS ทีละอย่าง แต่ I/O (network, disk) เป็น non-blocking — รอเสร็จค่อยเรียก callback ผ่าน event loop
  • เก่งเรื่อง I/O-bound พร้อมกันเยอะ: API คุย DB/เรียก API อื่น, webhook, real-time (socket)
  • อ่อนเรื่อง CPU-bound (ยีนวิดีโอ, คำนวณหนัก) จะ block event loop ทั้งอัน — แก้ด้วย worker_threads หรือแยก service
  • ใช้จริงที่ ENetworks: ทำ API/BFF รองรับ e-commerce และ webhook ของ LINE

Express middleware ทำงานยังไง จับ error ยังไง?

Node.jsENetworks#express
  • ฟังก์ชัน (req, res, next) เรียงลำดับ — next() ส่งต่อ, ไม่เรียก next และไม่ตอบ = request ค้าง
  • ใช้ทำ: logger, parser (express.json), auth, validate, rate limit
  • Error middleware ต้องมี 4 parameters (err, req, res, next) ไว้ท้ายสุด — จับ error จาก async ที่ส่งต่อมา (next(err))
  • Express รุ่น 5 / หรือ wrapper เช่น express-async-errors ทำให้ throw ใน async handler ไปโผล่ error middleware เองได้

REST API / HTTP

4 ข้อ

ออกแบบ REST API ที่ดีต้องคิดอะไรบ้าง?

REST API / HTTPKrungsri SecuritiesENetworksCIMB Thai#design
  • Resource-based URL เป็น คำนาม: /customers/{id}/accounts ไม่ใช่ /getCustomerAccount
  • Method ตามความหมาย: GET อ่าน, POST สร้าง, PATCH แก้บางส่วน, PUT แทนทำทั้งก้อน, DELETE ลบ
  • Status code ใช้ให้ถูก: 200/201 (สร้าง+Location)/204 (สำเร็จไม่มี body)/400 (ข้อมูลผิด)/401 (ไม่ผ่าน auth)/403 (จำกัดสิทธิ์)/404/409 (ชนกัน เช่น ข้อมูลซ้ำ)/422/429 (เร็วเกิน)/500/503
  • Error format มาตรฐานเดียวกันทั้งระบบ { code, message, details }
  • มี version (/api/v1/), pagination (offset หรือ cursor), และเอกสาร Swagger/OpenAPI

Method ไหน idempotent บ้าง แปลว่าอะไร?

REST API / HTTPKrungsri SecuritiesENetworksCIMB Thai#http
  • Idempotent = เรียกซ้ำกี่ครั้งผลลัพธ์ก็เหมือนเดิม
  • Idempotent: GET, PUT, DELETE (DELETE ตัวที่สองก็ยัง "ลบแล้ว")
  • ไม่ idempotent: POST (ทุกครั้งสร้างใหม่), PATCH (ขึ้นกับวิธีเขียน)
  • ประโยชน์จริง: retry ได้อย่างปลอดภัย — network ล่มแล้ว client ยิงซ้ำ PUT/DELETE ไม่พังข้อมูล แต่ POST ซ้ำต้องกันด้วย idempotency key

ยืนยันตัวตน API มีวิธีอะไรบ้าง (API key, JWT, OAuth2, 2FA)?

REST API / HTTPKrungsri SecuritiesENetworksCIMB Thai#auth#คำถามยอดฮิต
  • API key: ง่าย ใช้ระหว่างระบบ (server-to-server) — ต้องเก็บเป็นความลับ + หมุนเวียนได้
  • JWT (Bearer): token 3 ส่วน header.payload.signature — stateless server เช็คลายเซ็นได้เอง ไม่ต้อง query session; มี exp ควรอายุสั้น + refresh token; ห้ามเก็บข้อมูลลับใน payload (decode อ่านได้)
  • OAuth2: มอบสิทธิ์โดยไม่บอกรหัสผ่าน — flow หลักคือ Authorization Code + PKCE (LINE Login ใช้แบบนี้)
  • 2FA: ปัจจัยที่สอง SMS OTP (งาน Krungsri ทำ SMS Gateway + 2FA) หรือ TOTP — เพิ่มความปลอดภัยแม้รหัสหลุด
  • SSO (ทำที่ Krungsri): login ครั้งเดียวใช้หลายระบบ ผ่าน token exchange/identity provider

Pagination แบบ offset กับ cursor ต่างกันยังไง?

REST API / HTTPKrungsri SecuritiesENetworksCIMB Thai#pagination
  • Offset: ?page=3&pageSize=20 — เขียนง่าย กระโดดไปหน้าไหนก็ได้ แต่ข้อมูลเปลี่ยนระหว่างเปลี่ยนหน้าอาจซ้ำ/หลุด และหน้าลึก ๆ DB ต้อง scan ข้าม (OFFSET 100000 ช้า)
  • Cursor: ส่ง token ชี้ตำแหน่งสุดท้าย (?after=abc&limit=20) — เสถียร ไม่พลาดข้อมูล เร็ว (ใช้ index seek) แต่เลื่อนได้ทางเดียว
  • เลือก: admin ที่ต้องกระโดดหน้า → offset, feed/ระบบการเงินที่เนียนสำคัญ → cursor

ฐานข้อมูล / SQL

6 ข้อ

JOIN แต่ละแบบต่างกันยังไง?

ฐานข้อมูล / SQLKrungsri SecuritiesENetworksCIMB Thai#join
  • INNER JOIN: เฉพาะแถวที่ match ทั้งสองฝั่ง
  • LEFT JOIN: ทุกแถวฝั่งซ้าย + ข้อมูลขวาที่ match (ไม่ match เป็น NULL) — ใช้บ่อยสุด เช่น รายการทั้งหมด + เอาเฉพาะที่ยังไม่ถูกผูก (WHERE right.id IS NULL)
  • RIGHT JOIN: ตรงข้าม LEFT (นิยมกลับด้านเป็น LEFT แทน)
  • FULL OUTER JOIN: รวมทั้งสองฝั่ง ไม่ match ก็แสดง
  • CROSS JOIN: ทุกคู่เป็นไปได้ (cartesian) — ระวังพลาดเงื่อนไข join แล้วแถวระเบิด

Index ทำงานยังไง ทำไม query เร็วขึ้น และมีข้อแลกเปลี่ยนอะไร?

ฐานข้อมูล / SQLKrungsri SecuritiesENetworksCIMB Thai#index#คำถามยอดฮิต
  • ส่วนใหญ่เป็น B-Tree — เก็บคอลัมน์เรียงลำดับไว้ ค้นหาเป็น O(log n) แทน scan ทั้งตาราง O(n)
  • ใส่ index ที่คอลัมน์ที่ใช้ WHERE / JOIN / ORDER BY บ่อย
  • ข้อแลกเปลี่ยน: INSERT/UPDATE/DELETE ช้าลง (ต้องดูแล index ด้วย) + กินพื้นที่ — อย่าใส่เยอะไป
  • เงื่อนไขที่ทำให้ index ไม่ถูกใช้: ครอบคอลัมน์ด้วยฟังก์ชัน WHERE YEAR(d) = 2026 (แก้เป็น range), ขึ้นต้น wildcard LIKE '%xx', คอลัมน์เลือก (selectivity) ต่ำ
  • Composite index: เรียงคอลัมน์เป็นเงื่อนไข; covering index: index ครอบคอลัมน์ที่ SELECT ด้วย ไม่ต้องไปแตะตารางจริงเลย

Transaction / ACID คืออะไร isolation level ต่างกันยังไง?

ฐานข้อมูล / SQL🧪 แบบจำลองKrungsri SecuritiesENetworksCIMB Thai#transaction
  • ACID: Atomic (ม้วนกลับได้ครบ), Consistent (กติกาไม่พัง), Isolated (ทำพร้อมกันไม่เกิดเงื่อนไขแข่ง), Durable (commit แล้วอยู่ยั่งยืน)
  • ปัญหาที่ isolation ป้องกัน: dirty read (อ่านของที่ยังไม่ commit), non-repeatable read (อ่านซ้ำได้ค่าต่าง), phantom read (แถวโผล่เพิ่ม)
  • Level จากอ่อน → เข้ม: Read Uncommitted < Read Committed (default SQL Server/Oracle) < Repeatable Read < Serializable (ช้าสุด)
  • ตัวอย่าง: ระบบโอนเงินต้องครอบ "ตัดบัญชีต้นทาง + เพิ่มปลายทาง" ไว้ใน transaction เดียว ไม่งั้นเงินหายกลางทาง
🧪 แบบจำลอง: Transaction ตอนโอนเงิน (มี vs ไม่มี) — กดดูการทำงานทีละขั้น
🧑‍💻Client
🖥️API
🗄️Database
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/8

Query ช้า จะหาสาเหตุและแก้ยังไง?

ฐานข้อมูล / SQLKrungsri SecuritiesENetworksCIMB Thai#optimization
  • ดู execution plan ก่อน (SQL Server: Ctrl+M / PostgreSQL: EXPLAIN ANALYZE) — หา table/index scan ที่ควรเป็น seek
  • เช็ค: มี index ตรง WHERE/JOIN ไหม, ใช้ฟังก์ชันบนคอลัมน์ไหม, SELECT * ดึงคอลัมน์เกิน (ไม่ได้ใช้ covering index)
  • ตัด query ใหญ่เป็นชิ้นเล็ก / ย้าย aggregate มาทำใน SQL แทนที่จะดึงข้อมูลมากรองใน app
  • N+1 จาก ORM → รวมด้วย Include/join หรือดึงเป็นชุด (WHERE id IN (...))
  • ข้อมูลใหญ่มาก: partition, archive ข้อมูลเก่า, พิจารณา read replica แยกงานอ่าน

SQL Server, PostgreSQL, Oracle ต่างกันตรงไหน (ประสบการณ์ใช้จริง)?

ฐานข้อมูล / SQLKrungsri SecuritiesENetworksCIMB Thai#เปรียบเทียบ
  • SQL Server: ใช้กับ .NET เกือบหมดเส้น (Krungsri, CIMB) — T-SQL, SSMS, มี snapshot isolation, มักอยู่ในองค์กร Windows
  • PostgreSQL: open source ทรงพลัง, ข้อมูล JSONB ดี, extension เยอะ, นิยมกับ Go/Node และ cloud — LIMIT/OFFSET, PL/pgSQL
  • Oracle: องค์กรใหญ่/ธนาคาร, PL/SQL, license แพง, ชอบงาน batch/report หนัก (เจอที่ Kiatnakin/S.I. Solution)
  • พื้นฐาน SQL เหมือนกัน 90% ต่างกันที่ syntax เฉพาะ (TOP vs LIMIT vs ROWNUM/FETCH), function วันที่, และ tooling

Stored procedure เทียบกับ query จาก ORM ใช้เมื่อไหร่ไหนดี?

ฐานข้อมูล / SQLKrungsri SecuritiesENetworksCIMB Thai#stored procedure
  • Stored procedure: logic อยู่ใน DB — เร็ว (compile ครั้งเดียว), ลด network round-trip, คุมสิทธิ์ที่ DB ได้ — เหมาะ batch/รายงานหนัก, ระบบที่ DBA คุม
  • ORM/parameterized query: logic อยู่ในโค้ด — version control ง่าย, เทสต์/ย้าย DB ง่ายกว่า, ทีม dev ดูแลเองได้
  • ข้อเสีย SP: ยากตอน migrate ฐานข้อมูล, debug/version ลำบาก
  • งานจริงมักผสม: CRUD ปกติใช้ ORM, งาน batch คำนวณหนักใช้ SP

Security & Deployment

6 ข้อ

SQL Injection คืออะไร ป้องกันยังไง?

Security & DeploymentKrungsri SecuritiesENetworksCIMB Thai#injection
  • ผู้โจมตีฝัง SQL ในช่อง input เช่น กรอก ' OR '1'='1 ในช่อง login แล้ว query กลายเป็นผ่านเสมอ / ลบตารางได้
  • ป้องกันหลัก: parameterized query / prepared statement เสมอ (EF Core, GORM, db.Query(sql, args) ทำให้) — ค่าจาก user เป็น "ข้อมูล" ไม่มีทางกลายเป็น "คำสั่ง"
  • ข้อห้าม: ต่อ string SQL จาก input ("...WHERE name = '" + input + "'")
  • เสริม: validate input, จำกัดสิทธิ์ DB user ที่ app ใช้ (ไม่ใช่ sa), WAF, error message ไม่ leak SQL

Hashing กับ Encryption ต่างกันยังไง ใช้เมื่อไหร่?

Security & DeploymentKrungsri SecuritiesENetworksCIMB Thai#encryption
  • Hash: ทางเดียว ย้อนกลับไม่ได้ — เก็บรหัสผ่าน ต้องใช้ algorithm ช้า + salt สำหรับรหัสผ่าน เช่น bcrypt, argon2 (ไม่ใช่ MD5/SHA1 เร็วเกินไป ถูก brute force ง่าย)
  • Encryption: สองทาง (key ถอดกลับได้) — เก็บข้อมูลที่ต้องอ่านคืน เช่น เลขบัตร/เลขที่บัญชี ใช้ AES-256 + ดูแล key ใน KMS/HSM
  • กฎ: รหัสผ่าน hash, ข้อมูลลับที่ต้องใช้คืน encrypt, อย่าเก็บอะไรที่ไม่จำเป็นเลย (เรื่อง compliance ธนาคาร)

HTTPS/TLS certificate รู้จักแค่ไหน (ประสบการณ์ติดตั้งจริง)?

Security & DeploymentKrungsri Securities#https#cert
  • HTTPS = HTTP บน TLS — เข้ารหัสข้อมูลระหว่าง client↔server + ยืนยันตัว server ด้วย certificate
  • Certificate ออกโดย CA (เช่น Let's Encrypt / หน่วยงานขององค์กร) — หมดอายุต้องต่ออายุ/เปลี่ยน (เจอบ่อยสุดคือ cert หมดแล้วระบบล่ม)
  • งานที่ Krungsri: ติดตั้ง certificate บน Windows server/IIS เอง — ขั้นตอน: สร้าง CSR → CA ออก cert → import ลง server → bind กับ site/port 443
  • ตรวจพัง: openssl s_client, browser padlock, ผูก chain incomplete ทำให้บาง client ไม่ยอมเชื่อ

Docker คืออะไร image กับ container ต่างกันยังไง?

Security & DeploymentENetworks#docker
  • Image = แม่พิมพ์ (โปรแกรม+dependency แช่แข็งเป็น read-only layers), Container = instance ที่รันจาก image (เพิ่ม layer อ่าน/เขียนบนสุด)
  • Dockerfile: บอกขั้นตอนสร้าง image (FROM → COPY → RUN → CMD) — ใช้ multi-stage build ให้ image เล็ก (build ใน stage เต็ม, ตัวรันเอาแค่ artifact)
  • docker-compose.yml: รันหลาย container พร้อมกัน (app + db + redis) เชื่อ network เดียวกัน
  • ประโยชน์: "ทำงานได้ที่เครื่องผม" หายไป, deploy ผ่าน CI สม่ำเสมอ, scale ด้วยการ spawn เพิ่ม

ช่วง deploy จากโค้ดถึง production เป็นยังไง?

Security & DeploymentKrungsri SecuritiesENetworksCIMB Thai#ci/cd
  • Flow มาตรฐาน: push → CI (build → run tests → สร้าง artifact/docker image → push registry) → CD (deploy ไป environment ทดสอบ → อนุมัติ → production)
  • แยก environment (dev/uat/prod) ด้วย config ไม่แก้โค้ด; secret อยู่ใน secret manager/env ของตัว pipeline
  • ก่อนขึ้นจริง: migration DB แบบ backward-compatible (deploy ได้ทั้งเก่า-ใหม่), rollback plan
  • มี health check หลัง deploy + log/metrics ดูว่าใช้ได้จริง — เจอปัญหา rollback กลับ version เดิม

OWASP Top ที่ควรรู้มีอะไร สรุปสั้น ๆ?

Security & DeploymentKrungsri SecuritiesENetworksCIMB Thai#owasp
  • Injection (SQL/OS) → parameterized query
  • Broken Access Control → เช็คสิทธิ์ที่ server ทุก request ไม่เชื่อ UI
  • Cryptographic Failures → HTTPS เต็มระบบ, เก็บ secret ถูกวิธี
  • XSS → escape output, CSP
  • Security Misconfiguration → ปิด debug, default credential, CORS เปิด *`
  • แนวคิดหลักตอนตอบ: ไม่เชื่อ input ผู้ใช้ + ตรวจสิทธิ์ที่ server + เก็บข้อมูลลับให้ถูกวิธี

สถาปัตยกรรมระบบ

4 ข้อ

Layered / Clean Architecture เป็นยังไง ทำไมต้องแยกชั้น?

สถาปัตยกรรมระบบKrungsri SecuritiesENetworksCIMB Thai#layered
  • แยกเป็นชั้น: Controller/API (รับ-ตอบ HTTP) → Service (business logic) → Repository (DB) — แต่ละชั้นรู้จักเฉพาะชั้นถัดไปผ่าน interface
  • ประโยชน์: เทสต์แยกชั้น (mock repository, รัน service ได้ไม่ต้องมี DB), เปลี่ยน DB/framework กระทบน้อย, logic อ่านหาง่าย
  • Clean Architecture เพิ่มเติม: domain (entity) อยู่ตรงกลาง และ dependency ชี้เข้าหา domain เสมอ — framework/DB เป็น detail รอบนอก
  • คำเตือน: อย่าแยกชั้นแบบหุ่น — logic หนักอยู่ใน controller หมด แล้วเรียกว่าแยกชั้นก็ไม่มีประโยชน์

Monolith vs Microservices เลือกยังไง?

สถาปัตยกรรมระบบKrungsri SecuritiesENetworksCIMB Thai#microservices
  • Monolith: deploy ชิ้นเดียว, ทำงานข้ามโมดูลตรงไปตรงมา, เหมาะทีมเล็ก/กลาง — ข้อเสีย scale ต้องทั้งก้อน, แก้เล็ก ๆ ก็ deploy ทั้งระบบ
  • Microservices: แยกตาม business capability, deploy/scale แยกกัน, เทคโนโลยีต่างกันได้ — แต่แลกด้วย network latency, distributed transaction, tracing/log ซับซ้อน, ต้องมี infra พร้อม
  • คำตอบที่ดีในสัมภาษณ์: "เริ่ม monolith ให้ดีก่อน แยกเป็น service เมื่อมีปัญหาจริง (ทีมชนกัน, ส่วนหนึ่งต้อง scale สูง)" — ไม่ใช่แยกเพราะกระแส

ใช้ cache (เช่น Redis) เมื่อไหร่ และ invalidation ทำยังไง?

สถาปัตยกรรมระบบENetworksCIMB Thai#cache
  • ใส่ cache เมื่อ: ข้อมูลอ่านเยอะ เขียนน้อย + query แพง (report, master data, ผลคำนวณ)
  • แบบที่ใช้บ่อย: cache-aside — อ่าน cache ก่อน, miss ค่อย query DB แล้วเก็บเข้า cache พร้อม TTL
  • Invalidation คือปัญหาจริง: ตอนเขียนข้อมูล ต้องลบ/update cache ให้ตรง (delete ง่ายกว่า update), ระวัง race condition ข้อมูลเก่าเขียนทับใหม่
  • อย่า cache ข้อมูลที่ต้องสดมาก (ยอดเงิน) โดยไม่มีกลไกใหม่ชัดเจน

Message queue ใช้เมื่อไหร่ มีประโยชน์อะไร?

สถาปัตยกรรมระบบKrungsri SecuritiesENetworksCIMB Thai#message queue
  • ใช้เมื่องานไม่ต้องเสร็จทันที: ส่ง SMS/email, แจ้งเตือน, ประมวลผลไฟล์, batch end-of-month
  • ประโยชน์: แยกผู้ส่ง-ผู้รับ (API ตอบเร็ว), รองรับพีค (คิวกัน request ทับกัน), retry + dead letter งานพังไม่หายไปไหน
  • รูปแบบพื้นฐาน: producer ส่ง queue → consumer ทำทีละชิ้น ack เมื่อเสร็จ; publish/subscribe สำหรับกระจายหลายผู้สนใจ
  • ต้องคิดตาม: งานทำซ้ำได้ไหม (idempotent), ลำดับสำคัญไหม, ตรวจคิวค้างยังไง

Message Queue

10 ข้อ

Message Queue แต่ละเจ้าแบ่งกลุ่มยังไง ควรเริ่มเข้าใจจากตรงไหน?

Message Queue#ภาพรวม#เลือกใช้
  • มี 2 ตระกูลใหญ่:
  • Traditional Queue / Broker (RabbitMQ, ActiveMQ, Azure Service Bus, IBM MQ) — ข้อความถูกส่งต่อให้ consumer แล้วลบทิ้งหลัง ack เน้น "ส่งงานให้ทำให้จบ" + routing ได้หลากหลาย
  • Distributed Log / Streaming (Kafka, Pulsar, NATS JetStream) — ข้อความเก็บไว้ใน log ตาม retention ผู้อ่านเลื่อนตำแหน่งอ่านเอง อ่านซ้ำ/ย้อนหลังได้ เน้น event streaming หลายระบบอ่านข้อมูลชุดเดียวกัน
  • อีกแกนนึงคือ self-host vs cloud-managed (AWS SQS/SNS/EventBridge, Google Pub/Sub, Azure Service Bus) — ไม่ต้องดูแล broker เอง จ่ายตามจำนวน message แต่ผูกกับ cloud นั้น ๆ
  • คำถามตัดสิน: ข้อความ "หมดหน้าที่" ตอน consumer ทำเสร็จไหม (→ queue) หรือต้องเก็บให้อ่านหลายรอบ/หลายระบบ (→ log/stream)
  • เคล็ดลับศึกษา: ทำความเข้าใจ ack / retry / dead-letter / ordering / idempotent ให้แน่นก่อน — หลักการเดียวกันทุกเจ้า เปลี่ยนแค่ชื่อ feature

RabbitMQ ทำงานยังไง เหมาะกับงานแบบไหน?

Message Queue#rabbitmq#amqp
  • Broker ตระกูล AMQP — producer ไม่ได้ส่งตรงเข้า queue แต่ส่งเข้า exchange → exchange ใช้ binding rule กระจายข้อความเข้า queue(s) → consumer ดึงไปทำแล้วส่ง ack กลับ
  • Exchange 4 แบบ: direct (จับคู่ routing key ตรงเป๊ะ), topic (จับคู่แบบ pattern เช่น order.*.created), fanout (กระจายทุก queue ที่ผูกไว้), headers (จับคู่จาก header)
  • กลไกที่ต้องรู้: ack (consumer ไม่ตอบภายในเวลา = ข้อความถูกส่งใหม่), prefetch (จำกัดจำนวนงานที่ยังไม่ ack ต่อ consumer กันคนเร็วกินงานหมด), DLX (dead-letter exchange ดักข้อความที่ fail/ถูก reject)
  • เหมาะกับ: task queue (ส่งอีเมล, ประมวลผลไฟล์), routing ข้อความหลายรูปแบบ, ระบบเล็ก-กลางที่อยากได้ broker เบาแต่ยืดหยุ่น
  • ข้อจำกัด: throughput ต่ำกว่า Kafka มากเมื่องานเป็น stream เพราะทุกข้อความต้องผ่าน broker routing + ถูกลบหลัง ack อ่านย้อนหลังไม่ได้

Apache Kafka ทำงานยังไง ทำไมถึงเร็วมาก?

Message Queue#kafka#streaming#จุดเด่น
  • โมเดล: distributed append-only log — topic แบ่งเป็น partition (shard), producer เขียนข้อความต่อท้าย log และข้อมูลคงอยู่ตาม retention (ไม่ลบตอน consumer อ่านแล้ว)
  • Consumer group: consumer หลายตัวใน group เดียวกันแบ่งกันอ่านตาม partition (1 partition ต่อ 1 consumer) → scale การอ่านด้วยการเพิ่ม consumer; group อื่นอ่านข้อมูลชุดเดียวกันได้อิสระ (ต่างจาก queue ธรรมดา)
  • ตำแหน่งอ่านเก็บเป็น offset ที่ consumer คุมเอง → ขยับ offset กลับไปอ่านของเก่า (replay) ได้
  • เร็วเพราะ: sequential disk I/O (เขียนต่อท้ายอย่างเดียว ไม่ random seek), zero-copy ส่งข้อมูลจาก disk ตรงออก network, รวมข้อความเป็น batch
  • Ordering: การันตีเฉพาะใน partition เดียวกัน — ถ้าลำดับของ "user เดียวกัน" สำคัญ ให้ตั้ง message key เป็น userId เพื่อบังคับเข้า partition เดียวกัน
  • เหมาะกับ: event streaming, event sourcing, data pipeline ป้อน data warehouse/analytics, decouple ระบบแบบ throughput สูง
  • ต้องดูแล: rebalance (consumer ตาย → แจก partition ใหม่ ช่วงนั้นอาจกระตุก), เฝ้า consumer lag (อ่านตามไม่ทัน)

MQ ฝั่ง AWS มีอะไรบ้าง ใช้ตอนไหน?

Message Queue#aws#sqs#sns#eventbridge
  • SQS (queue เดี่ยว แบบ pull-based — consumer คือโค้ดเราที่เรียก receive หรือ Lambda ที่ถูก trigger):
  • visibility timeout — ข้อความถูกซ่อนระหว่างมีคนกำลังประมวลผล ถ้าไม่ ack ภายในเวลา ข้อความกลับมาให้ลองใหม่ (คล้าย ack timeout ของเจ้าอื่น)
  • เปิด long polling ลด cost + latency, ผูก DLQ ผ่าน redrive policy
  • SQS ปกติไม่การันตีลำดับ — ต้องการลำดับใช้ SQS FIFO ผูกกับ MessageGroupId (throughput ต่ำกว่า)
  • SNS: pub/sub แบบ push กระจายไปหลายปลายทาง (SQS, email, Lambda, HTTP) — pattern คลาสสิกคือ SNS → หลาย SQS (fan-out)
  • EventBridge: event bus กรองและส่งต่อตาม rule (match pattern ของ event) — ใช้ในสถาปัตยกรรม event-driven serverless เชื่อ service ใน AWS + SaaS ภายนอกได้
  • เลือกง่าย ๆ: งาน background ทั่วไป → SQS, แจ้งหลายระบบพร้อมกัน → SNS fan-out, event ระหว่าง service + ต้องกรองเงื่อนไข → EventBridge

เอา Redis มาทำ message queue ได้ไหม มีกี่แบบ?

Message Queue#redis#pub/sub#streams#bullmq
  • ได้ 2 แบบ ธรรมชาติต่างกันสิ้นเชิง:
  • Pub/Sub — ส่งแล้วลืม (fire-and-forget) ไม่มีการเก็บข้อความ ใครไม่ได้ subscribe ตอนนั้น = พลาดถาวร → เหมาะแจ้งเหตุการณ์ real-time (chat, สั่ง invalidate cache) ห้ามใช้กับงานสำคัญที่ห้ามหาย
  • Streams — คล้าย Kafka ย่อส่วน: เขียนด้วย XADD, อ่านเป็น consumer group ด้วย XREADGROUP, ยืนยันด้วย XACK, มี pending list ของข้อความที่รับไปแล้วแต่ยังไม่ ack, อ่านย้อนหลังได้
  • ใช้เมื่อ: มี Redis อยู่แล้ว + ปริมาณงานเล็ก-กลาง + ไม่อยากเพิ่ม broker ตัวใหม่ให้ดูแล
  • ข้อจำกัด: ข้อมูลอยู่บน memory (ความจุจำกัด-ต้นทุนต่อ GB สูง), persistence (AOF/RDB) ไม่ได้การันตีเท่า broker บน disk, ไม่มี sharding/retention แบบ Kafka
  • ในงานจริงคนมักไม่เขียนเอง — ใช้ BullMQ (Node.js) หรือ Sidekiq (Ruby) ที่ประกอบ Redis เป็น job queue เสร็จสรรพ์ (retry, backoff, rate limit, schedule) ไว้แล้ว

เจ้าอื่น ๆ (GCP / Azure / องค์กรเก่า) มีอะไร ต่างตรงไหน?

Message Queue#gcp#azure#nats#ibm mq
  • Google Pub/Sub: แยกเป็น topic + subscription — ข้อความเก็บใน topic ทุก subscription อ่านอิสระในของตัวเอง (เทียบได้กับ consumer group แยกชุด), มี ack deadline (คล้าย visibility timeout), รองรับ push และ pull, อยากได้ลำดับต้องเปิด ordering + ใช้ ordering key
  • Azure Service Bus: มีทั้ง Queue (แบบ classic) และ Topic/Subscription (pub/sub) — จุดเด่นระดับองค์กร: duplicate detection, sessions (การันตีลำดับ), TTL, dead-letter queue ในตัว
  • ActiveMQ / Artemis: broker สาย Java ตามมาตรฐาน JMS เจอในองค์กร Java เก่า — ปัจจุบันมักถูกแทนที่ด้วย Artemis หรือ Kafka
  • IBM MQ: เจ้าเก่าแก่ เสถียรมาก ราคาแพง ธนาคาร/สถาบันการเงินใช้เยอะ — ส่วนใหญ่ไม่ได้เลือกเองแต่ตามมาตรฐานขององค์กร
  • NATS: เบามาก — core NATS เป็น at-most-once (เหมือน pub/sub เร็ว ๆ), ส่วน JetStream เก็บข้อความได้แบบ stream; เด่นเรื่องทำ request-reply ผ่าน subject ได้เลย เหมาะ microservices ภายในคลัสเตอร์
  • สรุปตอนสัมภาษณ์: ทุก cloud มี MQ ของตัวเอง หลักการ ack/retry/ordering เหมือนกันหมด — เรียนรู้แนวคิดแล้วเปลี่ยนเจ้าได้ไม่ยาก

สรุปสั้น ๆ: งานแบบนี้ควรเลือกเจ้าไหน?

Message Queue#สรุป#เลือกใช้
  • Task queue ทั่วไป (ส่งเมล, ประมวลผลไฟล์, งาน background) → RabbitMQ / AWS SQS / BullMQ (Redis)
  • Event streaming throughput สูง, หลายฝั่งอ่านข้อมูลชุดเดียวกัน, ต้อง replay ได้ → Kafka
  • อยู่บน AWS ทำ serverless → SQS เป็นหลัก + EventBridge ต่อ event ระหว่าง service, อยาก fan-out เพิ่ม SNS
  • มี Redis อยู่แล้ว งานเล็ก-กลาง ทีม Node.js → BullMQ / Redis Streams
  • องค์กร .NET บน Azure → Azure Service Bus; องค์กรการเงินที่มีมาตรฐานเดิม → IBM MQ (ตามองค์กร ไม่ใช่ตามความชอบ)
  • Microservices ภายใน อยากได้เบา + request-reply ได้ → NATS
  • หลักการเลือกจริง: ถ้าไม่มีข้อจำกัดจากองค์กร/cloude ที่ใช้อยู่ เลือกตัวที่ทีมดูแลเป็น ดีกว่าเลือกเจ้าที่ "เจ๋งสุด" แต่ไม่มีใครเข้าใจมัน

ข้อความพังระหว่างทาง จัดการ ack/retry/DLQ ยังไงให้งานไม่หาย?

Message Queue#ack#retry#dlq#idempotent
  • Lifecycle มาตรฐาน: consumer รับข้อความ → ทำงาน → สำเร็จส่ง ack (broker ลบข้อความ) / พังส่ง nack-reject → broker redelivery ตามจำนวนครั้งที่ตั้งไว้
  • Poison message: ข้อความที่พังทุกครั้ง (โค้ด bug / ข้อมูลเสีย) ถ้าไม่จำกัด retry จะวนกิน resource ไม่รู้จบ → ครบจำนวนแล้วโยนเข้า DLQ (dead-letter queue) ให้คนเข้าไปดู/แก้แล้ว requeue ใหม่
  • Retry ควรเป็น exponential backoff — ตอน dependency ล่ม อย่ายิง retry ถี่ ๆ เต็มระบบ (ซ้ำเติมให้ล่มหนักขึ้น)
  • Idempotent consumer จำเป็นเสมอ: MQ เกือบทุกเจ้าให้การันตี at-least-once (ส่งถึงอย่างน้อยหนึ่งครั้ง = อาจซ้ำ) → งานทำซ้ำต้องได้ผลลัพธ์เดิม เช่น เช็ค unique key ก่อน insert, ใช้ message id กัน duplicate, ทำเป็น upsert
  • Monitoring ที่ต้องมี: ความลึกคิว (queue depth), อายุข้อความเก่าสุด, จำนวนข้อความใน DLQ, consumer lag (Kafka) — คิวค้างคือ incident ที่ผู้ใช้ไม่เห็นทันที เลยต้องเฝ้าเอง

บันทึก DB กับส่ง message ยังให้สอดคล้องกัน (Outbox pattern)?

Message Queue#outbox#ordering#transaction
  • ปัญหา dual-write: บันทึก DB สำเร็จแต่ส่ง MQ ล้ม (หรือกลับกัน) → ข้อมูลกับ event ขัดกัน และแก้ด้วย transaction เดียวครอบทั้งสองไม่ได้เพราะคนละระบบ
  • Outbox pattern: บันทึกข้อมูล + event ลงตาราง outbox ใน DB transaction เดียวกัน จากนั้นให้ตัว relay (poll ตาราง หรือใช้ CDC เช่น Debezium) อ่าน outbox ส่งเข้า MQ แล้ว mark ว่าส่งแล้ว → การันตี "บันทึกแล้ว = มี event แน่นอน"
  • ต้นทุนที่ต้องยอม: event อาจถูกส่งซ้ำ (relay ส่งสำเร็จแต่ตายก่อน mark) → consumer ต้อง idempotent เสมอ
  • เรื่องลำดับที่ต้องเข้าใจ: queue เดียว + consumer เดียว = ลำดับชัวร์แต่ scale ไม่ได้, competing consumers = scale ได้แต่ลำดับพัง → งานที่ลำดับสำคัญต้องจัดกลุ่มตาม key เช่น partition key ของ Kafka หรือ MessageGroupId ของ SQS FIFO (ได้ทั้งลำดับในกลุ่มและขนานระหว่างกลุ่ม)

Kafka กับ RabbitMQ ต่างกันยังไง ตอบสัมภาษณ์ยังไงให้โดนจุด?

Message Queue#เปรียบเทียบ#สัมภาษณ์
  • โมเดลแกน: Kafka = log เก็บข้อความ consumer เลื่อน offset อ่านเอง / RabbitMQ = broker ส่งต่อ แล้วลบทิ้งหลัง ack
  • Replay: Kafka อ่านซ้ำ/ย้อนหลังได้เพราะข้อมูลคงอยู่ตาม retention / RabbitMQ ส่งเสร็จหมดหน้าที่ อ่านของเก่าไม่ได้
  • Routing: RabbitMQ เก่งเรื่อง exchange/binding หลายรูปแบบ / Kafka มีแค่การแบ่ง partition ตาม key
  • Throughput: Kafka สูงกว่ามาก (batch + sequential I/O + zero-copy) / RabbitMQ พอสำหรับงานทั่วไปหลักหมื่น msg/วินาที
  • ใช้เมื่อ: Kafka = event streaming, analytics pipeline, event sourcing / RabbitMQ = task queue, complex routing, ระบบกลางที่อยากได้ broker เรียบง่าย
  • ประโยคปิดที่จำง่าย: "Kafka เป็น platform ของข้อมูล (data in motion) ส่วน RabbitMQ เป็นเครื่องมือส่งงาน (work queue)" — ที่จริงไม่ได้แทนกันตรง ๆ เลือกตามโจทย์

Docker (101 → Mid)

13 ข้อ

Container ต่างจาก VM ยังไง จริง ๆ มันทำงานบนอะไร?

Docker (101 → Mid)#พื้นฐาน#namespace#cgroup
  • VM = เลียนแบบเครื่องเต็มวงจร (hypervisor + guest OS ซ้อนใน) หนักเป็น GB แต่แยกขาดสนิท
  • Container = process ปกติของ host OS ที่ถูกจำกัดด้วย 3 เทคโนโลยีของ Linux kernel:
  • namespaces — แยก "มุมมอง" ของ process (มองเห็น pid, network, filesystem, user ของตัวเอง)
  • cgroups — จำกัดทรัพยากร CPU / memory / I/O
  • union filesystem (overlayfs) — ซ้อน image layer แบบ read-only + ชั้นเขียนบนสุด ทำให้หลาย container แชร์ layer กันได้
  • ผลลัพธ์: start เป็นวินาที, ใช้ memory น้อย เพราะแชร์ kernel กับ host (MB vs GB ของ VM)
  • ผลข้างเคียง: แชร์ kernel = isolation อ่อนกว่า VM จริง ๆ → งาน multi-tenant ความปลอดภัยสูงสุดยังต้อง VM/microVM
  • บน macOS/Windows: Docker แอบรัน Linux kernel ใน VM เบา ๆ ให้เบื้องหลัง (ลินุกซ์คอนเทนเนอร์รันบนลินุกซ์เท่านั้น)

เขียน Dockerfile ยังไงให้ image เล็กและ build เร็ว?

Docker (101 → Mid)#dockerfile#cache#image เล็ก
  • Layer cache: ทุก instruction = 1 layer ที่แคชตามเนื้อความ — ของที่เปลี่ยนบ่อย (โค้ด) ต้องอยู่ท้ายสุด, ของที่นาน ๆ เปลี่ยน (dependency) อยู่บนสุด
  • สูตร Node: COPY package*.json ./RUN npm ciCOPY . . — แก้โค้ด 1 ไฟล์ก็ไม่ต้องติดตั้ง dependency ใหม่
  • Multi-stage build: stage แรกมี toolchain เต็มไว้ build, stage สุดท้าย COPY แค่ artifact ไปรัน — Go/.NET ลดจาก 1 GB+ เหลือหลักสิบ MB
  • เลือก base image: slim (ตัดเครื่องมือเกิน) / alpine (เล็กสุด แต่ใช้ musl libc — บาง native package มีปัญหา) / distroless (มีแค่ runtime + app ไม่มี shell ปลอดภัยด้วย)
  • อย่าลืม .dockerignore (node_modules, .git, .env) — ลด build context + กัน secret หลุดเข้า image
  • CMD/ENTRYPOINT ใช้ exec form ["node", "app.js"] ไม่ใช่ shell form — ให้ app เป็น PID 1 และรับ signal ได้ (ต่อในข้อ be-082)

ENTRYPOINT กับ CMD ต่างกันยังไง และทำไมเรื่อง signal สำคัญ?

Docker (101 → Mid)#entrypoint#cmd#signal#pid 1
  • CMD = คำสั่ง default — ถ้าพิมพ์ docker run image อะไรเพิ่ม สิ่งที่พิมพ์จะแทนที่ CMD ทั้งหมด
  • ENTRYPOINT = คำสั่งหลักตายตัว — สิ่งที่พิมพ์ต่อท้ายจะกลายเป็น argument ของมัน → นิยมใช้คู่กัน: ENTRYPOINT คือตัวรัน, CMD คือ default args
  • exec form vs shell form: ["node","app.js"] รัน process ตรง ๆ รับ signal ได้ / รูปแบบ string จะถูกรันผ่าน /bin/sh -c = SIGTERM ไปไม่ถึง appdocker stop รอ 10 วิแล้ว SIGKILL ทิ้ง = งานถูกตัดกลางคัน
  • PID 1: ใน container ไม่มี init process — app ของเราคือ PID 1 ต้องรับผิดชอบจัดการ SIGTERM เอง (ปิด HTTP server, ปิด DB connection, จบ worker ค้าง)
  • ตัวอย่าง: Node.js ใช้ process.on('SIGTERM', ...) ปิด server แล้วค่อย exit / Go ใช้ signal.Notify + cancel context
  • ตรวจง่าย ๆ: docker stop แล้วดูว่า container จบเองภายใน ~10 วิไหม (ปรับเวลารอด้วย stop_grace_period ใน compose)

ข้อมูลใน container หายเมื่อไหร่ เก็บถาวรยังไง (volume / bind mount / tmpfs)?

Docker (101 → Mid)#volume#bind mount#ข้อมูลถาวร
  • ชั้นเขียนของ container ใช้ได้แต่ตายพร้อม container — ลบ container ข้อมูลหาย ("ใช้แล้วทิ้ง" คือพฤติกรรมปกติของ container)
  • Volume (-v mydata:/var/lib/postgresql/data): Docker สร้างและจัดการให้ เก็บบน host นอกเหนือจาก container → ทางการสำหรับข้อมูล DB ใน production ลบ container กี่รอบข้อมูลยังอยู่
  • Bind mount (-v ./src:/app/src): map โฟลเดอร์จริงของ host เข้าไปตรง ๆ → ใช้ตอน dev (แก้โค้ด hot reload) ห้ามใช้เก็บข้อมูล production (ผูก path เครื่อง + ปัญหาสิทธิ์)
  • tmpfs: เขียนใน memory อย่างเดียว เหมาะ temp/secret ที่ไม่ต้องการให้แตะ disk
  • กับดักที่เจอบ่อย: bind mount ของ Linux container บน Mac/Windows ช้ามาก (ข้อมูลวิ่งผ่าน VM) → ใช้ flag cached/delegated ช่วยได้บ้าง หรือหลีกเลี่ยงการ mount โฟลเดอร์ที่ I/O หนัก (เช่น node_modules — ใช้ anonymous volume ทับ)
  • อีกกับดักคลาสสิก: app ใน container รันเป็น uid 1000 แต่ volume เป็นของ root → permission denied แก้ด้วยการตั้ง user ให้ uid ตรงกันตอนสร้าง image

Container คุยกันเองและกับโลกภายนอกยังไง?

Docker (101 → Mid)#network#dns#port
  • Network driver หลัก:
  • bridge (default) — virtual network ในเครื่อง + NAT ออก internet, container อยู่ลึกข้างหลัง
  • host — แชร์ network stack ของ host ตรง ๆ (เร็วสุด แต่ port ชนกันง่าย ใช้กรณีพิเศษ)
  • none — ตัด network ทิ้ง (งาน batch ที่ไม่ต้องออกเน็ต)
  • overlay — เชื่อ container ข้ามเครื่อง (แนวคิดเดียวกับที่ k8s ใช้)
  • container คุยกัน: อยู่ network เดียวกันแล้วเรียกชื่อ service/ชื่อ container ได้เลย — Docker มี DNS ในตัว เช่น app ต่อ postgres:5432 ไม่ต้องจำ IP (IP เปลี่ยนทุกครั้งที่ restart)
  • เปิดรับจากนอก: -p 8080:80 = host port 8080 ส่งต่อเข้า container port 80 (publish) — ส่วน EXPOSE ใน Dockerfile เป็นแค่เอกสาร ไม่ได้เปิดอะไรจริง
  • docker compose สร้าง network ให้ทุก service ในไฟล์อัตโนมัติ เลยเรียกหากันด้วยชื่อ service ได้ทันที
  • เครื่องมือ debug: docker network ls, docker network inspect, จากใน container ลอง wget/curl หรือ nslookup เพื่อเช็ค DNS

docker compose เขียนใช้จริงมีอะไรต้องรู้บ้าง?

Docker (101 → Mid)#docker compose#healthcheck#env
  • ไฟล์เดียวประกาศทั้งชุดระบบ (services + networks + volumes) — คำสั่งหลัก: up -d / down / logs -f / exec svc sh / ps
  • จุดที่คนพลาดบ่อย:
  • depends_on ไม่ได้รอ "พร้อม" แค่รอ "สตาร์ท" — DB พร้อมใช้ช้ากว่านั้น → ใช้ depends_on: แบบ condition: service_healthy + ใส่ healthcheck ให้ DB
  • แก้ Dockerfile/โค้ดแล้วต้อง up --build ไม่งั้นรัน image เก่าต่อ
  • config ผ่าน .env (compose อ่านอัตโนมัติ) หรือ environment ต่อ service — อย่า hardcode secret ในไฟล์ที่ commit
  • แยก docker-compose.override.yml ไว้ใช้เฉพาะ dev (bind mount, debug port) โดยไม่ต้องแตะไฟล์หลัก
  • profiles ใช้กัน service เฉพาะงาน (เช่น mailhog ทดสอบเมล) ไม่ต้องรันทุกครั้ง
  • ตัวอย่างโครงมาตรฐาน:
services:
  app:
    build: .
    ports: ["3000:3000"]
    environment:
      DATABASE_URL: postgres://db:5432/app   # เรียกชื่อ service ได้เลย
    depends_on:
      db:
        condition: service_healthy           # รอ "พร้อม" ไม่ใช่แค่ "สตาร์ท"
  db:
    image: postgres:16
    volumes: ["pgdata:/var/lib/postgresql/data"]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
volumes:
  pgdata:

Container ทำตัวแปลก ๆ ตรวจสอบด้วยอะไร คำสั่งไหนใช้บ่อย?

Docker (101 → Mid)#คำสั่ง#debug#exit code
  • ดูว่าเกิดอะไรขึ้น: docker logs -f --tail 100 <ชื่อ>, docker inspect (ค่าจริงของ env/mount/IP เช่น docker inspect -f '{{.State.ExitCode}}' <ชื่อ>), docker stats (CPU/mem แบบสด)
  • ลองคำสั่งข้างใน: docker exec -it <ชื่อ> sh — image ที่ไม่มี shell (distroless) ใช้ docker debug หรือ ephemeral container แทน
  • ตายเพราะอะไร: docker ps -a ดู exit code — 137 = ถูก kill (ส่วนใหญ่คือ OOMKilled), 125 = คำสั่งตอน run ผิด, 1 = app error ธรรมดา; docker events ดูลำดับเหตุการณ์
  • เช็ค image ตัวเอง: docker history <image> ดูว่า layer ไหนทำให้อ้วน, เครื่องมือ dive ลงลึกถึงระดับไฟล์ต่อ layer
  • ทำความสะอาด: docker system df ดูว่าอะไรกิน disk, docker system prune (ใส่ --volumes ด้วยความระวัง — ข้อมูล volume หายเลย)
  • ทัศนคติที่ถูกต้อง: container ควร stateless — พังก็ down แล้ว up ใหม่ อย่านั่ง "ซ่อมของใน container เดิม" เพราะการแก้นั้นหายทันทีที่ container ถูกสร้างใหม่

ส่ง image ไปรันที่เครื่องอื่น/production ยังไง (registry + tag)?

Docker (101 → Mid)#registry#tag#multi-arch
  • Registry = คลังกลางเก็บ image — Docker Hub, GHCR, ของ cloud (ECR/ACR/GCR), Harbor แบบ self-host
  • Workflow: docker build -t repo/app:tag .docker push → เครื่องปลายทาง docker pull — ชื่อเต็มของ image คือ registry/repo:tag
  • กติกา tag ที่ดี: ใช้ version หรือ commit sha (app:1.4.2, app:git-abc1234) — เลี่ยง latest เพราะไม่มีวันรู้ว่ากำลังรัน version ไหนอยู่จริง และ rollback ยาก
  • Mac เป็น ARM แต่ server เป็น x86 → ใช้ docker buildx build --platform linux/amd64 หรือสร้าง multi-arch (linux/amd64,linux/arm64) ให้ image เดียวรันได้ทุกสถาปัตยกรรม
  • ลดขนาด image อีก: multi-stage, base slim/distroless, อย่า COPY ของที่ไม่ใช้, รวม RUN ที่ควรอยู่ด้วยกันและเคลียร์ cache ใน layer เดียวกัน เช่น RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
  • หลักปฏิบัติ: build จริงใน CI แล้ว push ขึ้น registry เท่านั้น — อย่า build ที่เครื่องตัวเองแล้วเอาไฟล์ไปรันที่ server

Hardening container ทำอะไรได้บ้าง (ระดับ mid)?

Docker (101 → Mid)#security#non-root#scan
  • รันเป็น non-root: สร้าง user ใน Dockerfile แล้วสั่ง USER appuser — root ใน container ถ้าหลุดออกมาทำอันตราย host ได้ง่ายกว่ากันมาก
  • ห้าม bake secret ลง image: ENV ที่ฝังตอน build อ่านคืนได้จาก docker history หรือ export image — ส่ง secret ตอนรันแทน (env, secret manager, mount)
  • ลดสิทธิ์ตอนรัน: --read-only (root filesystem อ่านอย่างเดียว + tmpfs จุดที่ต้องเขียน), หลีกเลี่ยง --privileged และการ mount /var/run/docker.sock เข้า container (เท่ากับมอบ root ของ host ให้มัน)
  • Scan หาช่องโหว่: trivy image app:tag หรือ docker scout ตรวจ CVE ของ base + dependency — ทำเป็น step ใน CI บล็อก image ที่มีระดับ high/critical
  • ตรึง version ด้วย digest (image@sha256:...) กัน base image เปลี่ยนโดยไม่บอก (supply chain เบื้องต้น) + ใช้ base ที่อัปเดตสม่ำเสมอ
  • อื่น ๆ: เปิด port เท่าที่จำเป็น, อย่ารัน SSH ใน container (ใช้ docker exec แทน), log ออก stdout ให้เก็บรวมศูนย์

จำกัดทรัพยากร container ยังไง และ OOMKilled คืออะไร?

Docker (101 → Mid)#resource limit#oom#exit 137
  • ตั้ง limit ตอนรัน: docker run --memory=512m --cpus=1.5 หรือใน compose ผ่าน deploy.resources.limits — กัน container ตัวเดียวกินทรัพยากรจนเพื่อนในเครื่องตายตามกัน (noisy neighbor)
  • OOMKilled (exit 137): process ใช้ memory เกิน limit จน kernel ฆ่าทิ้ง — อาการคือ "container ตายเงียบ ๆ เป็นระยะ" ไม่ใช่โค้ดพัง แต่คือ memory ไม่พอ/limit ต่ำไป
  • Runtime ที่มี GC ต้องรู้ว่าตัวเองอยู่ใน container:
  • Node.js: ตั้ง --max-old-space-size ให้ต่ำกว่า memory limit ของ container (เวอร์ชันใหม่อ่าน cgroup ได้เองแล้ว)
  • JVM เวอร์ชันเก่าอ่าน RAM ของ host ทั้งเครื่อง → heap ใหญ่เกิน limit โดน kill — ใช้ image JVM ใหม่หรือตั้ง -XX:MaxRAMPercentage
  • คู่กันด้วย soft limit: --memory-reservation เป็นการจองขั้นต่ำให้ scheduler พิจารณา
  • --cpus คือสัดส่วนเวลา CPU ไม่ใช่การจอง core — อยากกำหนด core จริงใช้ --cpuset-cpus
  • ตรวจ: docker stats และ docker inspect -f '{{.State.OOMKilled}}' <ชื่อ>

HEALTHCHECK มีบทบาทยังไง และเลิกรับงานอย่างสวยงาม (graceful) ทำยังไง?

Docker (101 → Mid)#healthcheck#graceful shutdown#readiness
  • HEALTHCHECK (ใน Dockerfile/compose): Docker รันคำสั่งตรวจเป็นระยะ → สถานะ healthy/unhealthy — จับได้ว่า process ยังรันอยู่แต่ทำงานจริงไม่ได้ (เช่น ต่อ DB ไม่ได้, deadlock) ซึ่งดู log อย่างเดียวไม่เห็น
  • แนวคิดที่ต้องแยกให้ชัด: liveness (ตายเลยไหม → ควร restart) กับ readiness (พร้อมรับ traffic ไหม → ยังไม่พร้อมไม่ต้อง restart เช่น กำลัง warm up) — Docker มีสถานะเดียวรวม ๆ กัน ส่วน Kubernetes แยกเป็น liveness/readiness probe ชัดเจน
  • ใช้จริงใน compose: ใส่ healthcheck ให้ DB แล้วใช้ depends_on: condition: service_healthy ให้ app รอจริง ไม่ใช่รันชนกันตอน DB ยังไม่พร้อม
  • Graceful shutdown ทำงานคู่กัน: docker stop ส่ง SIGTERM → app ควรหยุดรับ request ใหม่ เก็บงานค้าง ปิด connection แล้วออกเองภายในเวลา (stop_grace_period default 10 วิ) ก่อนถูก SIGKILL — สำคัญมากตอน rolling update ใน production
  • Endpoint /health ควรเบา (ไม่เรียก service ภายนอกหนัก ๆ), ไม่ต้อง auth, ตอบเร็ว

รู้ Docker แล้ว จะต่อยอดไป Kubernetes ต้องเข้าใจเพิ่มอะไร?

Docker (101 → Mid)#kubernetes#ต่อยอด#12-factor
  • Docker ให้ container ใน 1 เครื่อง, Kubernetes ให้ ระบบข้ามหลายเครื่อง: Pod (กลุ่ม container แชร์ network/IP เดียวกัน), Deployment (จำนวน replica + rolling update), Service (ชื่อ DNS + load balance นิ่ง), Ingress (ทางเข้า HTTP จากภายนอก)
  • แนวคิดที่ถูกแทนที่: compose file → YAML manifest, volume → PV/PVC, env file → ConfigMap/Secret, healthcheck → liveness/readiness probe, restart policy → self-healing ของ k8s เอง
  • สกิล Docker ที่เป็นพื้นก่อนขึ้น k8s: image เล็ก, non-root, รับ SIGTERM ได้ (จำเป็นสุดตอน rolling update — pod เก่าถูกสั่งหยุดตลอดเวลา), มี /health endpoint, รับ config ผ่าน env ทั้งหมด
  • สะพานเชื่อมคือ 12-factor app: config จาก env, log ออก stdout (ให้ platform เก็บรวมให้), stateless, disposability — ทำครบแล้วย้ายสภาพแวดล้อมใดก็ง่าย
  • ฝึกในเครื่อง: kind, minikube หรือ Kubernetes ที่ฝังใน Docker Desktop รันคลัสเตอร์จำลองได้เลย
  • คำเตือน: อย่ากระโดดข้าม Docker ไป k8s — ปัญหา image/สิทธิ์/signal ที่ไม่เข้าใจ จะกลายเป็นปัญหาที่ debug ยากขึ้นสิบเท่าในคลัสเตอร์

อาการ Docker ที่เจอบ่อยสุด และแนวคิดตอนแก้?

Docker (101 → Mid)#troubleshooting#อาการพบบ่อย
  • "port ถูกใช้แล้ว": ชนกับโปรแกรมอื่นบน host — หาตัวครองด้วย lsof -i :3000 แล้วเปลี่ยนฝั่ง host (-p 3001:3000) หรือปิดตัวครอง
  • แก้โค้ดแล้วไม่เห็นผล: ลืม rebuild (docker compose up --build) หรือลืมว่าไม่ได้ bind mount โค้ด — image เก่ายังถูกรันอยู่
  • Permission denied บน volume: uid ใน container ไม่ตรงกับเจ้าของโฟลเดอร์ — ตั้ง user/uid ให้ตรงกันตอนสร้าง image หรือ chown volume
  • Disk เต็มเรื่อย ๆ: image เก่า + dangling image + volume ตกค้าง — docker system df ดูก่อนแล้วค่อย prune ทีละประเภท อย่าลบ volume มั่ว
  • Container ตายทันทีแบบเงียบ ๆ: app crash ตอน start — docker logs ดูสาเหตุ, ตรวจ CMD/env ให้ครบ หรือ docker run -it ... sh เข้าไปรันคำสั่งเองเพื่อเห็น error จริง
  • เวลาไม่ตรง: container เริ่มที่ UTC เสมอ — ตั้ง env TZ หรือ (ดีกว่า) เก็บเป็น UTC แล้วแปลงตอนแสดงผลตาม timezone ผู้ใช้
  • Bind mount ช้าบน Mac: ข้อมูลวิ่งผ่าน VM — ใช้ volume แทน bind mount สำหรับโฟลเดอร์ที่ I/O หนัก (เช่น node_modules)

พื้นฐาน Flutter

5 ข้อ

Widget คืออะไร ทำไมบอกว่า Flutter คือ 'everything is a widget'?

พื้นฐาน FlutterENetworks#widget#พื้นฐาน
  • Widget = คำอธิบาย (configuration) ของ UI ส่วนหนึ่ง — เป็น immutable เปลี่ยน = สร้างใหม่
  • ทุกอย่างเป็น widget: layout (Row, Column), สไตล์ (Padding, TextStyle), ปุ่ม, แม้แต่ app เอง
  • โครงสร้างเป็น widget tree — Flutter เอา tree ไปสร้าง element tree แล้ววาดจริง (render object) เมื่อข้อมูลเปลี่ยนจะ diff และ rebuild เฉพาะส่วนที่ต่าง
  • ได้ผลลัพธ์เดียวกันทั้ง Android และ iOS จากโค้ดชุดเดียว (Dart)

StatelessWidget กับ StatefulWidget ต่างกันยังไง?

พื้นฐาน FlutterENetworks#state#คำถามยอดฮิต
  • Stateless: UI คงที่ ไม่มี state ภายใน — วาดจากข้อมูลที่รับเข้ามา (constructor) พอ
  • Stateful: มี state ที่เปลี่ยนได้ เก็บใน State object — setState() บอกว่า "ข้อมูลเปลี่ยนแล้ว จง rebuild"
  • setState จะ trigger build() ใหม่ของ widget นั้น (และลูก ๆ) เท่านั้น
  • เคล็ดลับ: แยก widget เล็ก ๆ ให้ rebuild กระทบน้อยที่สุด; state ใหญ่ข้ามหน้าใช้ state management (Provider/Riverpod/Bloc)

Layout ใน Flutter (Row/Column/Stack) กับ constraint เป็นยังไง?

พื้นฐาน FlutterENetworks#layout
  • Row / Column: เรียงลูกในแนวนอน/แนวตั้ง — mainAxisAlignment ตามแนวเรียง, crossAxisAlignment ตามแนวตั้งฉาก
  • Stack: วางซ้อนกัน (ตำแหน่งด้วย Positioned)
  • Expanded ยึดพื้นที่เหลือ, Flexible ยึดได้แต่ไม่เกินขนาดธรรมชาติ
  • กฎ constraint: "constraints ไหลลง (parent→child), sizes ไหลขึ้น (child→parent), parent ตั้งตำแหน่งลูก" — เจอ layout ไม่เป็นไปตามคาด มักเป็นเพราะ parent ส่ง constraint แบบ unbounded (เช่น ใส่ ListView ใน Column โดยไม่ครอบด้วย Expanded จะได้ error unbounded height)

Navigation ใน Flutter ทำยังไง?

พื้นฐาน FlutterENetworks#navigation
  • Navigator 1: Navigator.push(context, MaterialPageRoute(builder: (_) => DetailPage())) และ pop() กลับ — เขียนง่ายเหมาะแอปทั่วไป
  • Named routes: ตั้งชื่อ route ไว้ใน routes table เรียกด้วยชื่อ — จัดการรวมศูนย์
  • go_router / Navigator 2: รองรับ deep link, URL, guard — จำเป็นเมื่อ app ต้องเปิดหน้าเฉพาะจาก notification/ลิงก์ภายนอก
  • ส่งข้อมูลข้ามหน้า: ผ่าน constructor / argument ของ route; รอผลกลับด้วย await Navigator.push

ถ้าต้องใช้ความสามารถเฉพาะเครื่อง (native) Flutter ทำยังไง?

พื้นฐาน FlutterENetworks#native
  • ใช้ Platform Channel (MethodChannel): Dart เรียก method ที่เขียนด้วย Kotlin/Swift ฝั่ง native แล้วรอค่ากลับ
  • ตัวอย่าง: เรียก sensor เฉพาะเครื่อง, ผูก SDK ของผู้อื่น (จ่ายเงิน, สแกน) ที่ไม่มี Flutter plugin
  • ก่อนเขียนเองให้เช็ค pub.dev ก่อน — มักมี plugin สำเร็จรูปอยู่แล้ว (camera, location, firebase)
  • เรื่องจริงที่ควรเล่า: แอปที่ ENetworks ทำบางตัวใช้ LIFF เปิดใน in-app browser แทนการเขียน native เอง

State Management

1 ข้อ

เลือก state management (setState / Provider / Riverpod / Bloc) ยังไง?

State ManagementENetworks#provider#bloc
  • setState: state เล็ก ๆ ในจอเดียว — พอสำหรับฟอร์ม, ปุ่ม
  • Provider: มาตรฐานเบา ๆ อิง InheritedWidget — แชร์ state ข้าม widget ได้ (context.watch / read) เริ่มต้นที่ดี
  • Riverpod: ต่อยอด Provider แก้ข้อจำกัด (ไม่ผูก context, ทดสอบง่าย, compile-safe)
  • Bloc: event → state ผ่าน stream — เหมาะแอปใหญ่ flow ชัดเจน/ทีมใหญ่ ต้องการระเบียบสูง แต่ boilerplate เยอะ
  • คำตอบสัมภาษณ์: เลือกตามขนาดและทีม — ไม่มีตัวไหนดีที่สุดทุกงาน

การต่อ API / ข้อมูล

2 ข้อ

เรียก REST API ใน Flutter ยังไง และแปลง JSON ยังไงให้ปลอดภัย?

การต่อ API / ข้อมูลENetworks#api#json
  • ใช้ http หรือ dio (มี interceptor, timeout, retry — งานจริงนิยม dio)
  • แปลง JSON: เขียน fromJson/toJson เอง หรือ generate ด้วย json_serializable / freezed (type-safe, ลด human error, รองรับ immutability/copyWith)
  • แสดงผล async ใช้ FutureBuilder (snapshot.hasData/hasError) หรือจัดการใน state management + loading/error state ชัดเจน
  • ข้อควรระวัง: อย่า parse JSON ใน build method ทุกเฟรม, cache model ไว้

Future กับ Stream ต่างกันยังไง?

การต่อ API / ข้อมูลENetworks#async
  • Future: ค่าเดียวที่มา ภายหลัง (เช่นผล API) — async/await
  • Stream: ลำดับค่าหลายค่าตามเวลา (เช่น ตำแหน่ง GPS, websocket, ข้อความเข้า) — ฟังด้วย listen / StreamBuilder
  • UI: FutureBuilder วาดครั้งเดียวเมื่อค่ามา, StreamBuilder วาดใหม่ทุกครั้งที่มีค่าใหม่
  • เทียบ JS: Future ≈ Promise, Stream ≈ Observable/EventEmitter

Performance

1 ข้อ

ทำแอป Flutter ให้ลื่น (เร็ว/ไม่ค้าง) ทำอะไรได้บ้าง?

PerformanceENetworks#performance
  • ใส่ const หน้า constructor ที่เป็นไปได้ — widget เดิม reuse ได้ ไม่ต้อง rebuild
  • List ยาวใช้ ListView.builder (สร้างเฉพาะที่มองเห็น) อย่าใช้ Column + map เด็ดขาด
  • แยก widget ใหญ่เป็นชิ้นเล็ก + ยก state ลงลึก — rebuild กระทบน้อย
  • งานหนัก (คำนวณ/รูป) อย่าทำบน main isolate — ใช้ compute / Isolate.run
  • วัดด้วย DevTools timeline, เปิด flutter run --profile ดู jank จริง

Build & Release

1 ข้อ

Build ขึ้น store ต้องทำอะไรบ้าง (Android/iOS)?

Build & ReleaseENetworks#deploy
  • Android: สร้าง keystore แล้ว sign release (key.properties ไม่ลง git) → flutter build appbundle → อัป Play Console (internal → closed → production)
  • iOS: certificate + provisioning profile (App Store Connect), flutter build ipa → upload ผ่าน Transporter/Xcode → รอ review
  • ลดขนาด: --split-per-abi (แยกสถาปัตยกรรม CPU), เปิด code splitting, ระวัง asset ใหญ่
  • ก่อน submit: ทดสอบ release จริง (โหมด release บั๊กที่ debug ไม่เจอได้ เช่น permission/proguard)

LINE OA พื้นฐาน

2 ข้อ

LINE OA คืออะไร มีประเภทอะไรบ้าง?

LINE OA พื้นฐานCIMB Thai#oa#ภาพรวม
  • LINE Official Account = บัญชีทางการของธุรกิจบน LINE ใช้ทัก/ประชาสัมพันธ์/ทำบริการอัตโนมัติ
  • ประเภท: Unverified (ยืนยันไม่ครบ จำกัดฟีเจอร์) / Verified (ยืนยันตัวตนแล้ว มีบลูทิค) / Premium (ฟีเจอร์เต็ม เช่น API แบบพิเศษ)
  • โครงสร้างที่ต้องรู้ตอนทำระบบ: Provider → Channel (Messaging API อันหนึ่ง, LINE Login อีกอัน) แต่ละ channel มี channelSecret / channelAccessToken
  • ที่ CIMB ใช้ LINE OA ทำบริการ Withholding Tax — ลูกค้าถาม/ขอข้อมูลผ่านแชท แล้วระบบตอบกลับอัตโนมัติ/ผูกกับระบบหลังบ้าน

Rich Menu คืออะไร ใช้ทำอะไร?

LINE OA พื้นฐานENetworksCIMB Thai#rich menu
  • แถบเมนูใหญ่ 6 ช่อง (ใหญ่/กลาง/เล็ก) ที่ด้านล่างหน้าแชท — ผู้ใช้กดแล้วสั่ง action ได้ (เปิดลิงก์ LIFF, ส่งข้อความ, postback)
  • ทำหน้าที่เป็น navigation ของ bot — ให้ผู้ใช้เห็นเลยว่าทำอะไรได้บ้าง ไม่ต้องเดาคำสั่งพิมพ์
  • สร้างได้ใน LINE OA Manager หรือผ่าน API; รองรับ หลาย rich menu ต่อ user แล้วสลับตามภาษา/สถานะ (เช่น ก่อน-หลังผูกบัญชี)
  • ออกแบบภาพพื้นหลังให้ตรงช่อง area ที่ define ไว้

Messaging API

3 ข้อ

Webhook ของ Messaging API ทำงานยังไง?

Messaging API🧪 แบบจำลองENetworksCIMB Thai#webhook#คำถามยอดฮิต
  • เมื่อ user ทำอะไรกับ bot (ส่งข้อความ, กด postback, ติดตาม/บล็อก) LINE ยิง POST webhook event ไปที่ URL ที่เราตั้งไว้
  • เราได้ event หลายประเภท: message, follow / unfollow, postback (จากปุ่มใน Flex), join, memberJoined ฯลฯ
  • Reply token: มากับ message event ใช้ตอบกลับ ได้ครั้งเดียว และต้องใช้เร็ว (หมดอายุในเวลาสั้น ๆ) — ตอบหลายข้อความใส่ array ได้สูงสุด 5 ข้อความ
  • Endpoint ต้องตอบ 200 ภายในไม่กี่วินาที ไม่งั้น LINE retry → ควรทำงานหนักแบบ async (คิว) แล้วตอบ 200 ก่อน, ป้องกัน duplicate ด้วย webhook event id
🧪 แบบจำลอง: LINE Webhook + Reply Token — กดดูการทำงานทีละขั้น
🧑ผู้ใช้
💬LINE Platform
🪝Webhook API
⚙️คิว / Worker
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/6

Reply vs Push vs Multicast vs Broadcast ต่างกันยังไง?

Messaging API🧪 แบบจำลองENetworksCIMB Thai#reply#push
  • Reply: ตอบตาม event — ใช้ reply token, ไม่นับโควตา ใช้เมื่อตอบสนองทันที
  • Push: ส่งเองไปหา user/room/group เมื่อไหร่ก็ได้ — นับโควตา (แผนฟรีจำกัดมาก ประมาณ 200 ข้อความ/เดือน, แผนที่ชำระเงินโควตาเพิ่มขึ้น — เช็คตัวเลขล่าสุดใน console เพราะเปลี่ยนบ่อย)
  • Multicast: push ครั้งเดียวหา หลาย user (เช่นประกาศกลุ่มเป้าหมาย) — คุมโควตาดีกว่ายิง push รายคน
  • Broadcast: ส่งถึงทุกคนที่ติดตาม (จำกัดจำนวนครั้ง/เดือนตามแผน)
  • ออกแบบระบบ: ตอบแชทใช้ reply ให้คุ้ม, งานแจ้งเตือนภายหลังค่อยใช้ push/multicast
🧪 แบบจำลอง: Reply vs Push vs Multicast (+โควตา) — กดดูการทำงานทีละขั้น
🧑ผู้ใช้
💬LINE
🤖Bot ของเรา
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/7

ยืนยันว่า webhook มาจาก LINE จริง ๆ ทำยังไง (X-Line-Signature)?

Messaging APIENetworksCIMB Thai#security
  • ทุก request มี header X-Line-Signature = HMAC-SHA256 ของ raw request body ด้วย channelSecret แล้ว encode base64
  • ฝั่งเราคำนวณใหม่แล้วเทียบ — ตรงกับเป็นคำขอจริงจาก LINE, ไม่ตรงปฏิเสธ (403)
  • ตัวอย่าง Go: mac := hmac.New(sha256.New, []byte(secret)); mac.Write(body); sig := base64.StdEncoding.EncodeToString(mac.Sum(nil))
  • ข้อผิดพลาดที่เจอบ่อย: ใช้ body ที่ถูก parse แล้ว (เว้นวรรค/ลำดับ JSON เปลี่ยน) ทำให้เทียบไม่ตรง — ต้องใช้ raw body เดิม ๆ

Flex Message

1 ข้อ

Flex Message คืออะไร โครงสร้างยังไง มีข้อจำกัดอะไร?

Flex Message🧪 แบบจำลองENetworksCIMB Thai#flex#คำถามยอดฮิต
  • ข้อความแบบ "การ์ด" จัด layout ได้คล้าย HTML/CSS — ใช้ทำสินค้า, เมนู, ใบเสร็จ, ฟอร์มย่อในแชท
  • โครงสร้าง: type: flexcontainer bubble (การ์ดเดียว: hero / header / body / footer) หรือ carousel (หลาย bubble เลื่อนซ้ายขวา, สูงสุด 10 bubble)
  • ข้างในประกอบด้วย component: box, text, image, button, separator, filler — ปรับด้วย flex, weight, align, margin, color
  • altText จำเป็น (แสดงบน notification/เมื่ออุปกรณ์ไม่รองรับ)
  • ข้อจำกัด: ขนาด JSON ไม่เกิน ~10KB ต่อ bubble, ปรับแต่ง attribute/สีได้ไม่ทุกอย่าง — เลยต้องออกแบบกระชับ และทดสอบจริงทั้งมือถือและ desktop
  • ปุ่มใน Flex ทำได้ทั้ง uri (เปิดลิงก์ เช่น LIFF) / message (ส่งข้อความกลับ) / postback (ส่ง data แฝงไป webhook เหมาะทำ flow สั่งซื้อ/เลือกตัวเลือก)
🧪 แบบจำลอง: Flex Message + Postback (สั่งของในแชท) — กดดูการทำงานทีละขั้น
🧑ผู้ใช้
💬LINE
🤖Bot/Backend
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/6

LIFF

2 ข้อ

LIFF (LINE Front-end Framework) คืออะไร?

LIFFENetworksCIMB Thai#liff#คำถามยอดฮิต
  • = เว็บแอป (HTML/JS) ที่เปิดได้ในตัว LINE (in-app browser) หรือเบราว์เซอร์ปกติ — เอาประสบการณ์เว็บเต็มรูปแบบมาไว้ในแชท
  • ใช้ทำ: ฟอร์มสมัคร, หน้าสินค้า+ตะกร้า, ยืนยันตัวตน, ชำระเงิน — ต่อยอดจากแชท (กดปุ่มใน Flex → เปิด LIFF)
  • เริ่มต้น: สร้าง LIFF app ใน LINE Developers (ได้ liffId) แล้วหน้าเว็บเรียก liff.init({ liffId }) ก่อนใช้งาน
  • ขนาดหน้าต่าง (liff.init หรือปุ่มเปิด): Full เต็มจอ / Tall ~80% / Compact ~50% — เลือกตามงาน
  • LIFF ให้ข้อมูลผู้ใช้ (profile, userId) และ login พร้อมใช้ ทำให้ไม่ต้องสมัครสมาชิกใหม่

การ login และยืนยันตัวตนใน LIFF ทำยังไงให้ปลอดภัย?

LIFF🧪 แบบจำลองENetworksCIMB Thai#auth
  • ตรวจก่อนใช้: liff.isLoggedIn() — ยังไม่ login เรียก liff.login() (เปิดหน้า consent ของ LINE)
  • ได้ token ผ่าน SDK: liff.getAccessToken() (เรียก LINE API แทน user) และ liff.getIDToken() (พิสูจน์ตัวตนกับBackend ของเรา)
  • Backend ตรวจ ID token กับ LINE: POST /oauth2/v2.1/verify พร้อม id_token + client_id — ผ่านแล้วได้ sub (userId ปลอมไม่ได้) ค่อยออก session/JWT ของระบบเรา
  • ห้ามเชื่อ userId ที่ frontend ส่งมาเอง — ต้องยืนยันจาก ID token เสมอ ไม่งั้นปลอมตัวได้ง่าย
  • Token หมดอายุ → จัดการ refresh ให้ UX ไม่ดีดออกกลางคัน
🧪 แบบจำลอง: LIFF — ยืนยันตัวตนปลอดภัย — กดดูการทำงานทีละขั้น
📱LIFF (เว็บใน LINE)
🖥️Backend ของเรา
💬LINE API
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/8

LINE Login

1 ข้อ

LINE Login ทำงานยังไง (อธิบายเป็น OAuth 2.0 flow)?

LINE Login🧪 แบบจำลองENetworks#oauth
  • คือ OAuth 2.0 / OpenID Connect ของ LINE — เข้าสู่ระบบด้วยบัญชี LINE โดยไม่ต้องสร้าง user/password เอง
  • Flow (Authorization Code + PKCE):
  1. แอปส่ง user ไป access.line.me/oauth2/v2.1/authorize พร้อม client_id, redirect_uri, scope (profile, openid), state
  2. User กดยินยอม → LINE redirect กลับพร้อม code
  3. Backend แลก code เป็น access_token + id_token (ที่ /oauth2/v2.1/token)
  4. ดึง profile (/v2/profile) แล้วสร้าง session ของระบบเรา
  • state กัน CSRF, ตรวจ nonce กัน replay — LIFF ทำขั้นตอนพวกนี้ให้อัตโนมัติ
🧪 แบบจำลอง: LINE Login (OAuth 2.0 Authorization Code) — กดดูการทำงานทีละขั้น
🧑ผู้ใช้ (Browser)
📱Frontend
🖥️Backend
💬LINE
กด "ถัดไป" เพื่อเล่นแบบจำลองทีละขั้น (หรือกด "เล่นอัตโนมัติ")
ขั้นที่ 0/9

การออกแบบระบบบน LINE

2 ข้อ

ออกแบบระบบบริการบน LINE ทั้ง flow เป็นยังไง (เล่าจากประสบการณ์จริง)?

การออกแบบระบบบน LINEENetworksCIMB Thai#architecture
  • โครงหลัก: LINE (แชท/LIFF) ↔ Backend ของเรา ↔ ระบบหลังบ้าน/ฐานข้อมูล
  • แชท: webhook รับ event → ตรวจ signature → แยกเป้าหมายด้วย userId (ผูกกับ customer id ใน DB) → ตอบด้วย reply/Flex
  • งานหนัก/แจ้งเตือนภายหลัง: ทำ async ผ่านคิว แล้ว push/multicast กลับ (อย่ายำใน webhook เดี๋ยว timeout)
  • ธุรกิจ: LIFF ทำหน้าเลือกสินค้า/ฟอร์ม → backend ยืนยันตัวตนด้วย ID token → ปิดจบด้วย Flex ใบเสร็จในแชท
  • ต้องคิดเสมอ: โควตา push, retry/duplicate ของ webhook, ผู้ใช้ block bot (ส่งไม่ได้ — เก็บสถานะ), บันทึก log ทุก conversation เพื่อ audit
  • ตัวอย่าง: e-commerce แบบสั่งผ่านแชท (ENetworks) และบริการข้อมูลภาษี (CIMB) — pattern เดียวกัน: รับคำขอในแชท → ประมวลผลหลังบ้าน → ตอบกลับเป็น Flex/การ์ดข้อมูล

เจอโควตา/rate limit ของ LINE ต้องรับมือยังไง?

การออกแบบระบบบน LINEENetworksCIMB Thai#rate limit
  • รู้ก่อนว่าอะไรจำกัด: push มีโควตา/เดือน ตามแผน, broadcast จำกัดจำนวนครั้ง, API บางตัวมี limit ต่อวินาที — เช็คตัวเลขล่าสุดใน LINE Developers Console
  • กลยุทธ์:
  • ใช้ reply ทุกที่ที่ตอบตาม event ได้ (ไม่นับโควตา)
  • รวมผู้รับเป็น multicast แทน push ทีละคน
  • จัดลำดับความสำคัญของการแจ้งเตือน — อย่ายิงทุก event ไปหา user
  • เก็บสถานะการส่ง + retry ด้วยคิว เมื่อเจอ error 429/โควตา
  • มี monitoring ดูปริมาณส่งต่อวัน จะได้เห็นก่อนหมดโควตาจริง