Skip to content

Latest commit

ย 

History

History
545 lines (381 loc) ยท 17.8 KB

GraphQL.md

File metadata and controls

545 lines (381 loc) ยท 17.8 KB

GraphQL

์ž‘์„ฑ์ž : ์ด๋ณ‘๋ฏผ


GraphQL ApolloGraphQL

Reference

Grapyql

Grapyql-kr


GraphQL์ด๋ž€?

GraphQL์€ 2015๋…„๋„์— Facebook์—์„œ ๋ฐœํ‘œํ•œ "๋ฐ์ดํ„ฐ ์งˆ์˜์–ด"์ด๋‹ค.

GraphQLL์€ Graph Query Language์˜ ์•ฝ์ž๋กœ QueryLanguage ๋œป์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ์–ธ์–ด๋กœ ServerAPI ์ •๋ณด๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ๊ฒƒ์— ํŠนํ™”๋œ QueryLanguage์ด๋‹ค.


GraphQL ๊ณต์‹ ํŽ˜์ด์ง€์—์„œ์˜ GraphQL ์†Œ๊ฐœ

GraphQL์€ ํŠน์ •ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ํŠน์ •ํ•œ ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„๊ณผ ๊ด€๊ณ„๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉฐ ๊ธฐ์กด ์ฝ”๋“œ์™€ ๋ฐ์ดํ„ฐ์— ์˜ํ•ด ๋Œ€์ฒด๋œ๋‹ค.

GraphQL์€ API๋ฅผ ์œ„ํ•œ ์ฟผ๋ฆฌ ์–ธ์–ด์ด๋ฉฐ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ๋Ÿฐํƒ€์ž„์ด๋‹ค.

GraphQL์€ API์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์™„๋ฒฝํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ์„ค๋ช…์„ ์ œ๊ณตํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ํ•„์š”ํ•œ ๊ฒƒ์„ ์ •ํ™•ํ•˜๊ฒŒ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ API๋ฅผ ์‰ฝ๊ฒŒ ์ง„ํ™”์‹œํ‚ค๊ณ  ๊ฐ•๋ ฅํ•œ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ์ง€์›ํ•œ๋‹ค.

GraphQL ์žฅ์ 

  • ํ•„์š”ํ•œ ๊ฒƒ์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ•ญ์ƒ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ๋‹จ์ผ ์š”์ฒญ์œผ๋กœ ํ•„์š”ํ•œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ•๋ ฅํ•œ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๊ฐ€ ์žˆ๋‹ค. (์ž์ฒด์ ์œผ๋กœ API ํ…Œ์ŠคํŠธ ๋„๊ตฌ๊ฐ€์žˆ๋‹ค.)

GraphQL์— ๋Œ€ํ•œ ์„ค๋ฌธ

https://2020.stateofjs.com/ko-KR/technologies/datalayer/

โ€œstate of JS 2020โ€์˜ ์„ค๋ฌธ์—์„œ DataLayer ๋ถ€๋ถ„์—์„œ ๊ด€์‹ฌ๋„์™€ ๋งŒ์กฑ๋„ ๋น„์œจ 1์œ„๋ฅผ ์ฐจ์ง€ ํ–ˆ๋‹ค.
๋˜ํ•œ ์‚ฌ์šฉํ•ด๋ดค๊ณ  ๋‹ค์‹œ ์‚ฌ์šฉํ•  ๊ฒƒ์ž„์ด 45%, ๋“ค์–ด๋ดค๊ณ  ๋ฐฐ์šฐ๊ณ  ์‹ถ์Œ์ด 45%์œผ๋กœ ๊ธ์ •๋„๋„ ๋งค์šฐ ๋†’๋‹ค.


RESTAPI์˜ ๋ฌธ์ œ์ 

GraphQL์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ RESTAPI์˜ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋‹ค.

1. Over Fetching

๋‹ค์Œ์€ ๊ฐ™์€ ์ฑ… ์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ์—์„œ ๋ณด์—ฌ์ง€๋Š” ๋‹ค๋ฅธ ํŽ˜์ด์ง€์˜ ์ฑ… ์ •๋ณด์ด๋‹ค.

