Cloud Functions(2세대)로 Firestore 확장

Cloud Functions를 사용하면 Firestore 데이터베이스를 변경하여 트리거되는 이벤트를 처리하는 코드를 배포할 수 있습니다. 이를 통해 자체 서버를 실행하지 않고도 서버 측 기능을 추가할 수 있습니다.

Cloud Functions(2세대)로 Firestore 확장

Cloud Functions(2세대)는 Firestore 이벤트에 연결된 핸들러를 만들 수 있도록 다음 Firestore 이벤트 트리거를 지원합니다.

이벤트 유형 트리거
google.cloud.firestore.document.v1.created 문서를 처음으로 기록할 때 트리거됩니다.
google.cloud.firestore.document.v1.updated 이미 존재하는 문서에서 값이 변경되었을 때 트리거됩니다.
google.cloud.firestore.document.v1.deleted 문서가 삭제될 때 트리거됩니다.
google.cloud.firestore.document.v1.written created, updated 또는 deleted가 트리거될 때 트리거됩니다.

Firestore 이벤트는 문서 변경 시에만 트리거됩니다. 데이터가 변경되지 않는 Firestore 문서를 업데이트(노옵스(no-ops) 쓰기)할 때에는 업데이트 또는 쓰기 이벤트가 생성되지 않습니다. 특정 필드에 이벤트를 추가할 수 없습니다.

Firestore 트리거 함수 작성

Firestore 이벤트에 응답하는 함수를 작성하려면 배포 중에 다음을 지정할 수 있도록 준비합니다.

  • created, updated, deleted 또는 written의 트리거 이벤트 유형
  • 함수와 연결된 문서를 선택하는 트리거 이벤트 필터
  • 실행할 함수 코드

트리거 이벤트 필터

이벤트 필터를 지정할 때 정확한 문서 일치 또는 경로 패턴을 지정할 수 있습니다. 경로 패턴을 사용하여 와일드 카드(* 또는 **)가 포함된 여러 문서를 일치시킵니다.

예를 들어 다음 문서의 변경사항에 응답할 수 있습니다.

users/marie

와일드 카드(* 또는 **)를 사용하여 패턴과 일치하는 문서의 변경사항에 응답합니다. 와일드 카드 *는 단일 세그먼트와 일치하고, 다중 세그먼트 와일드 카드 **는 패턴의 0개 이상의 세그먼트와 일치합니다.

단일 세그먼트 일치(*)이 경우 명명된 캡처 그룹을 사용할 수도 있습니다. 예를 들면 users/{userId}입니다.

예를 들면 다음과 같습니다.

