활용 가이드
활용 가이드(Gutenberg) 블록

(Gutenberg) 블록

Gutenberg 블록 데이터를 가져오는 방법을 설명합니다.

GraphQL 스키마에는 모든 CustomPost 타입(Post, Page 등)에 다음 필드가 추가되어 있습니다.

  • blocks
  • blockDataItems
  • blockFlattenedDataItems

Classic Editor 플러그인이 활성화된 경우 이 필드들은 사용할 수 없습니다.

blocks

필드 CustomPost.blocks: [BlockUnion!]는 커스텀 포스트에 포함된 모든 블록의 목록을 가져옵니다.

blocks는 GraphQL 스키마에 매핑된 Block 타입의 목록을 반환합니다. 이 Block 타입들은 모두 BlockUnion 타입의 일부이며, Block 인터페이스를 구현합니다.

이 플러그인은 하나의 Block 타입인 GenericBlock을 구현하며, 이것만으로도 모든 블록의 데이터를 가져오기에 충분합니다(필드 attributes: JSONObject를 통해).

이 쿼리:

{
  post(by: { id: 1 }) {
    blocks {
      ...on Block {
        name
        attributes
        innerBlocks {
          ...on Block {
              name
              attributes
              innerBlocks {
                ...on Block {
                  name
                  attributes
                }
              }
            }
          }
        }
      }
    }
  }
}

...는 다음 응답을 생성합니다:

{
  "data": {
    "post": {
      "blocks": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/list",
          "attributes": {
            "ordered": false,
            "values": "<li>List item 1</li><li>List item 2</li><li>List item 3</li><li>List item 4</li>"
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  },
                  "innerBlocks": null
                }
              ]
            },
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/paragraph",
                  "attributes": {
                    "className": "layout-column-2",
                    "content": "Phosfluorescently morph intuitive relationships rather than customer directed human capital.",
                    "dropCap": false
                  },
                  "innerBlocks": null
                }
              ]
            }
          ]
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  },
                  "innerBlocks": null
                },
                {
                  "name": "core/columns",
                  "attributes": {
                    "isStackedOnMobile": true
                  },
                  "innerBlocks": [
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "33.33%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/heading",
                          "attributes": {
                            "fontSize": "large",
                            "content": "Life is so rich",
                            "level": 2
                          },
                          "innerBlocks": null
                        },
                        {
                          "name": "core/heading",
                          "attributes": {
                            "level": 3,
                            "content": "Life is so dynamic"
                          },
                          "innerBlocks": null
                        }
                      ]
                    },
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "66.66%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/paragraph",
                          "attributes": {
                            "content": "This rhyming poem is the spark that can reignite the fires within you. It challenges you to go out and live your life in the present moment as a \u201chero\u201d and leave your mark on this world.",
                            "dropCap": false
                          },
                          "innerBlocks": null
                        },
                        {
                          "name": "core/columns",
                          "attributes": {
                            "isStackedOnMobile": true
                          },
                          "innerBlocks": [
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 361,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/graphql-voyager-public-1024x622.jpg",
                                    "alt": ""
                                  },
                                  "innerBlocks": null
                                }
                              ]
                            },
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": null
                            },
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 362,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/namespaced-interactive-schema-1024x598.webp",
                                    "alt": ""
                                  },
                                  "innerBlocks": null
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

Block 타입의 GraphQL 스키마는 다음과 같습니다:

interface Block {
  name: String!
  attributes: JSONObject
  innerBlocks: [BlockUnion!]
  contentSource: HTML!
}
 
type GenericBlock implements Block {
  name: String!
  attributes: JSONObject
  innerBlocks: [BlockUnion!]
  contentSource: HTML!
}
 
union BlockUnion = GenericBlock

Block 필드

