คำถามสัมภาษณ์ Firebase 2026: การยืนยันตัวตนใน Flutter, Firestore และกฎความปลอดภัย

คำถามสัมภาษณ์ Firebase สำหรับนักพัฒนา Flutter พร้อมคำตอบเป็นโค้ด Dart: สตรีมสถานะการยืนยันตัวตน, Google Sign-In v7, CRUD และการออกแบบข้อมูลใน Firestore, กฎความปลอดภัย, custom claims, transaction และการเปลี่ยนแปลงใน FlutterFire 5

การผสานรวม Flutter และ Firebase แสดงสถาปัตยกรรมการยืนยันตัวตนและฐานข้อมูล Firestore

คำถามสัมภาษณ์ Firebase สำหรับตำแหน่ง Flutter มักวนอยู่กับห้าหัวข้อ ได้แก่ สตรีมสถานะการยืนยันตัวตนทั้งสามตัว การสร้างแบบจำลองข้อมูลใน Firestore กฎความปลอดภัย การเขียนขณะออฟไลน์ และโมเดลค่าใช้จ่ายเบื้องหลังการอ่านแต่ละครั้ง Firebase ยังคงเป็นแบ็กเอนด์มาตรฐานของแอป Flutter ในปี 2026 และตั้งแต่เดือนตุลาคม 2026 ปลั๊กอิน FlutterFire ออกเป็น BoM 5.x (firebase_core 5.1.0, firebase_auth 7.0.1, cloud_firestore 7.1.0) โดยยกระดับ minSdk ของ Android เป็น 24 และใช้ Firebase Apple SDK 13 อยู่เบื้องหลัง

FlutterFire BoM 5.1.0 (ตุลาคม 2026)

FlutterFire รุ่นปัจจุบันคือ BoM 5.1.0: firebase_auth 7.0.1, cloud_firestore 7.1.0, firebase_core 5.1.0 เวอร์ชันหลัก 5.0.0 กำหนดให้ใช้ minSdk ของ Android 24 สร้างบน Firebase Apple SDK 13.0.0 และเปลี่ยนชื่อพารามิเตอร์ของ activate ใน App Check ส่วน Firestore Pipelines ที่เพิ่มเข้ามาใน cloud_firestore 6.3.0 ได้รับ expression ใหม่เพิ่มขึ้นในทุกรุ่น

คำถามสัมภาษณ์ Firebase: สิ่งที่ถูกถามในปี 2026

ผู้สัมภาษณ์แทบไม่ถามรายละเอียด API แบบท่องจำ แต่จะตรวจสอบว่าผู้สมัครเข้าใจหรือไม่ว่า Firebase บังคับใช้กฎที่ใด แต่ละการดำเนินการมีค่าใช้จ่ายเท่าไร และเกิดอะไรขึ้นเมื่อเครือข่ายหลุด ตารางด้านล่างจับคู่หัวข้อที่พบซ้ำบ่อยกับสิ่งที่คำตอบที่ดีต้องมี ทุกหัวข้อมีคำตอบโดยละเอียดในส่วนคำถามสัมภาษณ์ Firebase สำหรับนักพัฒนา Flutterด้านล่าง

หัวข้อคำถามที่พบบ่อยสิ่งที่คำตอบที่ดีต้องมี
สตรีม authauthStateChanges() vs idTokenChanges() vs userChanges()แต่ละตัวส่งเหตุการณ์ใด และตัวไหนใช้ป้องกันการนำทาง
ความปลอดภัยการควบคุมสิทธิ์บังคับใช้ที่ใด?กฎความปลอดภัยบนเซิร์ฟเวอร์ ไม่ใช่การตรวจสอบฝั่งไคลเอนต์ ส่วน App Check เป็นอีกชั้นหนึ่ง
บทบาทจะสร้างบทบาท admin อย่างไร?Custom claims ที่ตั้งค่าผ่าน Admin SDK และอ่านผ่าน request.auth.token
แบบจำลองข้อมูลออกแบบข้อมูลสำหรับแอปแชทSubcollection, denormalize ข้อความล่าสุด, หนึ่ง document ต่อหนึ่งข้อความ
ความสอดคล้องTransaction หรือ batched write?Transaction อ่านแล้วเขียนและลองใหม่ได้ ส่วน batch เขียนอย่างเดียวแบบ atomic
ออฟไลน์การเขียนขณะออฟไลน์จะเป็นอย่างไร?แคชภายในเครื่อง, hasPendingWrites, ซิงค์เมื่อกลับมาออนไลน์
ค่าใช้จ่ายทำไมหน้าจอนี้แพง?คิดค่าบริการต่อการอ่าน document, listener, การแบ่งหน้าด้วย cursor
การเปรียบเทียบFirestore vs Realtime Databaseรูปแบบการ query, การขยายขนาด, หน่วยการคิดค่าบริการ

