๐Ÿ”ฌComputer Science/๋„คํŠธ์›Œํฌ

GraphQL ๊ตฌ์กฐ

hellohailie 2022. 8. 2. 12:49

 

GraphQL ๊ตฌ์กฐ

 

  • ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ, REST API์˜ ๊ฒฝ์šฐ GET ์š”์ฒญ์„ ํ–ˆ๋‹ค๋ฉด, GraphQL์—์„œ๋Š” query๋ฅผ ์ด์šฉํ•ด์„œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•œ๋‹ค. 
  • ๋ฐ์ดํ„ฐ๋ฅผ Create, Update, Delete ์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•œ๋‹ค๋ฉด, GraphQL์—์„œ๋Š” mutation๋ฅผ ์ด์šฉํ•œ๋‹ค. 
  • ๋” ๋‚˜์•„๊ฐ€ GraphQL์—์„œ๋Š” subscription์ด๋ผ๋Š” ๊ฐœ๋…์œผ๋กœ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. 

โœ”๏ธ ์ฟผ๋ฆฌ (query) = ๋ฐ์ดํ„ฐ ์กฐํšŒํ•˜๊ธฐ

โœป ํ•„๋“œ

์˜ˆ์‹œ1)

๐Ÿ‘‡์š”์ฒญ๐Ÿ‘‡

{
	pizza {
    	name
    }
}

๐Ÿ‘‡์‘๋‹ต๐Ÿ‘‡

{
  "data" : {
    "pizza":{
      "name" : "bulgogi pizza"
    }
  }
}

GraphQL์€ ์„œ๋ฒ„์— ์š”์ฒญํ•  ๋•Œ ์˜ˆ์ƒํ–ˆ๋˜ ๋Œ€๋กœ ์‘๋‹ต๋ฐ›๋Š”๋‹ค. (์™œ๋ƒ๋ฉด ์„œ๋ฒ„๋Š” GraphQL์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”๊ตฌํ•˜๋Š” ํ•„๋“œ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. )

 


์˜ˆ์‹œ2)

 

๐Ÿ‘‡์š”์ฒญ๐Ÿ‘‡

{
	pizza {
    	name
      topping {
        name
      }
    }
}

๐Ÿ‘‡์‘๋‹ต๐Ÿ‘‡

{
  "data" : {
    "pizza":{
      "name" : "bulgogi pizza",
      "topping" : [
        {
          "name" : "cheese"
        },
        {
          "name" : "bacon"
        }
      ]
    }
  }
}

โžฅ ์›ํ•˜๋Š” ํ•„๋“œ๋ฅผ ์ค‘์ฒฉํ•ด์„œ ์ฟผ๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅ!


โœป ์ „๋‹ฌ์ธ์ž

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

๐Ÿ‘‡์š”์ฒญ๐Ÿ‘‡ (id๊ฐ€ 5์ธ ํ•™์ƒ์˜ ์ด๋ฆ„๊ณผ ์ ์ˆ˜๋ฅผ ์ฟผ๋ฆฌ)

{
  student(id: '5'){
    name
    score
  }
}

๐Ÿ‘‡์‘๋‹ต๐Ÿ‘‡

{
  'data': {
    'student':{
      'name' : 'Lee',
      'score' : 100
    }
  }
}

(REST API๋ฅผ ์ด์šฉํ•œ๋‹ค๋ฉด ?id=1000 ์ด๊ฑฐ๋‚˜ /1000(/:id)์ผ ๋•Œ์™€ ๊ฐ™์€ ๋ชฉ์ ์œผ๋กœ ์ฟผ๋ฆฌ)


โœป ๋ณ„๋ช…

ํ•„๋“œ ์ด๋ฆ„์„ ์ค‘๋ณตํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ํ•„๋“œ ์ด๋ฆ„์„ ์ค‘๋ณต์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผํ•  ๊ฒฝ์šฐ์—๋Š” ๋ณ„๋ช…์„ ๋ถ™์—ฌ์„œ ์ฟผ๋ฆฌ

 

๐Ÿ‘‡ํ‹€๋ฆฐ ์š”์ฒญ๐Ÿ‘‡

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

