๋ธ”๋กœ๊ทธ

๐Ÿค” ์ƒˆ๋กœ์šด Gato GraphQL ๋ฆด๋ฆฌ์Šค์— ์™œ 1.5๋…„์ด๋‚˜ ๊ฑธ๋ ธ์„๊นŒ์š”?

Leonardo Losoviz
์ž‘์„ฑ์ž: Leonardo Losoviz ยท

Gato GraphQL ๋ฒ„์ „ 0.9๊ฐ€ ์ถœ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ถœ์‹œ๊นŒ์ง€ ๊ฑฐ์˜ 1.5๋…„๊ฐ„์˜ ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„๊ณผ 16,000๊ฐœ ์ด์ƒ์˜ ์ปค๋ฐ‹์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ •๋ง ๊ธด ์‹œ๊ฐ„์ด์—ˆ์Šต๋‹ˆ๋‹ค!

Hacker News์—์„œ ์ด ๋ฐœํ‘œ๋ฅผ ๊ณต์œ ํ–ˆ์„ ๋•Œ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์งˆ๋ฌธ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค:

[...] 16k ์ปค๋ฐ‹์— ๋ฌด์—‡์ด ๋“ค์–ด ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ฐธ์—ฌํ–ˆ๋˜ ํ”„๋กœ์ ํŠธ ์ค‘ ์ปค๋ฐ‹ ์ˆ˜๊ฐ€ ๋งŒ ๊ฐœ๋ฅผ ๋„˜๋Š” ๊ฒƒ๋“ค์€ ์ˆ˜์‹ญ ๋ช…์—์„œ ์ˆ˜๋ฐฑ ๋ช…์ด ํ’€ํƒ€์ž„์œผ๋กœ ์ผํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. [...] ์ด ๊ธ€์—์„œ ๋‹ค๋ฃจ์ง€ ์•Š์€, ๊ทน๋ณตํ•ด์•ผ ํ•  ๋ณต์žก์„ฑ์ด ์žˆ์—ˆ๋˜ ๊ฑธ๊นŒ์š”?

์ปค๋ฐ‹ ์ˆ˜๋Š” ๊ทธ๋‹ค์ง€ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ง€ํ‘œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋งค์šฐ ๋‹จ์ˆœํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ•˜๋‚˜์˜ ์ปค๋ฐ‹์œผ๋กœ ํ‘ธ์‹œํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. 16k ์ปค๋ฐ‹ ์ค‘ ๋งŽ์€ ์ˆ˜๋Š” "typo" ์ˆ˜์ •์ด๊ฑฐ๋‚˜, README์˜ ์„ค๋ช…์„ ์กฐ๊ธˆ ๊ฐœ์„ ํ•œ ๊ฒƒ์— ๋ถˆ๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , ์ปค๋ฐ‹ ์ˆ˜๋Š” ์‹ค์ œ ์ž‘์—…๋Ÿ‰์˜ ๋Œ€๋žต์ ์ธ ์ฒ™๋„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•œ ๋ฒˆ์— ์ˆ˜์‹ญ ๊ฐœ, ์‹ฌ์ง€์–ด ์ˆ˜๋ฐฑ ๊ฐœ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋‹ด์€ ๋Œ€๊ทœ๋ชจ ์ปค๋ฐ‹๋„ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ๋ฒ„์ „ 0.8๊ณผ 0.9 ์‚ฌ์ด์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์‹ค๋กœ ๋ฐฉ๋Œ€ํ•˜๋ฉฐ, ๊ทธ๊ฒƒ์„ ์™„์„ฑํ•˜๋Š” ๋ฐ ๋…ธ๋ ฅ๊ณผ ์‹œ๊ฐ„์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ธ”๋กœ๊ทธ ๊ธ€์—์„œ๋Š” ๊ทธ ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋“ค์ด ๋ฌด์—‡์ธ์ง€ ์„ค๋ช…ํ•˜์—ฌ, ์™œ ์ด๋ ‡๊ฒŒ ์˜ค๋žœ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ๋Š”์ง€ ๋ฐํžˆ๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ์™€ ํ•จ๊ป˜, ์ฝ”๋“œ๋ฒ ์ด์Šค์— ์ถ”๊ฐ€๋œ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์˜ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ๋“ค์€ ๊ณง ์ถœ์‹œ๋  ๋ฒ„์ „ 1.0๊ณผ ํ•จ๊ป˜ ๋น›์„ ๋ฐœํ•˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