การตั้งค่า Firebase ในโปรเจกต์ Flutter

ก่อนเขียนโค้ด Firebase ใด ๆ โปรเจกต์จำเป็นต้องได้รับการกำหนดค่าเริ่มต้นอย่างถูกต้อง FlutterFire CLI จะทำการลงทะเบียนแพลตฟอร์มโดยอัตโนมัติและสร้างไฟล์การกำหนดค่าสำหรับ Android, iOS, เว็บ และ macOS

main.dartdart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Initialize Firebase with platform-specific config
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

ไฟล์ firebase_options.dart ถูกสร้างขึ้นโดยอัตโนมัติเมื่อรัน flutterfire configure ไฟล์นี้มีคีย์ API, รหัสโปรเจกต์ และตัวระบุเฉพาะแพลตฟอร์ม จึงไม่ต้องคัดลอกสิ่งใดด้วยตนเองจาก Firebase console

Firebase Authentication: อีเมล, Google และหลายปัจจัย

Firebase Authentication จัดการตัวตนของผู้ใช้ด้วยโค้ดสำเร็จรูปที่น้อยที่สุด แพ็กเกจ firebase_auth รองรับอีเมล/รหัสผ่าน, ผู้ให้บริการ OAuth (Google, Apple, GitHub), การยืนยันผ่าน SMS ทางโทรศัพท์ และการยืนยันตัวตนแบบหลายปัจจัย รวมถึง TOTP บน macOS ตั้งแต่ firebase_auth 6.1.0

การลงทะเบียนด้วยอีเมลและรหัสผ่าน

ขั้นตอนที่พบบ่อยที่สุดเริ่มต้นด้วยอีเมล/รหัสผ่าน เมธอด createUserWithEmailAndPassword จะคืนค่า UserCredential ที่มีอ็อบเจกต์ User ที่ยืนยันตัวตนแล้ว

auth_service.dartdart
import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // Register with email and password
  Future<User?> register(String email, String password) async {
    try {
      final credential = await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      return credential.user;
    } on FirebaseAuthException catch (e) {
      // Handle specific error codes
      switch (e.code) {
        case 'email-already-in-use':
          throw Exception('This email is already registered');
        case 'weak-password':
          throw Exception('Password must be at least 6 characters');
        default:
          throw Exception('Registration failed: ${e.message}');
      }
    }
  }

  // Sign in with existing credentials
  Future<User?> signIn(String email, String password) async {
    final credential = await _auth.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
    return credential.user;
  }

  // Reactive auth state stream
  Stream<User?> get authStateChanges => _auth.authStateChanges();
}

สตรีม authStateChanges() เป็นวิธีที่แนะนำในการติดตามสถานะการเข้าสู่ระบบทั่วทั้งแอป โดยจะส่งค่า User ปัจจุบันเมื่อมีการสมัครรับข้อมูล และทำงานอีกครั้งในทุกเหตุการณ์การเข้าสู่ระบบหรือออกจากระบบ

กฎของรหัสผ่านไม่จำเป็นต้องอยู่ฝั่งไคลเอนต์เพียงอย่างเดียวอีกต่อไป API validatePassword (มีตั้งแต่ firebase_auth 6.0.0) ตรวจสอบรหัสผ่านเทียบกับนโยบายที่กำหนดไว้ใน Firebase console ซึ่งสามารถกำหนดความยาวขั้นต่ำ ตัวพิมพ์ใหญ่ ตัวพิมพ์เล็ก ตัวเลข และอักขระพิเศษได้

การผสานรวม Google Sign-In

ผู้ให้บริการ OAuth ต้องใช้แพ็กเกจเพิ่มเติม ตั้งแต่ google_sign_in 7.0 ปลั๊กอินนี้เป็น singleton ที่เรียกผ่าน GoogleSignIn.instance ต้อง initialize เพียงครั้งเดียวเท่านั้น และแยกการยืนยันตัวตนออกจากการอนุญาตสิทธิ์ การเรียก GoogleSignIn().signIn() ที่พบในบทเรียนเก่าจะคอมไพล์ไม่ผ่านอีกต่อไป

