Cloud Functions를 사용하면 클라이언트 코드를 업데이트하지 않고도 Firebase Realtime Database의 이벤트를 처리할 수 있습니다. Cloud Functions를 사용하면 전체 관리자 권한으로 Realtime Database 작업을 실행할 수 있으며 Realtime Database의 각 변경사항이 개별적으로 처리됩니다. 데이터 스냅샷 또는 Admin SDK를 통해 Firebase Realtime Database를 변경할 수 있습니다.
일반적인 수명 주기에서 Firebase Realtime Database 함수는 다음을 수행합니다.
- 특정 Realtime Database 경로가 변경되기를 기다립니다.
- 이벤트가 발생하면 트리거되어 작업을 수행합니다.
- 해당 경로에 저장된 데이터의 스냅샷이 포함된 데이터 객체를 수신합니다.
Firebase Realtime Database에 있는 데이터베이스 노드의 쓰기, 만들기, 업데이트, 삭제에 대한 응답으로 함수를 트리거할 수 있습니다. 함수가 트리거되는 시점을 제어하려면 이벤트 핸들러 중 하나를 지정하고 이벤트를 리슨할 Realtime Database 경로를 지정합니다.
함수 위치 설정
Realtime Database 인스턴스의 위치와 함수의 위치 간의 거리로 인해 상당한 네트워크 지연 시간이 발생할 수 있습니다. 또한 리전이 일치하지 않으면 배포가 실패할 수 있습니다. 이러한 상황을 방지하려면 데이터베이스 인스턴스 위치와 일치하도록 함수 위치를 지정합니다.
Realtime Database 이벤트 처리
함수를 통해 Realtime Database 이벤트를 처리할 수 있으며, 얼마나 세부적으로 처리할지에 따라 2가지 수준으로 나눠집니다. 즉, 작성, 생성, 업데이트 또는 삭제 이벤트만 리슨하거나 모든 유형의 참조 변경을 리슨할 수 있습니다.
Realtime Database 이벤트에 응답하는 다음 핸들러를 사용할 수 있습니다.
Node.js
onValueWritten()
Realtime Database에서 데이터가 생성, 업데이트 또는 삭제될 때 트리거됩니다.onValueCreated()
Realtime Database에서 데이터가 생성된 경우에만 트리거됩니다.onValueUpdated()
Realtime Database에서 데이터가 업데이트된 경우에만 트리거됩니다.onValueDeleted()
Realtime Database에서 데이터가 삭제된 경우에만 트리거됩니다.
Python
on_value_written()
Realtime Database에서 데이터가 생성, 업데이트 또는 삭제될 때 트리거됩니다.on_value_created()
Realtime Database에서 데이터가 생성된 경우에만 트리거됩니다.on_value_updated()
Realtime Database에서 데이터가 업데이트된 경우에만 트리거됩니다.on_value_deleted()
Realtime Database에서 데이터가 삭제된 경우에만 트리거됩니다.
필수 모듈 가져오기
함수 소스에서 사용하려는 SDK 모듈을 가져와야 합니다. 이 샘플에서는 Realtime Database에 쓰기 위해 Firebase Admin SDK 모듈과 함께 HTTP 및 Realtime Database 모듈을 가져와야 합니다.
Node.js
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
Python
# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn
# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db
app = initialize_app()
인스턴스 및 경로 지정
함수가 트리거되는 시점과 위치를 제어하려면 경로 및 필요한 경우 Realtime Database 인스턴스를 사용하여 함수를 구성합니다. 인스턴스를 지정하지 않으면 함수가 함수 리전의 모든 Realtime Database 인스턴스를 리슨합니다. Realtime Database의 인스턴스 패턴을 지정하여 동일한 리전의 특정 인스턴스 하위 집합에 배포할 수도 있습니다.
예를 들면 다음과 같습니다.
Node.js
// 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) => { // … } );
Python
# 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.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
reference=r"/user/{uid}",
instance="my-app-db-2",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
# 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.
@db_fn.on_value_written(
reference=r"/user/{uid=*@gmail.com}",
instance="my-app-db-*",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
이러한 파라미터는 함수가 Realtime Database 인스턴스 내의 특정 경로에서 쓰기를 처리하도록 지시합니다.
경로를 지정하면 해당 경로와 모든 하위 경로에 영향을 주는 모든 쓰기 이벤트와 매칭됩니다. 함수의 경로를 /foo/bar
로 설정하면 다음 두 위치 모두의 이벤트와 매칭됩니다.
/foo/bar
/foo/bar/baz/really/deep/path
두 경우 모두 /foo/bar
에서 이벤트가 발생한 것으로 해석되고, 이벤트 데이터에 /foo/bar
의 이전 데이터와 새 데이터가 포함됩니다. 이벤트 데이터가 큰 경우에는 데이터베이스 루트 근처에 단일 함수를 사용하는 대신 더 깊은 경로에 여러 함수를 사용하는 것이 좋습니다. 성능을 최적화하려면 최대한 깊은 수준의 데이터만 요청합니다.
와일드 카드 및 캡처
{key}
, {key=*}
, {key=prefix*}
, {key=*suffix}
를 사용하여 캡처할 수 있습니다. 단일 세그먼트 와일드 카드에는 *
, prefix*
, *suffix
를 사용하세요.
참고: **
는 Realtime Database에서 지원하지 않는 다중 세그먼트 와일드 카드를 나타냅니다.
경로 패턴 이해하기를 참조하세요.
경로 와일드 카드. 경로 구성요소를 와일드 카드로 지정할 수 있습니다.
- 별표(
*
) 사용. 예를 들어foo/*
는foo/
아래의 노드 계층 구조에서 모든 하위 수준과 매칭됩니다. - 정확히 별표(
*
)가 포함된 세그먼트 사용. 예를 들어foo/app*-us
는app
접두사 및-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세대) 및 Realtime Database를 사용하면 ref
및 instance
를 지정할 때 패턴을 사용할 수 있습니다. 각 트리거 인터페이스에는 함수 범위를 지정하는 다음과 같은 옵션이 있습니다.
ref 지정 |
instance 지정 |
동작 |
---|---|---|
단일(/foo/bar ) |
지정하지 않음 | 함수 리전 내 모든 인스턴스로 핸들러 범위를 지정합니다. |
단일(/foo/bar ) |
단일(‘my-new-db' ) |
함수 리전 내 특정 인스턴스로 핸들러 범위를 지정합니다. |
단일(/foo/bar ) |
패턴(‘inst-prefix*' ) |
함수 리전 내 패턴과 매칭되는 모든 인스턴스로 핸들러 범위를 지정합니다. |
패턴(/foo/{bar} ) |
지정하지 않음 | 함수 리전 내 모든 인스턴스로 핸들러 범위를 지정합니다. |
패턴(/foo/{bar} ) |
단일(‘my-new-db' ) |
함수 리전 내 특정 인스턴스로 핸들러 범위를 지정합니다. |
패턴(/foo/{bar} ) |
패턴(‘inst-prefix*' ) |
함수 리전 내 패턴과 매칭되는 모든 인스턴스로 핸들러 범위를 지정합니다. |
이벤트 데이터 처리
Realtime Database 이벤트가 트리거되면 Event
객체를 핸들러 함수에 전달합니다.
이 객체에는 생성 또는 삭제 이벤트의 경우 생성되거나 삭제된 데이터의 스냅샷이 포함된 data
속성이 있습니다.
이 예시에서 함수는 참조된 경로의 데이터를 가져와 해당 위치의 문자열을 대문자로 변환하고 수정된 문자열을 데이터베이스에 씁니다.
Node.js
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
"/messages/{pushId}/original",
(event) => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
logger.log("Uppercasing", event.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing
// asynchronous tasks inside a function, 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);
},
);
Python
@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Grab the value that was written to the Realtime Database.
original = event.data
if not isinstance(original, str):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)
이전 값 읽기
write
또는 update
이벤트의 경우 data
속성은 각각 트리거 이벤트 전후의 데이터 상태를 나타내는 스냅샷 2개가 포함된 Change
객체입니다.
Change
객체에는 이벤트가 발생하기 전에 Realtime Database에 저장된 데이터를 검사하는 before
속성과 이벤트가 발생한 후 데이터 상태를 나타내는 after
속성이 있습니다.
예를 들어 before
속성을 사용하여 데이터를 처음 만들 때만 함수가 텍스트를 대문자로 변환하도록 할 수 있습니다.
Node.js
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); });
Python
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Only edit data when it is first created.
if event.data.before is not None:
return
# Exit when the data is deleted.
if event.data.after is None:
return
# Grab the value that was written to the Realtime Database.
original = event.data.after
if not hasattr(original, "upper"):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)