스타터 빌드

이 문서에서는 이벤트가 발생할 때 Google Workspace Studio에 알리고 플로우 실행을 시작할 수 있는 시작 조건을 빌드하는 방법을 설명합니다. API에서 시작 조건은 workflowTriggers라고 합니다.

시작 조건은 플로우를 시작하는 반면 단계는 플로우를 포함하는 태스크 시퀀스의 단일 태스크입니다. 시작 조건을 빌드하면 사용자가 앱 또는 서비스의 실시간 이벤트에 반응하는 자동화된 플로우를 설정할 수 있습니다.

시작 조건을 빌드하려면 부가기능 매니페스트 파일에서 시작 조건을 선언하고 Google Apps Script에서 수명 주기 콜백을 구현하거나 Google Workspace Studio API 엔드포인트에 페이로드를 게시하여 시작 조건을 실행합니다.

기본 요건

API 사용 설정

Google Cloud 프로젝트에서 Google Workspace Studio API를 사용 설정합니다.

API 사용 설정

표준 Google Cloud 프로젝트로 전환 (Google Apps Script만 해당)

기본적으로 Apps Script 프로젝트는 API 사용 설정 또는 OAuth 동의 화면 구성을 지원하지 않는 기본 Cloud 프로젝트를 사용합니다. Apps Script로 시작 조건을 빌드하는 경우 표준 Google Cloud 프로젝트로 전환해야 합니다.

  1. Apps Script 편집기에서 프로젝트 설정 을 클릭합니다.
  2. Google Cloud Platform (GCP) 프로젝트에서 프로젝트 변경을 클릭합니다.
  3. 표준 Cloud 프로젝트 번호를 입력하고 프로젝트 설정 을 클릭합니다.
  4. 프로젝트를 전환한 후 편집기에서 함수를 실행하고 요청된 권한을 부여하여 스크립트를 다시 승인합니다.

자세한 내용은 표준 Google Cloud 프로젝트로 전환을 참고하세요.

OAuth 동의 화면 구성

Workspace Studio API 엔드포인트와 통신하려면 앱 또는 서비스가 OAuth 2.0을 사용하여 인증해야 합니다. 프로젝트의 OAuth 동의 화면을 구성하고 다음 전용 OAuth 범위를 추가합니다.

https://www.googleapis.com/auth/workspace.studio.trigger

이 범위는 앱이 Workspace Studio API를 호출하고 사용자가 해당 시작 조건에 대해 구성한 플로우를 실행하도록 승인합니다.

자세한 내용은 OAuth 동의 화면 구성 및 범위 선택을 참고하세요.

오프라인 사용 및 갱신 토큰

시작 조건은 사용자가 플로우를 구성한 후 몇 시간, 며칠 또는 몇 달 후에 발생할 수 있는 외부 서비스에서 이벤트가 발생할 때 Workspace Studio에 비동기식으로 알리므로 서비스는 API 엔드포인트를 호출할 때 유효한 OAuth 2.0 액세스 토큰을 제공해야 합니다.

시작 조건 구성 또는 수명 주기 콜백 요청과 같이 부가기능 이벤트 객체에서 Google이 제공하는 액세스 토큰은 수명이 짧고 1시간 동안만 유효합니다. 향후 시작 조건 이벤트를 비동기식으로 실행하는 데는 충분하지 않습니다. 시간이 지남에 따라 Workspace Studio API를 호출하려면 서비스에 오프라인 갱신 토큰 이 있어야 주문형으로 새 액세스 토큰을 생성할 수 있습니다.

