레슨 18: 웹훅을 통한 외부 서비스와의 연동
웹훅(Webhook)은 HTTP 기반의 콜백 함수로, 외부 서비스가 특정 이벤트를 알리기 위해 호출하며 데이터 페이로드를 함께 전송합니다. 웹훅을 통해 서로 다른 웹 앱과 서비스가 상호 통신할 수 있습니다.
웹훅을 호출하는 서비스의 예시는 다음과 같습니다:
- GitHub: 저장소에 커밋이 푸시되었을 때
- Dropbox: 문서가 업데이트되었을 때
- WooCommerce: 주문이 추가되었을 때
- Microsoft Teams: 리치 텍스트 메시지를 수신하고 공개 채널에 게시할 때
- ConvertKit: 구독자가 활성화되었을 때
Gato GraphQL을 사용하면 웹훅으로 동작하는 Persisted Query를 생성할 수 있습니다:
- Persisted Query는 고유한 URL로 노출되므로 웹훅의 대상으로 사용할 수 있습니다
- 각 Persisted Query는 특정 웹훅만을 전담하여 처리할 수 있습니다
이 튜토리얼 레슨에서는 ConvertKit과 연동하기 위한 Persisted Query를 생성합니다.
웹훅 문서 확인하기
첫 번째 단계는 해당 웹사이트의 문서를 읽고 페이로드를 통해 어떤 데이터가 전송되는지 파악하는 것입니다.
ConvertKit의 웹훅을 분석하면, 구독자 관련 이벤트는 다음과 같은 JSON 페이로드와 함께 URL로 POST 요청을 전송합니다:
{
"subscriber": {
"id": 1,
"first_name": "John",
"email_address": "John@example.com",
"state": "active",
"created_at": "2018-02-15T19:40:24.913Z",
"fields": {
"My Custom Field": "Value"
}
}
}페이로드에서 데이터 추출하기
요청이 POST로 전송되므로, HTTP 요청의 본문에서 데이터를 추출해야 합니다(이는 HTTP Request via Schema 확장 기능을 통해 지원됩니다).
HTTP 요청이 GET으로 실행되는 경우, Persisted Query는 URL 파라미터에서 직접 데이터 항목을 가져올 수 있습니다.
이 GraphQL 쿼리는 요청의 본문을 가져와 JSON 객체로 변환합니다. 그런 다음 JSON 객체에서 "subscriber.first_name"과 "subscriber.email_address"를 추출하여 동적 변수로 내보냅니다:
query ExtractPayloadData {
body: _httpRequestBody
bodyJSONObject: _strDecodeJSONObject(string: $__body)
subscriberFirstName: _objectProperty(
object: $__bodyJSONObject,
by: { path: "subscriber.first_name" }
)
@export(as: "subscriberFirstName")
subscriberEmail: _objectProperty(
object: $__bodyJSONObject,
by: { path: "subscriber.email_address" }
)
@export(as: "subscriberEmail")
}HTTP Request via Schema 확장 기능을 사용하면 다음 필드를 통해 현재 HTTP 요청의 모든 데이터를 가져올 수 있습니다:
_httpRequestBody: HTTP 요청의 본문_httpRequestClientHost: 클라이언트 호스트_httpRequestClientIP: 클라이언트 IP 주소(서버가 올바르게 설정되지 않은 경우null)_httpRequestCookie: 요청 쿠키 값_httpRequestCookies: 요청 쿠키 목록_httpRequestDomain: 요청된 URL의 도메인_httpRequestFullURL: 요청된 URL(쿼리 파라미터 포함)_httpRequestHasCookie: 요청에 특정 쿠키가 포함되어 있는지 여부_httpRequestHasHeader: 요청에 특정 헤더가 포함되어 있는지 여부_httpRequestHasParam: 요청에 특정 파라미터가 포함되어 있는지 여부_httpRequestHeader: 요청 헤더 값_httpRequestHeaders: 요청 헤더 목록_httpRequestHost: 요청된 URL의 호스트_httpRequestMethod: 요청 메서드_httpRequestStringParam:?param=value형식의 파라미터 값(POST 또는 GET 경유)_httpRequestStringListParam:?param[]=value1¶m[]=value2형식의 파라미터 값(POST 또는 GET 경유)_httpRequestParams: POST 또는 URL 쿼리를 통해 전달된 파라미터_httpRequestProtocol: 요청 프로토콜_httpRequestQuery: 쿼리 파라미터 문자열_httpRequestReferer: 요청 리퍼러_httpRequestRequestTime: 요청 시작 시각의 타임스탬프_httpRequestScheme: 요청된 URL의 스킴_httpRequestServerIP: 서버 IP 주소_httpRequestURL: 요청된 URL(쿼리 파라미터 제외)_httpRequestURLPath: 요청된 URL의 절대 경로(「/」로 시작)_httpRequestUserAgent: 유저 에이전트
데이터로 작업 실행하기
페이로드에서 데이터를 추출한 후, 해당 데이터를 활용하여 특정 작업을 실행할 수 있습니다.
이 GraphQL 쿼리는 subscriber.subscriber_unsubscribe 이벤트를 처리하여, 구독을 취소한 사람에게 피드백을 요청하는 이메일을 발송합니다.
query CreateEmailMessage
@depends(on: "ExtractPayloadData")
{
emailMessageTemplate: _strConvertMarkdownToHTML(
text: """
Hey {$subscriberFirstName}, it's sad to let you go!
Please be welcome to complete [this form](https://forms.gle/FpXNromWAsZYC1zB8) and let us know if there is anything we can do better.
Thanks. Hope to see you back!
"""
)
emailMessage: _strReplaceMultiple(
search: ["{$subscriberFirstName}"],
replaceWith: [$subscriberFirstName],
in: $__emailMessageTemplate
)
@export(as: "emailMessage")
}
mutation SendEmail @depends(on: "CreateEmailMessage") {
_sendEmail(
input: {
to: $subscriberEmail
subject: "Would you like to give us feedback on how we can improve?"
messageAs: {
html: $emailMessage
}
}
) {
status
}
}