푸시 알림 설정 및 수신

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
  • 알림을 트리거한 이벤트의 유형
  • messageIdpublishTime와 같이 Cloud Pub/Sub에서 설정한 다른 필드

알림은 자세한 양식 또는 응답 데이터를 포함하지 않습니다. 각 알림이 수신된 후 새로운 데이터를 가져오려면 별도의 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 가격 책정을 검토하고 Play Console 프로젝트에 결제를 사용 설정합니다.
  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
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';

async function runSample(query) {
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });
  const forms = google.forms({
    version: 'v1',
    auth: authClient,
  });
  const watchRequest = {
    watch: {
      target: {
        topic: {
          topicName: 'projects/<YOUR_TOPIC_PATH>',
        },
      },
      eventType: 'RESPONSES',
    },
  };
  const res = await forms.forms.watches.create({
    formId: formID,
    requestBody: watchRequest,
  });
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

시계 삭제

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

form/snippets/delete_watch.js
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';
const watchID = '<YOUR_FORMS_WATCH_ID>';

async function runSample(query) {
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });
  const forms = google.forms({
    version: 'v1',
    auth: authClient,
  });
  const res = await forms.forms.watches.delete({
    formId: formID,
    watchId: watchID,
  });
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

승인

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

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

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

양식의 감시점 나열

Python

form/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

form/snippets/list_watches.js
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';

async function runSample(query) {
  const auth = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/forms.responses.readonly',
  });
  const forms = google.forms({
    version: 'v1',
    auth: auth,
  });
  const res = await forms.forms.watches.list({formId: formID});
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

시계 갱신

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

form/snippets/renew_watch.js
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';
const watchID = '<YOUR_FORMS_WATCH_ID>';

async function runSample(query) {
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });
  const forms = google.forms({
    version: 'v1',
    auth: authClient,
  });
  const res = await forms.forms.watches.renew({
    formId: formID,
    watchId: watchID,
  });
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

사용 제한

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

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

한도

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

  • 총 조회수 최대 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()을 호출합니다.