overFetcing1

overFetcing2

์ฒซ๋ฒˆ์งธ ์‚ฌ์ง„์—๋Š” "์ œ๋ชฉ", "์ง€์€์ด", "์ถœํŒ์‚ฌ", "๊ฐ€๊ฒฉ"์™ธ์— "์ถœํŒ์ผ"๊ณผ ๊ฐ™์ด ์•„๋ž˜ ๋ถ€๋ถ„์˜ ์ •๋ณด์—๋Š” ์—†๋Š” ์ •๋ณด๊ฐ€ ์žˆ๋‹ค.

๋ณดํ†ต API๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฑ…์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ 

    {
        "title": "์ปดํ“จํ„ฐ๊ตฌ์กฐ๋ก  (๊น€์ข…ํ˜„)",
        "price": 28000,
        "publisher": "์ƒ๋Šฅ",
        "author": "๊น€์ข…ํ˜„",
        "publishedDate": "2019.02"
        ...
    }

๊ฐ ํŽ˜์ด์ง€์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ํ™”๋ฉด์— ๋ณด์—ฌ ์ฃผ๊ฒŒ ๋œ๋‹ค.
์ฆ‰, ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๊ฐ™์ด ์ „๋‹ฌ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

ํ•„์š”ํ•œ ์ •๋ณด๋ณด๋‹ค ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ ๋ฐ›์•„ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ๊ณจ๋ผ๋‚ด๊ธฐ ์œ„ํ•œ ์ž‘์—…์„ ์ถ”๊ฐ€๋กœ ์ง„ํ–‰ํ•˜์—ฌ์•ผํ•œ๋‹ค.

โ†’ ์ด๊ฒƒ์„ Over-Fetching์ด๋ผ ํ•œ๋‹ค.

2. Under Fetching

๋‹ค์Œ ๋ฌธ์ œ์ ์€ Under Fetcing์ด๋‹ค.

์‚ฌ์ดํŠธ๋ฅผ ๋“ค์–ด ๊ฐ€๊ฒŒ ๋˜๋ฉด ์ƒํ’ˆ์— ๋Œ€ํ•œ ์ •๋ณด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ค‘๊ฐ„ ์ค‘๊ฐ„ ๊ด‘๊ณ ๊ฐ€ ๋ณด์ด๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค.

์‚ฌ์ดํŠธ์˜ ๊ฒฝ์šฐ ๋‹จ ํ•œ๋ฒˆ์˜ API๋ฅผ ํ†ตํ•ด์„œ ๋ฐ›์•„ ์˜ค๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ํ”„๋ก ํŠธ์—”๋“œ์˜ ์˜์—ญ๋ณ„๋กœ ํŽ˜์ด์ง€์˜ ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ๊ฐ๊ธฐ ๋‹ค๋ฅธ API๋กœ ๋ถ€ํ„ฐ ๊ฐ’์„ ๋ฐ›์•„ ์˜ค๊ฒŒ ๋œ๋‹ค.

underFetcing

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํ•œ ํ™”๋ฉด์—์„œ ํ•„์š”๋กœํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ํ•˜๋‚˜์˜ endpoint๋กœ์˜ ์š”์ฒญ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์ง€ ๋ชปํ•˜๊ณ  ๊ทธ ์ด์ƒ์˜ endpoint์— ์š”์ฒญํ•ด์•ผํ•œ๋‹ค.

ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ํ˜ธ์ถœ์ด ํ•„์š”ํ•˜์—ฌ ์ถ”๊ฐ€์ ์ธ ๋ฆฌ์†Œ์Šค ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๊ณ , ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ํ†ตํ•ด ์ „๋‹ฌ ๋ฐ›์€ ์ •๋ณด๋ฅผ ์กฐํ•ฉํ•˜๋Š” ์ถ”๊ฐ€ ์ž‘์—…์ด ๋ฐœ์ƒํ•œ๋‹ค.

โ†’ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ Under-Fetching์ด๋ผ๊ณ  ํ•œ๋‹ค.

3. ๊ธฐํƒ€