google_auth.dartdart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

// Call once at startup, before any other GoogleSignIn method
Future<void> initGoogleSignIn() => GoogleSignIn.instance.initialize();

Future<UserCredential> signInWithGoogle() async {
  try {
    // Shows the native account picker; throws if the user cancels
    final googleUser = await GoogleSignIn.instance.authenticate();

    // Synchronous getter since v7: only the ID token is exposed here
    final googleAuth = googleUser.authentication;

    // Firebase only needs the ID token to create the credential
    final credential = GoogleAuthProvider.credential(
      idToken: googleAuth.idToken,
    );
    return FirebaseAuth.instance.signInWithCredential(credential);
  } on GoogleSignInException catch (e) {
    if (e.code == GoogleSignInExceptionCode.canceled) {
      throw Exception('Sign-in cancelled');
    }
    rethrow;
  }
}

รูปแบบนี้ใช้ได้กับผู้ให้บริการ OAuth ทั้งหมด: รับโทเค็นของผู้ให้บริการ ห่อหุ้มไว้ใน credential ของ Firebase แล้วเรียก signInWithCredential เมื่อแอปต้องเรียก API ของ Google ด้วย (Drive, Calendar) access token จะได้มาจากอีกขั้นตอนหนึ่งคือ authorizationClient.authorizeScopes ตามที่อธิบายไว้ในคู่มือการย้ายเวอร์ชันของ google_sign_in

กับดักในการสัมภาษณ์: Google Sign-In v7

ผู้สมัครที่เขียน GoogleSignIn().signIn() และอ่าน accessToken จาก authentication กำลังอ้างถึง API ก่อนเวอร์ชัน 7.0 ใน v7 นั้น authenticate() คืนค่าบัญชี authentication มีเพียง idToken และ access token สำหรับ scope เพิ่มเติมต้องเรียกการอนุญาตสิทธิ์อย่างชัดเจน

การดำเนินการ CRUD ของ Firestore และการสร้างแบบจำลองข้อมูล

Cloud Firestore จัดระเบียบข้อมูลเป็น collection และ document ต่างจากฐานข้อมูล SQL ตรงที่ Firestore ไม่มี schema เอกสารแต่ละฉบับสามารถมีฟิลด์ที่แตกต่างกันได้ ความยืดหยุ่นนี้เหมาะกับแอป Flutter แต่ต้องการการสร้างแบบจำลองข้อมูลอย่างมีวินัยเพื่อหลีกเลี่ยงปัญหาด้านประสิทธิภาพ

การเขียนและการอ่าน Document

การดำเนินการของ Firestore ทำได้อย่างตรงไปตรงมาด้วยแพ็กเกจ cloud_firestore ทุกการอ่านและการเขียนจะมุ่งเป้าไปที่เส้นทางของ document ที่เฉพาะเจาะจง

firestore_service.dartdart
import 'package:cloud_firestore/cloud_firestore.dart';

class TaskService {
  final _db = FirebaseFirestore.instance;
  final String _collection = 'tasks';

  // Create a new document with auto-generated ID
  Future<String> createTask(String userId, String title) async {
    final doc = await _db.collection(_collection).add({
      'userId': userId,
      'title': title,
      'completed': false,
      'createdAt': FieldValue.serverTimestamp(),
    });
    return doc.id;
  }

  // Read a single document by ID
  Future<Map<String, dynamic>?> getTask(String taskId) async {
    final snapshot = await _db.collection(_collection).doc(taskId).get();
    return snapshot.data();
  }

  // Update specific fields without overwriting the entire document
  Future<void> toggleComplete(String taskId, bool completed) async {
    await _db.collection(_collection).doc(taskId).update({
      'completed': completed,
      'updatedAt': FieldValue.serverTimestamp(),
    });
  }

  // Delete a document
  Future<void> deleteTask(String taskId) async {
    await _db.collection(_collection).doc(taskId).delete();
  }
}

FieldValue.serverTimestamp() รับประกัน timestamp ที่สอดคล้องกันในทุกอุปกรณ์ โดยใช้นาฬิกาของเซิร์ฟเวอร์ Firestore แทนนาฬิกาของไคลเอนต์ ตั้งแต่ cloud_firestore 7.1.0 มี FieldValue.minimum และ FieldValue.maximum เพิ่มเข้ามาในกลุ่มการแปลงค่าฝั่งเซิร์ฟเวอร์ ทั้งสองตัวเก็บค่าที่ต่ำกว่าหรือสูงกว่าระหว่างค่าที่บันทึกไว้กับค่าที่ส่งเข้ามาโดยไม่ต้องอ่านก่อน เหมาะกับตัวนับอย่างคะแนนสูงสุด