패턴 설명
/users/* 또는 /users/{userId} /users 컬렉션의 모든 문서와 일치합니다. /users/marie/messages/33e2IxYBD9enzS50SJ68과 같은 하위 컬렉션의 문서와 일치하지 않습니다.
/users/** /users 컬렉션의 모든 문서 및 /users/marie/messages/33e2IxYBD9enzS50SJ68과 같은 하위 컬렉션의 문서를 일치시킵니다.

경로 패턴에 대한 자세한 내용은 Eventarc 경로 패턴을 참조하세요.

와일드 카드를 사용할 때도 트리거는 항상 문서를 가리켜야 합니다. 예를 들어 {messageCollectionId=*}는 컬렉션이므로 users/{userId=*}/{messageCollectionId=*}는 유효하지 않습니다. 그러나 {messageId=*}가 항상 문서를 가리키므로 users/{userId=*}/{messageCollectionId}/{messageId=*}유효합니다.

예제 함수

다음 샘플은 Firestore 이벤트를 수신하는 방법을 보여줍니다. 이벤트와 관련된 문서 데이터로 작업하려면 valueold_value 필드를 확인하세요.

  • value: 작업 후 문서 스냅샷이 포함된 Document 객체입니다. 이 필드는 삭제 이벤트에 대해 채워지지 않습니다.
  • old_value: 작업 전 문서 스냅샷이 포함된 Document 객체입니다. 이 필드는 업데이트 및 삭제 이벤트에 대해서만 채워집니다.

Go

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.


// Package hellofirestore contains a Cloud Event Function triggered by a Cloud Firestore event.
package hellofirestore

import (
	"context"
	"fmt"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

func init() {
	functions.CloudEvent("helloFirestore", HelloFirestore)
}

// HelloFirestore is triggered by a change to a Firestore document.
func HelloFirestore(ctx context.Context, event event.Event) error {
	var data firestoredata.DocumentEventData
	if err := proto.Unmarshal(event.Data(), &data); err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	fmt.Printf("Function triggered by change to: %v\n", event.Source())
	fmt.Printf("Old value: %+v\n", data.GetOldValue())
	fmt.Printf("New value: %+v\n", data.GetValue())
	return nil
}

Java

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.logging.Logger;

public class FirebaseFirestore implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestore.class.getName());

  @Override
  public void accept(CloudEvent event) throws InvalidProtocolBufferException {
    DocumentEventData firestorEventData = DocumentEventData.parseFrom(event.getData().toBytes());

    logger.info("Function triggered by event on: " + event.getSource());
    logger.info("Event type: " + event.getType());

    logger.info("Old value:");
    logger.info(firestorEventData.getOldValue().toString());

    logger.info("New value:");
    logger.info(firestorEventData.getValue().toString());
  }
}

Node.js

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

protobufjs를 사용하여 이벤트 데이터를 디코딩합니다. 소스에 google.events.cloud.firestore.v1 data.proto를 포함합니다.
/**
 * Cloud Event Function triggered by a change to a Firestore document.
 */
const functions = require('@google-cloud/functions-framework');
const protobuf = require('protobufjs');

functions.cloudEvent('helloFirestore', async cloudEvent => {
  console.log(`Function triggered by event on: ${cloudEvent.source}`);
  console.log(`Event type: ${cloudEvent.type}`);

  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  console.log('\nOld value:');
  console.log(JSON.stringify(firestoreReceived.oldValue, null, 2));

  console.log('\nNew value:');
  console.log(JSON.stringify(firestoreReceived.value, null, 2));
});

Python

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

from cloudevents.http import CloudEvent
import functions_framework
from google.events.cloud import firestore

@functions_framework.cloud_event
def hello_firestore(cloud_event: CloudEvent) -> None:
    """Triggers by a change to a Firestore document.

    Args:
        cloud_event: cloud event with information on the firestore event trigger
    """
    firestore_payload = firestore.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    print(f"Function triggered by change to: {cloud_event['source']}")

    print("\nOld value:")
    print(firestore_payload.old_value)

    print("\nNew value:")
    print(firestore_payload.value)

C#

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Cloud.Firestore.V1;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace FirebaseFirestore;

public class Function : ICloudEventFunction<DocumentEventData>
{
    private readonly ILogger _logger;

    public Function(ILogger<Function> logger) =>
        _logger = logger;

    public Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Function triggered by event on {subject}", cloudEvent.Subject);
        _logger.LogInformation("Event type: {type}", cloudEvent.Type);
        MaybeLogDocument("Old value", data.OldValue);
        MaybeLogDocument("New value", data.Value);

        // In this example, we don't need to perform any asynchronous operations, so the
        // method doesn't need to be declared async.
        return Task.CompletedTask;
    }

    /// <summary>
    /// Logs the names and values of the fields in a document in a very simplistic way.
    /// </summary>
    private void MaybeLogDocument(string message, Document document)
    {
        if (document is null)
        {
            return;
        }

        // ConvertFields converts the Firestore representation into a .NET-friendly
        // representation.
        IReadOnlyDictionary<string, object> fields = document.ConvertFields();
        var fieldNamesAndTypes = fields
            .OrderBy(pair => pair.Key)
            .Select(pair => $"{pair.Key}: {pair.Value}");
        _logger.LogInformation(message + ": {fields}", string.Join(", ", fieldNamesAndTypes));
    }
}

