동적으로 구조화된 데이터 가져오기
WordPress에서는 중첩된 레벨의 데이터, 즉 동일한 타입의 자식 항목을 포함하는 엔티티를 가져올 수 있습니다. 예를 들어, 메뉴에는 하위 항목을 가질 수 있는 항목이 포함되어 있으며, 그 하위 항목 자체도 하위 항목을 포함할 수 있고, 이것이 여러 레벨에 걸쳐 계속됩니다. 마찬가지로, 댓글에는 답글이 있을 수 있으며, 그 답글 자체에도 또 답글이 있을 수 있습니다.
GraphQL에서 메뉴를 다루는 방법을 살펴보겠습니다. GraphQL에서 메뉴 데이터를 가져오려면 다양한 레벨에 걸쳐 메뉴 내 항목들을 쿼리해야 합니다. 예를 들어, 아래 쿼리에서 메뉴는 3단계 레벨을 가지며, 프래그먼트 MenuItemProps를 사용하여 모든 레벨의 메뉴 항목에 대해 동일한 필드(id, label, url)를 가져옵니다.
query GetMenu {
menu(by: { id: 176 }) {
id
items {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
}
}
}
}
}
fragment MenuItemProps on MenuItem {
id
label
url
}보시다시피, 레벨의 수는 GraphQL 쿼리에 반영됩니다. 애플리케이션의 메뉴가 3단계이기 때문에 GraphQL 쿼리도 3단계의 중첩을 갖습니다.
그러나 WordPress에서는 메뉴 생성이 사전에 결정되어 있지 않으며, 사이트 관리자가 메뉴 화면(「블록 테마」를 사용하지 않는 경우)을 통해 설정하고 DB에 저장됩니다.

이것은 문제를 야기합니다. 사용자 인터페이스를 통해 메뉴에 레벨을 추가할 경우, GraphQL 쿼리에도 추가 레벨을 넣어야 합니다. 그렇지 않으면 새 레벨이 사이트에 표시되지 않습니다.
이 문제를 해결하는 방법은 2가지가 있습니다. 더 간단한 방법은 처음부터 필요한 것보다 더 많은 레벨을 가져오는 GraphQL 쿼리를 작성하여 나중에 레벨을 계속 추가할 수 있는 여유를 두는 것입니다. 예를 들어, 애플리케이션이 3단계를 필요로 하는 경우, GraphQL 쿼리가 6단계(또는 10단계나 20단계)의 데이터를 가져오도록 설정하면 제한에 도달할 때까지 메뉴를 확장할 수 있는 충분한 공간이 확보됩니다.
query GetMenu {
menu(by: { id: 176 }) {
id
items {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
}
}
}
}
}
}
}
}
fragment MenuItemProps on MenuItem {
id
label
url
}두 번째 해결책은 필드 Menu.itemDataEntries를 사용하는 것으로, 모든 레벨과 하위 레벨을 포함한 메뉴 데이터 전체를 구조화된 JSONObject로 생성합니다.
query GetMenu {
menu(by: { id: 176 }) {
id
itemDataEntries
}
}이 쿼리에 대한 응답은 다음과 같습니다.
{
"data": {
"menu": {
"id": 176,
"itemDataEntries": [
{
"id": 735,
"objectID": "6",
"parentID": null,
"label": "About The Tests",
"url": "https://mywpsite.com/about/",
"children": [
{
"id": 1451,
"objectID": "1133",
"parentID": "735",
"label": "Page Image Alignment",
"url": "https://mywpsite.com/about/page-image-alignment/",
"children": []
},
{
"id": 1452,
"objectID": "1134",
"parentID": "735",
"label": "Page Markup And Formatting",
"url": "https://mywpsite.com/about/page-markup-and-formatting/",
"children": []
}
]
},
{
"id": 739,
"objectID": "174",
"parentID": null,
"label": "Level 1",
"url": "https://mywpsite.com/level-1/",
"children": [
{
"id": 740,
"objectID": "173",
"parentID": "739",
"label": "Level 2",
"url": "https://mywpsite.com/level-1/level-2/",
"children": [
{
"id": 741,
"objectID": "172",
"parentID": "740",
"label": "Level 3",
"url": "https://mywpsite.com/level-1/level-2/level-3/",
"children": []
},
{
"id": 1453,
"objectID": "747",
"parentID": "740",
"label": "Level 3a",
"url": "https://mywpsite.com/level-1/level-2/level-3a/",
"children": []
},
{
"id": 1454,
"objectID": "748",
"parentID": "740",
"label": "Level 3b",
"url": "https://mywpsite.com/level-1/level-2/level-3b/",
"children": []
}
]
}
]
},
{
"id": 742,
"objectID": "146",
"parentID": null,
"label": "Lorem Ipsum",
"url": "https://mywpsite.com/lorem-ipsum/",
"children": []
}
]
}
}
}이 방법은 가져오는 데이터가 사용자 인터페이스에 의해 완전히 제어되어 DB에 저장된 내용을 있는 그대로 반영한다는 장점이 있습니다. 따라서 메뉴에 추가 레벨(2개이든 20개이든)을 추가해도 애플리케이션을 업데이트할 필요가 없습니다.
그러나 이 방법에는 GraphQL의 강력한 타입 지정이 손실된다는 명확한 단점이 있습니다. url을 URL 타입, label을 String 타입, objectID를 ID 타입으로 강하게 타입 지정된 필드를 가진 메뉴 항목을 받는 대신, Apollo client나 Relay 같은 GraphQL 도구 및 클라이언트가 이해할 수 없는 일반 객체를 얻게 됩니다. 따라서 GraphQL의 장점을 충분히 활용할 수 없습니다.
WordPress 설정 데이터 가져오기
또 다른 문제는 사용자 인터페이스에 의해 제어되고 DB에 저장된 엔티티를 가져와야 하는 경우입니다. WordPress의 설정이 그 예로, 옵션 이름은 테마와 플러그인에 의해 동적으로 생성되기 때문에 GraphQL 서버에는 사전에 알려져 있지 않으며, 테마와 플러그인에 의해 정의될 수도 있는 메타 값도 마찬가지로 기본적으로 GraphQL 스키마에 매핑되지 않습니다.
이러한 이유로, Gato GraphQL이 생성하는 스키마는 옵션 이름과 그 타입을 하드코딩하지 않고, 옵션의 이름을 받아 임의의 내장 타입 값(AnyBuiltInScalar로 표현)을 반환하는 optionValue 필드(및 optionValues와 optionObjectValue)를 통해 이에 접근합니다.
type Root {
optionValue(name: String!): AnyBuiltInScalar
}모든 옵션을 API를 통해 공개하려는 것은 아니기 때문에, 사이트 관리자는 플러그인 설정에서 전체 이름 또는 정규식으로 이를 명시적으로 허용 목록에 추가해야 합니다.

이제 쿼리는 허용 목록에 등록된 옵션을 가져올 수 있습니다.
{
siteURL: optionValue(name: "siteurl")
siteName: optionValue(name: "blogname")
siteDescription: optionValue(name: "blogdescription")
}애플리케이션에 필요한 추가 옵션이 있는 경우, 설정 페이지의 허용 목록에 해당 항목을 추가하기만 하면 즉시 API에서 사용할 수 있습니다.