สตรีมแบบเรียลไทม์ด้วย snapshots()

Firestore โดดเด่นด้วยการซิงโครไนซ์แบบเรียลไทม์ เมธอด snapshots() คืนค่า Stream ที่ส่งข้อมูลใหม่ทุกครั้งที่ document ที่เกี่ยวข้องมีการเปลี่ยนแปลง โดยไม่ต้อง polling

real_time_tasks.dartdart
import 'package:cloud_firestore/cloud_firestore.dart';

class TaskStream {
  final _db = FirebaseFirestore.instance;

  // Stream all tasks for a specific user, ordered by creation date
  Stream<List<Map<String, dynamic>>> userTasks(String userId) {
    return _db
        .collection('tasks')
        .where('userId', isEqualTo: userId)
        .orderBy('createdAt', descending: true)
        .snapshots()
        .map((snapshot) => snapshot.docs.map((doc) {
              final data = doc.data();
              data['id'] = doc.id; // Include document ID
              return data;
            }).toList());
  }
}

ภายใน widget ของ Flutter สตรีมนี้ผสานรวมโดยตรงกับ StreamBuilder เพื่อสร้าง UI ใหม่ในทุกการเปลี่ยนแปลงของฐานข้อมูล Firestore จัดการการเชื่อมต่อ การแคช และการคงอยู่แบบออฟไลน์โดยอัตโนมัติ

พร้อมที่จะพิชิตการสัมภาษณ์ Flutter แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

กฎความปลอดภัยของ Firestore สำหรับแอป Flutter

กฎความปลอดภัยทำงานบนเซิร์ฟเวอร์ Firestore และควบคุมสิทธิ์การอ่าน/เขียนในระดับ document หากไม่มีกฎที่เหมาะสม ผู้ใช้ที่ยืนยันตัวตนแล้วทุกคนสามารถอ่านหรือแก้ไข document ใดก็ได้

firestore.rulesjavascript
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Users can only access their own profile
    match /users/{userId} {
      allow read, update: if request.auth != null
                          && request.auth.uid == userId;
      allow create: if request.auth != null;
      allow delete: if false; // Prevent self-deletion
    }

    // Tasks belong to the user who created them
    match /tasks/{taskId} {
      allow read, write: if request.auth != null
                         && resource.data.userId == request.auth.uid;
      allow create: if request.auth != null
                    && request.resource.data.userId == request.auth.uid;
    }
  }
}

ตัวแปร request.auth.uid มี ID ของผู้ใช้ที่ยืนยันตัวตนแล้วจาก Firebase Auth กฎที่อ้างอิง resource.data จะตรวจสอบฟิลด์ของ document ที่มีอยู่ ในขณะที่ request.resource.data จะตรวจสอบข้อมูลการเขียนที่กำลังเข้ามา

กฎความปลอดภัยไม่ใช่ทางเลือก

Firestore จะปฏิเสธการเข้าถึงทั้งหมดเป็นค่าเริ่มต้นในโหมด production ทุก collection ต้องมีกฎที่ชัดเจน ข้อผิดพลาดที่พบบ่อยในโปรเจกต์ Flutter: การพัฒนาด้วยกฎโหมดทดสอบ (allow read, write: if true) แล้วลืมล็อกก่อนนำขึ้น production

การคงอยู่แบบออฟไลน์และกลยุทธ์การแคช

Firestore เปิดใช้งานการคงอยู่แบบออฟไลน์เป็นค่าเริ่มต้นบนแพลตฟอร์มมือถือ เมื่ออุปกรณ์สูญเสียการเชื่อมต่อ การดำเนินการอ่านจะคืนค่าข้อมูลที่แคชไว้ และการดำเนินการเขียนจะเข้าคิวภายในเครื่องจนกว่าการเชื่อมต่อจะกลับมา