GraphQL ์„œ๋ฒ„์˜ ๋ฐฐ๊ฒฝ

๋จผ์ €, ์ด ์—”์ง„์˜ ์—ญ์‚ฌ์™€ ๋™์ž‘ ๋ฐฉ์‹์— ๊ด€ํ•œ ๊ธฐ์ˆ ์ ์ธ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ๊ฐ„๋žตํžˆ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

(์ด ๋‚ด์šฉ์€ ์ฃผ๋กœ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ธฐ์ˆ ์ ์ธ ๋‚ด์šฉ์— ๊ด€์‹ฌ์ด ์—†์œผ์‹  ๋ถ„์€ ๋‹ค์Œ ์„น์…˜์œผ๋กœ ๋„˜์–ด๊ฐ€์…”๋„ ๋ฉ๋‹ˆ๋‹ค.)

Gato GraphQL์€ PoP(PHP์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์—”์ง„์œผ๋กœ, JavaScript์˜ React๋‚˜ Vue์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค) ์œ„์— ๊ตฌ์ถ•๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์—”์ง„์— ๋Œ€ํ•œ ์˜์กด์„ฑ์€ ์ ˆ๋Œ€์ ์ด๋ฉฐ, ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํ”Œ๋Ÿฌ๊ทธ์ธ์€ GitHub์˜ GatoGraphQL/GatoGraphQL ๋ชจ๋…ธ๋ ˆํฌ์— ํ˜ธ์ŠคํŒ…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๋ถ€์ ์œผ๋กœ, ์ด ์˜์กด ๊ด€๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.

Gato GraphQL์€ GraphQL ์ฟผ๋ฆฌ๋ฅผ ๋จผ์ € ๋™๋“ฑํ•œ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋ธ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , PoP๊ฐ€ ๊ทธ ๋ชจ๋ธ์„ ํ•ด๊ฒฐํ•˜์—ฌ ํ•„์š”ํ•œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ๋’ค, ๋ฐ์ดํ„ฐ๊ฐ€ GraphQL ์ฟผ๋ฆฌ์˜ ํ˜•ํƒœ๋กœ ์ •ํ˜•ํ™”๋ฉ๋‹ˆ๋‹ค.

2013๋…„์—์„œ 2014๋…„๊ฒฝ์— PoP ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ GraphQL์€ ์•„์ง ์กด์žฌํ•˜์ง€ ์•Š์•˜์œผ๋ฉฐ, ์ปดํฌ๋„ŒํŠธ ๋ชจ๋ธ์„ ๋ฐ์ดํ„ฐ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•๋ก ์€ ์ฒ˜์Œ๋ถ€ํ„ฐ ์„ค๊ณ„ยท๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ฐธ์กฐํ•  ๋ชจ๋ธ์ด ์—†์—ˆ๋‹ค๋Š” ์ (๊ฐœ๋…์— ๋Œ€ํ•ด์„œ๋Š” GraphQL, ๊ตฌํ˜„์— ๋Œ€ํ•ด์„œ๋Š” graphql-js ์ฐธ์กฐ ํ”„๋กœ์ ํŠธ์™€ ๊ฐ™์€)์€ ๋‚˜์ค‘์— ์„ค๋ช…ํ•˜๊ฒ ์ง€๋งŒ, ์žฅ์• ๋ฌผ์ด ๋˜๊ธฐ๋„ ํ•˜๊ณ  ์ถ•๋ณต์ด ๋˜๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