Over-Fetching๊ณผ Under-Fetching ์ด์™ธ์—๋„ RESTAPI๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ผ์ผ์ด API์˜ URL๋ฅผ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผํ•˜๋Š”๋ฐ GraphQL์—์„œ๋Š” ๋‹จ์ผ endpoint๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ๋ถˆํŽธํ•จ๋„ ํ•ด์†Œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ API๋ฅผ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด์•ผ ํ•˜๋Š”๋ฐ GraphQL์—์„œ๋Š” ์ž์ฒด์ ์œผ๋กœ ๊ฐœ๋ฐœ ๋„๊ตฌ๋ฅผ ์ง€์›ํ•œ๋‹ค.

๊ฐœ๋ฐœ์ž ๋„๊ตฌ

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋”ฐ๋กœ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

RESTAPI๋ฅผ ํ…Œ์ŠคํŠธํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” Insomnia์™€ ๊ฐ™์€ ๊ฐœ๋ฐœ ๋„๊ตฌ ์ด๋‹ค.


GraphQL ๊ฐœ๋…

  • query

    ์ฟผ๋ฆฌ๋Š” RESTAPI์—์„œ ๋ณด๋ฉด CRUD์ค‘ R์ด๋ผ๊ณ  ๋ณด๋ฉด๋œ๋‹ค.
    ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ž‘์„ฑํ•ด์„œ ์š”์ฒญํ•˜๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

  • mutation

    ๋ฎคํ…Œ์ด์…˜์€ ์ฟผ๋ฆฌ์™€ ๋‹ค๋ฅธ ๊ฒƒ์€ ์—†์ง€๋งŒ RESTAPI์—์„œ CRUD์ค‘ CUD์— ํ•ด๋‹นํ•œ๋‹ค.
    ๋ฐ์ดํ„ฐ์˜ ์ถ”๊ฐ€, ์ˆ˜์ •, ์‚ญ์ œ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • schema

    ๊ฐ์ฒด type์„ ์ •์˜ํ•ด ๋‘๋Š” ๋ถ€๋ถ„์ด๋‹ค.

        type User {
            name: String!
        }

    User๋Š” GraphQL์˜ ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ๊ฐ์ฒด ํƒ€์ž…์ด๋‹ค.
    name์€ User์˜ ํƒ€์ž…์˜ ํ•„๋“œ ์ด๊ณ  String์€ ์Šค์นผ๋ผ ํƒ€์ž…์ค‘ ํ•˜๋‚˜์ด๋ฉฐ, !๋Š” ์ด ํ•„๋“œ๋ฅผ ์ฟผ๋ฆฌํ•  ๋•Œ ํ•ญ์ƒ ๊ฐ’์„ ๋ฐ˜ํ™˜ ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

  • resolver ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„ ๊ณผ์ •์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.
    ์š”์ฒญํ•˜๋Š” ๊ฐ’์˜ ๋ฐ์ดํ„ฐ์˜ ๊ตฌ์กฐ๋ฅผ ์›ํ•˜๋Š”๋ฐ๋กœ ๋ฐ”๊พธ์–ด์„œ ๋ฐ˜ํ™˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.


GraphQL ์˜ˆ์ œ

GraphQL์€ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ™์€ ๊ฒƒ์ด ์•„๋‹Œ ์งˆ์˜์–ด์ด๋‹ค.
๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ์—์„œ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์†”๋ฃจ์…˜์ด ์žˆ๋‹ค.

GraphQL์€ ๋ฐฑ์—”๋“œ ๋ฟ๋งŒ์•„๋‹ˆ๋ผ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.
๊ทธ์ค‘์—์„œ ๋Œ€ํ‘œ์ ์œผ๋กœ๋Š”apollo๊ฐ€ ์žˆ๋‹ค.
apollo๋Š” apollo-server์™€ apollo-client๋กœ ๋ฐฑ์—”๋“œ, ํ”„๋ก ํŠธ ์—”๋“œ๋ฅผ ๋ชจ๋‘ ์ ์šฉ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ,web ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ios,android์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ํ”Œ๋žซํผ์—๋„ ์ ์šฉ ํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ธ๊ธฐ๊ฐ€ ๋งŽ๋‹ค.

