๐ฅณ Gato GraphQL v0.9์ด ์ถ์๋์์ต๋๋ค!
์ฝ 1.5๋ ์ ๊ฐ๋ฐ ๊ธฐ๊ฐ๊ณผ 16,000๊ฐ ์ด์์ ์ปค๋ฐ์ ๊ฑฐ์ณ, Gato GraphQL์ ์ ๋ฒ์ ์ด ๋๋์ด ์ถ์๋์์ต๋๋ค! ๐ฅณ
๋ฒ์ 0.9์ ํ๋ฌ๊ทธ์ธ ์ญ์ฌ์ ๊ฐ์ฅ ํฐ ๋ฆด๋ฆฌ์ค์
๋๋ค. ๋ณ๊ฒฝ ๋ก๊ทธ๋ ์ฌ๊ธฐ์์ ํ์ธํ์ค ์ ์์ผ๋ฉฐ, ๋ชจ๋ ์ ๊ธฐ๋ฅ์ ์ ์ฒด ๋ด์ญ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
github.com/GatoGraphQL/GatoGraphQL/releases/tag/0.9.3
์ด ๋ฌธ์๋ ๋งค์ฐ ๊ธธ๊ธฐ ๋๋ฌธ์ (์ฝ๋ ๋ฐ 40๋ถ ์ด์ ์์!), ์๋์ ๊ฐ์ฅ ์ค์ํ ๋ณ๊ฒฝ ์ฌํญ์ TL;DR์ ์ ๋ฆฌํ์ต๋๋ค.
GraphQL ์คํค๋ง์ ๋ํญ ํ์ถฉ
WordPress ๋ฐ์ดํฐ ๋ชจ๋ธ์ด GraphQL ์คํค๋ง์ ๋ํญ ๋งคํ๋์์ต๋๋ค.

