스키마 튜토리얼
스키마 튜토리얼레슨 3: 블로그 포스트 복제하기

레슨 3: 블로그 포스트 복제하기

포스트를 복제하는 것은 Gato GraphQL이 사이트의 데이터를 가져오고 조작하여 다시 저장하는 능력의 한 예입니다.

블로그 포스트를 복제하는 GraphQL 쿼리

이 GraphQL 쿼리는 변수 $postId로 지정된 포스트를 복제합니다.

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  authorID: _echo(value: null)
    @export(as: "authorID")
    @remove
 
  categoryIDs: _echo(value: [])
    @export(as: "categoryIDs")
    @remove
 
  featuredImageID: _echo(value: null)
    @export(as: "featuredImageID")
    @remove
 
  tagIDs: _echo(value: [])
    @export(as: "tagIDs")
    @remove
}
 
query GetPostAndExportData($postId: ID!)
  @depends(on: "InitializeDynamicVariables")
{
  post(by: { id : $postId }) {
    # Fields not to be duplicated
    id
    slug
    date
    status
 
    # Fields to be duplicated
    author {
      id @export(as: "authorID")
    }
    categories {
      id @export(as: "categoryIDs", type: LIST)
    }
    rawContent @export(as: "rawContent")
    rawExcerpt @export(as: "excerpt")
    featuredImage {
      id @export(as: "featuredImageID")
    }
    tags {
      id @export(as: "tagIDs", type: LIST)
    }
    rawTitle @export(as: "title")
  }
}
 
mutation DuplicatePost
  @depends(on: "GetPostAndExportData")
{
  createPost(input: {
    status: draft,
    authorBy: {
      id: $authorID
    },
    categoriesBy: {
      ids: $categoryIDs
    },
    contentAs: {
      html: $rawContent
    },
    excerpt: $excerpt
    featuredImageBy: {
      id: $featuredImageID
    },
    tagsBy: {
      ids: $tagIDs
    },
    title: $title
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      # Fields not to be duplicated
      id
      slug
      date
      status
 
      # Fields to be duplicated
      author {
        id
      }
      categories {
        id
      }
      rawContent
      excerpt
      featuredImage {
        id
      }
      tags {
        id
      }
      title
    }
  }
}

단계별: GraphQL 쿼리 작성하기

아래는 쿼리가 작동하는 방식에 대한 상세한 분석입니다.

포스트 데이터 가져오기

이 GraphQL 쿼리는 포스트의 기본 데이터를 가져옵니다.

query GetPost($postId: ID!) {
  post(by: { id : $postId }) {
    # Fields not to be duplicated
    id
    slug
    date
    status
 
    # Fields to be duplicated
    author {
      id
    }
    categories {
      id
    }
    rawContent
    excerpt
    featuredImage {
      id
    }
    tags {
      id
    }
    title
  }
}

쿼리를 실행하면 ($postId 변수를 전달하여), 응답은 다음과 같을 수 있습니다.

{
  "data": {
    "post": {
      "id": 25,
      "slug": "public-or-private-api-mode-for-extra-security",
      "date": "2020-12-12T04:06:52+00:00",
      "author": {
        "id": 2
      },
      "categories": [
        {
          "id": 4
        },
        {
          "id": 3
        },
        {
          "id": 2
        }
      ],
      "rawContent": "<!-- wp:heading -->\n<h2>Verse Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:verse -->\n<pre class=\"wp-block-verse\">Write poetry and other literary expressions honoring all spaces and line-breaks.</pre>\n<!-- /wp:verse -->\n\n<!-- wp:heading -->\n<h2>Table Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:table {\"className\":\"is-style-stripes\"} -->\n<figure class=\"wp-block-table is-style-stripes\"><table><tbody><tr><td>Row 1 Column 1</td><td>Row 1 Column 2</td></tr><tr><td>Row 2 Column 1</td><td>Row 2 Column 2</td></tr><tr><td>Row 3 Column 1</td><td>Row 3 Column 2</td></tr></tbody></table></figure>\n<!-- /wp:table -->\n\n<!-- wp:heading -->\n<h2>Separator Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"/>\n<!-- /wp:separator -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Spacer Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:spacer -->\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n<!-- /wp:spacer -->",
      "excerpt": "Verse Block Write poetry and other literary expressions honoring all spaces and line-breaks. Table Block Row 1 Column 1 Row 1 Column 2 Row 2 Column 1 Row 2 Column 2 Row 3 Column 1 Row 3 Column 2 Separator Block Spacer Block",
      "featuredImage": {
        "id": 362
      },
      "tags": [
        {
          "id": 12
        },
        {
          "id": 7
        }
      ],
      "title": "Public or Private API mode, for extra security"
    }
  }
}