Block 인터페이스(및 GeneralBlock 타입)에는 다음 필드가 포함됩니다.

  • name은 블록의 이름을 가져옵니다: "core/paragraph", "core/heading", "core/image" 등.
  • attributes는 블록의 모든 속성을 포함한 JSON 객체를 가져옵니다.
  • innerBlocks[BlockUnion!]를 가져오므로, 내부 블록을 포함하는 블록의 계층 구조를 탐색하고, 콘텐츠 내 모든 레벨의 데이터를 가져올 수 있습니다.
  • contentSource는 블록의 (Gutenberg) HTML 소스 코드를 가져옵니다(속성을 포함하는 주석 구분자 포함). 단, 이 필드는 DB에 저장된 형식과 완전히 동일한 데이터를 가져오지는 않으므로(#2346 참조), 이 필드는 주의하여 사용하세요.

GeneralBlock를 직접 가져오기(BlockUnion 대신)

현재 블록을 매핑하는 Block 타입은 GeneralBlock 하나뿐이므로, CustomPost.blocks(및 Block.innerBlocks)가 BlockUnion 타입 대신 이 타입을 직접 가져오도록 설정하는 것이 합리적입니다.

설정 페이지의 Blocks 탭에서 Use single type instead of union type? 옵션에 체크하면 설정할 수 있습니다:

`BlockUnion` 대신 `GeneralBlock`을 직접 가져오도록 설정
`BlockUnion` 대신 `GeneralBlock`을 직접 가져오도록 설정

그러면 GraphQL 쿼리가 간소화됩니다:

{
  post(by: { id: 1 }) {
    blocks {
      name
      attributes
      innerBlocks {
        name
        attributes
      }
    }
  }
}

응답 타입을 BlockUnion으로 유지하는 것은 하위 호환성 측면에서 유리합니다. 향후 스키마에 블록별 타입을 추가하더라도(아래 섹션 참조), 파괴적 변경이 발생하지 않습니다.

블록별 타입 매핑

JSONObject 타입(Block.attributes로 가져오는)은 엄격한 타입 지정이 되어 있지 않습니다. 속성은 임의의 타입과 카디널리티(String, Int, [Boolean!] 등)를 가질 수 있으므로, 각 블록에 대해 이 정보를 파악하고 클라이언트에서 각 경우를 처리해야 합니다.

엄격한 타입 지정이 필요한 경우, PHP 코드를 통해 GraphQL 스키마를 확장하여 블록의 특정 속성을 필드로 매핑하는 블록별 타입을 추가하고, BlockUnion의 일부로 포함해야 합니다.

예를 들어, core/paragraph 블록을 매핑하는 CoreParagraphBlock 타입을 추가하고, String 타입의 필드 content를 가지도록 할 수 있습니다.

GraphQL 스키마를 확장하는 방법은 GatoGraphQL/GatoGraphQL의 문서를 참조하세요(현재 작업 중).

블록 필터링

필드 CustomPost.blocks에는 includeexclude 두 가지 속성을 가진 인자 filterBy가 있습니다. 이를 사용하여 블록 이름으로 가져올 블록을 필터링할 수 있습니다:

{
  post(by: { id: 1 }) {
    id
    blocks(
      filterBy: {
        include: [
          "core/heading",
          "core/gallery"
        ]
      }
    ) {
      name
      attributes
    }
  }
}

이는 다음을 생성합니다:

{
  "data": {
    "post": {
      "blocks": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlocks": null
        }
      ]
    }
  }
}

core/heading 타입의 블록이 모두 포함된 것은 아님을 유의하세요. core/column 아래에 중첩된 블록들은 제외되었는데, 이는 core/columnscore/column 블록 자체가 제외되어 있어 해당 블록에 도달할 방법이 없기 때문입니다.

필드 blocks의 불편한 점

필드 blocks는 커스텀 포스트에 포함된 전체 블록 데이터(내부 블록의 데이터, 그 내부 블록의 데이터 등 포함)를 가져오려면 콘텐츠 내 중첩 블록 레벨 수를 파악하고 GraphQL 쿼리에 반영해야 하는 불편함이 있습니다.

레벨 수를 알 수 없는 경우에는 모든 데이터가 가져와지도록 충분한 레벨로 쿼리를 작성해야 합니다.

예를 들어, 다음 쿼리는 내부 블록 7단계까지 가져옵니다:

{
  post(by: { id: 1 }) {
    blocks {
      ...BlockData
    }
  }
}
 