GraphQL๋ฅผ ๊ฐœ๋ฐœํ•œ Facebook์—์„œ ์ง์ ‘ ๋งŒ๋“  Relay๋Š” React๊ณ„์—ด๋งŒ ์ง€์›ํ•˜๊ณ  apollo๋Š” react,vue์™€ ๊ฐ™์€ ์›น ํ”„๋ ˆ์ž„์›Œํฌ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ios,android์™€ ๊ฐ™์€ app์—๋„ ์ ์šฉ ํ•  ์ˆ˜ ์žˆ์–ด ์ธ๊ธฐ๊ฐ€ ๋†’๋‹ค.

1. node.js express GraphQL ์‚ฌ์šฉ ํ•˜๊ธฐ

์„ค์น˜

  • express
  • graphql
  • express-graphql

๋‹ค์Œ ์ฝ”๋“œ๋Š” GraphQL ๊ณต์‹ ํŽ˜์ด์ง€์˜ ์˜ˆ์ œ ์ฝ”๋“œ ์ด๋‹ค.
๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ํŒจํ‚ค์ง€ ํŒŒ์ผ์„ ์„ค์น˜ํ•˜๊ณ  ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

./test-express-graphql

$ npm install

๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์‹ค์Šตํ•  ์ˆ˜ ์žˆ๋‹ค.

./test-express-graphql/app1.js

var express = require("express");
var { graphqlHTTP } = require("express-graphql");
var { buildSchema } = require("graphql");

var app = express();

var schema = buildSchema(`
  type Query {
    hello: String
  }
`);

var root = { hello: () => "Hello world!" };

app.use(
    "/graphql",
    graphqlHTTP({
        schema: schema,
        rootValue: root,
        graphiql: true,
    })
);
app.listen(4000, () => console.log("Now browse to localhost:4000/graphql"));

root์— ์žˆ๋Š” hello๋ฅผ Query๋กœ ๋‚ ๋ฆฌ๊ฒŒ ๋˜๋ฉด "Hello world!"๋ฅผ schema์— ์ •์˜๋œ Query์˜ ํƒ€์ž…์œผ๋กœ ๊ฐ’์„ ๋ฐ˜ํ™˜ ํ•ด์ค€๋‹ค.

  • GraphQL playground๋กœ ๊ฒฐ๊ณผ ํ™•์ธ

    app1

graphQL์€ ๋ฐ˜ํ™˜ํ•  ๋ฐ์ดํ„ฐ์˜ ๊ตฌ์กฐ๋ฅผ ๋ฏธ๋ฆฌ schema์— type์„ ์ •์˜๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

app.use()์— schema,root๋ฅผ ์ •์˜ํ•ด ๋‘๊ณ  graphiql: true๋ฅผ ์ง€์ •ํ•˜๊ฒŒ ๋˜๋ฉด ํ•ด๋‹จ URL์ธ /graphql๋กœ ์ ‘์†ํ•˜๊ฒŒ ๋  ์‹œ GraphQL playground๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