일부 필드는 복제 대상(저자, 제목, 콘텐츠 포함)이고, 그 외 필드(id, slug, 작성일 등)는 복제되지 않는다는 점에 유의하세요.

포스트 복제하기: 첫 번째 방식

Multiple Query Execution 확장 기능을 사용하면, 포스트의 데이터 항목을 내보내고 동일한 GraphQL 문서 내의 다른 query 또는 mutation에 다시 주입할 수 있습니다.

**Multiple Query Execution**을 사용하면 단일 요청 내에서 복잡한 기능을 실행하고, GraphQL 문서를 일련의 논리적/원자적 단위로 분리하여 로직을 더 잘 구성할 수 있습니다.

  • 파이프라인에 추가할 수 있는 작업의 수에는 제한이 없습니다
  • 모든 작업은 두 개 이상의 의존성을 선언할 수 있습니다.
query SomeQuery @depends(on: ["SomePreviousOp", "AnotherPreviousOp"]) {
  # ...
}
  • 모든 작업은 다른 작업에 의존할 수 있으며, 그 작업 자체도 또 다른 작업에 의존할 수 있습니다(이하 동일).
query ExecuteFirst
  # ...
}
query ExecuteSecond @depends(on: ["ExecuteFirst"]) {
  # ...
}
query ExecuteThird @depends(on: ["ExecuteSecond"]) {
  # ...
}
  • 문서 내의 모든 작업을 실행할 수 있습니다.

    • ?operationName=ExecuteThirdExecuteFirst > ExecuteSecond > ExecuteThird를 실행합니다
    • ?operationName=ExecuteSecondExecuteFirst > ExecuteSecond를 실행합니다
    • ?operationName=ExecuteFirstExecuteFirst를 실행합니다
  • @depends가 하나의 작업만 받을 경우, [String] 대신 String을 받을 수 있습니다.

query ExecuteFirst
  # ...
}
query ExecuteSecond @depends(on: "ExecuteFirst") {
  # ...
}
  • querymutation 작업 모두 서로 의존할 수 있습니다.
query GetAndExportData
  # ...
}
mutation MutateData @depends(on: "GetAndExportData") {
  # ...
}
query CountMutatedResults @depends(on: "MutateData") {
  # ...
}
  • 동적 변수는 작업 내에서 선언할 필요가 없습니다
  • @export(type:) 입력을 통해 동적 변수로 내보내는 데이터의 출력 형식을 선택할 수 있습니다.
    • SINGLE (기본값): 단일 필드 값
    • LIST: 여러 리소스의 필드 값을 포함하는 배열
    • DICTIONARY: 여러 리소스의 필드 값을 포함하는 딕셔너리 (키: ${resource ID}, 값: ${field value})

다음 쿼리는 GraphQL 문서 내에 두 작업(GetPostAndExportDataDuplicatePost)으로 구성된 파이프라인을 생성하여 서로 데이터를 공유할 수 있게 합니다.

  • DuplicatePost는 디렉티브 @depends를 통해 GetPostAndExportData를 먼저 실행하도록 지정합니다
  • GetPostAndExportData는 디렉티브 @export를 통해 데이터를 동적 변수로 내보냅니다
  • DuplicatePost는 동적 변수를 읽어 createPost 뮤테이션의 입력으로 사용합니다
