푸시 알림 설정 및 수신

Watches 컬렉션의 메서드를 사용하여 양식의 데이터가 변경될 때 알림을 받을 수 있습니다. 이 페이지에서는 푸시 알림을 설정하고 수신하는 방법에 관한 개념적 개요와 안내를 제공합니다.

개요

Google Forms API 푸시 알림 기능을 사용하면 애플리케이션이 양식의 데이터가 변경될 때 알림을 구독할 수 있습니다. 알림은 일반적으로 변경 후 몇 분 이내에 Cloud Pub/Sub 주제로 전송됩니다.

푸시 알림을 받으려면 Cloud Pub/Sub 주제를 설정하고 적절한 이벤트 유형의 감시를 만들 때 해당 주제의 이름을 제공해야 합니다.

다음은 이 문서에 사용된 주요 개념의 정의입니다.

  • 타겟은 알림이 전송되는 위치입니다. 지원되는 유일한 타겟은 Cloud Pub/Sub 주제입니다.
  • 이벤트 유형은 서드 파티 애플리케이션이 구독할 수 있는 알림 카테고리입니다.
  • 감시는 특정 양식의 특정 이벤트 유형에 대한 알림을 타겟에 전달하도록 Forms API에 지시하는 것입니다.

특정 양식에서 이벤트 유형에 대한 감시를 만들면 감시가 만료될 때까지 해당 양식의 이벤트에서 감시의 타겟 (Cloud Pub/Sub 주제)으로 알림이 전송됩니다. 워치는 일주일 동안 지속되지만 만료되기 전에 언제든지 watches.renew()에 요청하여 연장할 수 있습니다.

Cloud Pub/Sub 주제는 사용자가 제공한 사용자 인증 정보로 볼 수 있는 양식에 대한 알림만 수신합니다. 예를 들어 사용자가 애플리케이션의 권한을 취소하거나 모니터링 중인 양식에 대한 수정 액세스 권한을 잃으면 알림이 더 이상 전송되지 않습니다.

사용 가능한 이벤트 유형

Google Forms API는 현재 다음과 같은 두 가지 카테고리의 이벤트를 제공합니다.

  • EventType.SCHEMA: 양식의 콘텐츠 및 설정에 대한 수정사항을 알립니다.
  • EventType.RESPONSES: 양식 응답 (신규 및 업데이트된 응답 모두)이 제출될 때 알림을 보냅니다.

알림 응답

알림은 JSON으로 인코딩되며 다음을 포함합니다.

  • 트리거 양식의 ID
  • 트리거링 시계의 ID
  • 알림을 트리거한 이벤트 유형
  • Cloud Pub/Sub에서 설정한 기타 필드(예: messageIdpublishTime)

알림에는 자세한 양식 또는 응답 데이터가 포함되어 있지 않습니다. 각 알림을 수신한 후에는 최신 데이터를 가져오기 위해 별도의 API 호출이 필요합니다. 이 작업을 실행하는 방법은 권장 사용법을 참고하세요.

다음 스니펫은 스키마 변경에 관한 샘플 알림을 보여줍니다.

{
  "attributes": {
    "eventType": "SCHEMA",
    "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
    "watchId": "892515d1-a902-444f-a2fe-42b718fe8159"
  },
  "messageId": "767437830649",
  "publishTime": "2021-03-31T01:34:08.053Z"
}

다음 스니펫은 새 응답에 대한 샘플 알림을 보여줍니다.

{
  "attributes": {
    "eventType": "RESPONSES",
    "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
    "watchId": "5d7e5690-b1ff-41ce-8afb-b469912efd7d"
  },
  "messageId": "767467004397",
  "publishTime": "2021-03-31T01:43:57.285Z"
}

Cloud Pub/Sub 주제 설정

알림이 Cloud Pub/Sub 주제로 전송됩니다. Cloud Pub/Sub에서 웹훅을 통해 또는 구독 엔드포인트를 폴링하여 알림을 받을 수 있습니다.

Cloud Pub/Sub 주제를 설정하려면 다음 단계를 따르세요.

  1. Cloud Pub/Sub 기본 요건을 완료합니다.
  2. Cloud Pub/Sub 클라이언트 설정
  3. Cloud Pub/Sub 가격 책정을 검토하고 개발자 콘솔 프로젝트에 결제를 사용 설정합니다.
  4. 다음 세 가지 방법 중 하나로 Cloud Pub/Sub 주제를 만듭니다.

  5. Cloud Pub/Sub에서 구독을 만들어 Cloud Pub/Sub에 알림을 전송하는 방법을 알려줍니다.

  6. 마지막으로 주제를 타겟팅하는 감시를 만들기 전에 Forms 알림 서비스 계정(forms-notifications@system.gserviceaccount.com)에 주제에 게시할 수 있는 권한을 부여해야 합니다.

시계 만들기

Forms API 푸시 알림 서비스 계정이 게시할 수 있는 주제가 있으면 watches.create() 메서드를 사용하여 알림을 만들 수 있습니다. 이 메서드는 제공된 Cloud Pub/Sub 주제에 푸시 알림 서비스 계정이 도달할 수 있는지 확인하고 주제에 도달할 수 없는 경우 실패합니다. 예를 들어 주제가 없거나 해당 주제에 게시 권한을 부여하지 않은 경우입니다.

Python

forms/snippets/create_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)

service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

watch = {
    "watch": {
        "target": {"topic": {"topicName": "<YOUR_TOPIC_PATH>"}},
        "eventType": "RESPONSES",
    }
}

