Firebase Summit에서 발표된 모든 내용을 살펴보고 Firebase로 앱을 빠르게 개발하고 안심하고 앱을 실행하는 방법을 알아보세요. 자세히 알아보기

오프라인 기능 활성화

Firebase 애플리케이션은 앱의 네트워크 연결이 일시적으로 끊어져도 작동합니다. 또한 Firebase는 로컬에서 데이터를 유지하고, 존재를 관리하고, 지연 시간을 처리하기 위한 도구를 제공합니다.

디스크 지속성

Firebase 앱은 일시적인 네트워크 중단을 자동으로 처리합니다. 캐시된 데이터는 오프라인 상태에서 사용할 수 있으며 Firebase는 네트워크 연결이 복원되면 쓰기를 다시 보냅니다.

디스크 지속성을 활성화하면 사용자 또는 운영 체제가 앱을 다시 시작하더라도 앱이 오프라인 상태를 유지할 수 있도록 앱이 로컬로 데이터를 기록합니다.

단 한 줄의 코드로 디스크 지속성을 활성화할 수 있습니다.

FirebaseDatabase.instance.setPersistenceEnabled(true);

지속성 행동

지속성을 활성화하면 온라인 상태에서 Firebase 실시간 데이터베이스 클라이언트가 동기화하는 모든 데이터가 디스크에 유지되고 사용자 또는 운영 체제가 앱을 다시 시작해도 오프라인에서 사용할 수 있습니다. 즉, 앱은 캐시에 저장된 로컬 데이터를 사용하여 온라인에서와 같이 작동합니다. 리스너 콜백은 로컬 업데이트에 대해 계속 실행됩니다.

Firebase 실시간 데이터베이스 클라이언트는 앱이 오프라인일 때 수행되는 모든 쓰기 작업의 대기열을 자동으로 유지합니다. 지속성이 활성화되면 이 대기열은 디스크에도 유지되므로 사용자 또는 운영 체제가 앱을 다시 시작할 때 모든 쓰기를 사용할 수 있습니다. 앱이 다시 연결되면 모든 작업이 Firebase 실시간 데이터베이스 서버로 전송됩니다.

앱에서 Firebase 인증 을 사용하는 경우 Firebase 실시간 데이터베이스 클라이언트는 앱을 다시 시작해도 사용자의 인증 토큰을 유지합니다. 앱이 오프라인일 때 인증 토큰이 만료되면 앱이 사용자를 다시 인증할 때까지 클라이언트가 쓰기 작업을 일시 중지합니다. 그렇지 않으면 보안 규칙으로 인해 쓰기 작업이 실패할 수 있습니다.

데이터를 최신 상태로 유지

Firebase 실시간 데이터베이스는 활성 리스너에 대한 데이터의 로컬 사본을 동기화하고 저장합니다. 또한 특정 위치를 동기화 상태로 유지할 수 있습니다.

final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.keepSynced(true);

Firebase 실시간 데이터베이스 클라이언트는 이러한 위치에서 데이터를 자동으로 다운로드하고 참조에 활성 리스너가 없는 경우에도 동기화를 유지합니다. 다음 코드 줄을 사용하여 동기화를 다시 끌 수 있습니다.

scoresRef.keepSynced(false);

기본적으로 이전에 동기화된 10MB의 데이터가 캐시됩니다. 이것은 대부분의 응용 프로그램에 충분합니다. 캐시가 구성된 크기를 초과하면 Firebase 실시간 데이터베이스가 가장 최근에 사용된 데이터를 제거합니다. 동기화 상태로 유지되는 데이터는 캐시에서 제거되지 않습니다.

오프라인으로 데이터 쿼리

Firebase 실시간 데이터베이스는 오프라인일 때 사용하기 위해 쿼리에서 반환된 데이터를 저장합니다. 오프라인 상태에서 구성된 쿼리의 경우 Firebase 실시간 데이터베이스는 이전에 로드된 데이터에 대해 계속 작동합니다. 요청된 데이터가 로드되지 않은 경우 Firebase 실시간 데이터베이스는 로컬 캐시에서 데이터를 로드합니다. 네트워크 연결을 다시 사용할 수 있게 되면 데이터가 로드되고 쿼리가 반영됩니다.