query GetPostAndExportData($postId: ID!) {
  post(by: { id : $postId }) {
    # Fields not to be duplicated
    id
    slug
    date
    status
 
    # Fields to be duplicated
    author {
      id @export(as: "authorID")
    }
    categories {
      id @export(as: "categoryIDs", type: LIST)
    }
    rawContent @export(as: "rawContent")
    rawExcerpt @export(as: "excerpt")
    featuredImage {
      id @export(as: "featuredImageID")
    }
    tags {
      id @export(as: "tagIDs", type: LIST)
    }
    rawTitle @export(as: "title")
  }
}
 
mutation DuplicatePost
  @depends(on: "GetPostAndExportData")
{
  createPost(input: {
    status: draft,
    authorBy: {
      id: $authorID
    },
    categoriesBy: {
      ids: $categoryIDs
    },
    contentAs: {
      html: $rawContent
    },
    excerpt: $excerpt
    featuredImageBy: {
      id: $featuredImageID
    },
    tagsBy: {
      ids: $tagIDs
    },
    title: $title
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      # Fields not to be duplicated
      id
      slug
      date
      status
 
      # Fields to be duplicated
      author {
        id
      }
      categories {
        id
      }
      rawContent
      excerpt
      featuredImage {
        id
      }
      tags {
        id
      }
      title
    }
  }
}

응답에서 새 포스트의 필드가 실제로 동일함을 확인할 수 있습니다.

{
  "data": {
    "post": {
      "id": 25,
      "slug": "public-or-private-api-mode-for-extra-security",
      "date": "2020-12-12T04:06:52+00:00",
      "status": "publish",
      "author": {
        "id": 2
      },
      "categories": [
        {
          "id": 4
        },
        {
          "id": 3
        },
        {
          "id": 2
        }
      ],
      "rawContent": "<!-- wp:heading -->\n<h2>Verse Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:verse -->\n<pre class=\"wp-block-verse\">Write poetry and other literary expressions honoring all spaces and line-breaks.</pre>\n<!-- /wp:verse -->\n\n<!-- wp:heading -->\n<h2>Table Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:table {\"className\":\"is-style-stripes\"} -->\n<figure class=\"wp-block-table is-style-stripes\"><table><tbody><tr><td>Row 1 Column 1</td><td>Row 1 Column 2</td></tr><tr><td>Row 2 Column 1</td><td>Row 2 Column 2</td></tr><tr><td>Row 3 Column 1</td><td>Row 3 Column 2</td></tr></tbody></table></figure>\n<!-- /wp:table -->\n\n<!-- wp:heading -->\n<h2>Separator Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"/>\n<!-- /wp:separator -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Spacer Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:spacer -->\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n<!-- /wp:spacer -->",
      "excerpt": "Verse Block Write poetry and other literary expressions honoring all spaces and line-breaks. Table Block Row 1 Column 1 Row 1 Column 2 Row 2 Column 1 Row 2 Column 2 Row 3 Column 1 Row 3 Column 2 Separator Block Spacer Block",
      "featuredImage": {
        "id": 362
      },
      "tags": [
        {
          "id": 12
        },
        {
          "id": 7
        }
      ],
      "title": "Public or Private API mode, for extra security"
    },
    "createPost": {
      "status": "SUCCESS",
      "errors": null,
      "post": {
        "id": 1207,
        "slug": "public-or-private-api-mode-for-extra-security-2",
        "date": "2023-07-07T02:06:17+00:00",
        "status": "draft",
        "author": {
          "id": 2
        },
        "categories": [
          {
            "id": 4
          },
          {
            "id": 3
          },
          {
            "id": 2
          }
        ],
        "rawContent": "<!-- wp:heading -->\n<h2>Verse Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:verse -->\n<pre class=\"wp-block-verse\">Write poetry and other literary expressions honoring all spaces and line-breaks.</pre>\n<!-- /wp:verse -->\n\n<!-- wp:heading -->\n<h2>Table Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:table {\"className\":\"is-style-stripes\"} -->\n<figure class=\"wp-block-table is-style-stripes\"><table><tbody><tr><td>Row 1 Column 1</td><td>Row 1 Column 2</td></tr><tr><td>Row 2 Column 1</td><td>Row 2 Column 2</td></tr><tr><td>Row 3 Column 1</td><td>Row 3 Column 2</td></tr></tbody></table></figure>\n<!-- /wp:table -->\n\n<!-- wp:heading -->\n<h2>Separator Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:separator -->\n<hr class=\"wp-block-separator\"/>\n<!-- /wp:separator -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Spacer Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:spacer -->\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n<!-- /wp:spacer -->",
        "excerpt": "Verse Block Write poetry and other literary expressions honoring all spaces and line-breaks. Table Block Row 1 Column 1 Row 1 Column 2 Row 2 Column 1 Row 2 Column 2 Row 3 Column 1 Row 3 Column 2 Separator Block Spacer Block",
        "featuredImage": {
          "id": 362
        },
        "tags": [
          {
            "id": 12
          },
          {
            "id": 7
          }
        ],
        "title": "Public or Private API mode, for extra security"
      }
    }
  }
}

