Cloud Functions를 사용하면 코드를 배포하여 Firebase 데이터베이스의 변경을 통해 트리거되는 이벤트를 처리할 수 있습니다. 이를 통해 자체 서버를 실행하지 않고도 앱에 서버 측 기능을 추가할 수 있습니다.
Cloud Functions(2세대)
Cloud Run 및 Eventarc 기반 Cloud Functions for Firebase(2세대)를 통해 더 강력한 인프라, 성능 및 확장성에 대한 고급 제어, 함수 런타임에 대한 추가 제어를 사용할 수 있습니다. 2세대에 대한 자세한 내용은 Firebase용 Cloud Functions(2세대)를 참조하세요.
Firebase 함수 트리거
Cloud Functions for Firebase SDK는 특정 Firebase 이벤트에 연결된 핸들러를 생성할 수 있도록 다음 Firebase 이벤트 트리거를 내보냅니다.
Node.js
| 이벤트 유형 | 트리거 |
|---|---|
onDocumentCreated |
문서를 처음으로 기록할 때 트리거됩니다. |
onDocumentUpdated |
이미 존재하는 문서에서 값이 변경되었을 때 트리거됩니다. |
onDocumentDeleted |
문서가 삭제될 때 트리거됩니다. |
onDocumentWritten |
onDocumentCreated, onDocumentUpdated 또는 onDocumentDeleted가 트리거될 때 트리거됩니다. |
onDocumentCreatedWithAuthContext |
추가 인증 정보가 있는 onDocumentCreated |
onDocumentWrittenWithAuthContext |
추가 인증 정보가 있는 onDocumentWritten |
onDocumentDeletedWithAuthContext |
추가 인증 정보가 있는 onDocumentDeleted |
onDocumentUpdatedWithAuthContext |
추가 인증 정보가 있는 onDocumentUpdated |
Python
| 이벤트 유형 | 트리거 |
|---|---|
on_document_created |
문서를 처음으로 기록할 때 트리거됩니다. |
on_document_updated |
이미 존재하는 문서에서 값이 변경되었을 때 트리거됩니다. |
on_document_deleted |
문서가 삭제될 때 트리거됩니다. |
on_document_written |
on_document_created, on_document_updated 또는 on_document_deleted가 트리거될 때 트리거됩니다. |
on_document_created_with_auth_context |
추가 인증 정보가 있는 on_document_created |
on_document_updated_with_auth_context |
추가 인증 정보가 있는 on_document_updated |
on_document_deleted_with_auth_context |
추가 인증 정보가 있는 on_document_deleted |
on_document_written_with_auth_context |
추가 인증 정보가 있는 on_document_written |
Firebase 이벤트는 문서 변경 시에만 트리거됩니다. 데이터가 변경되지 않는 Firebase 문서를 업데이트(노옵스(no-ops) 쓰기)할 때에는 업데이트 또는 쓰기 이벤트가 생성되지 않습니다. 특정 필드에 이벤트를 추가할 수 없습니다.
Cloud Functions for Firebase가 사용 설정된 프로젝트가 없으면 Cloud Functions for Firebase(2세대) 시작하기를 읽고 Cloud Functions for Firebase 프로젝트를 구성 및 설정하세요.
Firebase 트리거 함수 작성
함수 트리거 정의
Firebase 트리거를 정의하려면 문서 경로와 이벤트 유형을 지정합니다.
Node.js
const {
onDocumentWritten,
onDocumentCreated,
onDocumentUpdated,
onDocumentDeleted,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
/* ... */
});
Python
from firebase_functions.firestore_fn import (
on_document_created,
on_document_deleted,
on_document_updated,
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
문서 경로는 특정 문서 또는 와일드 카드 패턴을 참조할 수 있습니다.
단일 문서 지정
다음 함수를 사용하면 특정 문서의 모든 변경에 이벤트를 트리거할 수 있습니다.
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.myfunction = onDocumentWritten("users/marie", (event) => {
// Your code here
});
Python
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/marie")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
와일드 카드를 사용한 문서 그룹 지정
특정 컬렉션의 문서 등 한 문서 그룹에 트리거를 연결하려면 문서 ID 대신 {wildcard}를 사용하면 됩니다.
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.myfunction = onDocumentWritten("users/{userId}", (event) => {
// If we set `/users/marie` to {name: "Marie"} then
// event.params.userId == "marie"
// ... and ...
// event.data.after.data() == {name: "Marie"}
});
Python
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# If we set `/users/marie` to {name: "Marie"} then
event.params["userId"] == "marie" # True
# ... and ...
event.data.after.to_dict() == {"name": "Marie"} # True
이 예시에서는 users의 문서에서 필드가 하나라도 변경되면 userId라는 와일드 카드와 일치시킵니다.
users의 문서에 하위 컬렉션이 있고, 이 하위 컬렉션 문서 중 하나에서 필드가 변경되면 userId 와일드 카드가 트리거되지 않습니다.
와일드 카드 일치는 문서 경로에서 추출되어 event.params에 저장됩니다.
개수 제한 없이 원하는 만큼 와일드 카드를 정의하여 명시적인 컬렉션 또는 문서 ID를 대체할 수 있습니다. 예를 들면 다음과 같습니다.
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.myfunction = onDocumentWritten("users/{userId}/{messageCollectionId}/{messageId}", (event) => {
// If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
// event.params.userId == "marie";
// event.params.messageCollectionId == "incoming_messages";
// event.params.messageId == "134";
// ... and ...
// event.data.after.data() == {body: "Hello"}
});
Python
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}/{messageCollectionId}/{messageId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
event.params["userId"] == "marie" # True
event.params["messageCollectionId"] == "incoming_messages" # True
event.params["messageId"] == "134" # True
# ... and ...
event.data.after.to_dict() == {"body": "Hello"}
와일드 카드를 사용할 때도 트리거는 항상 문서를 가리켜야 합니다.
예를 들어 {messageCollectionId}는 컬렉션이므로 users/{userId}/{messageCollectionId}는 유효하지 않습니다. 그러나 {messageId}가 항상 문서를 가리키므로 users/{userId}/{messageCollectionId}/{messageId}는 유효합니다.
이벤트 트리거
새 문서가 생성될 때 함수 트리거
컬렉션에 새 문서가 생성될 때마다 함수를 트리거할 수 있습니다. 다음은 새 사용자 프로필이 추가될 때마다 트리거되는 함수의 예시입니다.
Node.js
const {
onDocumentCreated,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.createuser = onDocumentCreated("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const snapshot = event.data;
if (!snapshot) {
console.log("No data associated with the event");
return;
}
const data = snapshot.data();
// access a particular field as you would any JS property
const name = data.name;
// perform more operations ...
});
추가 인증 정보의 경우 onDocumentCreatedWithAuthContext를 사용하세요.
Python
from firebase_functions.firestore_fn import (
on_document_created,
Event,
DocumentSnapshot,
)
@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
# Get a dictionary representing the document
# e.g. {'name': 'Marie', 'age': 66}
new_value = event.data.to_dict()
# Access a particular field as you would any dictionary
name = new_value["name"]
# Perform more operations ...
문서가 업데이트될 때 함수 트리거
문서가 업데이트될 때 함수를 트리거하여 실행할 수도 있습니다. 다음은 사용자가 프로필을 변경하면 실행되는 함수의 예시입니다.
Node.js
const {
onDocumentUpdated,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.updateuser = onDocumentUpdated("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = event.data.after.data();
// access a particular field as you would any JS property
const name = newValue.name;
// perform more operations ...
});
추가 인증 정보의 경우 onDocumentUpdatedWithAuthContext를 사용하세요.
Python
from firebase_functions.firestore_fn import (
on_document_updated,
Event,
Change,
DocumentSnapshot,
)
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get a dictionary representing the document
# e.g. {'name': 'Marie', 'age': 66}
new_value = event.data.after.to_dict()
# Access a particular field as you would any dictionary
name = new_value["name"]
# Perform more operations ...
문서가 삭제될 때 함수 트리거
문서가 삭제될 때 함수를 트리거할 수도 있습니다. 다음은 사용자가 자신의 사용자 프로필을 삭제하면 실행되는 함수의 예시입니다.
Node.js
const {
onDocumentDeleted,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.deleteuser = onDocumentDeleted("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const snap = event.data;
const data = snap.data();
// perform more operations ...
});
추가 인증 정보의 경우 onDocumentDeletedWithAuthContext를 사용하세요.
Python
from firebase_functions.firestore_fn import (
on_document_deleted,
Event,
DocumentSnapshot,
)
@on_document_deleted(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot|None]) -> None:
# Perform more operations ...
문서의 모든 변경사항에 대해 함수 트리거
실행되는 이벤트 유형에 관계없이 Firebase 문서의 모든 변경사항을 리슨하려면 '작성된 문서' 이벤트 트리거를 사용합니다. 다음은 사용자가 생성, 업데이트 또는 삭제될 때 실행되는 함수의 예시입니다.
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('firebase-functions/v2/firestore');
exports.modifyuser = onDocumentWritten("users/{userId}", (event) => {
// Get an object with the current document values.
// If the document does not exist, it was deleted
const document = event.data.after.data();
// Get an object with the previous document values
const previousValues = event.data.before.data();
// perform more operations ...
});
추가 인증 정보의 경우 onDocumentWrittenWithAuthContext를 사용하세요.
Python
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
# Get an object with the current document values.
# If the document does not exist, it was deleted.
document = (event.data.after.to_dict()
if event.data.after is not None else None)
# Get an object with the previous document values.
# If the document does not exist, it was newly created.
previous_values = (event.data.before.to_dict()
if event.data.before is not None else None)
# Perform more operations ...
데이터 읽기 및 쓰기
함수가 트리거되면 이벤트와 관련된 데이터 스냅샷이 제공됩니다. 이 스냅샷을 사용하면 이벤트를 트리거한 문서에 읽기 또는 쓰기를 수행하거나 Firebase Admin SDK를 사용해 데이터베이스의 다른 부분에 액세스할 수 있습니다.
이벤트 데이터
데이터 읽기
함수가 트리거될 때 업데이트된 문서의 데이터 또는 업데이트되기 전의 데이터를 가져와야 할 수 있습니다. 이전 데이터를 가져오려면 업데이트 전의 문서 스냅샷을 포함하는 event.data.before를 사용합니다.
마찬가지로 event.data.after는 업데이트 후의 문서 스냅샷 상태를 포함합니다.
Node.js
exports.updateuser2 = onDocumentUpdated("users/{userId}", (event) => {
// Get an object with the current document values.
// If the document does not exist, it was deleted
const newValues = event.data.after.data();
// Get an object with the previous document values
const previousValues = event.data.before.data();
});
Python
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get an object with the current document values.
new_value = event.data.after.to_dict()
# Get an object with the previous document values.
prev_value = event.data.before.to_dict()
다른 객체와 마찬가지로 속성에 액세스할 수 있습니다. 또는 get 함수를 사용하여 특정 필드에 액세스할 수 있습니다.
Node.js
// Fetch data using standard accessors
const age = event.data.after.data().age;
const name = event.data.after.data()['name'];
// Fetch data using built in accessor
const experience = event.data.after.data.get('experience');
Python
# Get the value of a single document field.
age = event.data.after.get("age")
# Convert the document to a dictionary.
age = event.data.after.to_dict()["age"]
데이터 쓰기
각 함수 호출은 Firebase 데이터베이스의 특정 문서와 연결됩니다. 함수로 반환되는 스냅샷에서 해당 문서에 액세스할 수 있습니다.
문서 참조에는 update(), set(), remove()와 같은 메서드가 포함되므로 함수를 트리거한 문서를 수정할 수 있습니다.
Node.js
const {onDocumentUpdated} = require('firebase-functions/v2/firestore');
exports.countnamechanges = onDocumentUpdated('users/{userId}', (event) => {
// Retrieve the current and previous value
const data = event.data.after.data();
const previousData = event.data.before.data();
// We'll only update if the name has changed.
// This is crucial to prevent infinite loops.
if (data.name == previousData.name) {
return null;
}
// Retrieve the current count of name changes
let count = data.name_change_count;
if (!count) {
count = 0;
}
// Then return a promise of a set operation to update the count
return event.data.after.ref.set({
name_change_count: count + 1
}, {merge: true});
});
Python
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get the current and previous document values.
new_value = event.data.after
prev_value = event.data.before
# We'll only update if the name has changed.
# This is crucial to prevent infinite loops.
if new_value.get("name") == prev_value.get("name"):
return
# Retrieve the current count of name changes
count = new_value.to_dict().get("name_change_count", 0)
# Update the count
new_value.reference.update({"name_change_count": count + 1})
사용자 인증 정보에 액세스
다음 이벤트 유형 중 하나를 사용하는 경우 이벤트를 트리거한 보안 주체에 대한 사용자 인증 정보에 액세스할 수 있습니다. 이 정보는 기본 이벤트에서 반환된 정보에 추가됩니다.
Node.js
onDocumentCreatedWithAuthContextonDocumentWrittenWithAuthContextonDocumentDeletedWithAuthContextonDocumentUpdatedWithAuthContext
Python
on_document_created_with_auth_contexton_document_updated_with_auth_contexton_document_deleted_with_auth_contexton_document_written_with_auth_context
인증 컨텍스트에서 사용할 수 있는 데이터에 관한 자세한 내용은 인증 컨텍스트를 참조하세요. 다음 예시는 인증 정보를 검색하는 방법을 보여줍니다.
Node.js
const {onDocumentWrittenWithAuthContext} = require('firebase-functions/v2/firestore');
exports.syncUser = onDocumentWrittenWithAuthContext("users/{userId}", (event) => {
const snapshot = event.data.after;
if (!snapshot) {
console.log("No data associated with the event");
return;
}
const data = snapshot.data();
// retrieve auth context from event
const { authType, authId } = event;
let verified = false;
if (authType === "system") {
// system-generated users are automatically verified
verified = true;
} else if (authType === "unknown" || authType === "unauthenticated") {
// admin users from a specific domain are verified
if (authId.endsWith("@example.com")) {
verified = true;
}
}
return data.after.ref.set({
created_by: authId,
verified,
}, {merge: true});
});
Python
@on_document_updated_with_auth_context(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get the current and previous document values.
new_value = event.data.after
prev_value = event.data.before
# Get the auth context from the event
user_auth_type = event.auth_type
user_auth_id = event.auth_id
트리거 이벤트 외부의 데이터
Cloud Functions은 신뢰할 수 있는 환경에서 실행됩니다. Cloud Functions는 프로젝트에서 서비스 계정으로 승인되며 Firebase Admin SDK를 사용하여 읽기 및 쓰기를 수행할 수 있습니다.
Node.js
const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');
initializeApp();
const db = getFirestore();
exports.writetofirestore = onDocumentWritten("some/doc", (event) => {
db.doc('some/otherdoc').set({ ... });
});
exports.writetofirestore = onDocumentWritten('users/{userId}', (event) => {
db.doc('some/otherdoc').set({
// Update otherdoc
});
});
Python
from firebase_admin import firestore, initialize_app
import google.cloud.firestore
initialize_app()
@on_document_written(document="some/doc")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
firestore_client: google.cloud.firestore.Client = firestore.client()
firestore_client.document("another/doc").set({
# ...
})
제한사항
Cloud Functions용 Cloud Firestore 트리거의 다음 제한사항에 유의하세요.
- Cloud Functions(1세대)는 Firestore 기본 모드의 기존 '(기본값)' 데이터베이스를 기본 요건으로 합니다. Cloud Firestore 이름이 지정된 데이터베이스 또는 Datastore 모드는 지원하지 않습니다. 이 경우 Cloud Functions(2세대)를 사용하여 이벤트를 구성하세요.
- Cloud Functions 및 Cloud Firestore 트리거를 사용한 프로젝트 간 설정은 제한사항입니다. Cloud Firestore 트리거를 설정하려면 Cloud Functions가 동일한 프로젝트에 있어야 합니다.
- 순서는 보장되지 않습니다. 급격하게 변경하면 예기치 않은 순서로 함수 호출이 트리거될 수 있습니다.
- 이벤트는 최소 1회 전송되지만 하나의 이벤트에 함수가 여러 번 호출될 수 있습니다. 정확히 한 번에 처리하는 메커니즘에 의존하지 말고 멱등 함수를 작성하세요.
- Datastore 모드의 Cloud Firestore에는 Cloud Functions(2세대)가 필요합니다. Cloud Functions(1세대)는 Datastore 모드를 지원하지 않습니다.
- 트리거는 단일 데이터베이스와 연결됩니다. 여러 데이터베이스와 일치하는 트리거를 만들 수 없습니다.
- 데이터베이스를 삭제해도 해당 데이터베이스의 트리거가 자동으로 삭제되지 않습니다. 트리거가 이벤트 제공을 중지하지만 트리거를 삭제하기 전까지 계속 존재합니다.
- 일치하는 이벤트가 최대 요청 크기를 초과하면 이벤트가 Cloud Functions(1세대)로 전달되지 않을 수 있습니다.
- 요청 크기로 인해 전송되지 않은 이벤트는 플랫폼 로그에 로깅되고 프로젝트의 로그 사용량에 반영됩니다.
- 이러한 로그는 로그 탐색기에서 '크기가 1세대... 한도를 초과하여 Cloud 함수에 이벤트를 전송할 수 없음'이라는
error심각도의 메시지와 함께 확인할 수 있습니다.functionName필드 아래에서 함수 이름을 찾을 수 있습니다.receiveTimestamp필드가 지금부터 1시간 이내인 경우 타임스탬프 전후의 스냅샷과 함께 해당 문서를 읽어 실제 이벤트 콘텐츠를 추론할 수 있습니다. - 이러한 주기를 피하려면 다음을 수행하면 됩니다.
- Cloud Functions(2세대)로 마이그레이션 및 업그레이드
- 문서 크기 줄이기
- 문제의 Cloud Functions 삭제
- 제외를 사용하여 로깅 자체를 사용 중지할 수 있지만 그래도 문제가 되는 이벤트가 전송되지 않습니다.