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

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

  1. 특정 실시간 데이터베이스 경로가 변경되기를 기다립니다.
  2. 이벤트가 발생하면 트리거되어 작업을 수행합니다.
  3. 해당 경로에 저장된 데이터의 스냅샷이 포함된 데이터 객체를 수신합니다.

Firebase 실시간 데이터베이스에서 데이터베이스 노드의 쓰기, 만들기, 업데이트, 삭제에 대한 응답으로 함수를 트리거할 수 있습니다.

Firebase 실시간 데이터베이스 변경 시 함수 트리거

firebase-functions/v2/database 하위 패키지를 사용하여 Firebase 실시간 데이터베이스 이벤트를 처리하는 함수를 만듭니다. 함수가 트리거되는 시점을 제어하려면 이벤트 핸들러 중 하나를 지정하고 이벤트를 리슨할 실시간 데이터베이스 경로를 지정합니다.

함수 위치 설정

실시간 데이터베이스 인스턴스의 위치와 함수의 위치 간의 거리로 인해 상당한 네트워크 지연 시간이 발생할 수 있습니다. 또한 리전이 일치하지 않으면 배포가 실패할 수 있습니다. 이러한 상황을 방지하려면 데이터베이스 인스턴스 위치와 일치하도록 함수 위치를 지정합니다.

실시간 데이터베이스 이벤트 처리

함수를 통해 실시간 데이터베이스 이벤트를 처리할 수 있으며, 얼마나 세부적으로 처리할지에 따라 2가지 수준으로 나눠집니다. 즉, 작성, 생성, 업데이트 또는 삭제 이벤트만 리슨하거나 모든 유형의 참조 변경을 리슨할 수 있습니다.

실시간 데이터베이스 이벤트에 응답하는 다음 핸들러를 사용할 수 있습니다.

  • onValueWritten() 실시간 데이터베이스에서 데이터가 작성된 경우에만 트리거됩니다.
  • onValueCreated() 실시간 데이터베이스에서 데이터가 생성된 경우에만 트리거됩니다.
  • onValueUpdated() 실시간 데이터베이스에서 데이터가 업데이트된 경우에만 트리거됩니다.
  • onValueDeleted() 실시간 데이터베이스에서 데이터가 삭제된 경우에만 트리거됩니다.

인스턴스 및 경로 지정

함수가 트리거되는 시점과 위치를 제어하려면 경로 및 필요한 경우 실시간 데이터베이스 인스턴스를 사용하여 함수를 구성합니다. 인스턴스를 지정하지 않으면 함수 리전의 모든 실시간 데이터베이스 인스턴스에 함수가 배포됩니다. 실시간 데이터베이스의 인스턴스 패턴을 지정하여 동일한 리전의 특정 인스턴스 하위 집합에 배포할 수도 있습니다.

예를 들어, 설명을 위해 onValueWritten()을 사용합니다.

# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
const onwrittenfunctiondefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
const onwrittenfunctioninstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onwrittenfunctioninstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

이러한 매개변수는 함수가 실시간 데이터베이스 인스턴스 내의 특정 경로에서 쓰기를 처리하도록 지시합니다.

경로를 지정하면 해당 경로와 모든 하위 경로에 영향을 주는 모든 쓰기 이벤트와 매칭됩니다. 함수의 경로를 /foo/bar로 설정하면 다음 두 위치 모두의 이벤트와 매칭됩니다.

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

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

와일드 카드 및 캡처

{key}, {key=*}, {key=prefix*}, {key=*suffix}를 사용하여 캡처할 수 있습니다. 단일 세그먼트 와일드 카드에는 *, prefix*, *suffix를 사용하세요. 참고: **는 RTDB에서 지원하지 않는 다중 세그먼트 와일드 카드를 나타냅니다. 경로 패턴 이해하기를 참고하세요.

경로 와일드 카드. 경로 구성요소를 와일드 카드로 지정할 수 있습니다.

  • 별표(*) 사용. 예를 들어 foo/*foo/ 아래의 노드 계층 구조에서 모든 하위 수준과 매칭됩니다.
  • 정확히 별표(*)가 포함된 세그먼트 사용. 예를 들어 foo/app*-usapp 접두사 및 -us 접미사를 갖는 foo/ 아래의 모든 하위 세그먼트와 매칭됩니다.

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

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

"hello": "world", "firebase": "functions"에 대해 한 번씩 "/foo/*" 경로와 두 번 매칭됩니다.

경로 캡처. 일치하는 경로를 이름이 지정된 변수로 캡처하여 함수 코드에서 사용할 수 있습니다(예: /user/{uid}, /user/{uid=*-us}).

캡처 변수의 값은 함수의 database.DatabaseEvent.params 객체 내에서 확인할 수 있습니다.

인스턴스 와일드 카드. 와일드 카드를 사용하여 인스턴스 구성요소를 지정할 수도 있습니다. 인스턴스 와일드 카드에는 접두사, 접미사 또는 둘 다(예: my-app-*-prod)가 포함될 수 있습니다.

와일드 카드 및 캡처 참조

Cloud Functions(2세대) 및 실시간 데이터베이스에서 refinstance를 지정할 때 패턴을 사용할 수 있습니다. 각 트리거 인터페이스에는 함수 범위를 지정하는 다음과 같은 옵션이 있습니다.

ref 지정 instance 지정 동작
단일(/foo/bar) 지정하지 않음 함수 리전 내 모든 인스턴스로 핸들러 범위를 지정합니다.
단일(/foo/bar) 단일(‘my-new-db') 함수 리전 내 특정 인스턴스로 핸들러 범위를 지정합니다.
단일(/foo/bar) 패턴(‘inst-prefix*') 함수 리전 내 패턴과 매칭되는 모든 인스턴스로 핸들러 범위를 지정합니다.
패턴(/foo/{bar}) 지정하지 않음 함수 리전 내 모든 인스턴스로 핸들러 범위를 지정합니다.
패턴(/foo/{bar}) 단일(‘my-new-db') 함수 리전 내 특정 인스턴스로 핸들러 범위를 지정합니다.
패턴(/foo/{bar}) 패턴(‘inst-prefix*') 함수 리전 내 패턴과 매칭되는 모든 인스턴스로 핸들러 범위를 지정합니다.

이벤트 데이터 처리

실시간 데이터베이스 이벤트를 처리할 때 반환되는 데이터 객체는 DataSnapshot입니다.

onValueWritten 또는 onValueUpdated 이벤트의 경우 첫 번째 매개변수는 각각 트리거 이벤트 전후의 데이터 상태를 나타내는 스냅샷 2개가 포함된 Change 객체입니다.

onValueCreatedonValueDeleted 이벤트의 경우 반환되는 데이터 객체는 생성하거나 삭제한 데이터의 스냅샷입니다.

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

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
export makeuppercase = onValueCreated("foo/bar", (event) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      functions.logger.log('Uppercasing', event.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 event.data.ref.parent.child('uppercase').set(uppercase);
    });

이전 값 읽기

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

예를 들어 before 속성을 사용하여 데이터를 처음 만들 때만 함수가 텍스트를 대문자로 변환하도록 할 수 있습니다.

    exports makeuppercase = onValueWritten("/messages/{pushId}/original", (event) => {
          // Only edit data when it is first created.
          if (event.data.before.exists()) {
            return null;
          }
          // Exit when the data is deleted.
          if (!event.data.after.exists()) {
            return null;
          }
          // Grab the current value of what was written to the Realtime Database.
          const original = event.data.after.val();
          console.log('Uppercasing', event.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 event.data.after.ref.parent.child('uppercase').set(uppercase);
        });