첫 번째 방식의 문제점

위 쿼리는 연결 필드가 비어 있을 경우 동적 변수가 내보내지지 않아 오류를 반환합니다.

예를 들어, 복제할 포스트에 대표 이미지가 없으면 필드 featuredImagenull이 되고, id @export(as: "featuredImageID")는 실행되지 않습니다.

{
  post {
    featuredImage {
      id @export(as: "featuredImageID")
    }
  }
}

동적 변수 $featuredImageID가 존재하지 않게 되므로, 응답은 오류를 반환합니다.

{
  "errors": [
    {
      "message": "No value has been exported for dynamic variable 'featuredImageID'",
      "locations": [
        {
          "line": 39,
          "column": 22
        }
      ]
    }
  ],
  "data": {
    // ...
  }
}

다음 두 가지 방식으로 이 문제를 해결합니다.

포스트 복제하기: 두 번째 방식

Gato GraphQL에서 연결 필드도 값을 저장합니다. 처음 해석될 때 이 필드들은 참조하는 리소스의 ID(연결된 리소스의 ID, 또는 연결된 리소스들의 ID 배열)를 포함합니다. 연결이 해석된 후에야 ID가 실제 리소스 객체로 대체됩니다.

예를 들어, 다음 쿼리에서는

{
  post {
    featuredImage {
      id
    }
    tags {
      id
    }
  }
}

…필드 featuredImage는 처음에 362(대표 이미지의 ID)를 포함하고, 필드 tags는 배열 [12, 7](태그 ID)을 포함합니다.

내보낼 값이 ID(예: $featuredImageID) 또는 ID 배열(예: $tagIDs)인 경우, 이 특성을 활용하여 연결 필드에서 이미 ID를 내보낼 수 있습니다.

다음과 같이 하는 대신에

{
  post {
    featuredImage {
      id @export(as: "featuredImageID")
    }
    tags {
      id @export(as: "tagIDs", type: LIST)
    }
  }
}

…다음과 같이 할 수 있습니다.

{
  post {
    featuredImage @export(as: "featuredImageID") {
      id 
    }
    tags @export(as: "tagIDs") {
      id
    }
  }
}

($tagIDs를 내보낼 때 연결 필드가 이미 리스트이므로 인수 type: LIST가 제거되었음에 유의하세요.)

이제 이러한 동적 변수는 항상 내보내지며, 다음 값을 가집니다.

  • 포스트에 대표 이미지가 없을 경우 $featuredImageIDnull
  • 포스트에 태그가 없을 경우 $tagIDs는 빈 배열 []

GraphQL 쿼리를 수정하면 다음과 같이 됩니다.

