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


Cloud Functions를 사용하면 Firebase Realtime Database: 클라이언트 코드를 업데이트할 필요가 없습니다. Cloud Functions를 사용하면 전체 관리 기능으로 Realtime Database 작업을 실행할 수 있습니다. Realtime Database에 대한 각 변경사항이 처리되도록 합니다. 각 단계마다 다릅니다 DataSnapshot 또는 Admin SDK를 통해 Firebase Realtime Database를 변경할 수 있습니다.

일반적인 수명 주기에서 Firebase Realtime Database 함수는 다음을 실행합니다.

  1. 특정 Realtime Database 위치가 변경되기를 기다립니다.
  2. 이벤트가 발생할 때 트리거되어 작업을 수행합니다. 어떻게 해야 하나요? Cloud Functions인가요? 사용 사례를 참조하세요.
  3. 지정된 문서에 저장된 데이터의 스냅샷이 포함된 데이터 객체를 수신합니다.

Realtime Database 함수 트리거

Realtime Database 이벤트의 새 함수 만들기 (functions.database) 받는사람 함수가 트리거되는 시점을 제어하고, 이벤트 핸들러 중 하나를 지정합니다. 이벤트를 리슨할 Realtime Database 경로를 지정합니다.

이벤트 핸들러 설정

함수를 사용하면 두 가지 수준의 특이도로 Realtime Database 이벤트를 처리할 수 있습니다. 특히 생성, 업데이트, 또는 삭제 이벤트를 수신 대기하거나 경로에 대한 모든 유형의 변경사항을 수신 대기할 수 있습니다. Cloud FunctionsRealtime Database에 다음 이벤트 핸들러를 지원합니다.

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

인스턴스 및 경로 지정

함수가 트리거되는 시점과 위치를 제어하려면 ref(path)를 호출하여 경로를 지정하고 필요하다면 instance('INSTANCE_NAME')Realtime Database 인스턴스를 지정합니다. 그렇지 않은 경우 인스턴스를 지정하면 함수가 다음 인스턴스의 기본 Realtime Database 인스턴스에 배포됩니다. Firebase 프로젝트 예시:

  • 기본 Realtime Database 인스턴스: functions.database.ref('/foo/bar')
  • 이름이 'my-app-db-2'인 인스턴스: functions.database.instance('my-app-db-2').ref('/foo/bar')

이러한 메서드는 함수가 내부의 특정 경로에서 쓰기를 처리하도록 지시합니다. Realtime Database 인스턴스 경로를 지정하면 경로와 터치하는 모든 쓰기와 일치하게 됩니다. 쓰기 권한, 발생할 수 있습니다. 경로를 설정하는 경우 함수를 /foo/bar로 사용하면 다음 두 위치 모두의 이벤트와 일치합니다.

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

두 경우 모두 /foo/bar에서 이벤트가 발생한 것으로 해석되고, 이벤트 데이터에 /foo/bar의 이전 데이터와 새 데이터가 포함됩니다. 이벤트 데이터가 큰 경우에는 데이터베이스 루트 근처에 단일 함수를 사용하는 대신 더 깊은 경로에 여러 함수를 사용하는 것이 좋습니다. 성능을 최적화하려면 최대한 깊은 수준의 데이터만 요청합니다.

경로 구성요소를 중괄호로 묶어 와일드 카드로 지정할 수 있습니다. ref('foo/{bar}')/foo의 모든 하위 경로와 매칭됩니다. 함수의 EventContext.params 객체에서 이 와일드 카드 경로 구성요소의 값을 확인할 수 있습니다. 이 예시에서는 값이 context.params.bar로 제공됩니다.

와일드 카드가 포함된 경로는 단일 쓰기 작업의 여러 이벤트와 일치할 수 있습니다. 다음을 삽입하면

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

"hello": "world""firebase": "functions"에 대해 각각 한 번씩 "/foo/{bar}" 경로와 두 번 일치하게 됩니다.

이벤트 데이터 처리

Realtime Database 이벤트 처리 시 반환되는 데이터 객체는 DataSnapshot onWrite 또는 onUpdate 이벤트의 경우 첫 번째 매개변수는 스냅샷 2개가 포함된 Change 객체입니다. 데이터의 상태를 나타내는 이벤트를 발생시킵니다. onCreateonDelete 이벤트의 경우 반환되는 데이터 객체는 생성하거나 삭제한 데이터의 스냅샷입니다.

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

// 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/v1');
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 속성을 사용하면 이벤트가 발생하기 전에 Realtime Database에 저장된 데이터를 검사할 수 있습니다. before 속성은 DataSnapshot을 반환합니다. 여기서 모든 메서드 (예: val()exists()) 이전 값을 참조하세요 다시 새 값을 읽으려면 원본 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);
    });