다음 예시는 영향을 받는 문서의 original 필드에 추가된 문자열을 대문자로 변환하고 새 값을 동일한 문서에 씁니다.

Go

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.


// Package upper contains a Firestore Cloud Function.
package upper

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"

	"cloud.google.com/go/firestore"
	firebase "firebase.google.com/go/v4"
	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

// set the GOOGLE_CLOUD_PROJECT environment variable when deploying.
var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")

// client is a Firestore client, reused between function invocations.
var client *firestore.Client

func init() {
	// Use the application default credentials.
	conf := &firebase.Config{ProjectID: projectID}

	// Use context.Background() because the app/client should persist across
	// invocations.
	ctx := context.Background()

	app, err := firebase.NewApp(ctx, conf)
	if err != nil {
		log.Fatalf("firebase.NewApp: %v", err)
	}

	client, err = app.Firestore(ctx)
	if err != nil {
		log.Fatalf("app.Firestore: %v", err)
	}

	// Register cloud event function
	functions.CloudEvent("MakeUpperCase", MakeUpperCase)
}

// MakeUpperCase is triggered by a change to a Firestore document. It updates
// the `original` value of the document to upper case.
func MakeUpperCase(ctx context.Context, e event.Event) error {
	var data firestoredata.DocumentEventData
	if err := proto.Unmarshal(e.Data(), &data); err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	if data.GetValue() == nil {
		return errors.New("Invalid message: 'Value' not present")
	}

	fullPath := strings.Split(data.GetValue().GetName(), "/documents/")[1]
	pathParts := strings.Split(fullPath, "/")
	collection := pathParts[0]
	doc := strings.Join(pathParts[1:], "/")

	var originalStringValue string
	if v, ok := data.GetValue().GetFields()["original"]; ok {
		originalStringValue = v.GetStringValue()
	} else {
		return errors.New("Document did not contain field \"original\"")
	}

	newValue := strings.ToUpper(originalStringValue)
	if originalStringValue == newValue {
		log.Printf("%q is already upper case: skipping", originalStringValue)
		return nil
	}
	log.Printf("Replacing value: %q -> %q", originalStringValue, newValue)

	newDocumentEntry := map[string]string{"original": newValue}
	_, err := client.Collection(collection).Doc(doc).Set(ctx, newDocumentEntry)
	if err != nil {
		return fmt.Errorf("Set: %w", err)
	}
	return nil
}

Java

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.firestore.SetOptions;
import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.events.cloud.firestore.v1.Value;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;

public class FirebaseFirestoreReactive implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestoreReactive.class.getName());
  private final Firestore firestore;

  private static final String FIELD_KEY = "original";
  private static final String APPLICATION_PROTOBUF = "application/protobuf";

  public FirebaseFirestoreReactive() {
    this(FirestoreOptions.getDefaultInstance().getService());
  }

  public FirebaseFirestoreReactive(Firestore firestore) {
    this.firestore = firestore;
  }

  @Override
  public void accept(CloudEvent event)
      throws InvalidProtocolBufferException, InterruptedException, ExecutionException {
    if (event.getData() == null) {
      logger.warning("No data found in event!");
      return;
    }

    if (!event.getDataContentType().equals(APPLICATION_PROTOBUF)) {
      logger.warning(String.format("Found unexpected content type %s, expected %s",
          event.getDataContentType(),
          APPLICATION_PROTOBUF));
      return;
    }

    DocumentEventData firestoreEventData = DocumentEventData
        .parseFrom(event.getData().toBytes());

    // Get the fields from the post-operation document snapshot
    // https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
    Map<String, Value> fields = firestoreEventData.getValue().getFieldsMap();
    if (!fields.containsKey(FIELD_KEY)) {
      logger.warning("Document does not contain original field");
      return;
    }
    String currValue = fields.get(FIELD_KEY).getStringValue();
    String newValue = currValue.toUpperCase();

    if (currValue.equals(newValue)) {
      logger.info("Value is already upper-case");
      return;
    }

    // Retrieve the document name from the resource path:
    // projects/{project_id}/databases/{database_id}/documents/{document_path}
    String affectedDoc = firestoreEventData.getValue()
        .getName()
        .split("/documents/")[1]
        .replace("\"", "");

    logger.info(String.format("Replacing values: %s --> %s", currValue, newValue));

    // Wait for the async call to complete
    this.firestore
        .document(affectedDoc)
        .set(Map.of(FIELD_KEY, newValue), SetOptions.merge())
        .get();
  }
}