query GetPostAndExportData($postId: ID!) {
  post(by: { id : $postId }) {
    # Fields not to be duplicated
    id
    slug
    date
    status
 
    # Fields to be duplicated
    author @export(as: "authorID") {
      id
    }
    categories @export(as: "categoryIDs") {
      id
    }
    rawContent @export(as: "rawContent")
    rawExcerpt @export(as: "excerpt")
    featuredImage @export(as: "featuredImageID") {
      id 
    }
    tags @export(as: "tagIDs") {
      id
    }
    rawTitle @export(as: "title")    
  }
}
 
mutation DuplicatePost
  @depends(on: "GetPostAndExportData")
{
  createPost(input: {
    status: draft,
    authorBy: {
      id: $authorID
    },
    categoriesBy: {
      ids: $categoryIDs
    },
    contentAs: {
      html: $rawContent
    },
    excerpt: $excerpt
    featuredImageBy: {
      id: $featuredImageID
    },
    tagsBy: {
      ids: $tagIDs
    },
    title: $title
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      # Fields not to be duplicated
      id
      slug
      date
      status
 
      # Fields to be duplicated
      author {
        id
      }
      categories {
        id
      }
      rawContent
      excerpt
      featuredImage {
        id
      }
      tags {
        id
      }
      title
    }
  }
}

…응답이 이제 올바르게 작동합니다.

{
  "data": {
    "post": {
      "id": 23,
      "slug": "graphql-or-rest-you-can-have-both",
      "date": "2020-12-12T04:04:54+00:00",
      "status": "publish",
      "author": {
        "id": 2
      },
      "categories": [
        {
          "id": 1
        }
      ],
      "rawContent": "<!-- wp:heading -->\n<h2>Audio Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:audio -->\n<figure class=\"wp-block-audio\"><audio controls src=\"https://freemusicarchive.org/file/music/WFMU/Broke_For_Free/Directionless_EP/Broke_For_Free_-_01_-_Night_Owl.mp3\"></audio></figure>\n<!-- /wp:audio -->\n\n<!-- wp:heading -->\n<h2>Video Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:video -->\n<figure class=\"wp-block-video\"><video controls src=\"https://archive.org/download/SlowMotionFlame/slomoflame_512kb.mp4\"></video></figure>\n<!-- /wp:video -->\n\n<!-- wp:heading -->\n<h2>Custom HTML Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:html -->\n<strong>This is a HTML block.</strong>\n<!-- /wp:html -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Preformatted Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:preformatted -->\n<pre class=\"wp-block-preformatted\">This is some preformatted text. Preformatted text keeps your s p a c e s, tabs and<br>linebreaks as they are.</pre>\n<!-- /wp:preformatted -->",
      "excerpt": "Audio Block Video Block Custom HTML Block This is a HTML block. Preformatted Block This is some preformatted text. Preformatted text keeps your s p a c e s, tabs andlinebreaks as they are.",
      "featuredImage": null,
      "tags": [],
      "title": "GraphQL or REST? Why not both?"
    },
    "createPost": {
      "status": "SUCCESS",
      "errors": null,
      "post": {
        "id": 1209,
        "slug": "graphql-or-rest-why-not-both",
        "date": "2023-07-07T03:24:31+00:00",
        "status": "draft",
        "author": {
          "id": 2
        },
        "categories": [
          {
            "id": 1
          }
        ],
        "rawContent": "<!-- wp:heading -->\n<h2>Audio Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:audio -->\n<figure class=\"wp-block-audio\"><audio controls src=\"https://freemusicarchive.org/file/music/WFMU/Broke_For_Free/Directionless_EP/Broke_For_Free_-_01_-_Night_Owl.mp3\"></audio></figure>\n<!-- /wp:audio -->\n\n<!-- wp:heading -->\n<h2>Video Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:video -->\n<figure class=\"wp-block-video\"><video controls src=\"https://archive.org/download/SlowMotionFlame/slomoflame_512kb.mp4\"></video></figure>\n<!-- /wp:video -->\n\n<!-- wp:heading -->\n<h2>Custom HTML Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:html -->\n<strong>This is a HTML block.</strong>\n<!-- /wp:html -->\n\n<!-- wp:heading {\"className\":\"has-top-margin\"} -->\n<h2 class=\"has-top-margin\">Preformatted Block</h2>\n<!-- /wp:heading -->\n\n<!-- wp:preformatted -->\n<pre class=\"wp-block-preformatted\">This is some preformatted text. Preformatted text keeps your s p a c e s, tabs and<br>linebreaks as they are.</pre>\n<!-- /wp:preformatted -->",
        "excerpt": "Audio Block Video Block Custom HTML Block This is a HTML block. Preformatted Block This is some preformatted text. Preformatted text keeps your s p a c e s, tabs andlinebreaks as they are.",
        "featuredImage": null,
        "tags": [],
        "title": "GraphQL or REST? Why not both?"
      }
    }
  }
}