PoP๋Š” ์ฒ˜์Œ์— ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ์›น์‚ฌ์ดํŠธ ์ „์ฒด๋ฅผ HTML๋กœ ๋ Œ๋”๋งํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์œผ๋ฉฐ, ํŽ˜์ด์ง€ URL์— ?output=json์„ ์ถ”๊ฐ€ํ•˜๋ฉด JSON ํ˜•์‹์œผ๋กœ ์›์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋…ธ์ถœํ•˜๊ณ , ์ถ”๊ฐ€ URL ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ€์ ธ์˜ฌ ๋ฐ์ดํ„ฐ(์„ค์ •, DB ๊ฐ์ฒด ๋ฐ์ดํ„ฐ)๋ฅผ ๋”์šฑ ์„ธ๋ฐ€ํ•˜๊ฒŒ ์„ ํƒํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜ ๋งํฌ๋“ค์„ ํด๋ฆญํ•ด ๋ณด์„ธ์š”(๋ชจ๋‘ ๋™์ผํ•œ ์›น ํŽ˜์ด์ง€๋ฅผ ๊ฐ€๋ฆฌํ‚ค์ง€๋งŒ URL ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค). ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅธ์ง€ ํ™•์ธํ•ด ๋ณด์„ธ์š”:

๋งˆ์ง€๋ง‰ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด ํ•œ ๊ฐ€์ง€ ๊นจ๋‹ฌ์Œ์ด ์ฐพ์•„์˜ต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์‚ฌ์‹ค์ƒ GraphQL์ž…๋‹ˆ๋‹ค! ์œ ์ผํ•œ ํฐ ์ฐจ์ด์ ์€ ์‘๋‹ต ๋‚ด ๋ฐ์ดํ„ฐ๊ฐ€ ์•”๋ฌต์ ์ด๋ผ๋Š” ๊ฒƒ์œผ๋กœ, ํŽ˜์ด์ง€์— ํฌํ•จ๋œ ์ปดํฌ๋„ŒํŠธ(PHP)์— ์˜ํ•ด ์ด๋ฏธ ์ •์˜๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด GraphQL์—์„œ๋Š” ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ์˜ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ 2019๋…„๊ฒฝ์— GraphQL์„ ์•Œ๊ฒŒ ๋˜์—ˆ์„ ๋•Œ, PoP๋กœ GraphQL ์„œ๋ฒ„๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ์ €์—๊ฒŒ ๋‹น์—ฐํ•œ ์ผ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ํ•ด์•ผ ํ•  ์ผ์€ GraphQL ์ฟผ๋ฆฌ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„, ๊ทธ ์ฟผ๋ฆฌ์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋ธ์„ ์ฆ‰์„์—์„œ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๋ฟ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ทธ๋ ‡๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž˜ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋А๋ ธ์Šต๋‹ˆ๋‹ค. PoP๋Š” ์ž์ฒด ์ž…๋ ฅ ํ˜•์‹์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, GraphQL ์ฟผ๋ฆฌ๋ฅผ PoP ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค:

  1. GraphQL ์ฟผ๋ฆฌ๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ , ๊ทธ๋‹ค์Œ
  2. ์ฟผ๋ฆฌ๋ฅผ PoP ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๊ทธ๋‹ค์Œ
  3. PoP ํ˜•์‹์„ ํŒŒ์‹ฑํ•œ๋‹ค

GraphQL ์ฟผ๋ฆฌ ํŒŒ์‹ฑ์ด ๋‘ ๋ฒˆ ์ด๋ฃจ์–ด์กŒ์œผ๋ฉฐ(GraphQL์šฉ ํ•œ ๋ฒˆ, PoP์šฉ ํ•œ ๋ฒˆ), PoP ํ˜•์‹์€ AST๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์„ ๋ฐ˜๋ณตํ•ด์„œ ํŒŒ์‹ฑํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ์Šต๋‹ˆ๋‹ค. (AST๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์€ ์กฐ์•…ํ•œ ์ฝ”๋”ฉ์ด์—ˆ์ง€๋งŒ, ๋”ฐ๋ฅผ ๋ช…์„ธ๊ฐ€ ์—†์—ˆ๊ณ  ๊ฐœ๋ฐœ์ด ์œ ๊ธฐ์ ์œผ๋กœ ์ง„ํ–‰๋˜์–ด ๋‹จ์ˆœํ•œ substr(...)์œผ๋กœ ๋งค์ผ๋งค์ผ์„ ํ•ด๊ฒฐํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.)

GraphQL ๋ช…์„ธ๊ฐ€ ์—†์—ˆ๋˜ ๊ฒƒ์ด ์žฅ์• ๋ฌผ์ด์—ˆ๋˜ ์ด์œ ๊ฐ€ ๋ฐ”๋กœ ์ด๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ œ ์†”๋ฃจ์…˜์€ ๋А๋ ธ๊ณ (๋ฒ„์ „ 0.8 ์‹œ์ ์˜ ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค), ์ˆ˜์ •ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์—”์ง„์„ GraphQL-first๋กœ ์ „ํ™˜ํ•˜๊ธฐ