๊ทธ ์ค์์๋ ์คํค๋ง์๋ ๋ค์๊ณผ ๊ฐ์ ๊ฐ์ ์ฌํญ์ด ํฌํจ๋์ด ์์ต๋๋ค:
- ํ ๋ง ๋ฐ ํ๋ฌ๊ทธ์ธ์ ํฌํจํ ๋ชจ๋ CPT์์ ๋ฐ์ดํฐ ์ฟผ๋ฆฌ ๊ฐ๋ฅ
- ์ปค์คํ ํ์๋ ธ๋ฏธ(ํ๊ทธ์ ์นดํ ๊ณ ๋ฆฌ) ๋งคํ
- ๋ ์ ํฉํ GraphQL ํ์
์์ฑ ๋ฐ ๋ฐํ (์:
HTML,URL,DateTime) - ์ธํ ๊ฐ์ฒด๋ฅผ ํตํ ํ๋ ์ธ์ ์ ๋ฆฌ
- oneof ์ธํ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ํ ์์ฑ(์:
id,slug)์ผ๋ก ์ํฐํฐ ์ ํ - ๋ฎคํ ์ด์ ํ์ด๋ก๋ ๋ฐํ
- ์ค์ (
wp_options๋ก๋ถํฐ) ๋ฐ ๋ฉํ ๊ฐ(ํฌ์คํธ, ์ฌ์ฉ์, ๋๊ธ, ํ์๋ ธ๋ฏธ์ฉ) ์ฟผ๋ฆฌ
์ปค์คํ ์ค์นผ๋ผ
GraphQL ์๋ฒ์ ์ปค์คํ ์ค์นผ๋ผ ํ์ ์ง์์ด ์ถ๊ฐ๋์์ต๋๋ค. ์ปค์คํ ์ค์นผ๋ผ๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋ ์ธ์๋ฅผ ํตํ ์ ๋ ฅ ์์ ์ด๋ ์๋ต์์์ ์ปค์คํ ์ถ๋ ฅ ์ธ์ ๋ฑ ๋ฐ์ดํฐ๋ฅผ ๋ ์ ์ ํ๊ฒ ํํํ ์ ์์ต๋๋ค.
์ฌ๋ฌ ํ์ค ์ปค์คํ ์ค์นผ๋ผ ํ์ ์ด ๊ตฌํ๋์ด GraphQL ์คํค๋ง์์ ์ฆ์ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค:
DateDateTimeEmailHTMLURLURLAbsolutePath
์ปค์คํ enum
์ปค์คํ enum ํ์ ์ด ์ด์ ์ง์๋ฉ๋๋ค. enum์ ํน์ ํ์ฉ ๊ฐ์ ์งํฉ์ผ๋ก ์ ํ๋ ํน์ํ ์ข ๋ฅ์ ์ค์นผ๋ผ์ ๋๋ค. ์ด๋ฅผ ํตํด ๋ค์์ด ๊ฐ๋ฅํด์ง๋๋ค:
- ์ด ํ์ ์ ์ธ์๊ฐ ํ์ฉ๋ ๊ฐ ์ค ํ๋์ธ์ง ์ ํจ์ฑ ๊ฒ์ฌ
- ํ์ ์์คํ ์ ํตํด ํ๋๊ฐ ํญ์ ์ ํํ ๊ฐ์ ์งํฉ ์ค ํ๋์์ ์ ๋ฌ
์ฌ๋ฌ enum ํ์ ์ด ๊ตฌํ๋์ด GraphQL ์คํค๋ง์์ ์ ์ ํ ๊ณณ์ ์ฌ์ฉ๋ฉ๋๋ค. ๋ค์์ด ํฌํจ๋ฉ๋๋ค:
CommentOrderByEnumCommentStatusEnumCommentTypeEnumCustomPostOrderByEnumCustomPostStatusEnumMediaItemOrderByEnumMenuOrderByEnumTaxonomyOrderByEnumUserOrderByEnum
์ธํ ๊ฐ์ฒด
GraphQL ์๋ฒ๋ ์ด์ ์ธํ ํ์ ์ ์ง์ํ๋ฉฐ, ๊ณ ์ ํ ์ธํ ๊ฐ์ฒด๋ฅผ GraphQL ์คํค๋ง์ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์ธํ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋์ ๋ํ ์ ๋ ฅ์ผ๋ก ๋ณต์กํ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ ์ ์์ด, ๋ฎคํ ์ด์ ์ ํนํ ์ ์ฉํฉ๋๋ค.
์คํค๋ง์ ์ ์ ํ ๊ณณ์ ์ฌ๋ฌ ์ธํ ๊ฐ์ฒด๊ฐ ์ถ๊ฐ๋์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ฐ์ดํฐ๋ฅผ ์ฟผ๋ฆฌํ๋ ํ๋(posts, users, comments ๋ฑ)๋ ํ๋ ์ธ์ filter, sort, pagination ์๋์ ๋ณต์กํ ์ธํ ๊ฐ์ฒด๋ฅผ ์์ ํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ํ๋(createPost, addCommentToCustomPost ๋ฑ)๋ ํ๋ ์ธ์ input ์๋์ ์ธํ ๊ฐ์ฒด๋ฅผ ์์ ํฉ๋๋ค.
Oneof ์ธํ ๊ฐ์ฒด
ใoneofใ ์ธํ ๊ฐ์ฒด๋ ํน์ํ ์ธํ ๊ฐ์ฒด๋ก, ์ธํ ํ๋ ์ค ์ ํํ ํ๋๋ง ์ ๋ ฅ์ผ๋ก ์ ๊ณตํด์ผ ํ๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ๊ฐ ๋ฐํ๋ฉ๋๋ค. ์ด ๋์์ ์ ๋ ฅ์ ๋คํ์ฑ์ ๋์ ํฉ๋๋ค.
์๋ฅผ ๋ค์ด, ํ๋ Root.post์ ํ๋ ์ธ์ by๊ฐ ์ถ๊ฐ๋์์ต๋๋ค. ์ด๋ oneof ์ธํ ๊ฐ์ฒด๋ก, id๋ slug ๋ฑ ๋ค์ํ ์์ฑ์ ์ฌ์ฉํ์ฌ ํฌ์คํธ๋ฅผ ๊ฒ์ํ ์ ์์ต๋๋ค:
{
postByID: post(by: {
id: 1
}) {
id
title
}
postBySlug: post(by: {
slug: "hello-world"
}) {
id
title
}
}์ด ๋ฐฉ์์ ์ฅ์ ์ ํ๋์ ํ๋๋ก ๋ค์ํ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ฒ๋ฆฌํ ์ ์์ด, ๊ฐ ์ฌ์ฉ ์ฌ๋ก๋ณ๋ก ๋ณ๋์ ํ๋(postByID, postBySlug ๋ฑ)๋ฅผ ๋ง๋ค ํ์๊ฐ ์์ด์ง๋ฏ๋ก GraphQL ์คํค๋ง๊ฐ ๋ ๊ฐ๊ฒฐํ๊ณ ์ฐ์ํด์ง๋ค๋ ์ ์
๋๋ค.
์ฌ๋ฌ Oneof ์ธํ ๊ฐ์ฒด๊ฐ ๊ตฌํ๋์์ต๋๋ค:
Root.customPost(by:)Root.mediaItem(by:)Root.menu(by:)Root.page(by:)Root.postCategory(by:)Root.postTag(by:)Root.post(by:)Root.user(by:)
์คํผ๋ ์ด์ ๋๋ ํฐ๋ธ
GraphQL ์คํผ๋ ์ด์
(์ฆ, query ๋ฐ mutation ์คํผ๋ ์ด์
)๋ ์ด์ ๋๋ ํฐ๋ธ๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค.
ํน์ ํ์ ์ผ๋ก ๋๋ ํฐ๋ธ ์ ํ
(ํ๋) ๋๋ ํฐ๋ธ๋ฅผ ํน์ ํ์
์ ํ๋์๋ง ์ ์ฉ๋๋๋ก ์ ํํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ํ๋ ๊ฐ์ ๋๋ฌธ์๋ก ๋ณํํ๋ ๋๋ ํฐ๋ธ @strUpperCase๋ String ํ๋์๋ง ์๋ฏธ๊ฐ ์์ผ๋ฉฐ, Int, Float, Boolean์๋ ์ ์ฉํ ํ์๊ฐ ์์ต๋๋ค. ์ด ์ ํ์ ๋๋ ํฐ๋ธ ๋ฆฌ์กธ๋ฒ์์ ์ ์ธํ ์ ์๊ฒ ๋์์ต๋๋ค.
์ค๋ฅ๋ฅผ ๋ฐ์์ํจ GraphQL ์ฟผ๋ฆฌ ๋ ธ๋์ ์ ์ฒด ๊ฒฝ๋ก ์ถ๋ ฅ
์๋ต์ ์ค๋ฅ๋ฅผ ๋ฐํํ GraphQL ์ฟผ๋ฆฌ์ ๋
ธ๋์ ๋ํ ์ ์ฒด ๊ฒฝ๋ก๊ฐ ํฌํจ๋๊ฒ ๋์์ต๋๋ค(์๋ธ ์ํธ๋ฆฌ extensions.path ์๋). ์ด๋ฅผ ํตํด ๋ฌธ์ ์ ์์ธ์ ๋ ์ฝ๊ฒ ํ์
ํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ๋ค์ ์ฟผ๋ฆฌ์์ ๋๋ ํฐ๋ธ @nonExisting์ ์กด์ฌํ์ง ์์ต๋๋ค:
query {
myField @nonExisting
}์๋ต์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
{
"errors": [
{
"message": "There is no directive with name 'nonExisting'",
"locations": [
{
"line": 2,
"column": 7
}
],
"extensions": {
"type": "QueryRoot",
"field": "myField @nonExisting",
"path": [
"@nonExisting",
"myField @nonExisting",
"query { ... }"
],
"code": "PoP\\ComponentModel\\e20"
}
}
],
"data": {
"id": "root"
}
}์์ ํ์ง ์์ ๊ธฐ๋ณธ ์ค์ ํ์ฑํ
Gato GraphQL์ ์์ ํ ๊ธฐ๋ณธ ์ค์ ์ ์ ๊ณตํฉ๋๋ค:
- ๋จ์ผ ์๋ํฌ์ธํธ๊ฐ ๋นํ์ฑํ๋จ
- GraphQL ์คํค๋ง์ ใ๋ฏผ๊ฐํใ ๋ฐ์ดํฐ ์์(์:
User.roles๋๋status๋ก ํฌ์คํธ ํํฐ๋ง)๊ฐ ๋ ธ์ถ๋์ง ์์ - ์ฟผ๋ฆฌํ ์ ์๋ ์ค์ ์ต์ ๋ฐ ๋ฉํ ํค(ํฌ์คํธ, ์ฌ์ฉ์ ๋ฑ)๊ฐ ์ผ๋ถ๋ก ์ ํ๋จ
- ํ ๋ฒ์ ์ฟผ๋ฆฌํ ์ ์๋ ์ํฐํฐ ์๊ฐ ์ ํ๋จ(ํฌ์คํธ, ์ฌ์ฉ์ ๋ฑ)
์ด๋ฌํ ์์ ํ ๊ธฐ๋ณธ ์ค์ ์ ์ ์์ ์ธ ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด ใ๋ผ์ด๋ธใ ์ฌ์ดํธ๋ฅผ ์์ ํ๊ฒ ์ ์งํ๋ ๋ฐ ํ์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ใ์คํํฑใ ์ฌ์ดํธ๋ฅผ ๊ตฌ์ถํ ๋(๋ ธํธ๋ถ์ ๊ฐ๋ฐ ์ฌ์ดํธ, ์์ ํ ๋ฐฉํ๋ฒฝ ๋ค, ๋๋ ์ธํฐ๋ท์ ๋ ธ์ถ๋์ง ์์ WordPress ์ฌ์ดํธ ๋ฑ ๊ณต๊ฒฉ์ ์ทจ์ฝํ์ง ์์ ๊ฒฝ์ฐ)์๋ ํ์ํ์ง ์์ต๋๋ค.
v0.9๋ถํฐ wp-config.php์ ๋ค์์ ์ถ๊ฐํ์ฌ ์์ ํ์ง ์์ ๊ธฐ๋ณธ๊ฐ์ ํ์ฑํํ ์ ์์ต๋๋ค:
define( 'GRAPHQL_API_ENABLE_UNSAFE_DEFAULTS', true );๋๋ ๋์ผํ ํค/๊ฐ์ ํ๊ฒฝ ๋ณ์๋ก ์ ์ํ ์๋ ์์ต๋๋ค.
์์ ํ์ง ์์ ๊ธฐ๋ณธ๊ฐ์ ํ์ฑํํ๋ฉด ํ๋ฌ๊ทธ์ธ์ ๊ธฐ๋ณธ ์ค์ ์ด ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝ๋ฉ๋๋ค:
- ๋จ์ผ ์๋ํฌ์ธํธ๊ฐ ํ์ฑํ๋จ
- GraphQL ์คํค๋ง์์ ใ๋ฏผ๊ฐํใ ๋ฐ์ดํฐ ์์๊ฐ ๋ ธ์ถ๋จ
- ๋ชจ๋ ์ค์ ์ต์ ๋ฐ ๋ฉํ ํค๋ฅผ ์ฟผ๋ฆฌํ ์ ์๊ฒ ๋จ
- ํ ๋ฒ์ ์ฟผ๋ฆฌํ ์ ์๋ ์ํฐํฐ ์๊ฐ ๋ฌด์ ํ์ด ๋จ
์นดํ ๊ณ ๋ฆฌ๋ณ ์ปค์คํ ์๋ํฌ์ธํธ ๋ฐ Persisted ์ฟผ๋ฆฌ ์ ๋ฆฌ
์ปค์คํ ์๋ํฌ์ธํธ ๋๋ Persisted ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ๋, ๋ชจ๋ ์๋ํฌ์ธํธ๋ฅผ ์ ๋ฆฌํ๊ธฐ ์ํด ใGraphQL ์๋ํฌ์ธํธ ์นดํ ๊ณ ๋ฆฌใ๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค:

