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

실시간 데이터베이스 트리거

Cloud Functions를 사용하면 클라이언트 코드를 업데이트할 필요 없이 Firebase 실시간 데이터베이스에서 이벤트를 처리할 수 있습니다. Cloud Functions를 사용하면 전체 관리 권한으로 실시간 데이터베이스 작업을 실행할 수 있으며 실시간 데이터베이스에 대한 각 변경 사항이 개별적으로 처리되도록 할 수 있습니다. DataSnapshot 또는 Admin SDK 를 통해 Firebase 실시간 데이터베이스를 변경할 수 있습니다.

일반적인 수명 주기에서 Firebase 실시간 데이터베이스 기능은 다음을 수행합니다.

  1. 특정 실시간 데이터베이스 위치가 변경될 때까지 기다립니다.
  2. 이벤트가 발생할 때 트리거하고 작업을 수행합니다(사용 사례의 예는 Cloud Functions로 무엇을 할 수 있나요? 참조).
  3. 지정된 문서에 저장된 데이터의 스냅샷이 포함된 데이터 개체를 받습니다.

실시간 데이터베이스 기능 트리거

functions.database 를 사용하여 실시간 데이터베이스 이벤트에 대한 새 functions.database 를 만듭니다. 함수가 트리거되는 시기를 제어하려면 이벤트 핸들러 중 하나를 지정하고 이벤트를 수신할 실시간 데이터베이스 경로를 지정합니다.

이벤트 핸들러 설정

함수를 사용하면 두 가지 특정 수준에서 실시간 데이터베이스 이벤트를 처리할 수 있습니다. 생성, 업데이트 또는 삭제 이벤트에 대해서만 구체적으로 수신하거나 경로에 대한 모든 종류의 변경을 수신할 수 있습니다. Cloud Functions는 실시간 데이터베이스에 대해 다음 이벤트 핸들러를 지원합니다.

  • 실시간 데이터베이스에서 데이터가 생성, 업데이트 또는 삭제될 때 트리거되는 onWrite() .
  • 실시간 데이터베이스에서 새 데이터가 생성될 때 트리거되는 onCreate() .
  • onUpdate() - 실시간 데이터베이스에서 데이터가 업데이트될 때 트리거됩니다.
  • onDelete() - 실시간 데이터베이스에서 데이터가 삭제될 때 트리거됩니다.

인스턴스 및 경로 지정

함수가 트리거되는 시기와 위치를 제어하려면 ref(path) 를 호출하여 경로를 지정하고 선택적으로 instance('INSTANCE_NAME') 로 실시간 데이터베이스 인스턴스를 지정합니다. 인스턴스를 지정하지 않으면 함수가 Firebase 프로젝트의 기본 실시간 데이터베이스 인스턴스에 배포됩니다. 예를 들면 다음과 같습니다.

  • 기본 실시간 데이터베이스 인스턴스: functions.database.ref('/foo/bar')
  • "my-app-db-2"라는 인스턴스: functions.database.instance('my-app-db-2').ref('/foo/bar')

이러한 메서드는 함수가 실시간 데이터베이스 인스턴스 내의 특정 경로에서 쓰기를 처리하도록 지시합니다. 경로 사양은 경로 아래에서 발생하는 쓰기를 포함하여 경로에 닿는 모든 쓰기와 일치합니다. 함수의 경로를 /foo/bar 로 설정하면 다음 두 위치의 이벤트와 일치합니다.

 /foo/bar
 /foo/bar/baz/really/deep/path

두 경우 모두 Firebase는 이벤트가 /foo/bar 에서 발생하고 이벤트 데이터에 /foo/bar 의 이전 데이터와 새 데이터가 포함된 것으로 해석합니다. 이벤트 데이터가 클 수 있는 경우 데이터베이스 루트 근처의 단일 함수 대신 더 깊은 경로에서 여러 함수를 사용하는 것이 좋습니다. 최상의 성능을 위해 가능한 가장 깊은 수준의 데이터만 요청하십시오.

경로 구성 요소를 중괄호로 묶어 와일드카드로 지정할 수 있습니다. ref('foo/{bar}')/foo 의 모든 자식과 일치합니다. 이러한 와일드카드 경로 구성 요소의 값은 함수의 EventContext.params 개체 내에서 사용할 수 있습니다. 이 예에서 값은 context.params.bar 로 사용할 수 있습니다.

와일드카드가 있는 경로는 단일 쓰기에서 여러 이벤트를 일치시킬 수 있습니다. 의 삽입

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

"/foo/{bar}" 경로를 두 번 일치시킵니다. 한 번은 "hello": "world" 로, 또 한 번은 "firebase": "functions" 로 일치합니다.

이벤트 데이터 처리

실시간 데이터베이스 이벤트를 처리할 때 반환되는 데이터 객체는 DataSnapshot 입니다. onWrite 또는 onUpdate 이벤트의 경우 첫 번째 매개변수는 트리거 이벤트 전후의 데이터 상태를 나타내는 두 개의 스냅샷이 포함된 Change 객체입니다. onCreateonDelete 이벤트의 경우 반환되는 데이터 개체는 생성되거나 삭제된 데이터의 스냅샷입니다.

이 예에서 함수는 지정된 경로에 대한 스냅샷을 snap 으로 검색하고 해당 위치의 문자열을 대문자로 변환하고 수정된 문자열을 데이터베이스에 씁니다.

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      functions.logger.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return snapshot.ref.parent.child('uppercase').set(uppercase);
    });

사용자 인증 정보 접근

EventContext.authEventContext.authType 에서 함수를 트리거한 사용자에 대한 권한을 포함한 사용자 정보에 액세스할 수 있습니다. 이것은 보안 규칙을 시행하는 데 유용할 수 있으며 함수가 사용자의 권한 수준에 따라 다른 작업을 완료할 수 있도록 합니다.

const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.simpleDbFunction = functions.database.ref('/path')
    .onCreate((snap, context) => {
      if (context.authType === 'ADMIN') {
        // do something
      } else if (context.authType === 'USER') {
        console.log(snap.val(), 'written by', context.auth.uid);
      }
    });

또한 사용자 인증 정보를 활용하여 사용자를 "가장"하고 사용자 대신 쓰기 작업을 수행할 수 있습니다. 동시성 문제를 방지하려면 아래와 같이 앱 인스턴스를 삭제해야 합니다.

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

이전 값 읽기

Change 개체에는 이벤트 전에 실시간 데이터베이스에 저장된 내용을 검사할 수 있는 before 속성이 있습니다. before 속성은 모든 메서드(예: val()exists() )가 이전 값을 참조하는 DataSnapshot 을 반환합니다. 원본 DataSnapshot 을 사용하거나 after 속성을 읽어 새 값을 다시 읽을 수 있습니다. Change 에 대한 이 속성은 이벤트가 발생한 데이터 상태를 나타내는 또 다른 DataSnapshot 입니다.

예를 들어 before 속성을 사용하여 함수가 처음 생성될 때 텍스트만 대문자로 표시되도록 할 수 있습니다.

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite((change, context) => {
      // Only edit data when it is first created.
      if (change.before.exists()) {
        return null;
      }
      // Exit when the data is deleted.
      if (!change.after.exists()) {
        return null;
      }
      // Grab the current value of what was written to the Realtime Database.
      const original = change.after.val();
      console.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return change.after.ref.parent.child('uppercase').set(uppercase);
    });