์ œ๊ฐ€ ๊ฒฐ์ •ํ•œ ํ•ด๊ฒฐ์ฑ…์€ PoP๊ฐ€ ๋„ค์ดํ‹ฐ๋ธŒ๋กœ GraphQL ์–ธ์–ด๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด PoP์— ์ž…๋ ฅ์œผ๋กœ GraphQL ์ฟผ๋ฆฌ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ, ์ถ”๊ฐ€ ์–ด๋Œ‘ํ„ฐ ์—†์ด, ๋˜ํ•œ ์ž‘์—…์„ ๋‘ ๋ฒˆ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ ๋„ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋ธ๋กœ ๋ณ€ํ™˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ PoP ํ”„๋กœ์ ํŠธ๋ฅผ, GraphQL ์ฟผ๋ฆฌ๋ฅผ ํ•ด๊ฒฐํ•˜๋„๋ก ๊ฐœ์กฐ๋œ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์›น์‚ฌ์ดํŠธ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง PHP ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ, ์‹ค์ œ GraphQL ์„œ๋ฒ„ ์ž์ฒด๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ–ˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” ์ดํ›„ ๋Œ€๊ทœ๋ชจ ๋ณ€ํ˜์„ ๊ฑฐ์ณ, ์—”์ง„ ๋‚ด ๋ชจ๋“  PHP ์„œ๋น„์Šค ๊ฐ„ ์ƒํƒœ ์ „๋‹ฌ์˜ ๊ธฐ๋ฐ˜์œผ๋กœ GraphQL AST๊ฐ€ ๋„์ž…๋˜์—ˆ์Šต๋‹ˆ๋‹ค. GraphQL AST ๊ฐ์ฒด๊ฐ€ PoP์˜ ์ž…๋ ฅ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค(์ฟผ๋ฆฌ ๋ฌธ์ž์—ด ๋Œ€์‹ ).

PHP ๊ธฐ๋ฐ˜์˜ ๋‹ค๋ฅธ GraphQL ์„œ๋ฒ„๋“ค์€ graphql-php์— ์˜์กดํ•˜์ง€๋งŒ, Gato GraphQL ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์œ ์ง€๋ณด์ˆ˜ ์ธก๋ฉด์—์„œ๋Š” ๋‚˜์œ ์†Œ์‹์ด์ง€๋งŒ(๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ฝ”๋”ฉํ•œ ๊ฒƒ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—), ๋…๋ฆฝ์„ฑ ์ธก๋ฉด์—์„œ๋Š” ์ข‹์€ ์†Œ์‹์ž…๋‹ˆ๋‹ค. ์ œ ์†๋„์™€ ํŒ๋‹จ์— ๋”ฐ๋ผ ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์ปค์Šคํ…€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๊ทธ๋ž˜์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ด๋ฏธ ใ€Œoneofใ€ ์ž…๋ ฅ ๊ฐ์ฒด๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค).

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜ ์„น์…˜์—์„œ ๋ณด์—ฌ๋“œ๋ฆฌ๊ฒ ์ง€๋งŒ, ์ด๊ฒƒ์€ ํฐ ์ด์ ์ž…๋‹ˆ๋‹ค.

GraphQL์— ๋…์ฐฝ์ ์ธ ๊ธฐ๋Šฅ ํ†ตํ•ฉํ•˜๊ธฐ

GraphQL์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ์ดํ„ฐ ํŽ˜์นญ๊ณผ ์—ฐ๊ด€๋ฉ๋‹ˆ๋‹ค. ๋‹น์—ฐํžˆ Gato GraphQL์—์„œ ์ž„์˜์˜ ๋ฐ์ดํ„ฐ(๊ฒŒ์‹œ๋ฌผ, ์‚ฌ์šฉ์ž, ๋Œ“๊ธ€ ๋“ฑ)๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