Node.js

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

protobufjs를 사용하여 이벤트 데이터를 디코딩합니다. 소스에 google.events.cloud.firestore.v1 data.proto를 포함합니다.
const functions = require('@google-cloud/functions-framework');
const Firestore = require('@google-cloud/firestore');
const protobuf = require('protobufjs');

const firestore = new Firestore({
  projectId: process.env.GOOGLE_CLOUD_PROJECT,
});

// Converts strings added to /messages/{pushId}/original to uppercase
functions.cloudEvent('makeUpperCase', async cloudEvent => {
  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  const resource = firestoreReceived.value.name;
  const affectedDoc = firestore.doc(resource.split('/documents/')[1]);

  const curValue = firestoreReceived.value.fields.original.stringValue;
  const newValue = curValue.toUpperCase();

  if (curValue === newValue) {
    // Value is already upper-case
    // Don't perform a(nother) write to avoid infinite loops
    console.log('Value is already upper-case.');
    return;
  }

  console.log(`Replacing value: ${curValue} --> ${newValue}`);
  affectedDoc.set({
    original: newValue,
  });
});

Python

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

from cloudevents.http import CloudEvent
import functions_framework
from google.cloud import firestore
from google.events.cloud import firestore as firestoredata

client = firestore.Client()

# Converts strings added to /messages/{pushId}/original to uppercase
@functions_framework.cloud_event
def make_upper_case(cloud_event: CloudEvent) -> None:
    firestore_payload = firestoredata.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    path_parts = firestore_payload.value.name.split("/")
    separator_idx = path_parts.index("documents")
    collection_path = path_parts[separator_idx + 1]
    document_path = "/".join(path_parts[(separator_idx + 2) :])

    print(f"Collection path: {collection_path}")
    print(f"Document path: {document_path}")

    affected_doc = client.collection(collection_path).document(document_path)

    cur_value = firestore_payload.value.fields["original"].string_value
    new_value = cur_value.upper()

    if cur_value != new_value:
        print(f"Replacing value: {cur_value} --> {new_value}")
        affected_doc.set({"original": new_value})
    else:
        # Value is already upper-case
        # Don't perform a second write (which can trigger an infinite loop)
        print("Value is already upper-case.")

C#

Firestore에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.

using CloudNative.CloudEvents;
using Google.Cloud.Firestore;
using Google.Cloud.Functions.Framework;
using Google.Cloud.Functions.Hosting;
using Google.Events.Protobuf.Cloud.Firestore.V1;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace FirestoreReactive;

public class Startup : FunctionsStartup
{
    public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) =>
        services.AddSingleton(FirestoreDb.Create());
}

// Register the startup class to provide the Firestore dependency.
[FunctionsStartup(typeof(Startup))]
public class Function : ICloudEventFunction<DocumentEventData>
{
    private readonly ILogger _logger;
    private readonly FirestoreDb _firestoreDb;

    public Function(ILogger<Function> logger, FirestoreDb firestoreDb) =>
        (_logger, _firestoreDb) = (logger, firestoreDb);