app.use(
    "/graphql",
    graphqlHTTP({
        schema: schema,
        rootValue: root,
        graphiql: true,
    })
);
  • ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ (Over-Fetching ํ•ด๊ฒฐ)

    ./test-express-graphql/app2.js

    var schema = buildSchema(
        `
            type Query {
                users: [User]
            }
            type User{
                index : Int
                name : String
                age : Int
                id : String
            }
        `
    );
    
    var root = { users: () => myData1 };

    ํ˜ธ์ถœํ•˜๋ ค๊ณ  ํ•˜๋Š” ๋ฐ์ดํ„ฐ myData1๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด myData1์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ์˜ type์„ User์— ์ •์˜ํ•˜๊ณ  Query์— users๋ฅผ ํ˜ธ์ถœํ•  ์‹œ User ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์งœ๊ณ  root์—์„œ users๋ฅผ ํ˜ธ์ถœ์‹œ ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ๋กœ myData1๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

    app2

    ์ด๋•Œ ์ „์ฒด ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹Œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ์„ ํ˜ธ์ถœํ•˜์—ฌ Over Fetching ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋‹ค. app2

  • ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์˜ endpoint๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ (Under-Fetching ํ•ด๊ฒฐ)

    app3.js ํŒŒ์ผ์—์„œ๋Š” ๋‘๊ฐ€์ง€ ๋ฐ์ดํ„ฐ myData1๊ณผ myData2๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.

    app3.js๋ฅผ ์‹คํ–‰ ์‹œํ‚ค๋ช… ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•œ๋ฒˆ์— ๋ฐ์ดํ„ฐ ๋“ค์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

    ./test-express-graphql/app3.js

    app3

    app4.js ํŒŒ์ผ์€ http://localhost:4000/ ๊ฒฝ๋กœ๋กœ ์ ‘์†์‹œ์— ์›ํ•˜๋Š” ๊ฐ’๋งŒQuery๋กœ ๋‚ ๋ ค์„œ Ajax๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ๋ฐ›์•„์˜จ๋‹ค.

    ํ”„๋ก ํŠธ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์›ํ•˜๋Š” ๊ฐ’๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

    myData1์—์„œ "id","name"๊ฐ’์„ myData2์—์„œ "password"์™€ "gender"๊ฐ’์„ ๊ฐ๊ฐ ํ•œ๋ฒˆ์˜ ๋ฐ์ดํ„ฐ๋กœ ๋ฐ›์•„์™€์„œ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ ๋ชจ์Šต์ด๋‹ค.

    ./test-express-graphql/app4.js

    app4

2. apollo-server ์‚ฌ์šฉํ•˜๊ธฐ

  • ์˜ˆ์ œ ์‹ค์Šต

    ./test-apollo-server

    $ npm install
  • ์ง์ ‘ ์ƒ์„ฑ

    $ npm init
    $ npm install apollo-server

