[SEB FE] Section4 unit6 [API] GraphQL (1)

๋™ํ™”ยท2022๋…„ 12์›” 1์ผ
0

์ฝ”๋“œ์Šคํ…Œ์ด์ธ 

๋ชฉ๋ก ๋ณด๊ธฐ
30/32
post-thumbnail
post-custom-banner



(์ถœ์ฒ˜: https://maggieappleton.com/graphql)


GraphQL ๊ตฌ์กฐ ๐ŸŽŸ

GraphQL Keywords

  • Query: ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (REST์˜ GET๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.)
  • Mutation: ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์ˆ˜์ •ํ•˜๊ธฐ
    ย  ย ย - Create: ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
    ย  ย ย - Update: ๊ธฐ์กด์˜ ๋ฐ์ดํ„ฐ ์ˆ˜์ •
    ย  ย ย - Delete: ๊ธฐ์กด์˜ ๋ฐ์ดํ„ฐ ์‚ญ์ œ
  • Subscription: ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ ์‹œ ์„œ๋ฒ„๊ฐ€ ๋Œ€์‘ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „์†ก



์ฟผ๋ฆฌ(query, ๋ฐ์ดํ„ฐ ์กฐํšŒ)

ํ•„๋“œ(field)

{
  hero {
    name
  }
}

[์ฝ”๋“œ] hero์˜ name์„ ์ฟผ๋ฆฌ

{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

[์ฝ”๋“œ] ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ–ˆ์„ ๋•Œ์˜ ๊ฒฐ๊ณผ

  • ํ•„๋“œ์˜ name์€ String ํƒ€์ž…์„ ๋ฐ˜ํ™˜
  • ์ฟผ๋ฆฌ์™€ ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ๊ฐ™์€ ๋ชจ์–‘์„ ํ•˜๊ณ  ์žˆ์Œ
  • GraphQL์€ ์„œ๋ฒ„์— ์š”์ฒญํ–ˆ์„ ๋•Œ ์˜ˆ์ƒํ–ˆ๋˜ ๋Œ€๋กœ ๋Œ๋ ค๋ฐ›๊ณ , ์„œ๋ฒ„๋Š” GraphQL์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”๊ตฌํ•˜๋Š” ํ•„๋“œ๋ฅผ ์ •ํ™•ํžˆ ์•Œ๊ธฐ ๋•Œ๋ฌธ

{
  hero {
    name
    # ์ด๋Ÿฐ ์‹์œผ๋กœ GraphQL ๋‚ด์—์„œ ์ฃผ์„๋„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    friends {
      name
    }
  }
}

[์ฝ”๋“œ] ํžˆ์–ด๋กœ์˜ ์ด๋ฆ„๊ณผ ํžˆ์–ด๋กœ์˜ ์นœ๊ตฌ ์ด๋ฆ„์„ ๊ฐ™์ด ์ฟผ๋ฆฌ

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

[์ฝ”๋“œ] ํžˆ์–ด๋กœ์˜ ์ด๋ฆ„๊ณผ ํžˆ์–ด๋กœ์˜ ์นœ๊ตฌ์˜ ์ด๋ฆ„์ด ์กฐํšŒ๋˜์–ด ๋‚˜์˜ด

  • ์›ํ•˜๋Š” ํ•„๋“œ๋ฅผ ์ค‘์ฒฉํ•˜์—ฌ ์ฟผ๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅ
  • friends ํ•„๋“œ๋Š” ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜
  • GraphQL ์ฟผ๋ฆฌ๋Š” ๊ด€๋ จ ๊ฐ์ฒด ๋ฐ ํ•„๋“œ๋ฅผ ์ˆœํšŒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ณ ์ „์ ์ธ REST API์—์„œ ๊ทธ๋Ÿฌํ–ˆ๋“ฏ ๋‹ค์–‘ํ•œ endpoint๋ฅผ ๋งŒ๋“ค์–ด ๊ฐ๊ธฐ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋Œ€์‹  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ์š”์ฒญ์„ ๋ณด๋ƒ„์œผ๋กœ์จ ๊ด€๋ จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ



์ „๋‹ฌ์ธ์ž(Arguments)

ํ•„๋“œ์— ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ถ€๋ถ„์„ ์ถ”๊ฐ€ํ•˜๊ฒŒ ๋˜๋ฉด ์ฟผ๋ฆฌ์˜ ํ•„๋“œ ๋ฐ ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋“ค์— ์ „๋‹ฌํ•˜์—ฌ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ๐Ÿ˜„

{
  human(id: "1000") {
    name
    height
  }
}

[์ฝ”๋“œ] id๊ฐ€ 1000์ธ human์˜ name๊ณผ height๋ฅผ ์ฟผ๋ฆฌ

{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 1.72
    }
  }
}