    public async Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken)
    {
        // Get the recently-written value. This expression will result in a null value
        // if any of the following is true:
        // - The event doesn't contain a "new" document
        // - The value doesn't contain a field called "original"
        // - The "original" field isn't a string
        string currentValue = data.Value?.ConvertFields().GetValueOrDefault("original") as string;
        if (currentValue is null)
        {
            _logger.LogWarning($"Event did not contain a suitable document");
            return;
        }

        string newValue = currentValue.ToUpperInvariant();
        if (newValue == currentValue)
        {
            _logger.LogInformation("Value is already upper-cased; no replacement necessary");
            return;
        }

        // The CloudEvent subject is "documents/x/y/...".
        // The Firestore SDK FirestoreDb.Document method expects a reference relative to
        // "documents" (so just the "x/y/..." part). This may be simplified over time.
        if (cloudEvent.Subject is null || !cloudEvent.Subject.StartsWith("documents/"))
        {
            _logger.LogWarning("CloudEvent subject is not a document reference.");
            return;
        }
        string documentPath = cloudEvent.Subject.Substring("documents/".Length);

        _logger.LogInformation("Replacing '{current}' with '{new}' in '{path}'", currentValue, newValue, documentPath);
        await _firestoreDb.Document(documentPath).UpdateAsync("original", newValue);
    }
}

소스에 proto 종속 항목 포함

함수의 소스 디렉터리에 Firestore data.proto 파일을 포함해야 합니다. 이 파일은 소스 디렉터리에 포함해야 하는 다음 proto를 가져옵니다.

종속 항목에 동일한 디렉터리 구조를 사용합니다. 예를 들어 struct.protogoogle/protobuf 내에 배치합니다.

이러한 파일은 이벤트 데이터를 디코딩하기 위해 필요합니다. 함수 소스에 이러한 파일이 포함되지 않으면 실행 시 오류가 반환됩니다.

이벤트 속성

각 이벤트에는 이벤트가 트리거된 시간과 같은 이벤트 정보가 포함된 데이터 속성이 포함됩니다. Firestore는 이벤트와 관련된 데이터베이스 및 문서에 대한 추가 데이터를 추가합니다. 다음과 같이 이러한 속성에 액세스할 수 있습니다.

자바
logger.info("Function triggered by event on: " + event.getSource());
logger.info("Event type: " + event.getType());
logger.info("Event time " + event.getTime());
logger.info("Event project: " + event.getExtension("project"));
logger.info("Event location: " + event.getExtension("location"));
logger.info("Database name: " + event.getExtension("database"));
logger.info("Database document: " + event.getExtension("document"));
Node.js
console.log(`Function triggered by event on: ${cloudEvent.source}`);
console.log(`Event type: ${cloudEvent.type}`);
console.log(`Event time: ${cloudEvent.time}`);
console.log(`Event project: ${cloudEvent.project}`);
console.log(`Event location: ${cloudEvent.location}`);
console.log(`Database name: ${cloudEvent.database}`);
console.log(`Document name: ${cloudEvent.document}`);
Python
print(f"Function triggered by change to: {cloud_event['source']}")
print(f"Event type: {cloud_event['type']}")
print(f"Event time: {cloud_event['time']}")
print(f"Event project: {cloud_event['project']}")
print(f"Location: {cloud_event['location']}")
print(f"Database name: {cloud_event['database']}")
print(f"Document: {cloud_event['document']}")

함수 배포

Cloud Functions 배포 사용자에게는 Cloud Functions 개발자 IAM 역할 또는 동일한 권한이 포함된 역할이 있어야 합니다. 또한 배포 추가 구성을 참조하세요.

gcloud CLI 또는 Google Cloud 콘솔을 사용하여 함수를 배포할 수 있습니다. 아래 예시는 gcloud CLI를 사용한 배포를 보여줍니다. Google Cloud 콘솔을 사용한 배포에 대한 자세한 내용은 Cloud Functions 배포를 참조하세요.

