확장 프로그램에 사용자 후크 추가
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
확장 프로그램을 설치한 사용자에게 자체 커스텀 로직을 확장 프로그램 실행에 삽입할 수 있는 기능을 제공할 수 있습니다. 여기에는 두 가지 방법이 있습니다.
Eventarc 이벤트: 사용자에게 이벤트에 비동기식으로 반응하는 방법을 제공하려면 Eventarc에 게시하면 됩니다. 예를 들어 장기 실행 태스크가 완료된 후 알림을 전송하는 이벤트 핸들러 함수를 배포하거나 자체 후처리 함수를 정의할 수 있습니다.
동기식 후크: 확장 프로그램에 차단 로직을 추가하는 방법을 사용자에게 제공하려면 확장 프로그램 작업의 사전 정의된 지점에 동기식 후크를 추가하면 됩니다. 이 지점에서 사용자 제공업체 함수를 실행하고 완료 후에만 계속합니다. 전처리 태스크가 종종 이 카테고리에 속합니다.
확장 프로그램에서는 두 가지 방법 중 하나 또는 둘 다 사용할 수 있습니다.
Eventarc 이벤트
확장 프로그램에서 이벤트를 게시하는 방법은 다음과 같습니다.
extension.yaml
파일에 게시할 이벤트 유형을 선언합니다.
events:
- type: publisher-id.extension-name.version.event-name
description: event-description
- type: publisher-id.extension-name.version.another-event-name
description: another-event-description
type
식별자는 점으로 구분된 여러 필드로 구성됩니다. 게시자 ID, 확장 프로그램 이름, 이벤트 이름 필드는 필수 필드입니다. 버전 필드는 권장 필드입니다. 게시하는 이벤트 유형마다 고유하고 구체적인 이벤트 이름을 선택합니다.
예를 들어 storage-resize-images
확장 프로그램은 단일 이벤트 유형을 선언합니다.
events:
- type: firebase.extensions.storage-resize-images.v1.complete
description: |
Occurs when image resizing completes. The event will contain further
details about specific formats and sizes.
사용자는 확장 프로그램을 설치할 때 구독할 이벤트를 선택할 수 있습니다.
확장 프로그램 함수의 Admin SDK에서 Eventarc API를 가져오고 사용자의 설치 설정을 사용하여 이벤트 채널을 초기화합니다.
이러한 설정은 다음 환경 변수를 사용하여 표시됩니다.
EVENTARC_CHANNEL
: 사용자가 이벤트를 게시하기로 선택한 Eventarc 채널의 정규화된 이름입니다.
EXT_SELECTED_EVENTS
: 사용자가 게시하기 위해 선택한 이벤트 유형을 쉼표로 구분한 목록입니다. 이 값으로 채널을 초기화하면 Admin SDK는 사용자가 선택하지 않은 이벤트를 자동으로 필터링합니다.
EVENTARC_CLOUD_EVENT_SOURCE
: Cloud 이벤트 소스 식별자입니다. Admin SDK는 게시된 이벤트의 source
필드에 이 값을 자동으로 전달합니다. 일반적으로 이 변수는 명시적으로 사용하지 않아도 됩니다.
설치 시 이벤트가 사용 설정되지 않은 경우 이러한 변수가 정의되지 않습니다. 이벤트가 사용 설정된 경우에만 이 사실을 사용하여 이벤트 채널을 초기화할 수 있습니다.
import * as admin from "firebase-admin";
import {getEventarc} from 'firebase-admin/eventarc';
admin.initializeApp();
// Set eventChannel to a newly-initialized channel, or `undefined` if events
// aren't enabled.
const eventChannel =
process.env.EVENTARC_CHANNEL &&
getEventarc().channel(process.env.EVENTARC_CHANNEL, {
allowedEventTypes: process.env.EXT_SELECTED_EVENTS,
});
사용자에게 표시할 확장 프로그램의 지점에서 채널에 이벤트를 게시합니다. 예를 들면 다음과 같습니다.
// If events are enabled, publish a `complete` event to the configured
// channel.
eventChannel && eventChannel.publish({
type: 'firebase.extensions.storage-resize-images.v1.complete',
subject: filename, // the name of the original file
data: {
// ...
}
});
게시하는 이벤트를 PREINSTALL 또는 POSTINSTALL 파일에 문서화합니다.
각 이벤트에 대해 다음 사항을 문서화합니다.
- 사용 목적
- 확장 프로그램 로직에서 실행되는 지점
- 포함된 출력 데이터
- 실행 조건
또한 동일한 확장 프로그램을 트리거하여 무한 루프가 발생할 수 있는 작업을 이벤트 핸들러에서 수행하지 않도록 사용자에게 경고합니다.
확장 프로그램에서 이벤트를 게시하면 사용자가 이벤트 핸들러를 배포하여 커스텀 로직으로 응답할 수 있습니다.
예를 들어 다음 예시에서는 이미지 크기를 조절한 후 원본 이미지를 삭제합니다. 이 예시 핸들러는 이벤트의 subject
속성(이 경우 이미지의 원래 파일 이름)을 사용합니다.
exports.onimageresized = onCustomEventPublished(
"firebase.extensions.storage-resize-images.v1.complete",
(event) => {
logger.info("Received image resize completed event", event);
// For example, delete the original.
return admin.storage()
.bucket("my-project.firebasestorage.app")
.file(event.subject)
.delete();
});
자세한 내용은 커스텀 이벤트 트리거를 참조하세요.
예
공식 이미지 크기 조절 확장 프로그램은 이미지 크기를 조절한 후 Eventarc에 게시하여 비동기식 후크를 제공합니다.
동기식 후크
확장 프로그램 함수 중 하나가 제대로 작동하도록 하는 후크를 사용자에게 제공하려면 동기식 후크를 사용합니다.
동기식 후크는 사용자 정의 HTTPS 호출 가능 Cloud 함수를 호출하고 완료될 때까지(반환된 값이 있을 수 있음) 대기합니다. 사용자 제공 함수에 오류가 발생하면 확장 프로그램 함수에서 오류가 발생합니다.
동기식 후크를 노출하는 방법은 다음과 같습니다.
사용자가 커스텀 Cloud 함수 URL로 확장 프로그램을 구성할 수 있도록 확장 프로그램에 매개변수를 추가합니다. 예를 들면 다음과 같습니다.
- param: PREPROCESSING_FUNCTION
label: Pre-processing function URL
description: >
An HTTPS callable function that will be called to transform the input data
before it is processed by this function.
type: string
example: https://us-west1-my-project-id.cloudfunctions.net/preprocessData
required: false
확장 프로그램에서 후크를 노출하려는 지점에 URL을 사용하여 함수를 호출합니다. 예를 들면 다음과 같습니다.
const functions = require('firebase-functions/v1');
const fetch = require('node-fetch');
const preprocessFunctionURL = process.env.PREPROCESSING_FUNCTION;
exports.yourFunctionName = functions.firestore.document("collection/{doc_id}")
.onWrite((change, context) => {
// PREPROCESSING_FUNCTION hook begins here.
// If a preprocessing function is defined, call it before continuing.
if (preprocessFunctionURL) {
try {
await fetch(preprocessFunctionURL); // Could also be a POST request if you want to send data.
} catch (e) {
// Preprocessing failure causes the function to fail.
functions.logger.error("Preprocessor error:", e);
return;
}
}
// End of PREPROCESSING_FUNCTION hook.
// Main function logic follows.
// ...
});
PREINSTALL 또는 POSTINSTALL 파일에서 사용 가능한 후크를 문서화합니다.
각 후크에 대해 다음을 문서화합니다.
- 사용 목적
- 확장 프로그램 로직에서 실행되는 지점
- 예상되는 입력 및 출력
- 실행 조건(또는 옵션)
또한 동일한 확장 프로그램을 트리거하여 무한 루프가 발생할 수 있는 작업을 후크 함수에서 수행하지 않도록 사용자에게 경고합니다.
예
Algolia 검색 확장 프로그램은 Algolia에 작성하기 전에 사용자 제공 변환 함수를 호출하기 위한 동기식 후크를 제공합니다.
달리 명시되지 않는 한 이 페이지의 콘텐츠에는 Creative Commons Attribution 4.0 라이선스에 따라 라이선스가 부여되며, 코드 샘플에는 Apache 2.0 라이선스에 따라 라이선스가 부여됩니다. 자세한 내용은 Google Developers 사이트 정책을 참조하세요. 자바는 Oracle 및/또는 Oracle 계열사의 등록 상표입니다.
최종 업데이트: 2025-08-13(UTC)
[null,null,["최종 업데이트: 2025-08-13(UTC)"],[],[],null,["# Add user hooks to an extension\n\n\u003cbr /\u003e\n\nYou can provide users who install your extension the ability to insert their own\ncustom logic into the execution of your extension. There are two ways to\naccomplish this:\n\n- **Eventarc events**: to give users a way to asynchronously react to\n events, you can publish to Eventarc. Users can deploy event handler\n functions that, for example, send notifications after long-running\n tasks complete, or they can define their own post-processing functions.\n\n- **Synchronous hooks**: to give users a way to add blocking logic to your\n extension, you can add synchronous hooks at predefined points in the\n extension's operation. At these points, you run a user-provider function\n and proceed only after it completes. Pre-processing tasks often fall under\n this category.\n\nAn extension can use either or both methods.\n\nEventarc events\n---------------\n\nTo publish events from an extension:\n\n1. Declare the event types you will publish in the `extension.yaml` file:\n\n events:\n - type: publisher-id.extension-name.version.event-name\n description: event-description\n - type: publisher-id.extension-name.version.another-event-name\n description: another-event-description\n\n The `type` identifier is made of several dot-delimited fields. The\n [publisher ID](/docs/extensions/publishers/register), extension name, and event name fields are\n required. The version field is recommended. Choose a unique and descriptive\n event name for each event type you publish.\n\n For example, the [`storage-resize-images` extension](https://github.com/firebase/extensions/blob/next/storage-resize-images/extension.yaml)\n declares a single event type: \n\n events:\n - type: firebase.extensions.storage-resize-images.v1.complete\n description: |\n Occurs when image resizing completes. The event will contain further\n details about specific formats and sizes.\n\n Users will be able to choose which events to subscribe to when they\n install the extension.\n2. In your extension functions, import the Eventarc API from the Admin SDK\n and initialize an event channel using the user's installation settings.\n These settings are exposed using the following environment variables:\n\n - `EVENTARC_CHANNEL`: the fully-qualified name of the Eventarc channel to which the user chose to publish events.\n - `EXT_SELECTED_EVENTS`: a comma-separated list of event types the user chose to publish. When you initialize a channel with this value, the Admin SDK automatically filters out events user did not select.\n - `EVENTARC_CLOUD_EVENT_SOURCE`: the Cloud Event source identifier. The Admin SDK automatically passes this value in the `source` field of published events. You typically don't need to explicitly use this variable.\n\n If events weren't enabled at installation, these variables will be\n undefined. You can use this fact to initialize an event channel only when\n events are enabled: \n\n import * as admin from \"firebase-admin\";\n import {getEventarc} from 'firebase-admin/eventarc';\n\n admin.initializeApp();\n\n // Set eventChannel to a newly-initialized channel, or `undefined` if events\n // aren't enabled.\n const eventChannel =\n process.env.EVENTARC_CHANNEL &&\n getEventarc().channel(process.env.EVENTARC_CHANNEL, {\n allowedEventTypes: process.env.EXT_SELECTED_EVENTS,\n });\n\n3. Publish events to the channel at the points in your extension you want to\n expose to users. For example:\n\n // If events are enabled, publish a `complete` event to the configured\n // channel.\n eventChannel && eventChannel.publish({\n type: 'firebase.extensions.storage-resize-images.v1.complete',\n subject: filename, // the name of the original file\n data: {\n // ...\n }\n });\n\n4. Document the events you publish, in either the PREINSTALL or POSTINSTALL\n file.\n\n For each event, document the following:\n - Its intended purpose\n - The point in your extension's logic it runs\n - The output data it includes\n - The conditions for its execution\n\n Additionally, warn users not to perform any actions in their event\n handlers that might trigger the same extension, resulting in an infinite\n loop.\n\nWhen you publish events from an extension, users can deploy event handlers\nto respond with custom logic.\n\nFor example, the following example deletes the original image after it has been\nresized. Note that this example handler makes use of the `subject` property of\nthe event, which in this case is the image's original filename. \n\n exports.onimageresized = onCustomEventPublished(\n \"firebase.extensions.storage-resize-images.v1.complete\",\n (event) =\u003e {\n logger.info(\"Received image resize completed event\", event);\n // For example, delete the original.\n return admin.storage()\n .bucket(\"my-project.firebasestorage.app\")\n .file(event.subject)\n .delete();\n });\n\nSee [Custom event triggers](/docs/functions/custom-events#handle-events) for more\ninformation.\n\n### Example\n\nThe official [Resize Images extension](https://github.com/firebase/extensions/tree/next/storage-resize-images)\nprovides an asynchronous hook by [publishing to Eventarc](https://github.com/firebase/extensions/blob/c29781c7e67c004e2491e4ce3c43b25b05bd3de6/storage-resize-images/functions/src/index.ts#L109-L117)\nafter resizing an image.\n\nSynchronous hooks\n-----------------\n\nWhen you want to provide users with a hook that must complete successfully\nfor one of your extension functions to operate, use *synchronous hooks*.\n\nA synchronous hook calls a user-defined [HTTPS callable Cloud\nFunction](/docs/functions/http-events) and awaits completion (possibly with a\nreturned value) before continuing. An error in the user-provided function\nresults in an error in the extension function.\n\nTo expose a synchronous hook:\n\n1. Add a parameter to your extension that allows users to configure the\n extension with the URL to their custom Cloud Function. For example:\n\n - param: PREPROCESSING_FUNCTION\n label: Pre-processing function URL\n description: \u003e\n An HTTPS callable function that will be called to transform the input data\n before it is processed by this function.\n type: string\n example: https://us-west1-my-project-id.cloudfunctions.net/preprocessData\n required: false\n\n2. At the point in your extension where you want to expose the hook, call the\n function using its URL. For example:\n\n const functions = require('firebase-functions/v1');\n const fetch = require('node-fetch');\n\n const preprocessFunctionURL = process.env.PREPROCESSING_FUNCTION;\n\n exports.yourFunctionName = functions.firestore.document(\"collection/{doc_id}\")\n .onWrite((change, context) =\u003e {\n // PREPROCESSING_FUNCTION hook begins here.\n // If a preprocessing function is defined, call it before continuing.\n if (preprocessFunctionURL) {\n try {\n await fetch(preprocessFunctionURL); // Could also be a POST request if you want to send data.\n } catch (e) {\n // Preprocessing failure causes the function to fail.\n functions.logger.error(\"Preprocessor error:\", e);\n return;\n }\n }\n // End of PREPROCESSING_FUNCTION hook.\n\n // Main function logic follows.\n // ...\n });\n\n3. Document any hooks you make available in either the PREINSTALL or\n POSTINSTALL file.\n\n For each hook, document the following:\n - Its intended purpose\n - The point in your extension's logic it runs\n - Its expected inputs and outputs\n - The conditions (or options) for its execution\n\n Additionally, warn users not to perform any actions in the hook\n function that might trigger the same extension, resulting in an infinite\n loop.\n\n### Example\n\nThe [Algolia Search extension](https://github.com/algolia/firestore-algolia-search/)\nprovides a synchronous hook to [call a user-supplied transform function](https://github.com/algolia/firestore-algolia-search/blob/34592d513eac22691d76917874a6466032976f67/functions/src/transform.ts)\nprior to writing to Algolia."]]