query {
  posts(
    pagination: { limit: 5, offset: 20 }
    sort: { by: DATE, order: ASC }
  ) {
    id
    title
    content
    url
    author {
      id
      name
      url
    }
    comments {
      id
      date
      content
    }
  }
}

ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ์‰ฝ๊ฒŒ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊ฒฐ๊ณผ์— ๋ถˆ๊ณผํ•ฉ๋‹ˆ๋‹ค. GraphQL์€ ๋ฐ์ดํ„ฐ ์กฐ์ž‘ ๋ฐ ๋ณ€ํ™˜, ์‹ฌ์ง€์–ด ์„œ๋น„์Šค ๊ฐ„ ์ค‘๊ฐœ ์—ญํ• ์„ ํ•˜๋Š” ํŒŒ์ดํ”„๋ผ์ธ์— GraphQL์„ ํ†ตํ•ฉํ•˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ๋‹ค๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€์—๋„ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GraphQL์ด ์œ ์šฉํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€์˜ ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • ํ•˜๋‚˜ ์ด์ƒ์˜ ์†Œ์Šค์—์„œ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๊ณ (์˜ˆ: WordPress ์‚ฌ์ดํŠธ์˜ ์‚ฌ์šฉ์ž์™€ Mailchimp์˜ ๋‰ด์Šค๋ ˆํ„ฐ ์—ฐ๋ฝ์ฒ˜ ๋ฐ์ดํ„ฐ), ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ๋‹จ์ผ ๋ฐ์ดํ„ฐ์…‹์œผ๋กœ ๋ถ„์„ํ•˜๊ธฐ
  • ์‚ฌ์ดํŠธ์˜ ์ฝ˜ํ…์ธ ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ์ž‘์—… ์‹คํ–‰ํ•˜๊ธฐ:
    • ์ผํšŒ์„ฑ ์ž‘์—…์œผ๋กœ, ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์ดํŠธ๋ฅผ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์œผ๋กœ ์ด์ „ํ•  ๋•Œ ์ฝ˜ํ…์ธ ์™€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ „์ฒด์—์„œ "www.myoldsite.com"์„ "mynewsite.com"์œผ๋กœ ๊ต์ฒดํ•˜๊ธฐ
    • ์ง€์†์ ์ธ ์ž‘์—…์œผ๋กœ, ์ž‘์„ฑ์ž๊ฐ€ ์ƒˆ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์„ ๋ฐœํ–‰ํ•  ๋•Œ๋งˆ๋‹ค "http://"๋ฅผ "https://"๋กœ ๊ต์ฒดํ•˜๊ธฐ
  • Google Translate API์— ์—ฐ๊ฒฐํ•˜์—ฌ ๋ชจ๋“  ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์„ ๋‹ค๋ฅธ ์–ธ์–ด๋กœ ๋ฒˆ์—ญํ•˜๊ธฐ
  • ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์ด ๋ฐœํ–‰๋œ ํ›„ ์ž๋™์œผ๋กœ ํŠธ์œ— ์ „์†กํ•˜๊ธฐ

PoP๋Š” ์ด๋Ÿฌํ•œ ๋‹ค๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์ง€์›ํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์œผ๋ฉฐ, GraphQL์—์„œ (๊ธฐ๋ณธ์ ์œผ๋กœ) ์ง€์›๋˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ๋“ค์„ ํ†ตํ•ด ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค:

  • ์Šคํ‚ค๋งˆ์˜ ๋ชจ๋“  ํƒ€์ž…์— ์ถ”๊ฐ€๋˜๋Š” ใ€Œ๊ธฐ๋Šฅใ€ ํ•„๋“œ(ใ€Œ๋ฐ์ดํ„ฐใ€ ํ•„๋“œ์— ๋”ํ•˜์—ฌ) ์ง€์›
  • ๋™์ผํ•œ ์ฟผ๋ฆฌ ๋‚ด์—์„œ ํ•œ ํ•„๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅธ ํ•„๋“œ์˜ ์ž…๋ ฅ์œผ๋กœ ์ „๋‹ฌํ•˜๊ธฐ
  • ๋””๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ํ•ฉ์„ฑํ•˜์—ฌ ํ•œ ๋””๋ ‰ํ‹ฐ๋ธŒ๊ฐ€ ๋‹ค๋ฅธ ๋””๋ ‰ํ‹ฐ๋ธŒ์˜ ๋™์ž‘์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ
  • ํ•„๋“œ ๊ฐ’์— ๋”ฐ๋ผ ๋””๋ ‰ํ‹ฐ๋ธŒ ์ ์šฉ ์—ฌ๋ถ€๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•˜๊ธฐ

