스키마 튜토리얼
스키마 튜토리얼레슨 25: 외부 API의 데이터 변환하기

레슨 25: 외부 API의 데이터 변환하기

이 튜토리얼 레슨에서는 외부 API의 응답을 필요한 형태로 변환하는 예시를 소개합니다.

각 항목에 기본값과 추가 속성 부여하기

REST API 엔드포인트 newapi.getpop.org/wp-json/wp/v2/users/?_fields=id,name,url은 사용자 데이터를 반환하지만, 일부 사용자의 url 속성이 비어 있습니다.

[
  {
    "id": 1,
    "name": "leo",
    "url": "https://leoloso.com"
  },
  {
    "id": 7,
    "name": "Test",
    "url": ""
  },
  {
    "id": 2,
    "name": "Theme Demos",
    "url": ""
  }
]

아래 GraphQL 쿼리는 이 응답을 변환합니다.

  • url 속성이 비어 있는 사용자에게 기본 URL을 추가합니다.
  • 각 사용자 항목에 link 속성을 추가합니다 (사용자의 이름과 URL 값을 사용하여 구성).
query {
  # Retrieve data from the external API
  usersWithLinkAndDefaultURL: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/wp/v2/users/?_fields=id,name,url"
    }
  )
    # Set a default URL for users without any
    @underEachArrayItem
      @underJSONObjectProperty(
        by: {
          key: "url"
        }
      )
        @default(
          value: "https://mysite.com"
          condition: IS_EMPTY
        )
 
    # Add a new "link" entry on the JSON object
    @underEachArrayItem(
      affectDirectivesUnderPos: [1, 2, 3, 4],
      passValueOnwardsAs: "userListItem"
    )
      @applyField(
        name: "_objectProperty",
        arguments: {
          object: $userListItem,
          by: {
            key: "name"
          }
        },
        passOnwardsAs: "userName"
      )
      @applyField(
        name: "_objectProperty",
        arguments: {
          object: $userListItem,
          by: {
            key: "url"
          }
        },
        passOnwardsAs: "userURL"
      )
      @applyField(
        name: "_sprintf",
        arguments: {
          string: "<a href=\"%s\">%s</a>",
          values: [$userURL, $userName]
        },
        passOnwardsAs: "userLink"
      )
      @applyField(
        name: "_objectAddEntry",
        arguments: {
          object: $userListItem,
          key: "link",
          value: $userLink
        },
        setResultInResponse: true
      )
}

응답은 다음과 같습니다.

{
  "data": {
    "usersWithLinkAndDefaultURL": [
      {
        "id": 1,
        "name": "leo",
        "url": "https://leoloso.com",
        "link": "<a href=\"https://leoloso.com\">leo</a>"
      },
      {
        "id": 7,
        "name": "Test",
        "url": "https://mysite.com",
        "link": "<a href=\"https://mysite.com\">Test</a>"
      },
      {
        "id": 2,
        "name": "Theme Demos",
        "url": "https://mysite.com",
        "link": "<a href=\"https://mysite.com\">Theme Demos</a>"
      }
    ]
  }
}

컴포저블 디렉티브는 하나 이상의 디렉티브를 중첩할 수 있습니다. 여러 디렉티브를 중첩할 때는 인수 affectDirectivesUnderPos를 사용하여 지정합니다. 이 인수에는 해당 디렉티브에서 중첩된 디렉티브까지의 상대 위치가 포함됩니다.

위의 GraphQL 쿼리에서 디렉티브 @underEachArrayItem(Field Value Iteration and Manipulation 확장이 제공)은 컴포저블 디렉티브입니다. 첫 번째 사용 시에는 하나의 디렉티브만 중첩하므로 인수 affectDirectivesUnderPos를 생략할 수 있습니다.

    @underEachArrayItem
      @underJSONObjectProperty(
        # ...
      )

(참고로, @underJSONObjectProperty도 컴포저블 디렉티브로서 @default 디렉티브를 중첩합니다.)

두 번째 사용 시에는 인수 affectDirectivesUnderPos에 값 [1, 2, 3, 4]를 지정하여 오른쪽에 있는 4개의 디렉티브를 중첩합니다.

    @underEachArrayItem(
      affectDirectivesUnderPos: [1, 2, 3, 4],
      # ...
    )
      @applyField(
        name: "_objectProperty",
        # ...
      )
      @applyField(
        name: "_objectProperty",
        # ...
      )
      @applyField(
        name: "_sprintf",
       # ...
      )
      @applyField(
        name: "_objectAddEntry",
        # ...
      )



🔥 팁:

디렉티브 @applyField(Field on Field 확장이 제공)에는 출력을 보낼 수 있는 두 가지 대상이 있습니다.

  • 인수 passOnwardsAs: "someVariableName"를 지정하면 새 값이 동적 변수 $someVariableName에 할당되어 이후 중첩된 디렉티브에서 참조할 수 있습니다.
      @applyField(
        name: "_objectProperty",
        arguments: {
          object: $userListItem,
          by: {
            key: "name"
          }
        },
        passOnwardsAs: "userName"
      )
  • 인수 setResultInResponse: true를 지정하면 새 값이 필드에 다시 할당되어 응답이 수정됩니다.
      @applyField(
        name: "_objectAddEntry",
        arguments: {
          object: $userListItem,
          key: "link",
          value: $userLink
        },
        setResultInResponse: true
      )