두 번째 방식의 문제점

위 방법은 ID를 내보내는 경우에만 작동합니다(연결 필드에 저장된 값이 ID이므로). 태그 슬러그와 같은 다른 값에는 작동하지 않습니다.

{
  post {
    tags {
      slug @export(as: "tagSlugs", type: LIST)
    }
  }
}

다음 방식으로 이 문제를 해결합니다.

포스트 복제하기: 세 번째 방식

처음에 추가 작업을 실행하여 PHP Functions Via Schema 확장 기능의 전역 필드 _echo를 통해 각 동적 변수를 null 또는 빈 값으로 초기화할 수 있습니다.

이렇게 하면 각 동적 변수는 최소 한 번은 항상 내보내집니다. 필드 값이 비어 있지 않으면 다시 내보내지고, 이 두 번째 값이 첫 번째 값을 덮어씁니다.

이 쿼리에서 동적 변수 $tagSlugs는 빈 배열로 초기화되며, 포스트에 슬러그가 있으면 다시 내보내집니다.

query InitializeDynamicVariables {
  tagSlugs: _echo(value: []) @export(as: "tagSlugs")
}
 
query ExportData
  @depends(on: "InitializeDynamicVariables")
{
  post {
    tags {
      slug @export(as: "tagSlugs", type: LIST)
    }
  }
}
  • 전역 필드 _echo는 타입에 관계없이 제공된 값을 그대로 반환합니다.
query {
  string: _echo(value: "page")
  int: _echo(value: 3)
  bool: _echo(value: true)
  jsonObject: _echo(value: {
    name: "Robert"
    surname: "Spencer"
  })
  null: _echo(value: null)
  arrayOfString: _echo(value: ["something", "new"])
  arrayOfInt: _echo(value: [1, 3, 5])
  arrayOfArraysOfBool: _echo(value: [[true, false], [false]])
  arrayOfMixed: _echo(value: [1, true, "string", [1, 3, 5], {key: "value"}])
}

이 방법은 ID뿐만 아니라 모든 종류의 데이터를 내보낼 수 있어 이전 방식보다 더 포괄적입니다.

GraphQL 쿼리를 수정하면 다음과 같이 됩니다.

query InitializeDynamicVariables {
  authorID: _echo(value: null) @export(as: "authorID")
  categoryIDs: _echo(value: []) @export(as: "categoryIDs")
  featuredImageID: _echo(value: null) @export(as: "featuredImageID")
  tagIDs: _echo(value: []) @export(as: "tagIDs")}
 
query GetPostAndExportData($postId: ID!)
  @depends(on: "InitializeDynamicVariables")
{
  post(by: { id : $postId }) {
    # Fields not to be duplicated
    id
    slug
    date
    status
 
    # Fields to be duplicated
    author {
      id @export(as: "authorID")
    }
    categories {
      id @export(as: "categoryIDs", type: LIST)
    }
    rawContent @export(as: "rawContent")
    rawExcerpt @export(as: "excerpt")
    featuredImage {
      id @export(as: "featuredImageID")
    }
    tags {
      id @export(as: "tagIDs", type: LIST)
    }
    rawTitle @export(as: "title")
  }
}
 