๊ทธ๋ฆฌ๊ณ  ์ €๋Š” ์ด ๊ธฐ๋Šฅ๋“ค์„ GraphQL ์„œ๋ฒ„์—์„œ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ ์ฝ”๋”ฉํ•ด ๋‘์—ˆ๊ณ , ๋ถ„๋ช…ํžˆ ๊ฐ€์น˜ ์žˆ๋Š” ๊ฒƒ๋“ค์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

v0.9๊ฐ€ ์ด๋ ‡๊ฒŒ ์˜ค๋ž˜ ๊ฑธ๋ฆฐ ๋‘ ๋ฒˆ์งธ ์ด์œ ๋Š”, GraphQL ๋ช…์„ธ๋ฅผ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š๋Š” ๋ฐฉ์‹์œผ๋กœ ์ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๋“ค์„ GraphQL์— ํ†ตํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค(์˜ˆ๋ฅผ ๋“ค์–ด, GraphQL ๊ตฌ๋ฌธ์— ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์€ ํ—ˆ์šฉ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค).

GraphQL์—์„œ์˜ ๋ฐ์ดํ„ฐ ์กฐ์ž‘ ์˜ˆ์‹œ

ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ GraphQL์— ๋„์ž…๋œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๋“ค์€ ๋ฒ„์ „ 1.0์ด ์ถœ์‹œ๋˜๋ฉด ๊ฐ€๊นŒ์šด ๋ฏธ๋ž˜์— ๋”์šฑ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณด์ด๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ผ๋ถ€๋Š” ์ด๋ฏธ ๋ง›๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ GraphQL ์ฟผ๋ฆฌ๋Š” ์™ธ๋ถ€ REST API์—์„œ ์‚ฌ์šฉ์ž ํ•ญ๋ชฉ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ค๊ณ (์‘๋‹ต์—์„œ @removeํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค), ๋™์ผํ•œ ์ฟผ๋ฆฌ ๋‚ด ๋‹ค๋ฅธ ํ•„๋“œ์— ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•˜๊ณ , ๊ฐ ํ•ญ๋ชฉ์—์„œ ์ด๋ฉ”์ผ ์†์„ฑ์„ ์ถ”์ถœํ•˜๋ฉฐ, ๋งˆ์ง€๋ง‰์œผ๋กœ ๋™์ผ ํ•ญ๋ชฉ์˜ ์–ธ์–ด๊ฐ€ ์˜์–ด ๋˜๋Š” ๋…์ผ์–ด์ธ ๊ฒฝ์šฐ์—๋งŒ ์ด๋ฉ”์ผ์„ ๋Œ€๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

###################################################################
# Fetch data from a REST endpoint, extract the emails, and make
# uppercase those ones from users with a special language.
###################################################################
query ExtractEmailsFromAPIAndUpperCaseSpecialOnes
{
  # Retrieve data from a REST API endpoint
  userEntries: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/newsletter/v1/subscriptions"
    }
  ) # @remove   # <= Uncomment this directive to not print the API data
 
  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": {
    "userEntries": [
      {
        "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"
      }
    ],
    "emails": [
      "ABRACADABRA@GANGA.COM",
      "longon@caramanon.com",
      "RANCOTANTO@PARABARA.COM",
      "quezarapadon@quebrulacha.net",
      "TEST@TEST.COM",
      "emilanga@pedrola.com"
    ]
  }
}

์ง์ ‘ ํ™•์ธํ•ด ๋ณด์„ธ์š”! "Run" ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”:

###################################################################
# Fetch data from a REST endpoint, extract the emails, and make
# uppercase those ones from users with a special language.
###################################################################
query ExtractEmailsFromAPIAndUpperCaseSpecialOnes {
  # Retrieve data from a REST API endpoint
  userEntries: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/newsletter/v1/subscriptions"
    }
  )
  # @remove   # <= Uncomment this directive to not print the API data
  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
}