예를 들어, 이 코드는 점수 데이터베이스에서 마지막 4개 항목을 쿼리합니다.

final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.orderByValue().limitToLast(4).onChildAdded.listen((event) {
  debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});

사용자가 연결이 끊어지고 오프라인이 된 다음 앱을 다시 시작한다고 가정합니다. 여전히 오프라인인 동안 앱은 동일한 위치에서 마지막 두 항목을 쿼리합니다. 앱이 위의 쿼리에서 4개 항목을 모두 로드했기 때문에 이 쿼리는 마지막 2개 항목을 성공적으로 반환합니다.

scoresRef.orderByValue().limitToLast(2).onChildAdded.listen((event) {
  debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});

앞의 예에서 Firebase 실시간 데이터베이스 클라이언트는 지속형 캐시를 사용하여 가장 높은 점수를 받은 두 공룡에 대해 '하위 추가' 이벤트를 발생시킵니다. 그러나 앱이 온라인 상태에서 해당 쿼리를 실행한 적이 없기 때문에 '값' 이벤트를 발생시키지 않습니다.

앱이 오프라인 상태에서 마지막 6개 항목을 요청하는 경우 캐시된 4개 항목에 대한 '하위 추가' 이벤트가 즉시 발생합니다. 기기가 다시 온라인 상태가 되면 Firebase 실시간 데이터베이스 클라이언트가 서버와 동기화하고 앱에 대한 마지막 2개의 '하위 추가' 및 '값' 이벤트를 가져옵니다.

오프라인 거래 처리

앱이 오프라인일 때 수행되는 모든 트랜잭션은 대기열에 추가됩니다. 앱이 네트워크에 다시 연결되면 트랜잭션이 실시간 데이터베이스 서버로 전송됩니다.

Firebase 실시간 데이터베이스에는 오프라인 시나리오 및 네트워크 연결을 처리하기 위한 많은 기능이 있습니다. 이 가이드의 나머지 부분은 지속성을 활성화했는지 여부에 관계없이 앱에 적용됩니다.

존재감 관리

실시간 애플리케이션에서는 클라이언트가 연결 및 연결 해제되는 시점을 감지하는 것이 종종 유용합니다. 예를 들어 클라이언트의 연결이 끊길 때 사용자를 '오프라인'으로 표시할 수 있습니다.

Firebase 데이터베이스 클라이언트는 클라이언트가 Firebase 데이터베이스 서버에서 연결을 끊을 때 데이터베이스에 쓰는 데 사용할 수 있는 간단한 기본 요소를 제공합니다. 이러한 업데이트는 클라이언트의 연결이 완전히 끊어졌는지 여부에 관계없이 발생하므로 연결이 끊어지거나 클라이언트가 충돌하는 경우에도 업데이트에 의존하여 데이터를 정리할 수 있습니다. 설정, 업데이트, 제거를 포함한 모든 쓰기 작업은 연결이 끊어진 상태에서 수행할 수 있습니다.

다음은 onDisconnect 프리미티브를 사용하여 연결이 끊길 때 데이터를 쓰는 간단한 예입니다.

final presenceRef = FirebaseDatabase.instance.ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");

연결 해제 작동 방식

onDisconnect() 작업을 설정하면 작업이 Firebase 실시간 데이터베이스 서버에 유지됩니다. 서버는 보안을 확인하여 사용자가 요청된 쓰기 이벤트를 수행할 수 있는지 확인하고 유효하지 않은 경우 앱에 알립니다. 그런 다음 서버는 연결을 모니터링합니다. 연결 시간이 초과되거나 실시간 데이터베이스 클라이언트가 연결을 닫으면 서버는 보안을 두 번째로 확인하여(작업이 여전히 유효한지 확인) 이벤트를 호출합니다.

try {
    await presenceRef.onDisconnect().remove();
} catch (error) {
    debugPrint("Could not establish onDisconnect event: $error");
}

onDisconnect 이벤트는 .cancel() 을 호출하여 취소할 수도 있습니다.

final onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();

연결 상태 감지