gcloud

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. gcloud functions deploy 명령어를 사용하여 함수를 배포합니다.

    gcloud functions deploy YOUR_FUNCTION_NAME \
    --gen2 \
    --region=FUNCTION_LOCATION \
    --trigger-location=TRIGGER_LOCATION \
    --runtime=YOUR_RUNTIME \
    --source=YOUR_SOURCE_LOCATION \
    --entry-point=YOUR_CODE_ENTRYPOINT \
    --trigger-event-filters="type=EVENT_FILTER_TYPE" \
    --trigger-event-filters="database=DATABASE" \
    --trigger-event-filters-path-pattern="document=DOCUMENT" \
    

    첫 번째 인수인 YOUR_FUNCTION_NAME은 배포된 함수의 이름입니다. 함수 이름은 문자로 시작해야 합니다. 이어서 최대 62자(영문 기준)의 문자, 숫자, 하이픈, 밑줄이 와야 하며 문자나 숫자로 끝나야 합니다.

    • --gen2 플래그는 Cloud Functions(2세대)에 배포하도록 지정합니다. 이 플래그를 생략하면 Cloud Functions(1세대)에 배포됩니다.

    • --region 플래그는 함수를 배포할 리전을 지정합니다.

      근접성을 극대화하려면 Firestore 데이터베이스와 가까운 리전으로 설정합니다. Firestore 데이터베이스가 멀티 리전 위치에 있는 경우 nam5의 데이터베이스에 대해 us-central1로 설정하고 eur3의 데이터베이스에 대해 europe-west4로 설정합니다. 리전 Firestore 위치의 경우 동일한 리전으로 설정합니다.

    • --trigger-location 플래그는 트리거 위치를 지정합니다. 이 플래그를 Firestore 데이터베이스의 위치로 설정해야 합니다.

    • --runtime 플래그는 함수에 사용되는 언어 런타임을 지정합니다. Cloud Functions는 여러 런타임을 지원합니다. 자세한 내용은 런타임을 참조하세요.

    • --source 플래그는 함수 소스 코드의 위치를 지정합니다. 세부정보는 다음 단계를 참조하세요.

    • --entry-point 플래그는 소스 코드에서 함수의 진입점을 지정합니다. 이것은 함수가 실행될 때 수행되는 코드입니다. 이 플래그의 값은 소스 코드에 있는 함수 이름 또는 정규화된 클래스 이름이어야 합니다. 자세한 내용은 함수 진입점을 참조하세요.

    • EVENT_FILTER_TYPE: Firestore는 다음 이벤트 유형을 지원합니다.

      • google.cloud.firestore.document.v1.created: 문서를 처음으로 기록할 때 이벤트가 전송됩니다.
      • google.cloud.firestore.document.v1.updated: 문서가 이미 존재하고 값이 변경되면 이벤트가 전송됩니다.
      • google.cloud.firestore.document.v1.deleted: 문서가 삭제되면 이벤트가 전송됩니다.
      • google.cloud.firestore.document.v1.written: 문서가 생성, 업데이트 또는 삭제되면 이벤트가 전송됩니다.
    • DATABASE: Firestore 데이터베이스 기본 데이터베이스 이름으로는 (default)를 사용합니다.

    • DOCUMENT: 데이터가 생성, 업데이트 또는 삭제될 때 이벤트를 트리거하는 데이터베이스 경로입니다. 연산자는 다음 중 하나일 수 있습니다.

      • 같음, 예: --trigger-event-filters=document='users/marie'
      • 경로 패턴, 예: --trigger-event-filters-path-pattern=document='users/*' 자세한 내용은 경로 패턴 이해를 참조하세요.

    함수를 배포할 때 선택적으로 추가 구성, 네트워킹, 보안 옵션을 지정할 수 있습니다.

    배포 명령어와 플래그에 대한 자세한 내용은 gcloud functions deploy 문서를 참조하세요.

배포 예시

다음 예시에서는 Google Cloud CLI를 사용한 배포를 보여줍니다.