พฤติกรรมนี้ไม่ต้องเขียนโค้ด Flutter เพิ่มเติม การเรียก get() และ snapshots() เดิมยังคงทำงานได้ขณะออฟไลน์ อย่างไรก็ตาม มีสองสิ่งที่ต้องพิจารณาสำหรับแอป production:

  • ขนาดแคช: Firestore แคช document ทั้งหมดที่ไคลเอนต์ได้อ่าน สำหรับแอปที่มีข้อมูลจำนวนมาก ให้กำหนดค่า Settings(cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED) หรือกำหนดขีดจำกัดที่เฉพาะเจาะจง
  • ตัวบ่งชี้การเขียนที่รอดำเนินการ: ใช้ SnapshotMetadata.hasPendingWrites เพื่อแสดงให้ผู้ใช้ทราบว่าการเปลี่ยนแปลงในเครื่องยังไม่ได้ซิงค์ไปยังเซิร์ฟเวอร์
offline_aware_widget.dartdart
StreamBuilder<DocumentSnapshot>(
  stream: FirebaseFirestore.instance
      .collection('tasks')
      .doc(taskId)
      .snapshots(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) return const CircularProgressIndicator();

    final data = snapshot.data!;
    final isPending = data.metadata.hasPendingWrites;

    return Row(
      children: [
        Text(data['title']),
        if (isPending) const Icon(Icons.cloud_upload, size: 16),
      ],
    );
  },
)

การจัดโครงสร้างแอป Flutter Firebase สำหรับ Production

สถาปัตยกรรมที่ขยายขนาดได้จะแยกตรรกะของ Firebase ออกจากโค้ด UI รูปแบบ repository มอบชั้นการนามธรรมที่ชัดเจนซึ่งช่วยให้การทดสอบและการย้ายแบ็กเอนด์ในอนาคตง่ายขึ้น

task_repository.dartdart
abstract class TaskRepository {
  Future<String> create(String userId, String title);
  Stream<List<Task>> watchAll(String userId);
  Future<void> update(String id, Map<String, dynamic> fields);
  Future<void> delete(String id);
}

// firebase_task_repository.dart
class FirebaseTaskRepository implements TaskRepository {
  final _db = FirebaseFirestore.instance;

  
  Future<String> create(String userId, String title) async {
    final doc = await _db.collection('tasks').add({
      'userId': userId,
      'title': title,
      'completed': false,
      'createdAt': FieldValue.serverTimestamp(),
    });
    return doc.id;
  }

  
  Stream<List<Task>> watchAll(String userId) {
    return _db
        .collection('tasks')
        .where('userId', isEqualTo: userId)
        .orderBy('createdAt', descending: true)
        .snapshots()
        .map((s) => s.docs.map(Task.fromFirestore).toList());
  }

  // ... update and delete implementations
}

ด้วยรูปแบบนี้ โซลูชันการจัดการ state อย่าง Riverpod หรือ BLoC จะใช้อินเทอร์เฟซ repository โดยไม่ต้องรู้เกี่ยวกับ Firestore การทดสอบหน่วยสามารถแทนที่ด้วย mock repository ที่คืนค่าข้อมูลที่กำหนดไว้ล่วงหน้าได้

อัปเกรดเป็น FlutterFire 5: สิ่งที่พัง

รุ่นหลักเดือนตุลาคม 2026 (BoM 5.0.0, firebase_auth 7.0.0, cloud_firestore 7.0.0, firebase_core 5.0.0) เปลี่ยนข้อกำหนดในการ build มากกว่า API ของ Dart จุดที่ทำให้ build ล้มเหลวหรือไม่ผ่านการรีวิวมีดังนี้

  • Android minSdk 24: ปลั๊กอินสร้างบน Firebase Android BoM 35 โปรเจกต์ที่ยังใช้ minSdk 21 หรือ 23 จะคอมไพล์ไม่ผ่าน
  • Apple SDK 13: Firebase Apple SDK ขยับเป็น 13.0.0 เมื่อใช้ Swift Package Manager (12.19.0 เมื่อใช้ CocoaPods) และแพ็กเกจ Swift ต้องใช้ macOS 11
  • App Check: พารามิเตอร์ของ activate เปลี่ยนชื่อเป็น providerWeb, providerAndroid และ providerApple
  • Deprecation: setIndexConfigurationFromJSON ถูกประกาศ deprecated ใน cloud_firestore 7.0.0 และ firebase_ml_model_downloader ไม่ถูกเผยแพร่อีกต่อไป
  • ถูกลบไปแล้วตั้งแต่สาย 6.x: fetchSignInMethodsForEmail() และ User.updateEmail() โค้ดที่ตรวจสอบว่าอีเมลใช้ผู้ให้บริการใดก่อนเข้าสู่ระบบต้องออกแบบใหม่ เพราะการป้องกัน email enumeration จะซ่อนข้อมูลนั้นไว้