mutation DuplicatePost
  @depends(on: "GetPostAndExportData")
{
  createPost(input: {
    status: draft,
    authorBy: {
      id: $authorID
    },
    categoriesBy: {
      ids: $categoryIDs
    },
    contentAs: {
      html: $rawContent
    },
    excerpt: $excerpt
    featuredImageBy: {
      id: $featuredImageID
    },
    tagsBy: {
      ids: $tagIDs
    },
    title: $title
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      # Fields not to be duplicated
      id
      slug
      date
      status
 
      # Fields to be duplicated
      author {
        id
      }
      categories {
        id
      }
      rawContent
      excerpt
      featuredImage {
        id
      }
      tags {
        id
      }
      title
    }
  }
}

세 번째 방식의 경고

동적 변수가 두 번 이상 내보내질 때마다, GraphQL 엔진은 기본적으로 GraphQL 응답에 경고를 추가합니다.

{
  "extensions": {
    "warnings": [
      {
        "message": "Dynamic variable with name 'tagSlugs' had already been set, had its value overridden",
        "locations": [
          {
            "line": 22,
            "column": 21
          }
        ]
      }
    ]
  },
  "data": {
    // ...
  }
}

다음의 통합 방식에서 이 경고를 해결합니다.

포스트 복제하기: 통합 방식

이전 방식의 GraphQL 쿼리를 사용하여 다음을 최적화합니다.

  • "동적 변수 중복" 경고를 발생시키지 않도록
  • InitializeDynamicVariables의 필드 값을 GraphQL 응답에 출력하지 않도록 (필요하지 않고 단지 헬퍼 필드이므로)

이 요소들은 각각 다음과 같이 처리합니다.

  • 작업에 디렉티브 @configureWarningsOnExportingDuplicateVariable(enabled: false)를 추가하여 경고 발생을 비활성화
  • 제거할 각 필드에 Field Response Removal 확장 기능의 @remove 디렉티브를 추가

이것이 포스트를 복제하는 통합 GraphQL 쿼리입니다.

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  authorID: _echo(value: null)
    @export(as: "authorID")
    @remove
 
  categoryIDs: _echo(value: [])
    @export(as: "categoryIDs")
    @remove
 
  featuredImageID: _echo(value: null)
    @export(as: "featuredImageID")
    @remove
 
  tagIDs: _echo(value: [])
    @export(as: "tagIDs")
    @remove
}
 
query GetPostAndExportData($postId: ID!)
  @depends(on: "InitializeDynamicVariables")
{
  post(by: { id : $postId }) {
    # Fields not to be duplicated
    id
    slug
    date
    status
 
    # Fields to be duplicated
    author {
      id @export(as: "authorID")
    }
    categories {
      id @export(as: "categoryIDs", type: LIST)
    }
    rawContent @export(as: "rawContent")
    rawExcerpt @export(as: "excerpt")
    featuredImage {
      id @export(as: "featuredImageID")
    }
    tags {
      id @export(as: "tagIDs", type: LIST)
    }
    rawTitle @export(as: "title")
  }
}
 
mutation DuplicatePost
  @depends(on: "GetPostAndExportData")
{
  createPost(input: {
    status: draft,
    authorBy: {
      id: $authorID
    },
    categoriesBy: {
      ids: $categoryIDs
    },
    contentAs: {
      html: $rawContent
    },
    excerpt: $excerpt
    featuredImageBy: {
      id: $featuredImageID
    },
    tagsBy: {
      ids: $tagIDs
    },
    title: $title
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      # Fields not to be duplicated
      id
      slug
      date
      status
 
      # Fields to be duplicated
      author {
        id
      }
      categories {
        id
      }
      rawContent
      excerpt
      featuredImage {
        id
      }
      tags {
        id
      }
      title
    }
  }
}