스키마 튜토리얼
스키마 튜토리얼레슨 18: 웹훅을 통한 외부 서비스와의 연동

레슨 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&param[]=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
  }
}