form_id = "<YOUR_FORM_ID>"

# Print JSON response after form watch creation
result = service.forms().watches().create(formId=form_id, body=watch).execute()
print(result)

Node.js

forms/snippets/create_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Creates a watch on a form to get notifications for new responses.
 */
async function createWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // The request body to create a watch.
  const watchRequest = {
    watch: {
      target: {
        topic: {
          // TODO: Replace with a valid Cloud Pub/Sub topic name.
          topicName: 'projects/<YOUR_TOPIC_PATH>',
        },
      },
      // The event type to watch for. 'RESPONSES' is the only supported type.
      eventType: 'RESPONSES',
    },
  };

  // Send the request to create the watch.
  const result = await formsClient.forms.watches.create({
    formId: formID,
    requestBody: watchRequest,
  });

  console.log(result.data);
  return result.data;
}

시계 삭제

Python

forms/snippets/delete_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after deleting a form watch
result = (
    service.forms().watches().delete(formId=form_id, watchId=watch_id).execute()
)
print(result)

Node.js

forms/snippets/delete_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Deletes a watch from a form.
 */
async function deleteWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to delete the watch.
  const result = await formsClient.forms.watches.delete({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}

승인

Forms API에 대한 모든 호출과 마찬가지로 watches.create()에 대한 호출은 승인 토큰으로 승인되어야 합니다. 토큰에는 알림이 전송되는 데이터에 대한 읽기 액세스 권한을 부여하는 범위가 포함되어야 합니다.

  • 스키마 변경의 경우 forms.get()을 사용하여 forms에 대한 읽기 액세스 권한을 부여하는 모든 범위가 이에 해당합니다.
  • 응답의 경우 forms.responses.list()를 사용하는 등 양식 응답에 대한 읽기 액세스 권한을 부여하는 모든 범위가 이에 해당합니다.

알림이 전송되려면 애플리케이션이 필요한 범위가 있는 승인된 사용자의 OAuth 부여를 유지해야 합니다. 사용자가 애플리케이션을 연결 해제하면 알림이 중단되고 시계가 오류와 함께 정지될 수 있습니다. 승인을 다시 얻은 후 알림을 다시 시작하려면 워치 갱신하기를 참고하세요.

양식의 워치 목록 표시

Python

forms/snippets/list_watches.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"

# Print JSON list of form watches
result = service.forms().watches().list(formId=form_id).execute()
print(result)

Node.js

forms/snippets/list_watches.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Lists the watches for a given form.
 */
async function listWatches() {
  // Authenticate with Google and get an authorized client.
  const auth = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/forms.responses.readonly',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth,
  });

  // Get the list of watches for the form.
  const result = await formsClient.forms.watches.list({
    formId: formID,
  });

  console.log(result.data);
  return result.data;
}

시계 갱신

Python

forms/snippets/renew_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after renewing a form watch
result = (
    service.forms().watches().renew(formId=form_id, watchId=watch_id).execute()
)
print(result)

Node.js

forms/snippets/renew_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Renews a watch on a form.
 */
async function renewWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to renew the watch.
  const result = await formsClient.forms.watches.renew({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}

제한

알림이 제한됩니다. 각 시계는 30초마다 최대 1개의 알림을 수신할 수 있습니다. 이 빈도 기준은 변경될 수 있습니다.

스팸 방지 기능으로 인해 단일 알림이 여러 이벤트에 해당할 수 있습니다. 즉, 알림은 마지막 알림 이후 하나 이상의 이벤트가 발생했음을 나타냅니다.

한도

언제든지 특정 양식 및 이벤트 유형에 대해 각 Cloud 콘솔 프로젝트는 다음을 가질 수 있습니다.

  • 총 20개까지 시계
  • 최종 사용자당 최대 1개의 시계

또한 언제든지 각 양식은 모든 Cloud 콘솔 프로젝트에서 이벤트 유형당 총 50개의 모니터링으로 제한됩니다.

시계는 해당 사용자의 사용자 인증 정보로 생성되거나 갱신될 때 최종 사용자와 연결됩니다. 연결된 최종 사용자가 양식에 대한 액세스 권한을 잃거나 앱의 양식 액세스 권한을 취소하면 시계가 정지됩니다.

안정성

비상 상황을 제외하고 각 이벤트 후에는 각 시계에 최소 한 번 알림이 전송됩니다. 대부분의 경우 이벤트 발생 후 몇 분 이내에 알림이 전송됩니다.

오류

시계의 알림이 지속적으로 전송되지 않으면 시계 상태가 SUSPENDED이 되고 시계의 errorType 필드가 설정됩니다. 정지된 시계의 상태를 ACTIVE로 재설정하고 알림을 다시 시작하려면 시계 갱신하기를 참고하세요.

추천 사용량

  • 단일 Cloud Pub/Sub 주제를 여러 감시의 타겟으로 사용합니다.
  • 주제에 관한 알림을 수신할 때 양식 ID가 알림 페이로드에 포함됩니다. 이벤트 유형과 함께 사용하여 가져올 데이터와 데이터를 가져올 양식을 알 수 있습니다.
  • EventType.RESPONSES 알림 후 업데이트된 데이터를 가져오려면 forms.responses.list()를 호출하세요.
    • 요청의 필터를 timestamp > timestamp_of_the_last_response_you_fetched로 설정합니다.
  • EventType.SCHEMA 알림 후 업데이트된 데이터를 가져오려면 forms.get()을 호출합니다.