GraphQL ๋ช…์„ธ์— ๊ตฌ์†๋˜์ง€ ์•Š์•˜๋˜ ๊ฒƒ์ด ์žฅ์• ๋ฌผ์ด์—ˆ๋‹ค๊ณ  ๋งํ–ˆ์ง€๋งŒ, (๋Œ์ด์ผœ ๋ณด๋ฉด) ์ถ•๋ณต์ด๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค. GraphQL ๋ช…์„ธ์˜ ์ œ์•ฝ์ด ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Ÿฌํ•œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๋“ค์„ ์ž์œ ๋กญ๊ฒŒ ๊ฟˆ๊ฟ€ ์ˆ˜ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด์ œ ์ด ๊ธฐ๋Šฅ๋“ค์ด Gato GraphQL๋กœ ์ด์ „๋˜๋ฉด์„œ, WordPress ์‚ฌ์ดํŠธ์˜ ์ฝ˜ํ…์ธ  ๊ฒ€์ƒ‰, ์กฐ์ž‘, ๋ณ€ํ™˜๊ณผ ๊ด€๋ จ๋œ ๋ชจ๋“  ์šฉ๋„์—์„œ ๋งค์šฐ ์œ ์šฉํ•œ ๋™๋ฐ˜์ž๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋‹ค๋งŒ, ์ด ๊ธฐ๋Šฅ๋“ค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๊ณง ์ถœ์‹œ๋  v1.0๋ถ€ํ„ฐ์ž…๋‹ˆ๋‹ค.)

์‹œ๊ฐ„์ด ๊ฑธ๋ ธ์ง€๋งŒ, ๊ทธ ๋…ธ๋ ฅ์€ ๋ถ„๋ช… ๊ฐ€์น˜ ์žˆ๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ ๋ฐ”๋กœ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”!

์˜ค๋žœ ๊ธฐ๋‹ค๋ฆผ์ด ๊ทธ๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ์—ˆ๋‹ค๊ณ  ํ™•์‹ ํ•˜์…จ๋‚˜์š”? ๊ทธ๋ ‡๊ฒŒ ์ƒ๊ฐํ•ด ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์ง์ ‘ ํ™•์ธํ•ด ๋ณด์„ธ์š”:

๊ฐœ๋ฐœ ํ˜„ํ™ฉ, ์ƒˆ๋กœ์šด ๋ฌธ์„œ, v1.0์„ ํฌํ•จํ•œ ํ–ฅํ›„ ๋ฆด๋ฆฌ์Šค ์†Œ์‹์„ ๋ฐ›์•„๋ณด๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”? ๋‰ด์Šค๋ ˆํ„ฐ ๊ตฌ๋…์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

GitHub์—์„œ ์˜คํ”ˆ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”? GatoGraphQL/GatoGraphQL์„ ๋ฐฉ๋ฌธํ•ด ๋ณด์„ธ์š” (๋ณ„์„ ์ฃผ์‹œ๋Š” ๊ฒƒ๋„ ์–ธ์ œ๋“ ์ง€ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค... ๋ณ„์€ ์ •๋ง ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค! โญ๏ธโญ๏ธโญ๏ธ)

๊ทธ๋Ÿฐ๋ฐ, WordPress์—์„œ ์–ด๋–ค ์ฝ˜ํ…์ธ  ๋ณ€ํ™˜์ด ํ•„์š”ํ•˜์‹ ๊ฐ€์š”(์ด๋ฏธ ์ „์šฉ ์ƒ์—…์šฉ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ๊ณ„์‹  ๊ฒฝ์šฐ๋„ ํฌํ•จํ•˜์—ฌ)? ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์•Œ๋ ค ์ฃผ์„ธ์š”.

์ด ๋‚ด์šฉ์ด ๋งˆ์Œ์— ๋“œ์‹ ๋‹ค๋ฉด, ์นœ๊ตฌ์™€ ๋™๋ฃŒ์—๊ฒŒ ๊ณต์œ ํ•˜์—ฌ ์‚ฌ๋ž‘์„ ๋„๋ฆฌ ์ „ํ•ด ์ฃผ์„ธ์š” โค๏ธ.


๋‰ด์Šค๋ ˆํ„ฐ ๊ตฌ๋…ํ•˜๊ธฐ

Gato GraphQL์˜ ๋ชจ๋“  ์—…๋ฐ์ดํŠธ๋ฅผ ๋†“์น˜์ง€ ๋งˆ์„ธ์š”.