๐Ÿ‘‡์˜ฌ๋ฐ”๋ฅธ ์š”์ฒญ๐Ÿ‘‡ (ํ•„๋“œ์ด๋ฆ„ ์ค‘๋ณต๋˜๋ฉด ๋ณ„๋ช…๋ถ™์ด๊ธฐ!)

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

๐Ÿ‘‡์‘๋‹ต๐Ÿ‘‡

{
  'data':{
    'empireHero':{
      'name':'Lee'
    }
    'jediHero':{
      'name':'Sue'
    }
  }
}

โœป ์˜คํผ๋ ˆ์ด์…˜ ๋„ค์ž„

์ฝ”๋“œ๋ฅผ ๋ชจํ˜ธํ•˜์ง€ ์•Š๊ฒŒ query keyword, query name ์ž‘์„ฑํ•˜๊ธฐ

 

๐Ÿ‘‡์š”์ฒญ๐Ÿ‘‡ / ์˜คํผ๋ ˆ์ด์…˜ ํƒ€์ž…์€ ์•„๋ž˜์ฒ˜๋Ÿผ query๋„ ์žˆ๊ณ , mutation, subscription, describes ๋“ฑ์ด ์žˆ๋‹ค. 

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

๐Ÿ‘‡์‘๋‹ต๐Ÿ‘‡

{
  'data':{
    'hero':{
      'name':'Lee',
      'friends':[
        {'name':'Sue'},
        {'name':'Bae'},
      ]
    }
  }
}

โœป ๋ณ€์ˆ˜

์œ„์˜ ์˜ˆ์‹œ๋Š” ๊ณ ์ •๋œ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋Š” ์˜ˆ์ œ์ด๋‹ค. 

๋™์ ์œผ๋กœ ์ธ์ˆ˜๋ฅผ ๋ฐ›์•„์„œ ์ฟผ๋ฆฌํ•˜๋ ค๋ฉด ๋ณ€์ˆ˜๋ฅผ ์จ์•ผํ•œ๋‹ค. 

 

์˜คํผ๋ ˆ์ด์…˜ ๋„ค์ž„ ์˜†์— ๋ณ€์ˆ˜๋ฅผ $๋ณ€์ˆ˜ ์ด๋ฆ„:ํƒ€์ž… ํ˜•ํƒœ๋กœ ์“ฐ๊ธฐ

๋งŒ์•ฝ $episode: Episode ์—์„œ ๋’ค์— !๊ฐ€ ๋ถ™์œผ๋ฉด episode๋Š” ๋ฐ˜๋“œ์‹œ Episode์—ฌ์•ผ ํ•œ๋‹ค! (! ๋Š” ์˜ต์…”๋„!!)

 

๐Ÿ‘‡์š”์ฒญ๐Ÿ‘‡

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

 

 


 

โœ”๏ธ ๋ฎคํ…Œ์ด์…˜ (mutation) = ๋ฐ์ดํ„ฐ ์ˆ˜์ •ํ•˜๊ธฐ

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

โœ”๏ธ ์Šคํ‚ค๋งˆ/ํƒ€์ž… (Schema/Type)

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

โœ”๏ธ ๋ฆฌ์กธ๋ฒ„(Resolver)

์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๊ฒฐ์ •ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜์ด๋‹ค. 

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

(์ด๋Ÿฐ ํ•จ์ˆ˜๋“ค์ด ๋ชจ์—ฌ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Resolvers๋ผ๊ณ  ํ•จ)

 

* GraphQL์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ตฌ์ฒด์ ์ธ ๊ณผ์ • (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ, ์›๊ฒฉ API ์š”์ฒญ)์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š”๋ฐ ์ด๋ฅผ 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 () => {
      ...
		}
  }
};

 

 

๋” ๊ณต๋ถ€ํ•˜๊ธฐ)

https://graphql.org/learn/

 

 

๐Ÿ˜ƒ ์ž˜๋ชป๋œ ๊ฐœ๋… ์ „๋‹ฌ์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์ €์˜ ์„ฑ์žฅ์— ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค๐Ÿค“