รายการทั้งหมดแยกตามแพ็กเกจอยู่ในบันทึกประจำรุ่นของ FlutterFire และ changelog ของ cloud_firestore

คำถามสัมภาษณ์ Firebase สำหรับนักพัฒนา Flutter

การสัมภาษณ์ทดสอบทั้งความเข้าใจเชิงแนวคิดและการนำบริการ Firebase ไปใช้จริง คำถามต่อไปนี้ปรากฏเป็นประจำในการสัมภาษณ์นักพัฒนา Flutter ในปี 2026 ส่วนด้านเฟรมเวิร์กมีอยู่ในคู่มือคำถามสัมภาษณ์ Flutterที่ครอบคลุมกว้างกว่า

authStateChanges() แตกต่างจาก idTokenChanges() และ userChanges() อย่างไร?

authStateChanges() ทำงานเฉพาะในเหตุการณ์การเข้าสู่ระบบและออกจากระบบเท่านั้น idTokenChanges() จะทำงานเพิ่มเติมเมื่อ ID token ถูกรีเฟรช (ประมาณทุกหนึ่งชั่วโมง) userChanges() จับทุกอย่างข้างต้นรวมถึงการอัปเดตโปรไฟล์อย่าง updateDisplayName() สำหรับ navigation guard ส่วนใหญ่ authStateChanges() ก็เพียงพอแล้ว

เกิดอะไรขึ้นเมื่อมีการเขียน Firestore ขณะที่อุปกรณ์ออฟไลน์?

การเขียนจะถูกเก็บไว้ในแคชภายในเครื่องและคิวการเขียนที่รอดำเนินการ ตัวรับฟัง snapshots() จะได้รับข้อมูลที่อัปเดตทันทีพร้อม metadata.hasPendingWrites == true เมื่อการเชื่อมต่อกลับมา Firestore จะซิงค์การเขียนที่รอดำเนินการโดยอัตโนมัติ และการเขียนที่ไปถึงเซิร์ฟเวอร์เป็นลำดับสุดท้ายจะเป็นค่าที่ใช้

ควรสร้างแบบจำลองข้อมูล Firestore สำหรับแอปแชทอย่างไร?

ใช้ collection chats ที่แต่ละ document มี metadata (ผู้เข้าร่วม, timestamp ของข้อความล่าสุด) และ subcollection messages ภายใต้แต่ละ document ของแชท โครงสร้างนี้ช่วยให้สามารถ query รายการแชทของผู้ใช้ได้อย่างมีประสิทธิภาพ พร้อมทั้งแบ่งหน้าข้อความภายในแต่ละบทสนทนา ควร denormalize ข้อความล่าสุดเข้าไปใน document แม่ เพื่อหลีกเลี่ยงการอ่าน subcollection สำหรับมุมมองรายการ

ทำไมจึงควรใช้ FieldValue.serverTimestamp() มากกว่า DateTime.now()?

DateTime.now() ใช้นาฬิกาของอุปกรณ์ ซึ่งอาจไม่แม่นยำหรือถูกดัดแปลงได้ FieldValue.serverTimestamp() ใช้นาฬิกาของเซิร์ฟเวอร์ Firestore ซึ่งรับประกันลำดับที่สอดคล้องกันในทุกไคลเอนต์ สิ่งนี้สำคัญสำหรับฟีเจอร์อย่างการเรียงลำดับข้อความ ฟีดกิจกรรม และบันทึกการตรวจสอบ

การ query แบบ composite จัดการอย่างไรใน Firestore และมีข้อกำหนดเรื่อง index อย่างไร?

Firestore ต้องการ composite index สำหรับ query ที่รวมหลายฟิลด์ด้วยอนุประโยค where และ orderBy index แบบฟิลด์เดียวจะถูกสร้างโดยอัตโนมัติ แต่ composite index ต้องถูกกำหนดด้วยตนเองใน firestore.indexes.json หรือผ่าน Firebase console Firestore จะบันทึกลิงก์โดยตรงเพื่อสร้าง index ที่ขาดหายไปเมื่อ query ล้มเหลว

ควรใช้ transaction แทน batched write เมื่อใด?