fragment BlockData on Block {
  name
  attributes
  innerBlocks {
    ...on Block {
      name
      attributes
      innerBlocks {
        ...on Block {
          name
          attributes
          innerBlocks {
            ...on Block {
              name
              attributes
              innerBlocks {
                ...on Block {
                  name
                  attributes
                  innerBlocks {
                    ...on Block {
                      name
                      attributes
                      innerBlocks {
                        ...on Block {
                          name
                          attributes
                          innerBlocks {
                            ...on Block {
                              name
                              attributes
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

blockDataItems

필드 blocks가 모든 데이터(내부 블록의 데이터, 그 내부 블록의 데이터 등 포함)를 가져올 때의 불편함을 해소하기 위해 필드 CustomPost.blockDataItems가 존재합니다.

이 필드는 [BlockUnion]을 반환하는 대신 [JSONObject!]를 반환합니다:

type CustomPost {
  blockDataItems: [JSONObject!]
}

즉, 엔티티 간 관계를 탐색하는 전형적인 GraphQL 방식 대신, 최상위 레벨의 각 Block 엔티티가 자기 자신과 모든 자식의 전체 블록 데이터를 단일 JSONObject 결과로 생성합니다.

JSON 객체에는 블록의 속성(nameattributes 항목)과 내부 블록의 속성(innerBlocks 항목)이 재귀적으로 포함됩니다.

예를 들어, 다음 쿼리:

{
  post(by: { id: 1 }) {
    blockDataItems
  }
}

...는 다음을 생성합니다:

{
  "data": {
    "post": {
      "blockDataItems": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          }
        },
        {
          "name": "core/list",
          "attributes": {
            "ordered": false,
            "values": "<li>List item 1</li><li>List item 2</li><li>List item 3</li><li>List item 4</li>"
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          }
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  }
                }
              ]
            },
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/paragraph",
                  "attributes": {
                    "className": "layout-column-2",
                    "content": "Phosfluorescently morph intuitive relationships rather than customer directed human capital.",
                    "dropCap": false
                  }
                }
              ]
            }
          ]
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          }
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  }
                },
                {
                  "name": "core/columns",
                  "attributes": {
                    "isStackedOnMobile": true
                  },
                  "innerBlocks": [
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "33.33%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/heading",
                          "attributes": {
                            "fontSize": "large",
                            "content": "Life is so rich",
                            "level": 2
                          }
                        },
                        {
                          "name": "core/heading",
                          "attributes": {
                            "level": 3,
                            "content": "Life is so dynamic"
                          }
                        }
                      ]
                    },
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "66.66%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/paragraph",
                          "attributes": {
                            "content": "This rhyming poem is the spark that can reignite the fires within you. It challenges you to go out and live your life in the present moment as a \u201chero\u201d and leave your mark on this world.",
                            "dropCap": false
                          }
                        },
                        {
                          "name": "core/columns",
                          "attributes": {
                            "isStackedOnMobile": true
                          },
                          "innerBlocks": [
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 361,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/graphql-voyager-public-1024x622.jpg",
                                    "alt": ""
                                  }
                                }
                              ]
                            },
                            {
                              "name": "core/column",
                              "attributes": {}
                            },
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 362,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/namespaced-interactive-schema-1024x598.webp",
                                    "alt": ""
                                  }
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

블록 데이터 항목 필터링

blocks와 마찬가지로 blockDataItemsfilterBy 인자를 통해 가져올 블록을 필터링할 수 있습니다.

이 쿼리:

{
  post(by: { id: 1 }) {
    id
    blockDataItems(
      filterBy: {
        include: [
          "core/heading"
        ]
      }
    )
  }
}

...는 다음을 생성합니다:

{
  "data": {
    "post": {
      "blockDataItems": [
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlocks": null
        }
      ]
    }
  }
}

blocks와 마찬가지로, core/heading 타입의 블록이 모두 포함된 것은 아님을 유의하세요. core/column 아래에 중첩된 블록들은 제외되었는데, core/columnscore/column 블록 자체가 제외되어 있어 해당 블록에 도달할 방법이 없기 때문입니다.

blockFlattenedDataItems

blocksblockDataItems 두 필드 모두 filterBy 인자를 통해 가져올 블록을 필터링할 수 있습니다. 두 경우 모두, 블록이 포함 조건을 충족하더라도 조건을 충족하지 않는 블록 내에 중첩되어 있으면 제외됩니다.

그러나 계층 구조 내 위치에 관계없이 커스텀 포스트에서 특정 타입의 모든 블록을 가져와야 하는 경우가 있습니다. 예를 들어, 블로그 게시물에 포함된 모든 이미지를 가져오기 위해 core/image 타입의 모든 블록을 포함하고 싶을 수 있습니다.

이러한 필요를 충족하기 위해 필드 CustomPost.blockFlattenedDataItems가 존재합니다. blocksblockDataItems 필드와 달리, 블록 계층 구조를 단일 레벨로 평탄화합니다.

이 쿼리:

{
  post(by: { id: 1 }) {
    blockFlattenedDataItems
  }
}