[์ฝ”๋“œ] ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ



๋ณ„๋ช…(Aliases)

  • ํ•„๋“œ ์ด๋ฆ„์„ ์ค‘๋ณตํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ํ•„๋“œ ์ด๋ฆ„์„ ์ค‘๋ณต์œผ๋กœ ์‚ฌ์šฉํ•ด์„œ ์ฟผ๋ฆฌ๋ฅผ ํ•ด์•ผ ํ•  ๋•Œ๋Š” ๋ณ„๋ช…์„ ๋ถ™์—ฌ์„œ ์ฟผ๋ฆฌ
  • ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ๋ณ„๋ช…์„ ์ง€์ •ํ•˜๋ฉด ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ๋‘ ๊ฐœ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ๋‘ ์–ป์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

โŒ

{
	hero(episode: EMPIRE) {
    name
  }
  hero(episode: JEDI) {
    name
  }
}

์ด๋Ÿฐ ์‹์œผ๋กœ ์ค‘๋ณตํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ

{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

์•ž์— ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ณ„๋ช…์„ ๋ถ™์—ฌ์„œ ์ฟผ๋ฆฌ

{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}

์ฟผ๋ฆฌ ๊ฒฐ๊ณผ



์˜คํผ๋ ˆ์ด์…˜ ๋„ค์ž„(Operation name)

์—ฌํƒœ๊ป ์ฟผ๋ฆฌ์™€ ์ฟผ๋ฆฌ ๋„ค์ž„์„ ๋ชจ๋‘ ์ƒ๋žตํ•˜๋Š” ์ถ•์•ฝํ˜• ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค๋งŒ, ์‹ค์ œ ์•ฑ์—์„œ๋Š” ์ฝ”๋“œ๋ฅผ ๋ชจํ˜ธํ•˜์ง€ ์•Š๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”

query HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}

[์ฝ”๋“œ] ์ด๋Ÿฐ ์‹์œผ๋กœ query keyword์™€ query name์„ ์ž‘์„ฑ

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}
  • ์•ž์˜ ์ฟผ๋ฆฌ๋Š” ์˜คํผ๋ ˆ์ด์…˜ ํƒ€์ž…
  • ์˜คํผ๋ ˆ์ด์…˜ ํƒ€์ž…์—๋Š” query ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ mutation, subscription, describes
  • ์ฟผ๋ฆฌ๋ฅผ ์•ฝ์‹์œผ๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š๋Š” ํ•œ ์ด๋Ÿฐ ์˜คํผ๋ ˆ์ด์…˜ ํƒ€์ž…์€ ๋ฐ˜๋“œ์‹œ ํ•„์š”
  • ์˜คํผ๋ ˆ์ด์…˜ ๋„ค์ž„์„ ์ž‘์„ฑํ•  ๋•Œ๋Š” ์˜คํผ๋ ˆ์ด์…˜ ํƒ€์ž…์— ๋งž๋Š” ์ด๋ฆ„์œผ๋กœ ์ž‘์„ฑํ•  ๊ฒƒ! (๊ฐ€๋…์„ฑ)



๋ณ€์ˆ˜(Variables)

๋™์ ์œผ๋กœ ์ธ์ˆ˜๋ฅผ ๋ฐ›์•„ ์ฟผ๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ (๋Œ€๋‹ค์ˆ˜)

query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

[์ฝ”๋“œ] ๋ณ€์ˆ˜๋ฅผ ์จ์„œ ์ž‘์„ฑ๋œ ์ฟผ๋ฆฌ

  • ์˜คํผ๋ ˆ์ด์…˜ ๋„ค์ž„ ์˜†์— ๋ณ€์ˆ˜๋ฅผ $๋ณ€์ˆ˜ ์ด๋ฆ„: ํƒ€์ž… ํ˜•ํƒœ ๋กœ ์ •์˜
  • $episode: Episode์ผ ๋•Œ, ๋’ค์— !๊ฐ€ ๋ถ™๋Š”๋‹ค๋ฉด episode๋Š” ๋ฐ˜๋“œ์‹œ Episode์—ฌ์•ผ ํ•œ๋‹ค๋Š” ๋œป (!๋Š” ์˜ต์…”๋„ํ•œ ์‚ฌํ•ญ)







