Cloud Firestore는 오프라인 데이터 지속성을 지원합니다. 이 기능은 앱이 활발하게 사용하는 Cloud Firestore 데이터의 사본을 캐시하므로 기기가 오프라인일 때 앱이 데이터에 액세스할 수 있습니다. 캐시된 데이터를 쓰고, 읽고, 듣고, 쿼리할 수 있습니다. 기기가 다시 온라인 상태가 되면 Cloud Firestore는 앱에서 변경한 모든 로컬 변경 사항을 Cloud Firestore 백엔드와 동기화합니다.
오프라인 지속성을 사용하기 위해 Cloud Firestore 데이터에 액세스하는 데 사용하는 코드를 변경할 필요가 없습니다. 오프라인 지속성을 사용하면 Cloud Firestore 클라이언트 라이브러리가 온라인 및 오프라인 데이터 액세스를 자동으로 관리하고 기기가 다시 온라인 상태가 되면 로컬 데이터를 동기화합니다.
오프라인 지속성 구성
Cloud Firestore를 초기화할 때 오프라인 지속성을 활성화하거나 비활성화할 수 있습니다.
- Android 및 Apple 플랫폼의 경우 오프라인 지속성이 기본적으로 활성화됩니다. 지속성을 비활성화하려면
PersistenceEnabled
옵션을false
로 설정합니다. - 웹의 경우 오프라인 지속성은 기본적으로 비활성화되어 있습니다. 지속성을 활성화하려면
enablePersistence
메서드를 호출합니다. Cloud Firestore의 캐시는 세션 간에 자동으로 지워지지 않습니다. 따라서 웹 앱이 민감한 정보를 처리하는 경우 지속성을 활성화하기 전에 사용자가 신뢰할 수 있는 장치에 있는지 확인하십시오.
Web version 9
import { enableIndexedDbPersistence } from "firebase/firestore"; enableIndexedDbPersistence(db) .catch((err) => { if (err.code == 'failed-precondition') { // Multiple tabs open, persistence can only be enabled // in one tab at a a time. // ... } else if (err.code == 'unimplemented') { // The current browser does not support all of the // features required to enable persistence // ... } }); // Subsequent queries will use persistence, if it was enabled successfully
Web version 8
firebase.firestore().enablePersistence() .catch((err) => { if (err.code == 'failed-precondition') { // Multiple tabs open, persistence can only be enabled // in one tab at a a time. // ... } else if (err.code == 'unimplemented') { // The current browser does not support all of the // features required to enable persistence // ... } }); // Subsequent queries will use persistence, if it was enabled successfully
빠른
let settings = FirestoreSettings() settings.isPersistenceEnabled = true // Any additional options // ... // Enable offline data persistence let db = Firestore.firestore() db.settings = settings
목표-C
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; settings.persistenceEnabled = YES; // Any additional options // ... // Enable offline data persistence FIRFirestore *db = [FIRFirestore firestore]; db.settings = settings;
Kotlin+KTX
val settings = firestoreSettings { isPersistenceEnabled = true } db.firestoreSettings = settings
Java
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder() .setPersistenceEnabled(true) .build(); db.setFirestoreSettings(settings);
Dart
// Apple and Android db.settings = const Settings(persistenceEnabled: true); // Web await db .enablePersistence(const PersistenceSettings(synchronizeTabs: true));
캐시 크기 구성
지속성이 활성화되면 Cloud Firestore는 오프라인 액세스를 위해 백엔드에서 수신한 모든 문서를 캐시합니다. Cloud Firestore는 캐시 크기에 대한 기본 임계값을 설정합니다. 기본값을 초과한 후 Cloud Firestore는 사용하지 않는 오래된 문서를 주기적으로 정리하려고 시도합니다. 다른 캐시 크기 임계값을 구성하거나 정리 프로세스를 완전히 비활성화할 수 있습니다.
Web version 9
import { initializeFirestore, CACHE_SIZE_UNLIMITED } from "firebase/firestore"; const firestoreDb = initializeFirestore(app, { cacheSizeBytes: CACHE_SIZE_UNLIMITED });
Web version 8
firebase.firestore().settings({ cacheSizeBytes: firebase.firestore.CACHE_SIZE_UNLIMITED });
빠른
// The default cache size threshold is 100 MB. Configure "cacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "FirestoreCacheSizeUnlimited" // to disable clean-up. let settings = Firestore.firestore().settings settings.cacheSizeBytes = FirestoreCacheSizeUnlimited Firestore.firestore().settings = settings
목표-C
// The default cache size threshold is 100 MB. Configure "cacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "kFIRFirestoreCacheSizeUnlimited" // to disable clean-up. FIRFirestoreSettings *settings = [FIRFirestore firestore].settings; settings.cacheSizeBytes = kFIRFirestoreCacheSizeUnlimited; [FIRFirestore firestore].settings = settings;
Kotlin+KTX
// The default cache size threshold is 100 MB. Configure "setCacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "CACHE_SIZE_UNLIMITED" // to disable clean-up. val settings = FirebaseFirestoreSettings.Builder() .setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED) .build() db.firestoreSettings = settings
Java
// The default cache size threshold is 100 MB. Configure "setCacheSizeBytes" // for a different threshold (minimum 1 MB) or set to "CACHE_SIZE_UNLIMITED" // to disable clean-up. FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder() .setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED) .build(); db.setFirestoreSettings(settings);
Dart
db.settings = const Settings( persistenceEnabled: true, cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED, );
오프라인 데이터 듣기
장치가 오프라인 상태일 때 오프라인 지속성을 활성화한 경우 리스너는 로컬로 캐시된 데이터가 변경될 때 수신 이벤트를 수신합니다. 문서, 컬렉션 및 쿼리를 들을 수 있습니다.
데이터를 서버에서 받는지 캐시에서 받는지 확인하려면 스냅샷 이벤트의 SnapshotMetadata
에서 fromCache
속성을 사용하세요. fromCache
가 true
인 경우 데이터는 캐시에서 가져온 것이며 오래되었거나 불완전할 수 있습니다. fromCache
가 false
이면 데이터가 완전하고 서버의 최신 업데이트로 최신 상태입니다.
기본적으로 SnapshotMetadata
만 변경된 경우 이벤트가 발생하지 않습니다. fromCache
값에 의존하는 경우 수신 핸들러를 연결할 때 includeMetadataChanges
수신 옵션을 지정하십시오.
Web version 9
import { collection, onSnapshot, where, query } from "firebase/firestore"; const q = query(collection(db, "cities"), where("state", "==", "CA")); onSnapshot(q, { includeMetadataChanges: true }, (snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { console.log("New city: ", change.doc.data()); } const source = snapshot.metadata.fromCache ? "local cache" : "server"; console.log("Data came from " + source); }); });
Web version 8
db.collection("cities").where("state", "==", "CA") .onSnapshot({ includeMetadataChanges: true }, (snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { console.log("New city: ", change.doc.data()); } var source = snapshot.metadata.fromCache ? "local cache" : "server"; console.log("Data came from " + source); }); });
빠른
// Listen to metadata updates to receive a server snapshot even if // the data is the same as the cached data. db.collection("cities").whereField("state", isEqualTo: "CA") .addSnapshotListener(includeMetadataChanges: true) { querySnapshot, error in guard let snapshot = querySnapshot else { print("Error retreiving snapshot: \(error!)") return } for diff in snapshot.documentChanges { if diff.type == .added { print("New city: \(diff.document.data())") } } let source = snapshot.metadata.isFromCache ? "local cache" : "server" print("Metadata: Data fetched from \(source)") }
목표-C
// Listen to metadata updates to receive a server snapshot even if // the data is the same as the cached data. [[[db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"] addSnapshotListenerWithIncludeMetadataChanges:YES listener:^(FIRQuerySnapshot *snapshot, NSError *error) { if (snapshot == nil) { NSLog(@"Error retreiving snapshot: %@", error); return; } for (FIRDocumentChange *diff in snapshot.documentChanges) { if (diff.type == FIRDocumentChangeTypeAdded) { NSLog(@"New city: %@", diff.document.data); } } NSString *source = snapshot.metadata.isFromCache ? @"local cache" : @"server"; NSLog(@"Metadata: Data fetched from %@", source); }];
Kotlin+KTX
db.collection("cities").whereEqualTo("state", "CA") .addSnapshotListener(MetadataChanges.INCLUDE) { querySnapshot, e -> if (e != null) { Log.w(TAG, "Listen error", e) return@addSnapshotListener } for (change in querySnapshot!!.documentChanges) { if (change.type == DocumentChange.Type.ADDED) { Log.d(TAG, "New city: ${change.document.data}") } val source = if (querySnapshot.metadata.isFromCache) "local cache" else "server" Log.d(TAG, "Data fetched from $source") } }
Java
db.collection("cities").whereEqualTo("state", "CA") .addSnapshotListener(MetadataChanges.INCLUDE, new EventListener<QuerySnapshot>() { @Override public void onEvent(@Nullable QuerySnapshot querySnapshot, @Nullable FirebaseFirestoreException e) { if (e != null) { Log.w(TAG, "Listen error", e); return; } for (DocumentChange change : querySnapshot.getDocumentChanges()) { if (change.getType() == Type.ADDED) { Log.d(TAG, "New city:" + change.getDocument().getData()); } String source = querySnapshot.getMetadata().isFromCache() ? "local cache" : "server"; Log.d(TAG, "Data fetched from " + source); } } });
Dart
db .collection("cities") .where("state", isEqualTo: "CA") .snapshots(includeMetadataChanges: true) .listen((querySnapshot) { for (var change in querySnapshot.docChanges) { if (change.type == DocumentChangeType.added) { final source = (querySnapshot.metadata.isFromCache) ? "local cache" : "server"; print("Data fetched from $source}"); } } });
오프라인 데이터 가져오기
기기가 오프라인일 때 문서를 받으면 Cloud Firestore는 캐시에서 데이터를 반환합니다.
컬렉션을 쿼리할 때 캐시된 문서가 없으면 빈 결과가 반환됩니다. 특정 문서를 가져올 때 대신 오류가 반환됩니다.
오프라인 데이터 쿼리
쿼리는 오프라인 지속성과 함께 작동합니다. 이전 섹션에서 설명한 대로 직접 가져오기 또는 수신을 통해 쿼리 결과를 검색할 수 있습니다. 장치가 오프라인 상태일 때 로컬로 지속되는 데이터에 대한 새 쿼리를 만들 수도 있지만 쿼리는 처음에는 캐시된 문서에 대해서만 실행됩니다.
오프라인 쿼리 인덱스 구성
기본적으로 Firestore SDK는 오프라인 쿼리를 실행할 때 로컬 캐시의 컬렉션에 있는 모든 문서를 스캔합니다. 이 기본 동작을 사용하면 사용자가 장기간 오프라인 상태일 때 오프라인 쿼리 성능이 저하될 수 있습니다.
로컬 쿼리 인덱스를 구성하여 오프라인 쿼리의 성능을 향상시킬 수 있습니다.
빠른
Apple 플랫폼 SDK는 동일한 인덱스 정의 형식 에 따라 서버에서 인덱스를 구성하는 데 사용되는 동일한 JSON 구조 구성을 읽는 setIndexConfiguration
메서드를 제공합니다.
// You will normally read this from a file asset or cloud storage. let indexConfigJson = """ { indexes: [ ... ], fieldOverrides: [ ... ] } """ // Apply the configuration. Firestore.firestore().setIndexConfiguration(indexConfigJson)
목표-C
Apple 플랫폼 SDK는 동일한 색인 정의 형식 에 따라 서버에서 색인을 구성하는 데 사용되는 동일한 JSON 구조 구성을 읽는 setIndexConfiguration
메서드를 제공합니다.
// You will normally read this from a file asset or cloud storage. NSString *indexConfigJson = @" { " " indexes: [ " " ... " " ], " " fieldOverrides: [ " " ... " " ] " " } "; // Apply the configuration. [[FIRFirestore firestore] setIndexConfigurationFromJSON:indexConfigJson completion:^(NSError * _Nullable error) { // ... }];
Java
Android SDK는 동일한 색인 정의 형식 에 따라 서버에서 색인을 구성하는 데 사용되는 동일한 JSON 구조 구성을 읽는 setIndexConfiguration
메서드를 제공합니다.
// You will normally read this from a file asset or cloud storage. String indexConfigJson = " { " + " indexes: [ " + " ... " + " ], " + " fieldOverrides: [ " + " ... " + " ] " + " } "; // Apply the configuration. FirebaseFirestore.getInstance().setIndexConfiguration(indexConfigJson);
Kotlin+KTX
Android SDK는 동일한 색인 정의 형식 에 따라 서버에서 색인을 구성하는 데 사용되는 동일한 JSON 구조 구성을 읽는 setIndexConfiguration
메서드를 제공합니다.
// You will normally read this from a file asset or cloud storage. val indexConfigJson = """ { indexes: [ ... ], fieldOverrides: [ ... ] } """ // Apply the configuration. FirebaseFirestore.getInstance().setIndexConfiguration(indexConfigJson)
Dart
Flutter SDK는 동일한 인덱스 정의 형식 에 따라 서버에서 인덱스를 구성하는 데 사용되는 동일한 JSON 구조 구성을 읽는 setIndexConfigurationFromJSON
메서드를 제공합니다.
// You will normally read this from a file asset or cloud storage. var indexConfigJson = """ { indexes: [ ... ], fieldOverrides: [ ... ] } """; // Apply the configuration. await FirebaseFirestore.instance.setIndexConfigurationFromJSON(json: indexConfigJson);
또는 setIndexConfiguration
메서드를 사용하여 클래스 기반 API로 인덱스를 구성할 수 있습니다.
var indexes = [ Index( collectionGroup: "posts", queryScope: QueryScope.collection, fields: [ IndexField(fieldPath: "author", arrayConfig: ArrayConfig.contains), IndexField(fieldPath: "timestamp", order: Order.descending) ], ), ]; await FirebaseFirestore.instance.setIndexConfiguration(indexes: indexes);
사용할 오프라인 인덱스 구성은 앱이 오프라인 상태에서 많이 액세스하는 컬렉션 및 문서와 원하는 오프라인 성능에 따라 다릅니다. 클라이언트에서 사용하기 위해 백엔드 색인 구성을 내보낼 수 있지만 앱의 오프라인 액세스 패턴은 온라인 액세스 패턴과 크게 다를 수 있으므로 온라인 색인 구성은 오프라인 사용에 적합하지 않을 수 있습니다. 앱에서 고성능으로 오프라인에 액세스하기를 원하는 컬렉션과 문서는 무엇입니까? 앱의 동작을 분석한 후에는 인덱싱 가이드 의 인덱스 정의 원칙을 따르십시오.
클라이언트 앱에서 오프라인 인덱스 구성을 로드할 수 있도록 하려면 다음을 수행하십시오.
- 앱과 함께 컴파일 및 배포
- CDN에서 다운로드
- Firebase용 Cloud Storage 와 같은 스토리지 시스템에서 가져옵니다.
네트워크 액세스 비활성화 및 활성화
아래 방법을 사용하여 Cloud Firestore 클라이언트에 대한 네트워크 액세스를 비활성화할 수 있습니다. 네트워크 액세스가 비활성화된 동안 모든 스냅샷 수신기 및 문서 요청은 캐시에서 결과를 검색합니다. 쓰기 작업은 네트워크 액세스가 다시 활성화될 때까지 대기합니다.
Web version 9
import { disableNetwork } from "firebase/firestore"; await disableNetwork(db); console.log("Network disabled!"); // Do offline actions // ...
Web version 8
firebase.firestore().disableNetwork() .then(() => { // Do offline actions // ... });
빠른
Firestore.firestore().disableNetwork { (error) in // Do offline things // ... }
목표-C
[[FIRFirestore firestore] disableNetworkWithCompletion:^(NSError *_Nullable error) { // Do offline actions // ... }];
Kotlin+KTX
db.disableNetwork().addOnCompleteListener { // Do offline things // ... }
Java
db.disableNetwork() .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { // Do offline things // ... } });
Dart
db.disableNetwork().then((_) { // Do offline things });
다음 방법을 사용하여 네트워크 액세스를 다시 활성화하십시오.
Web version 9
import { enableNetwork } from "firebase/firestore"; await enableNetwork(db); // Do online actions // ...
Web version 8
firebase.firestore().enableNetwork() .then(() => { // Do online actions // ... });
빠른
Firestore.firestore().enableNetwork { (error) in // Do online things // ... }
목표-C
[[FIRFirestore firestore] enableNetworkWithCompletion:^(NSError *_Nullable error) { // Do online actions // ... }];
Kotlin+KTX
db.enableNetwork().addOnCompleteListener { // Do online things // ... }
Java
db.enableNetwork() .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { // Do online things // ... } });
Dart
db.enableNetwork().then((_) { // Back online });