승인을 처리하고 갱신 토큰을 가져오는 방법은 부가기능 런타임에 따라 다릅니다.

  • HTTP 부가기능 (대체 런타임): HTTP 부가기능의 경우 백엔드 서비스는 오프라인 사용을 요청(access_type=offline)하고 갱신 토큰을 수신하기 위해 기본 제공 부가기능 승인과 독립적인 별도의 OAuth 2.0 승인 플로우를 구현해야 합니다.

    사용자가 Workspace Studio에서 시작 조건을 구성할 때 로그인 또는 승인 카드를 표시하여 사용자에게 이 연결을 승인하도록 요청할 수 있습니다. 승인 카드를 반환하고 OAuth 플로우를 처리하는 방법에 관한 자세한 내용은 Google Workspace 부가기능을 서드 파티 서비스에 연결하기 (Google Workspace를 연결하는 서드 파티 서비스로 취급)를 참고하세요.

    백엔드 서비스는 갱신 토큰을 안전하게 저장해야 합니다 (예: 서비스의 데이터베이스에서 triggerId와 함께). 또한 이벤트가 발생할 때마다 이를 사용하여 시작 조건의 notifyUri 또는 triggers.fire API 엔드포인트에 요청을 보내기 전에 새 액세스 토큰을 가져와야 합니다.

  • Google Apps Script 부가기능: 예약된 (시간 기반) 트리거를 사용하여 이벤트를 폴링하는 Google Apps Script 기반 부가기능은 독립적인 OAuth 플로우 구현을 건너뛸 수 있습니다. 예약된 트리거는 Google Apps Script 런타임 환경 내에서 직접 실행되므로 Google Apps Script는 매니페스트에 선언된 범위를 사용하여 OAuth 토큰을 자동으로 관리하고 갱신합니다. ScriptApp.getOAuthToken()을 사용하여 OAuth 액세스 토큰을 가져올 수 있습니다.

매니페스트 파일에서 시작 조건 정의

시작 조건을 정의하려면 addOns.studio.flows.workflowElements 블록 내의 부가기능 매니페스트 파일 (appsscript.json)에 추가합니다. 이 구성은 Apps Script 및 HTTP 런타임(대체 런타임) 모두에 필요합니다. 단계를 정의할 때 사용되는 workflowAction 대신 요소를 workflowTrigger로 구성합니다. 자세한 내용은 Google Workspace 부가기능의 매니페스트 구조를 참고하세요.

workflowTrigger 블록 내에서 다음을 지정합니다.

  • inputs: 사용자가 구성 카드에서 구성하는 변수 (예: 프로젝트 이름, 리소스 필터 등)
  • outputs: 시작 조건이 플로우의 다운스트림 단계로 반환할 수 있는 변수
  • onConfigFunction: 사용자 구성 인터페이스를 표시하는 콜백 함수의 이름
  • onManageFunction: 시작 조건 정기 결제 생성 및 삭제를 처리하기 위해 Google에서 호출하는 콜백 함수의 이름

다음 코드 샘플은 이벤트 시작 조건의 매니페스트 정의 예를 보여줍니다.

JSON

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/workspace.studio.trigger"
  ],
  "urlFetchWhitelist": [
    "https://workspacestudio.googleapis.com/"
  ],
  "addOns": {
    "common": {
      "name": "Trigger App",
      "logoUrl": "https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/start/default/24px.svg",
      "useLocaleFromApp": true
    },
    "studio": {
      "flows": {
        "workflowElements": [
          {
            "id": "triggerDemo",
            "state": "ACTIVE",
            "name": "Event Trigger",
            "description": "Fires when a event occurs in the app.",
            "workflowTrigger": {
              "inputs": [
                {
                  "id": "projectId",
                  "description": "The project identifier to watch.",
                  "cardinality": "SINGLE",
                  "dataType": {
                    "basicType": "STRING"
                  }
                }
              ],
              "outputs": [
                {
                  "id": "eventName",
                  "description": "The name of the triggered event.",
                  "cardinality": "SINGLE",
                  "dataType": {
                    "basicType": "STRING"
                  }
                },
                {
                  "id": "eventMessage",
                  "description": "Detailed event message description.",
                  "cardinality": "SINGLE",
                  "dataType": {
                    "basicType": "STRING"
                  }
                }
              ],
              "onConfigFunction": "onConfigTrigger",
              "onManageFunction": "onManageTrigger"
            }
          }
        ]
      }
    }
  }
}

시작 조건 정기 결제 수명 주기 처리

사용자가 시작 조건을 포함하는 플로우를 구성하고 사용 설정하거나 플로우가 사용 중지 또는 삭제된 경우 Google은 매니페스트에 선언된 onManageFunction 콜백 함수를 사용하여 부가기능을 호출합니다.