ต้องใช้ transaction เมื่อการเขียนขึ้นอยู่กับข้อมูลที่อ่านในการดำเนินการเดียวกัน เช่น ลดจำนวนสต็อกเฉพาะเมื่อยังมากกว่าศูนย์ SDK จะเรียก callback ซ้ำหาก document ที่อ่านไปมีการเปลี่ยนแปลงก่อน commit ดังนั้น callback ต้องไม่มี side effect ส่วน batched write จะเขียนหลายรายการแบบ atomic โดยไม่มีการอ่านเลย เช่น สร้าง document หนึ่งพร้อมอัปเดตตัวนับในอีกที่หนึ่ง ทั้งสองแบบทำงานต่างกันเมื่อออฟไลน์: batch จะถูกเข้าคิว ส่วน transaction จะล้มเหลวหากไม่มีการเชื่อมต่อ

dart
final db = FirebaseFirestore.instance;

// Transaction: read then write, retried on contention
await db.runTransaction((tx) async {
  final ref = db.collection('products').doc(id);
  final snap = await tx.get(ref);
  final stock = snap.get('stock') as int;
  if (stock <= 0) throw Exception('Out of stock');
  tx.update(ref, {'stock': stock - 1});
});

// Batched write: atomic writes, no reads
final batch = db.batch();
batch.set(db.collection('orders').doc(), {'productId': id});
batch.update(db.collection('stats').doc('orders'), {
  'count': FieldValue.increment(1),
});
await batch.commit();

จะสร้างบทบาท admin ด้วย Firebase อย่างไร?

ใช้ custom claims ที่ตั้งค่าจากสภาพแวดล้อมที่เชื่อถือได้ผ่าน Admin SDK และไม่ตั้งจากไคลเอนต์ Flutter เด็ดขาด claim จะเดินทางไปพร้อมกับ ID token ทำให้กฎอ่านค่าได้ในรูป request.auth.token.admin == true โดยไม่ต้องค้นหา document เพิ่ม เอกสาร custom claims จำกัด payload ไว้ที่ 1000 ไบต์ และไคลเอนต์จะเห็น claim ใหม่หลังจากโทเค็นถูกรีเฟรชแล้วเท่านั้น ซึ่งสามารถบังคับได้ด้วย currentUser.getIdToken(true)

กฎความปลอดภัยกับ App Check ต่างกันอย่างไร?

กฎความปลอดภัยตัดสินว่าผู้ใช้คนหนึ่งสามารถอ่านหรือเขียน document หนึ่งได้หรือไม่ ส่วน App Check ยืนยันว่าคำขอมาจากแอปตัวจริงบนอุปกรณ์จริง ซึ่งช่วยบล็อกสคริปต์ที่นำคีย์ API สาธารณะไปใช้ซ้ำ ทั้งสองตอบคำถามคนละข้อ แอป production จึงต้องมีทั้งคู่ ตั้งแต่ FlutterFire 5 การเปิดใช้งาน App Check ใช้ providerAndroid, providerApple และ providerWeb

ทำไมหน้าจอที่ใช้ Firestore จึงอาจมีค่าใช้จ่ายสูง และแบ่งหน้าอย่างไร?

Firestore คิดค่าบริการต่อการอ่าน การเขียน และการลบ document แต่ละครั้ง listener บน collection ที่ไม่จำกัดขอบเขตจะอ่านทุก document ที่ตรงเงื่อนไขในการโหลดครั้งแรก จากนั้นนับหนึ่งการอ่านต่อ document ที่เปลี่ยนแปลง การแบ่งหน้าใช้ query cursor แทน offset: limit(20) สำหรับหน้าแรก แล้วใช้ startAfterDocument(lastDoc) สำหรับหน้าถัดไป ทำให้แต่ละหน้าเสียค่าใช้จ่ายเฉพาะ document ที่คืนกลับมาเท่านั้น

Firestore หรือ Realtime Database: ควรเลือกตัวไหนและเพราะอะไร?

Firestore เก็บ document ไว้ใน collection รองรับ query แบบผสมพร้อม index และคิดค่าบริการต่อการดำเนินการ Realtime Database เก็บข้อมูลเป็น JSON tree เดียว รองรับ query แบบตื้นบน child key เพียงตัวเดียว และคิดค่าบริการหลัก ๆ ตามแบนด์วิดท์และพื้นที่จัดเก็บ ซึ่งเหมาะกับการอัปเดตเล็ก ๆ ที่เกิดถี่มากอย่างตัวบ่งชี้สถานะออนไลน์ แอป Flutter ใหม่ส่วนใหญ่เริ่มต้นด้วย Firestore และเพิ่ม Realtime Database เฉพาะสำหรับ presence