많은 존재 관련 기능의 경우 앱이 온라인인지 오프라인인지 아는 것이 유용합니다. Firebase 실시간 데이터베이스는 Firebase 실시간 데이터베이스 클라이언트의 연결 상태가 변경될 때마다 업데이트되는 /.info/connected 의 특수 위치를 제공합니다. 다음은 예입니다.

final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
  final connected = event.snapshot.value as bool? ?? false;
  if (connected) {
    debugPrint("Connected.");
  } else {
    debugPrint("Not connected.");
  }
});

/.info/connected 는 클라이언트의 상태에 따라 달라지기 때문에 실시간 데이터베이스 클라이언트 간에 동기화되지 않는 부울 값입니다. 즉, 한 클라이언트가 /.info/connected 를 false로 읽는 경우 별도의 클라이언트도 false를 읽는다는 보장이 없습니다.

처리 지연 시간

서버 타임스탬프

Firebase 실시간 데이터베이스 서버는 서버에서 생성된 타임스탬프를 데이터로 삽입하는 메커니즘을 제공합니다. onDisconnect 와 결합된 이 기능은 실시간 데이터베이스 클라이언트 연결이 끊긴 시간을 안정적으로 기록하는 쉬운 방법을 제공합니다.

final userLastOnlineRef =
    FirebaseDatabase.instance.ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(ServerValue.timestamp);

클록 스큐

ServerValue.timestamp 가 훨씬 더 정확하고 대부분의 읽기/쓰기 작업에 더 적합하지만 Firebase 실시간 데이터베이스의 서버와 관련하여 클라이언트의 시계 왜곡을 추정하는 것이 때때로 유용할 수 있습니다. /.info/serverTimeOffset 위치에 콜백을 연결하여 Firebase 실시간 데이터베이스 클라이언트가 로컬 보고 시간(에포크 시간(밀리초))에 추가하여 서버 시간을 추정하는 값(밀리초)을 얻을 수 있습니다. 이 오프셋의 정확도는 네트워킹 대기 시간의 영향을 받을 수 있으므로 주로 클록 시간에서 큰(> 1초) 불일치를 발견하는 데 유용합니다.

final offsetRef = FirebaseDatabase.instance.ref(".info/serverTimeOffset");
offsetRef.onValue.listen((event) {
  final offset = event.snapshot.value as num? ?? 0.0;
  final estimatedServerTimeMs =
      DateTime.now().millisecondsSinceEpoch + offset;
});

샘플 프레즌스 앱

연결 해제 작업과 연결 상태 모니터링 및 서버 타임스탬프를 결합하여 사용자 존재 시스템을 구축할 수 있습니다. 이 시스템에서 각 사용자는 실시간 데이터베이스 클라이언트가 온라인인지 여부를 나타내기 위해 데이터베이스 위치에 데이터를 저장합니다. 클라이언트는 온라인 상태일 때 이 위치를 true로 설정하고 연결이 끊길 때 타임스탬프를 설정합니다. 이 타임스탬프는 주어진 사용자가 마지막으로 온라인 상태였던 시간을 나타냅니다.

두 명령을 모두 서버로 보내기 전에 클라이언트의 네트워크 연결이 끊어지는 경우 경쟁 조건을 피하기 위해 앱은 사용자가 온라인으로 표시되기 전에 연결 끊기 작업을 대기열에 넣어야 합니다.

// Since I can connect from multiple devices, we store each connection
// instance separately any time that connectionsRef's value is null (i.e.
// has no children) I am offline.
final myConnectionsRef =
    FirebaseDatabase.instance.ref("users/joe/connections");

// Stores the timestamp of my last disconnect (the last time I was seen online)
final lastOnlineRef =
    FirebaseDatabase.instance.ref("/users/joe/lastOnline");

final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
  final connected = event.snapshot.value as bool? ?? false;
  if (connected) {
    final con = myConnectionsRef.push();

    // When this device disconnects, remove it.
    con.onDisconnect().remove();

    // When I disconnect, update the last time I was seen online.
    lastOnlineRef.onDisconnect().set(ServerValue.timestamp);

    // Add this device to my connections list.
    // This value could contain info about the device or a timestamp too.
    con.set(true);
  }
});