수명 주기 이벤트 객체

콜백 함수는 작업 컨텍스트가 포함된 WorkflowEventObject를 수신합니다. 시작 조건의 경우 다음이 포함됩니다.

  • 트리거 생성 (event.workflow.triggerCreation): 플로우가 게시되거나 사용 설정될 때 실행됩니다.

    • triggerId: 이 시작 조건 등록 인스턴스를 식별하는 고유한 UUID 문자열입니다.

    • notifyUri: 이 시작 조건 등록과 연결된 고유한 REST API 엔드포인트 URL입니다 (예: https://workspacestudio.googleapis.com/v1/triggers/YOUR_TRIGGER_ID:fire). 이벤트 페이로드에서 notifyUri가 생략된 경우 triggerId를 사용하여 엔드포인트 URL을 직접 구성합니다. https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire

    • inputs: 사용자가 카드에서 구성한 변수 입력입니다.

  • 트리거 삭제 (event.workflow.triggerDeletion): 시작 조건이 플로우에서 삭제되거나 전체 플로우가 사용 중지 또는 삭제될 때 실행됩니다.

    • triggerId: 정리할 정기 결제 인스턴스의 고유한 UUID 문자열입니다.

대체 런타임 (HTTP API) 정기 결제 수명 주기

대체 런타임을 사용하여 빌드된 부가기능의 경우 정기 결제 수명 주기 알림은 onManageFunction 콜백 함수에서 지정한 작업 이름으로 부가기능의 구성된 HTTP 엔드포인트 URL에 대한 HTTP POST 요청을 사용하여 전달됩니다. 페이로드는 WorkflowEventObject의 JSON 표현과 일치합니다.

대체 런타임에 관한 자세한 내용은 HTTP 엔드포인트를 사용하여 Google Workspace 부가기능 빌드를 참고하세요.

Apps Script에서 수명 주기 콜백 구현

다음 Apps Script 예는 사용자 인터페이스 카드를 구성하고, onManageTrigger를 사용하여 정기 결제 수명 주기 이벤트를 처리하고, 이벤트가 발생할 때 시작 조건 요청을 Google에 다시 실행하는 방법을 보여줍니다.

Apps Script

/**
 * Generates and returns the user configuration card to collect inputs.
 */
function onConfigTrigger() {
  const projectInput = CardService.newTextInput()
    .setFieldName("projectId")
    .setTitle("Project ID")
    .setHint("Enter the project identifier to watch");

  const section = CardService.newCardSection()
    .setHeader("Configure Event Trigger")
    .addWidget(projectInput);

  const card = CardService.newCardBuilder()
    .addSection(section)
    .build();

  return card;
}

/**
 * Handles subscription lifecycle events sent from Google Workspace Studio.
 *
 * @param {Object} event The Workspace Studio event object.
 */
function onManageTrigger(event) {
  const triggerCreation = event.workflow.triggerCreation;
  const triggerDeletion = event.workflow.triggerDeletion;

  if (triggerCreation) {
    const triggerId = triggerCreation.triggerId;
    // Fall back to constructing the endpoint URL if notifyUri is omitted.
    const notifyUri = triggerCreation.notifyUri ||
        ("https://workspacestudio.googleapis.com/v1/triggers/" +
         triggerId + ":fire");
    const inputs = triggerCreation.inputs;

    // Extract input values configured by the user.
    const projectId = inputs["projectId"].stringValues[0];

    // Tip: In Apps Script, use PropertiesService to persist triggerId and notifyUri:
    // const props = PropertiesService.getUserProperties();
    // props.setProperty("notifyUri", notifyUri);
    // props.setProperty("triggerId", triggerId);
    //
    // TODO: Save triggerId, notifyUri, and projectId in your database/service.
    // Your backend service listens for events related to 'projectId'
    // and calls notifyUri when those events occur.
    console.log("Trigger subscription created: " + triggerId +
                ", Notify URI: " + notifyUri +
                ", Match Project: " + projectId);

  } else if (triggerDeletion) {
    const triggerId = triggerDeletion.triggerId;

    // In Apps Script, clean up stored properties:
    // PropertiesService.getUserProperties().deleteProperty("notifyUri");
    // PropertiesService.getUserProperties().deleteProperty("triggerId");
    //
    // TODO: Remove references to triggerId from your database and stop
    // sending future event notifications to the associated notifyUri.
    console.log("Trigger subscription deleted: " + triggerId);
  }
}

/**
 * Mock function showing how your backend service fires the trigger.
 * This logic runs on your service when a watched event occurs.
 *
 * @param {string} notifyUri The stored notifyUri associated with the trigger.
 * @param {string} triggerId The stored triggerId.
 * @param {string} [userAccessToken] The OAuth 2.0 access token for the user.
 *     In Apps Script, defaults to ScriptApp.getOAuthToken().
 */
function simulateEventFire(notifyUri, triggerId, userAccessToken) {
  // In Apps Script, obtain the OAuth token using ScriptApp.getOAuthToken().
  const token = userAccessToken || ScriptApp.getOAuthToken();
  // A unique UUID version 4 is recommended as the requestId for idempotency.
  const requestId = Utilities.getUuid();

  const payload = {
    "name": "triggers/" + triggerId,
    "outputs": {
      "eventName": { "stringValues": ["EventOccurred"] },
      "eventMessage": { "stringValues": ["Hello from the service!"] }
    },
    "requestId": requestId
  };

  const options = {
    "method": "POST",
    "contentType": "application/json",
    "headers": {
      "Authorization": "Bearer " + token
    },
    "payload": JSON.stringify(payload),
    "muteHttpExceptions": true
  };

  const response = UrlFetchApp.fetch(notifyUri, options);
  const responseCode = response.getResponseCode();

  if (responseCode === 200) {
    console.log("Trigger successfully fired!");
  } else if (responseCode === 404) {
    // 404 means the trigger registration is invalid or deleted.
    console.log("Trigger not found. Stop sending events for this trigger.");
    // TODO: Clean up the trigger from your backend database.
  } else if (responseCode === 429 || responseCode >= 500) {
    console.log("Temporary error (" + responseCode + "). Retry using exponential backoff.");
  } else {
    console.log("Failed to fire trigger. HTTP Code: " + responseCode + " - " + response.getContentText());
  }
}

Workspace Studio API 사용

Workspace Studio API(workspacestudio.googleapis.com)를 사용하여 시작 조건 이벤트를 Google에 프로그래매틱 방식으로 알릴 수 있습니다.

엔드포인트는 기본 경로 https://workspacestudio.googleapis.com/v1 아래에 있습니다.

시작 조건 이벤트 알림

triggers.fire 메서드를 사용하여 시작 조건을 실행하여 플로우 실행을 시작합니다.

  • HTTP 메서드: POST
  • 경로: /v1/triggers/{triggerId}:fire ({triggerId}는 트리거 정기 결제 생성 중에 가져온 고유 식별자)
  • OAuth 범위: https://www.googleapis.com/auth/workspace.studio.trigger

다음 코드 샘플은 요청에서 시작 조건을 실행하는 방법을 보여줍니다.

요청

{
  "name": "triggers/TRIGGER_ID",
  "outputs": {
    "eventName": {
      "stringValues": [
        "EventOccurred"
      ]
    },
    "eventMessage": {
      "stringValues": [
        "Hello from the service!"
      ]
    }
  },
  "log": {
    "textFormatElements": [
      {
        "text": "An event occurred in the app."
      }
    ]
  },
  "requestId": "UNIQUE_REQUEST_ID"
}
  • name (문자열, 필수): 시작 조건의 리소스 이름이며 triggers/{triggerId} 형식입니다.
  • outputs (맵, 선택사항): 이벤트 데이터를 나타내는 시작 조건 출력 변수의 맵입니다. 각 값은 유형이 지정된 목록(예: stringValues, booleanValues, integerValues)을 지원하는 VariableData 객체입니다.
  • log (객체, 선택사항): Workspace Studio 실행 활동 로그에 표시되는 TextFormat 마크업 표현입니다.
  • requestId (문자열, 선택사항): 재시도 시 API 멱등성을 보장하기 위한 최대 36개의 ASCII 문자로 구성된 고유 식별자 (UUID v4 권장)입니다.

응답

응답은 성공 시 빈 JSON 객체 {}를 반환합니다.

Workspace Studio API 할당량

workspacestudio.googleapis.com 서비스로 전송되는 트래픽은 시스템 과부하를 방지하고, 리소스의 공정한 사용을 장려하며, 전반적인 Google Workspace 성능을 보호하기 위해 제한됩니다.

다음 할당량이 적용됩니다.

할당량 유형 Quota
프로젝트별 분당 시작 조건 요청 1,000개
사용자별 분당 시작 조건 요청 100개

할당량 유형은 다음과 같습니다.

  • 프로젝트별 분당: 시작 조건을 실행하는 모든 사용자를 대상으로 단일 개발자의 Google Cloud 프로젝트에서 실행되는 시작 조건 이벤트의 누적 수를 분당 1,000개 요청으로 제한합니다.
  • 사용자별 분당: 지정된 Cloud 프로젝트에서 단일 최종 사용자의 누적 시작 조건 호출을 분당 100개 요청으로 제한합니다.

시간 기반 할당량 오류 처리

이러한 할당량을 초과하면 API는 비율 할당량이 초과되었음을 나타내는 HTTP 429 Too Many Requests (또는 429 Resource Exhausted) 오류 코드를 반환합니다.

이러한 오류를 해결하려면 코드가 예외를 포착하고 잘린 지수 백오프 전략을 사용해야 합니다. 지수 백오프는 여러 클라이언트가 동시에 동기화되고 재시도되는 것을 방지하기 위해 무작위 지터 (각 반복에서 무작위 지연 시간 재계산)를 포함하여 시도 간에 점차 더 긴 지연 시간을 사용하여 실패한 요청을 재시도합니다.

  1. Workspace Studio API에 요청을 전송합니다.
  2. 429 오류로 요청이 실패하면 1 second + random_number_milliseconds를 기다린 후 재시도합니다.
  3. 다시 실패하면 2 seconds + random_number_milliseconds를 기다린 후 재시도합니다.
  4. 다시 실패하면 4 seconds + random_number_milliseconds를 기다린 후 재시도합니다.
  5. 이 루프를 계속하여 지연 시간을 maximum_backoff 임계값(일반적으로 32 또는 64초)까지 두 배로 늘립니다.
  6. 최대 백오프 기간에 도달하면 최대 재시도 한도에 도달할 때까지 해당 상수 지연 시간을 사용하여 재시도한 후 중지하고 오류를 로깅합니다.

권장사항

시작 조건을 설계하고 구현할 때는 다음 권장사항을 고려하세요.

일괄 처리 목록 대신 단일 이벤트 내보내기

항목의 일괄 처리 또는 목록이 포함된 단일 이벤트를 내보내는 대신 각 고유 발생(예: 업데이트된 단일 레코드, 게시된 새 메시지 또는 할당된 태스크)에 대해 개별 이벤트를 내보내도록 시작 조건을 설계합니다.

  • 기본 제공 시작 조건과의 일관성: Workspace Studio에서 기본 제공 Google Workspace 시작 조건 (예: Gmail에서 이메일 수신 또는 Google Chat에서 사용자가 스페이스에 참여)은 단일 이벤트에서 트리거됩니다. 단일 항목 이벤트를 내보내는 것은 이 동작과 일치하며 모든 시작 조건에서 사용자에게 일관되고 예측 가능한 환경을 제공합니다.
  • 더 간단한 플로우 구성: 플로우의 다운스트림 단계는 일반적으로 한 번에 하나의 항목을 처리합니다. 단일 항목 이벤트를 내보내면 사용자가 배열을 반복하거나 목록을 파싱하는 복잡한 단계를 추가하지 않고도 변수를 직접 매핑할 수 있습니다.
  • 폴링 및 일괄 처리 변경사항 개별적으로 처리: 백엔드 서비스가 외부 API를 폴링하고 단일 폴링 간격 동안 여러 변경된 항목을 감지하는 경우 항목을 하나의 일괄 처리 이벤트로 번들링하는 대신 각 항목에 대해 개별 시작 조건 이벤트를 실행합니다.
  • 이벤트 비율 및 할당량 관리: 변경된 항목 여러 개에 대해 개별 이벤트를 실행하면 요청이 갑자기 폭주할 수 있으므로 서비스가 Workspace Studio API 할당량(예: 사용자당 분당 100개 요청 한도) 내에 유지되도록 합니다. 폴링 주기가 많은 항목 (예: 변경된 레코드 100개 초과)을 생성하는 경우 429 Too Many Requests 오류를 방지하기 위해 시간이 지남에 따라 이벤트 디스패치를 페이싱하거나 제한합니다.

핵심 동작 및 특이 사례

시작 조건을 통합할 때 개발자는 특정 오류 동작 및 런타임 기능을 처리해야 합니다.

  • 테스트 실행 지원 없음: Workspace Studio는 시작 조건의 테스트 실행을 지원하지 않습니다.
  • 멱등성 및 재생 방지: 엄격하게 요구되지는 않지만 HTTP 또는 Apps Script 페이로드에 고유한 requestId (예: UUID)를 포함해야 합니다. requestId를 제공하면 API가 중복 알림을 감지하고 무시하여 멱등성을 보장하므로 단일 이벤트에 대해 플로우가 여러 번 실행되는 것을 방지할 수 있습니다.
  • 사용 중지 및 다시 사용 설정된 플로우: 시작 조건을 포함하는 플로우가 Workspace Studio에서 사용 중지되면 Google은 triggerDeletion 수명 주기 이벤트를 onManageFunction 콜백으로 보냅니다. 또한 연결된 FireTrigger 메서드에 대한 모든 호출은 404 Not Found 오류 반환 코드 (Requested entity was not found.)를 반환합니다. 서비스는 해당 시작 조건 인스턴스 ID에 대한 향후 활동 알림 전달을 중지하여 404 오류에 반응해야 합니다.

    사용자가 나중에 플로우를 다시 사용 설정하면 Google은 새 triggerId 및 notifyUri가 포함된 새 triggerCreation 이벤트로 onManageFunction 콜백을 호출하여 새 정기 결제 수명 주기를 시작합니다. 이전 triggerId는 영구적으로 사용 중지되며 다시 활성화되지 않으므로 서비스는 이전 트리거 인스턴스가 다시 사용 설정되었는지 폴링하거나 확인해서는 안 됩니다. 자세한 내용은 시작 조건 정기 결제 수명 주기 처리를 참고하세요.

  • 멱등성 정기 결제 삭제: onManageFunction 콜백 함수는 Google의 시작 조건 삭제 요청을 멱등성으로 처리해야 합니다. Google이 동일한 triggerId에 대해 삭제 후크를 여러 번 호출하는 경우 (예: 일시적인 연결 손실로 인한 재시도 중) 함수는 성공적으로 반환되어야 합니다.

  • 플로우 할당량: Workspace Studio API 할당량 외에도 사용자 플로우에는 추가 내부 할당량 관리가 적용됩니다. 고빈도 루프 또는 과도한 이벤트 볼륨은 안전 임계값을 초과하여 플로우가 자동으로 사용 중지될 수 있습니다.

  • 트리거 실행 시 권한 거부 (403): triggers.fire 엔드포인트 호출이 403 PERMISSION_DENIED 오류 상태로 SERVICE_DISABLED를 반환하는 경우 다음을 확인합니다.

    • Google Cloud 프로젝트에서 Google Workspace Studio API가 사용 설정되어 있습니다.
    • Google Apps Script를 사용하는 경우 프로젝트가 기본 Apps Script Cloud 프로젝트가 아닌 표준 Cloud 프로젝트에 연결되어 있습니다.
    • OAuth 액세스 토큰에 https://www.googleapis.com/auth/workspace.studio.trigger 범위가 포함되어 있습니다.