สำหรับการเตรียมตัวสัมภาษณ์ Flutter เพิ่มเติม โมดูลฝึกฝนเฉพาะทางครอบคลุมหัวข้อเหล่านี้ด้วยแบบทดสอบเชิงโต้ตอบและความท้าทายแบบจับเวลา

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

แหล่งข้อมูลอ้างอิง

สรุปประเด็นสำคัญของ Flutter Firebase สำหรับการสัมภาษณ์

  • FlutterFire BoM 5.1.0 มาพร้อม firebase_auth 7.0.1 และ cloud_firestore 7.1.0 และกำหนดให้ใช้ minSdk ของ Android 24
  • google_sign_in 7 แทนที่ GoogleSignIn().signIn() ด้วย GoogleSignIn.instance.initialize() ตามด้วย authenticate() และ Firebase ต้องการเพียง idToken
  • authStateChanges() มอบสตรีมแบบ reactive สำหรับจัดการสถานะการเข้าสู่ระบบทั่วทั้งแอปโดยไม่ต้องตรวจสอบด้วยตนเอง
  • การซิงโครไนซ์แบบเรียลไทม์ผ่าน snapshots() ขจัดการ polling และผสานรวมโดยตรงกับ StreamBuilder เพื่อการอัปเดต UI โดยอัตโนมัติ
  • กฎความปลอดภัยเป็นสิ่งจำเป็นสำหรับ production ควรตรวจสอบ request.auth.uid เทียบกับฟิลด์ความเป็นเจ้าของของ document เสมอ และใช้ custom claims สำหรับบทบาท
  • Transaction อ่านแล้วเขียนพร้อมการลองใหม่ batched write เขียนอย่างเดียว การแบ่งหน้าใช้ cursor เพราะการอ่าน document ทุกครั้งมีค่าใช้จ่าย
  • การคงอยู่แบบออฟไลน์ทำงานบนมือถือได้โดยไม่ต้องเขียนโค้ดเพิ่ม ใช้ hasPendingWrites เพื่อสื่อสารสถานะการซิงค์ให้ผู้ใช้ทราบ
  • รูปแบบ repository แยก Firebase ออกจากตรรกะทางธุรกิจ ทำให้การทดสอบและการย้ายในอนาคตเป็นเรื่องง่าย
ชาเลนจ์ประจำวัน

คุณหาบั๊กใน Flutter เจอไหม

โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

Anthony Fillion-Maillet

เขียนโดย

Anthony Fillion-Maillet

ผู้ก่อตั้ง SharpSkill

เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่

อัปเดตเมื่อ 9 พฤศจิกายน 2569

แท็ก

#flutter
#firebase
#authentication
#firestore
#dart
#mobile development

แชร์

บทความที่เกี่ยวข้อง

คำถามสัมภาษณ์งาน Flutter สำหรับนักพัฒนาแอปมือถือ

20 คำถามสัมภาษณ์งาน Flutter ที่พบบ่อยที่สุดสำหรับนักพัฒนาแอปมือถือ

เตรียมตัวสัมภาษณ์งาน Flutter ด้วยคำถาม 20 ข้อที่ถูกถามบ่อยที่สุด ครอบคลุม Widget, State Management, Dart, สถาปัตยกรรม และแนวทางปฏิบัติที่ดีที่สุด พร้อมคำอธิบายอย่างละเอียดและตัวอย่างโค้ด

คู่มือการสร้างแอปพลิเคชันมือถือตัวแรกด้วย Flutter และ Dart

Flutter: สร้างแอปพลิเคชันข้ามแพลตฟอร์มตัวแรก

คู่มือฉบับสมบูรณ์สำหรับการสร้างแอปพลิเคชันมือถือข้ามแพลตฟอร์มด้วย Flutter และ Dart ครอบคลุม Widget การจัดการสถานะ การนำทาง และแนวทางปฏิบัติที่ดีสำหรับผู้เริ่มต้น

Dart Isolates และ Concurrency ใน Flutter 2026

Dart Isolates และ Concurrency 2026: compute, Async และคำถามสัมภาษณ์

คู่มือฉบับสมบูรณ์เกี่ยวกับ Dart isolates, ฟังก์ชัน compute ใน Flutter และ pattern ของ concurrency สำหรับการพัฒนาแอปที่ตอบสนองรวดเร็วในปี 2026