์๋ฅผ ๋ค์ด, ํด๋ผ์ด์ธํธ, ์ ํ๋ฆฌ์ผ์ด์ ๋๋ ๊ธฐํ ํ์ํ ์ ๋ณด๋ณ๋ก ์๋ํฌ์ธํธ๋ฅผ ๊ด๋ฆฌํ๋ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค:

์ปค์คํ ์๋ํฌ์ธํธ ๋ฐ Persisted ์ฟผ๋ฆฌ ๋ชฉ๋ก์์ ๊ฐ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ์นดํ ๊ณ ๋ฆฌ ๋งํฌ๋ฅผ ํด๋ฆญํ๊ฑฐ๋ ์๋จ์ ํํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ์นดํ ๊ณ ๋ฆฌ์ ํญ๋ชฉ๋ง ํ์๋ฉ๋๋ค.

์ธํธ๋ก์คํ์ ์ ํตํ ์คํค๋ง ํ์ฅ ์ฟผ๋ฆฌ
์คํค๋ง ์์์ ์ฒจ๋ถ๋ ์ปค์คํ
๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ด์ ํ๋ extensions๋ฅผ ํตํด ์ฟผ๋ฆฌํ ์ ์์ต๋๋ค.
์คํค๋ง์ ๋ชจ๋ ์ธํธ๋ก์คํ์
์์๊ฐ ์ ํ๋๋ก ์
๊ทธ๋ ์ด๋๋์ด, ๊ฐ๊ฐ ํด๋นํ๋ ใExtensionsใ ํ์
์ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ฒ ๋์์ต๋๋ค. ์ด ํ์
์ ํด๋น ์์์ ์ปค์คํ
์์ฑ์ ๋
ธ์ถํฉ๋๋ค.
# Using "_" instead of "__" in introspection type name to avoid errors in graphql-js
type _SchemaExtensions {
# Is the schema being namespaced?
isNamespaced: Boolean!
}
extend type __Schema {
extensions: _SchemaExtensions!
}
type _NamedTypeExtensions {
# The type name
elementName: String!
# The "namespaced" type name
namespacedName: String!
# Enum-like "possible values" for EnumString type resolvers, `null` otherwise
possibleValues: [String!]
# OneOf Input Objects are a special variant of Input Objects where the type system asserts that exactly one of the fields must be set and non-null, all others being omitted.
isOneOf: Boolean!
}
extend type __Type {
# Non-null for named types, null for wrapping types (Non-Null and List)
extensions: _NamedTypeExtensions
}
type _DirectiveExtensions {
# If no objects are returned in the field (eg: because they failed validation), does the directive still need to be executed?
needsDataToExecute: Boolean!
# Names or descriptions of the types the field directives is restricted to, or `null` if it supports any type (i.e. it defines no restrictions)
fieldDirectiveSupportedTypeNamesOrDescriptions: [String!]
}
extend type __Directive {
extensions: _DirectiveExtensions!
}
type _FieldExtensions {
isGlobal: Boolean!
# Useful for nested mutations
isMutation: Boolean!
# `true` => Only exposed when "Expose "sensitive" data elements" is enabled
isSensitiveDataElement: Boolean!
}
extend type __Field {
extensions: _FieldExtensions!
}
type _InputValueExtensions {
isSensitiveDataElement: Boolean!
}
extend type __InputValue {
extensions: _InputValueExtensions!
}
type _EnumValueExtensions {
isSensitiveDataElement: Boolean!
}
extend type __EnumValue {
extensions: _EnumValueExtensions!
}GraphQL ์๋ฒ ์ฝ๋์ WordPress ๋ถ๋ฆฌ ์๋ฃ
ํ๋ฌ๊ทธ์ธ์ ๊ตฌ๋ํ๋ ๊ธฐ๋ฐ GraphQL ์๋ฒ๋ ์ด์ ๋ ๋ฆฝ์ ์ธ PHP ์ปดํฌ๋ํธ๋ก ์ค์น ๋ฐ ์คํํ ์ ์์ต๋๋ค. ์ฆ, WordPress์ ๋ ๋ฆฝ์ ์ผ๋ก ๋์ํฉ๋๋ค.
์ด๋ฅผ ํตํด Gato GraphQL์ ๋ค๋ฅธ ํ๋ ์์ํฌ(์: Laravel)์์ ์ฌ์ฉํ๊ฑฐ๋, WordPress์ ์ฌ์ฉ ๊ฐ๋ฅ ์ฌ๋ถ์ ๊ด๊ณ์์ด(์ง์์ ํตํฉ ์์ ์คํ ์ ๋ฑ) ๋ชจ๋ PHP ํ๊ฒฝ์์ ํ์ฉํ ์ ์๋ ๊ธธ์ด ์ด๋ฆฝ๋๋ค.
์คํค๋ง ๊ตฌ์ฑ, ์ปค์คํ ์๋ํฌ์ธํธ, Persisted ์ฟผ๋ฆฌ ํธ์ง ์ ๋ฌธ์ ํ์
์คํค๋ง ๊ตฌ์ฑ, ์ปค์คํ ์๋ํฌ์ธํธ, Persisted ์ฟผ๋ฆฌ ํธ์ง ์ ํ์๋๋ ๋ชจ๋ ๋ธ๋ก์ ใinfoใ ๋ฒํผ์ด ์ถ๊ฐ๋์์ต๋๋ค. ํด๋ฆญํ๋ฉด ๋ชจ๋ฌ ์ฐฝ์ ๋ฌธ์๊ฐ ํ์๋ฉ๋๋ค.


๊ทธ ์ธ ๋ ๋ง์ ๊ธฐ๋ฅ
๋ค๋ฅธ ๋ชจ๋ ์ ๊ธฐ๋ฅ์ ํ์ธํ๋ ค๋ฉด ์ ๋ฆด๋ฆฌ์ค์ ์ ์ฒด ์ค๋ช ์ ์ฐธ์กฐํ์๊ฑฐ๋, ๋ณ๊ฒฝ ๋ก๊ทธ๋ฅผ ์ดํด๋ณด์ธ์.
๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์์ ํ๋ฌ๊ทธ์ธ์ ๋ค์ด๋ก๋ํ์ธ์.
๋ง์์ ๋์ จ๋ค๋ฉด, ์ฌ๋์ ๋๋ฆฌ ์ ํํด ์ฃผ์ธ์ โค๏ธ