...는 다음을 생성합니다:

{
  "data": {
    "post": {
      "blockFlattenedDataItems": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/list",
          "attributes": {
            "ordered": false,
            "values": "<li>List item 1</li><li>List item 2</li><li>List item 3</li><li>List item 4</li>"
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlockPositions": [
            5,
            7
          ],
          "parentBlockPosition": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 4,
          "innerBlockPositions": [
            6
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 1701,
            "className": "layout-column-1",
            "url": "https://d.pr/i/fW6V3V+",
            "alt": ""
          },
          "parentBlockPosition": 5,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 4,
          "innerBlockPositions": [
            8
          ]
        },
        {
          "name": "core/paragraph",
          "attributes": {
            "className": "layout-column-2",
            "content": "Phosfluorescently morph intuitive relationships rather than customer directed human capital.",
            "dropCap": false
          },
          "parentBlockPosition": 7,
          "innerBlockPositions": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlockPositions": [
            11
          ],
          "parentBlockPosition": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 10,
          "innerBlockPositions": [
            12,
            13
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 1701,
            "className": "layout-column-1",
            "url": "https://d.pr/i/fW6V3V+",
            "alt": ""
          },
          "parentBlockPosition": 11,
          "innerBlockPositions": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "parentBlockPosition": 11,
          "innerBlockPositions": [
            14,
            17
          ]
        },
        {
          "name": "core/column",
          "attributes": {
            "width": "33.33%"
          },
          "parentBlockPosition": 13,
          "innerBlockPositions": [
            15,
            16
          ]
        },
        {
          "name": "core/heading",
          "attributes": {
            "fontSize": "large",
            "content": "Life is so rich",
            "level": 2
          },
          "parentBlockPosition": 14,
          "innerBlockPositions": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "level": 3,
            "content": "Life is so dynamic"
          },
          "parentBlockPosition": 14,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {
            "width": "66.66%"
          },
          "parentBlockPosition": 13,
          "innerBlockPositions": [
            18,
            19
          ]
        },
        {
          "name": "core/paragraph",
          "attributes": {
            "content": "This rhyming poem is the spark that can reignite the fires within you. It challenges you to go out and live your life in the present moment as a \u201chero\u201d and leave your mark on this world.",
            "dropCap": false
          },
          "parentBlockPosition": 17,
          "innerBlockPositions": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "parentBlockPosition": 17,
          "innerBlockPositions": [
            20,
            22,
            23
          ]
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 19,
          "innerBlockPositions": [
            21
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 361,
            "sizeSlug": "large",
            "linkDestination": "none",
            "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/graphql-voyager-public-1024x622.jpg",
            "alt": ""
          },
          "parentBlockPosition": 20,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 19,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 19,
          "innerBlockPositions": [
            24
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 362,
            "sizeSlug": "large",
            "linkDestination": "none",
            "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/namespaced-interactive-schema-1024x598.webp",
            "alt": ""
          },
          "parentBlockPosition": 23,
          "innerBlockPositions": null
        }
      ]
    }
  }
}

블록이 더 이상 중첩되지 않으므로 innerBlocks 속성이 사라진 것을 확인할 수 있습니다. 대신 응답에는 블록 계층 구조를 재구성할 수 있는 두 가지 다른 속성이 포함됩니다.

  • parentBlockPosition: 반환된 배열 내 해당 블록의 부모 블록 위치, 최상위 블록인 경우 null
  • innerBlockPositions: 반환된 배열 내 해당 블록의 내부 블록 위치를 담은 배열

평탄화된 블록 데이터 항목 필터링

블록 계층 구조가 평탄화되었으므로, core/heading으로 필터링하면 제외된 블록 아래에 중첩되어 있던 것까지 포함하여 해당 타입의 모든 블록이 반환됩니다.

이 쿼리:

{
  post(by: { id: 1 }) {
    id
    blockFlattenedDataItems(
      filterBy: {
        include: [
          "core/heading"
        ]
      }
    )
  }
}

...는 다음을 생성합니다:

{
  "data": {
    "post": {
      "blockFlattenedDataItems": [
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "fontSize": "large",
            "content": "Life is so rich",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "level": 3,
            "content": "Life is so dynamic"
          }
        }
      ]
    }
  }
}

필터링 시 parentBlockPositioninnerBlockPositions 두 추가 속성은 더 이상 의미가 없으므로 제거됩니다.