apollo-server์˜ ํŒŒ์ผ ๊ตฌ์กฐ

  • apollo-server ์ถ”๊ฐ€ํ•˜๊ธฐ

    import { ApolloServer, gql } from "apollo-server";
  • typeDefs ์ƒ์„ฑ

    GraphQL ๋ช…์„ธ์—์„œ ์‚ฌ์šฉ๋  ๋ฐ์ดํ„ฐ, ์š”์ฒญ์˜ ํƒ€์ž…์„ ์ง€์ •ํ•ด์„œ ์Šคํ‚ค๋งˆ๋ฅผ gql(template literal tag)๋กœ ์ƒ์„ฑํ•œ๋‹ค.
    ์–ด๋–ค ํ•„๋“œ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š”์ง€, ์–ด๋–ค ์ข…๋ฅ˜์˜ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š”์ง€, ํ•˜์œ„ ๊ฐ์ฒด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•„๋“œ๋Š” ๋ฌด์—‡์ธ์ง€๋ฅผ ์ •์˜ํ•ด๋‘”๋‹ค.

    const typeDefs = gql`
    type Query {
        tests: [Test]
    }
    type Test {
        hello: String
    }
  • resolvers

    ์š”์ฒญ์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜, ์ž…๋ ฅ, ์ˆ˜์ • ์‚ญ์ œ ํ•œ๋‹ค.
    ์ •์˜๋œ ์Šคํ‚ค๋งˆ ํ•„๋“œ์— ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜์˜ ์‹ค์ œ ํ–‰๋™์„ ์ •์˜ํ•œ๋‹ค.

    const resolvers = {
        Query: {
            tests: () => test,
        },
    };
  • ์‹คํ–‰

    ApolloServer๋Š” typeDefs์™€ resolvers๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„์„œ ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑํ•˜๊ณ  server.listen()๋ช…๋ น์–ด๋กœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

    const server = new ApolloServer({ typeDefs, resolvers });
    server.listen().then(({ url }) => {
        console.log(`๐Ÿš€  Server ready at ${url}`);
    });

์‹ค์Šตํ•˜๊ธฐ

  1. ์›ํ•˜๋Š” ๊ฐ’๋งŒ ์š”์ฒญํ•˜๊ธฐ

    ./test-apollo/index1.js

    ์ž„์‹œ๋กœ ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ ./database/products.js import ์‹œํ‚ค๊ธฐ

    import { products } from "./database/products.js";

    ์Šคํ‚ค๋งˆ ์ •์˜
    ./database/products.jsํŒŒ์ผ์˜ ํƒ€์ž…์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

    const typeDefs = gql`
        type Query {
            products: [Product]
        }
        type Product {
            title: String
            price: Int
            salePer: String
            salePrice: String
            star: String
            starNum: String
        }
    `;

    ์ฟผ๋ฆฌ ํ˜ธ์ถœ์‹œ ๋ฐ˜ํ™˜ํ•  ๋ฐ์ดํ„ฐ ์ •์˜ํ•˜๊ธฐ

    const resolvers = {
        Query: {
            products: () => products,
        },
    };

    http://localhost:4000/graphql๋กœ ์ ‘์†ํ•˜์—ฌ ์š”์ฒญ ๋ฐ›๊ณ  ์‹ถ์€ ๊ฐ’์„ ์„ ํƒํ•ด์„œ ์š”์ฒญํ•˜๊ธฐ

    index1

  2. ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ๋กœ ๋™์‹œ์— ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ ๋ฐ›๊ธฐ

    ./test-apollo/index2.js

    import { products } from "./database/products.js";
    import { ho } from "./database/ho.js";
    import { name } from "./database/name.js";
    import { ApolloServer, gql } from "apollo-server";
    
    const typeDefs = gql`
        type Query {
            products: [Product]
            hos: [Ho]
            names: [Name]
        }
        type Product {
            title: String
            price: Int
            salePer: String
            salePrice: String
            star: String
            starNum: String
        }
        type Ho {
            index: Int
            Ho: String
        }
        type Name {
            index: Int
            Name: String
        }
    `;
    
    const resolvers = {
        Query: {
            products: () => products,
            hos: () => ho,
            names: () => name,
        },
    };
    
    const server = new ApolloServer({ typeDefs, resolvers });
    server.listen().then(({ url }) => {
        console.log(`๐Ÿš€  Server ready at ${url}`);
    });

    index2.js๋Š” index1.js์—์„œ ๋ฐ์ดํ„ฐ Ho์™€ Name ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

    index2

  3. ์›ํ•˜๋Š” ๊ฐ’๋งŒ ํ•„ํ„ฐ๋งํ•ด์„œ ๋ฐ›๊ธฐ

    resolvers์— ๊ฐ’์„ ํ•„ํ„ฐ๋งํ•ด์„œ under fetching ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋‹ค.

    ./test-apollo/index3.js

    type Query {
        product(price: Int): [Product]
    }

    ๋‹ค์Œ๊ณผ ๊ฐ™์ด Query๋ถ€๋ถ„์— ๊ธฐ์กด์— ์žˆ๋˜ products: [Product]์— ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›์œผ๋ ค๊ณ  ๊ฐ’์„ product(price: Int): [Product]๋กœ ์ •์˜ํ•ด์ค€๋‹ค.

    const resolvers = {
        Query: {
            // products.price์ด ์š”์ฒญ ์ฟผ๋ฆฌ ๊ฐ’๋ณด๋‹ค ํด ๊ฒฝ์šฐ๋งŒ ์ถœ๋ ฅ
            product: (parent, args, context, info) =>
                products.filter((product) => {
                    return product.price >= args.price;
                }),
        },
    };

    ๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ resolvers์—์„œ ์›ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค.

    ๋งŒ์•ฝ ์š”์ฒญ๊ฐ’๋ณด๋‹ค ๋†’์€ ๊ฐ’์„ ๋ฐ›๋Š”๋‹ค๊ณ  ํ•˜๋ฉด ์ด๋•Œ ์š”์ฒญ์ธ์ž๋กœ๋Š” args๋ฅผ ๋ฐ›๋Š”๋‹ค.
    filter๋กœ ์š”์ฒญํ•œ ์ธ์ž์ธ args.price๋ณด๋‹ค ํฐ ๊ฐ’๋งŒ ๋‚˜์˜ค๊ฒŒ ํ•˜๋ฉด ์š”์ฒญ๋ฐ›์€ ์ธ์ž๋ณด๋‹ค ํฐ๊ฐ’๋งŒ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

    price๊ฐ€ 8000๋ณด๋‹ค ํฐ ๊ฐ’ ์ถœ๋ ฅํ•˜๊ธฐ

    index3

  4. ์›ํ•˜๋Š” ๊ฐ’๋งŒ ํ•„ํ„ฐ๋งํ•˜๊ธฐ2

    index2.jsํŒŒ์ผ์—์„œ ์‹คํ–‰ํ–ˆ์„๋•Œ ๋ณด๋ฉด

    "data":{
        "A":[
            {
            "a": "value",
            },
        ],
        "B":[
            {
            "b": "value",
            },
        ]
    }

    ํ•˜๋‚˜์˜ endpoint๋กœ data์•ˆ์— ๊ฐ’์€ ์žˆ์ง€๋งŒ ๊ฐ๊ฐ A, B๋กœ ๋‚˜๋ˆ„์–ด์ ธ ์˜จ๋‹ค.

    type Ho {
        index: Int
        Ho: String
        names: [Name]
    }
    type Name {
        index: Int
        Name: String
    }

    Ho์•ˆ์— Name์„ ๋„ฃ์–ด์„œ Ho๋ฅผ ํ˜ธ์ถœํ• ๋•Œ Name์„ ๊ฐ™์ด ํ˜ธ์ถœํ•˜๋„๋ก ๋„ฃ์–ด์ฃผ๋ฉด๋œ๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  resolvers๋ฅผ ์ง€์ •ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.
    ์ง€์ •ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋ƒฅ ํ˜ธ์ถœํ•˜๋ฉด Ho.names์•ˆ์— ๋ชจ๋“  names๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  resolvers์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด filter๋กœ index๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜๋Š” ๊ฐ’์„ 1:1๊ด€๊ณ„๋กœ ํ˜ธ์ถœ๋˜๊ฒŒ ํ•ด์ค€๋‹ค.

    hos: () =>
    ho.map((ho) => {
        ho.names = name.filter((v) => {
            return ho.index == v.index;
        });
        return ho;
    }),

    index4

  5. HTML๊ณผ ์—ฐ๋™ํ•˜๊ธฐ

    index5.js์„ :4000 ํฌํŠธ ์‹คํ–‰์‹œํ‚จ ํ›„ index.html๋ฅผ LiveServer๋กœ :5050 ํฌํŠธ๋กœ ์‹คํ–‰ ์‹œํ‚จ๋‹ค.

    A B C
    index5 index5 index5

    ./test-apollo/index5.html

    ./test-apollo/index5.js

    ์œ„์˜ ์ฝ”๋“œ๋Š” ๋‚ด์šฉ์€ ๊ฒ€์ƒ‰ํ•œ ๊ฐ’ ์ด์ƒ์˜ ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค. ๋ผ๋””์˜ค๋ฐ•์Šค์— ์žˆ๋Š” ๊ฐ’์„ ์„ ํƒํ•œ ๊ฐ’๋งŒ ์š”์ฒญํ•ด์„œ ๊ฐ€์ ธ์˜จ๋‹ค. (Over Fetching)
    "B"๋Š” ํ• ์ธ์œจ ์„ ํƒ ํ–ˆ์„๋•Œ ๊ฒฐ๊ณผ์ด๊ณ  "C"๋Š” ํ• ์ธ์œจ, ํ• ์ธ๊ฐ€ ๋ชจ๋‘๋ฅผ ์š”์ฒญํ•œ ๊ฒฐ๊ณผ์ด๋‹ค.

    ์š”์ฒญํ•˜์ง€ ์•Š์€ ๊ฒฐ๊ณผ๊ฐ’์— ๋Œ€ํ•ด์„œ๋Š” undefined๊ฐ€ ๋œจ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.