๋ฎคํ…Œ์ด์…˜(mutation, ๋ฐ์ดํ„ฐ ์ˆ˜์ •)

mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

REST API์—์„œ GET ์š”์ฒญ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ,
POST ํ˜น์€ PUT ์š”์ฒญ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ GraphQL๋„ ์œ ์‚ฌ

GraphQL์€ mutation์ด๋ผ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„ ์ธก ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •







์Šคํ‚ค๋งˆ/ํƒ€์ž…(Schema/Type)

GraphQL ์Šคํ‚ค๋งˆ์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ์„œ๋น„์Šค์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด์˜ ์ข…๋ฅ˜, ๊ทธ๋ฆฌ๊ณ  ํฌํ•จํ•˜๋Š” ํ•„๋“œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด ์œ ํ˜•

type Character {
  name: String!
  appearsIn: [Episode!]!
}
  • Character๋Š” GraphQL ๊ฐ์ฒด ํƒ€์ž…์ด๋ฉฐ, ์ฆ‰ ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ํƒ€์ž…์ž„์„ ์˜๋ฏธ (์Šคํ‚ค๋งˆ์— ์žˆ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ํƒ€์ž…์€ ๊ฐ์ฒด ํƒ€์ž…)
  • name ๊ณผ appearIn ์€ Character ํƒ€์ž…์˜ ํ•„๋“œ๋กœ, ์ฆ‰ name ๊ณผ appearIn ์€ GraphQL ์ฟผ๋ฆฌ์˜ Character ํƒ€์ž… ์–ด๋””์„œ๋“  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•„๋“œ
  • String์€ ๋‚ด์žฅ๋œ ์Šค์นผ๋ผ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๋กœ ๋‹จ์ผ ์Šค์นผ๋ผ ๊ฐ์ฒด๋กœ ํ™•์ธ๋˜๋Š” ์œ ํ˜•์ด๋ฉฐ ์ฟผ๋ฆฌ์—์„œ ํ•˜์œ„ ์„ ํƒ์„ ๊ฐ€์งˆ ์ˆ˜ ์—†๋‹ค. ์Šค์นผ๋ผ ํƒ€์ž…์—๋Š” ID, Int๋„ ์žˆ๋‹ค.
  • !๊ฐ€ ๋ถ™๋Š”๋‹ค๋ฉด ์ด ํ•„๋“œ๋Š” nullableํ•˜์ง€ ์•Š๊ณ  ๋ฐ˜๋“œ์‹œ ๊ฐ’์ด ๋“ค์–ด์˜จ๋‹ค๋Š” ์˜๋ฏธ โžก๏ธ ์ด๊ฒƒ์„ ๋ถ™์—ฌ ์ฟผ๋ฆฌํ•œ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ž€ ์˜ˆ์ƒ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • [ ]๋Š” ๋ฐฐ์—ด์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐ์—ด์—๋„ !๊ฐ€ ๋ถ™์„ ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ! ์ด ๋’ค์— ๋ถ™์–ด ์žˆ์–ด null ๊ฐ’์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ•ญ์ƒ 0๊ฐœ ์ด์ƒ์˜ ์š”์†Œ๋ฅผ ํฌํ•จํ•œ ๋ฐฐ์—ด์„ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค







๋ฆฌ์กธ๋ฒ„(Resolver)

์Šคํ‚ค๋งˆ๋ฅผ ์ •์˜ํ•˜๋ฉด ๊ทธ ์Šคํ‚ค๋งˆ ํ•„๋“œ์— ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜์˜ ์‹ค์ œ ํ–‰๋™์„ Resolver์—์„œ ์ •์˜

const db = require("./../db")
const resolvers = {
  Query: { // **Query :** ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ (REST ์— GET ๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.)
		getUser: async (_, { email, pw }) => {
			db.findOne({
				where: { email, pw }
			}) ... // ์‹ค์ œ ๋””๋น„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. 
			...
		}
  },
  Mutation: { // **Mutation :** ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์ˆ˜์ •ํ•˜๊ธฐ ( Create , Update , Delete )
		createUser: async (_, { email, pw, name }) => {
			...
		}
  }
  Subscription: { // **Subscription :** ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
    newUser: async () => {
      ...
		}
  }
};
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€