JSON 객체에서 특정 속성 추출하기

REST API 엔드포인트 newapi.getpop.org/wp-json/newsletter/v1/subscriptions는 구독자의 이메일 주소와 언어를 포함한 이메일 구독 데이터 컬렉션을 반환합니다.

[
  {
    "email": "abracadabra@ganga.com",
    "lang": "de"
  },
  {
    "email": "longon@caramanon.com",
    "lang": "es"
  },
  {
    "email": "rancotanto@parabara.com",
    "lang": "en"
  },
  {
    "email": "quezarapadon@quebrulacha.net",
    "lang": "fr"
  },
  {
    "email": "test@test.com",
    "lang": "de"
  },
  {
    "email": "emilanga@pedrola.com",
    "lang": "fr"
  }
]

아래 GraphQL 쿼리는 각 항목에서 email 속성을 추출하여 필드 값으로 대체함으로써 API 응답에서 이메일 주소만 출력합니다.

query {
  emails: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/newsletter/v1/subscriptions"
    }
  )
    @underEachArrayItem(
      passValueOnwardsAs: "userEntry"
    )
      @applyField(
        name: "_objectProperty"
        arguments: {
          object: $userEntry,
          by: {
            key: "email"
          }
        }
        setResultInResponse: true
      )
}

응답은 다음과 같습니다.

{
  "data": {
    "emails": [
      "abracadabra@ganga.com",
      "longon@caramanon.com",
      "rancotanto@parabara.com",
      "quezarapadon@quebrulacha.net",
      "test@test.com",
      "emilanga@pedrola.com"
    ]
  }
}

조건에 따라 필드 값 수정하기

이 예시는 이전 예시를 이어받아 응답의 이메일 주소 형식도 변환합니다.

아래 GraphQL 쿼리는 API 응답에서 이메일 주소를 추출하고, 컴포저블 디렉티브 @if(Conditional Field Manipulation 확장이 제공)를 사용하여 언어가 영어 또는 독일어인 사용자의 이메일 주소를 대문자로 변환합니다.

query {
  # Retrieve data from a REST API endpoint
  userEntries: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/newsletter/v1/subscriptions"
    }
  )
    @remove
 
  emails: _echo(value: $__userEntries)
 
    # Iterate all the entries, passing every entry
    # (under the dynamic variable $userEntry)
    # to each of the next 4 directives
    @underEachArrayItem(
      passValueOnwardsAs: "userEntry"
      affectDirectivesUnderPos: [1, 2, 3, 4]
    )
 
      # Extract property "lang" from the entry
      # via the functionality field `_objectProperty`,
      # and pass it onwards as dynamic variable $userLang
      @applyField(
        name: "_objectProperty"
        arguments: {
          object: $userEntry,
          by: {
            key: "lang"
          }
        }
        passOnwardsAs: "userLang"
      )
 
      # Execute functionality field `_inArray` to find out
      # if $userLang is either "en" or "de", and place the
      # result under dynamic variable $isSpecialLang
      @applyField(
        name: "_inArray"
        arguments: {
          value: $userLang,
          array: ["en", "de"]
        }
        passOnwardsAs: "isSpecialLang"
      )
 
      # Extract property "email" from the entry
      # and set it back as the value for that entry
      @applyField(
        name: "_objectProperty"
        arguments: {
          object: $userEntry,
          by: {
            key: "email"
          }
        }
        setResultInResponse: true
      )
 
      # If $isSpecialLang is `true` then execute
      # directive `@strUpperCase` 
      @if(condition: $isSpecialLang)
        @strUpperCase
}

응답은 다음과 같습니다.

{
  "data": {
    "emails": [
      "ABRACADABRA@GANGA.COM",
      "longon@caramanon.com",
      "RANCOTANTO@PARABARA.COM",
      "quezarapadon@quebrulacha.net",
      "TEST@TEST.COM",
      "emilanga@pedrola.com"
    ]
  }
}

Gato GraphQL에서 조건 로직의 실행은 동적으로 만들 수 있습니다. 쿼리 대상 객체에서 평가된 동적 변수를 @if(condition:)(및 @unless(condition:))에 전달하면 해당 엔티티의 조건에 따라 로직의 실행 여부가 결정됩니다.

이를 통해 다음과 같은 조건을 기반으로 일부 엔티티에 대해서만 동적으로 응답을 수정할 수 있습니다.

  • 게시물에 댓글이 있는가?
  • 댓글에 답글이 있는가?
  • 사용자가 관리자인가?
  • 태그/카테고리가 어떤 게시물에 적용되어 있는가?
  • 기타