us-west2 리전의 데이터베이스에 함수를 배포합니다.

gcloud functions deploy gcfv2-trigger-firestore-node \
--gen2 \
--region=us-west2 \
--trigger-location=us-west2 \
--runtime=nodejs18 \
--source=gs://CLOUD_STORAGE_BUCKET/firestoreEventFunction.zip \
--entry-point=makeUpperCase \
--trigger-event-filters=type=google.cloud.firestore.document.v1.written \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern=document='messages/{pushId}'

nam5 멀티 리전의 데이터베이스에 함수를 배포합니다.

gcloud functions deploy gcfv2-trigger-firestore-python \
--gen2 \
--region=us-central1 \
--trigger-location=nam5 \
--runtime=python311 \
--source=gs://CLOUD_STORAGE_BUCKET/firestoreEventFunction.zip \
--entry-point=make_upper_case \
--trigger-event-filters=type=google.cloud.firestore.document.v1.written \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern=document='messages/{pushId}'

제한사항

Cloud Functions용 Firestore 트리거의 다음 제한사항에 유의하세요.

  • 순서는 보장되지 않습니다. 급격하게 변경하면 예기치 않은 순서로 함수 호출이 트리거될 수 있습니다.
  • 이벤트는 최소 1회 전송되지만 하나의 이벤트에 함수가 여러 번 호출될 수 있습니다. 정확히 한 번에 처리하는 메커니즘에 의존하지 말고 멱등 함수를 작성하세요.
  • Datastore 모드의 Cloud Firestore에는 Cloud Functions(2세대)가 필요합니다. Cloud Functions(1세대)는 Datastore 모드를 지원하지 않습니다.
  • Cloud Functions(1세대)는 '(기본값)' 데이터베이스에서만 작동하며 이름이 지정된 Firestore 데이터베이스를 지원하지 않습니다. Cloud Functions(2세대)를 사용하여 이름이 지정된 데이터베이스의 이벤트를 구성하세요.

미리보기 제한사항

이 기능의 미리보기에는 다음 제한사항이 적용됩니다.

Eventarc 및 Firestore 위치

Eventarc는 Firestore 이벤트 트리거에 대해 멀티 리전을 지원하지 않지만 멀티 리전 위치에서 Firestore 데이터베이스에 대한 트리거를 만들 수 있습니다. Eventarc는 Firestore 멀티 리전 위치를 다음 Eventarc 리전에 매핑합니다.

Firestore 멀티 리전 Eventarc 리전
nam5 us-central1
eur3 europe-west4

Cloud Functions 2세대와 1세대의 차이점

Cloud Functions(2세대)는 모든 런타임에 Eventarc 이벤트를 사용합니다. 이전에는 Cloud Functions(1세대)에서 일부 런타임에만 Eventarc 이벤트를 사용했습니다. Eventarc 이벤트에서는 Cloud Functions (1세대)와 다음과 같은 차이점이 있습니다.

  • Eventarc용 Firestore 트리거는 Cloud Functions 외의 추가 대상을 지원합니다. CloudEvents는 Cloud Run, GKE, Workflows를 포함하되 이에 국한되지 않는 여러 대상으로 라우팅할 수 있습니다.

  • Eventarc용 Firestore 트리거는 데이터베이스 쓰기 작업 시작 시 트리거 정의를 검색하고 이 정의를 사용하여 Firestore에서 이벤트를 내보내야 하는지 결정합니다. 쓰기 작업은 실행 시 발생할 수 있는 정의를 트리거하는 변경사항을 고려하지 않습니다.

    Cloud Functions(1세대)는 데이터베이스 쓰기 평가 중에 트리거 정의를 검색하고 평가 중에 트리거 변경사항을 실행하면 Firestore에서 이벤트를 내보낼지 여부에 영향을 줄 수 있습니다.

자세한 내용은 Cloud Functions 버전 비교를 참조하세요.