Skip to content

Apollo server flow

Ahrim Yang edited this page Nov 30, 2020 · 3 revisions

์ž‘์„ฑ์ž: J054 ๊น€์ง„๊ด€

์ •ํ™•ํ•˜๊ฒŒ Apollo server์— ๋Œ€ํ•œ ๋™์ž‘ ๋ฐฉ์‹์— ๋Œ€ํ•ด ์ž์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋Š” ๊ธ€์„ ์ฐพ๊ธฐ ํž˜๋“ค์–ด์„œ GraphQL query๊ฐ€ ์ „๋ฐ˜์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์‹คํ–‰๋˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•œ๋‹ค. ์•„๋งˆ Apollo๋Š” GraphQL์˜ ๊ตฌํ˜„์ฒด๋กœ query๋ฅผ implementํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š”๋ฐ ๋„์›€์„ ์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด์ง€ ์•Š์„๊นŒ ํ•˜๋Š” ์ƒ๊ฐ์„ ํ•˜๊ณ  ์žˆ๋‹ค.

Execution


validation ์ดํ›„์— GraphQL query๋Š” GraphQL server์— ์˜ํ•ด ๋Œ€๊ฐœ JSON๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์š”์ฒญ query์˜ ๋ชจ์Šต๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€์˜ ๋ชจ์Šต์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

GraphQL์€ type system ์—†์ด query๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†๋‹ค.

์˜ˆ์ œ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด type์„ ์ •์˜ํ•œ๋‹ค.

type Query {
  human(id: ID!): Human
}
 
type Human {
  name: String
  appearsIn: [Episode]
  starships: [Starship]
}
 
enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
 
type Starship {
  name: String
}

query๊ฐ€ ์–ด๋–ค์‹์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ธฐ ์ „์— ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// request
{
  human(id: 1002) {
    name
    appearsIn
    starships {
      name
    }
  }
}

// response
{
  "data": {
    "human": {
      "name": "Han Solo",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "starships": [
        {
          "name": "Millenium Falcon"
        },
        {
          "name": "Imperial shuttle"
        }
      ]
    }
  }
}

GraphQL query์˜ field๋Š” next type์„ ๋ฐ˜ํ™˜ํ•˜๋Š” previous type์˜ function์ด๋‚˜ method๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. type์˜ ๊ฐ field๋Š” GraphQL server developer๊ฐ€ ์ •์˜ํ•œ resolver์„ ๋ถ€๋ฅด๋Š” ํ•จ์ˆ˜๋กœ ๋’ท๋ฐ›์นจ๋œ๋‹ค. field๊ฐ€ ์‹คํ–‰๋  ๋•Œ, ๋Œ€์‘๋˜๋Š” resolver๊ฐ€ ๋ถˆ๋ ค์ง€๊ณ  ๋‹ค์Œ value๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

field๊ฐ€ scalar type์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์‹คํ–‰์€ ์™„๋ฃŒ๋œ๋‹ค. ํ•˜์ง€๋งŒ field๊ฐ€ ํŠน์ • object๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด scalar๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ๊นŒ์ง€ ์ด๋ฅผ ๋ฐ˜๋ณตํ•œ๋‹ค.

Root fields & resolvers

GraphQL server์˜ ์ตœ์ƒ์œ„ ๋ ˆ๋ฒจ์€ GraphQL API๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  entry point๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” type์ด๋‹ค. ์ด๋Š” Root type ๋˜๋Š” Query type์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

Query: {
  human(obj, args, context, info) {
    return context.db.loadHumanByID(args.id).then(
      userData => new Human(userData)
    )
  }
}

์œ„์˜ ์˜ˆ์ œ์—์„œ, Query type์€ human field๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. ์ด field์— ๋Œ€ํ•œ resolver๋Š” database์— ์ ‘์€ํ•ด์„œ Human ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์˜ˆ์ œ๋Š” Javascript๋กœ ์”Œ์—ฌ์กŒ๊ณ , GraphQL server๋Š” ๋งŽ์€ ๋‹ค๋ฅธ ์–ธ์–ด๋ฅผ ์ง€์›ํ•œ๋‹ค.

resolver function์€ ๋‹ค์Œ 4๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›๋Š”๋‹ค.

  • obj: ์ด์ „(๋ถ€๋ชจ) object๋กœ root Query์—์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • args: ์ด GraphQL query์— ์ „๋‹ฌ๋œ arguments
  • context: ๋ชจ๋“  resolver๊ฐ€ ๊ณต์œ ํ•˜๋Š” ๊ฐ’์œผ๋กœ ์ฃผ๋กœ ๋กœ๊ทธ์ธํ•œ user, database๋กœ์˜ ์ ‘๊ทผ ๋“ฑ๊ณผ ๊ฐ™์€ contextual information์„ ๋‹ด๊ณ  ์žˆ๋‹ค.
  • info: ํ˜„์žฌ query์™€ ๊ด€๋ จ๋œ field์— ๋Œ€ํ•œ ๊ตฌ์ฒด์ ์ธ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๊ฐ’

Asynchronous resolvers

DB์—์˜ ์ ‘๊ทผ๊ณผ ๊ฐ™์ด ๋น„๋™๊ธฐ ์ž‘์—…์„ ์œ„ํ•ด์„œ resolver๋Š” Promise, Futures, Tasks์™€ ๊ฐ™์€ ๋™์ž‘์„ ์•Œ๊ณ  ์žˆ์„ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ Query๋Š” ๋‹จ์ง€ ์ •์˜ํ•œ field๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธธ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Query ์ž์ฒด๋Š” Promise ๊ฐ™์€ ๋™์ž‘์„ ์ดํ•ดํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋‹จ์ง€ ์‹คํ–‰ ์ค‘์— Promise์™€ ๊ฐ™์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•˜๋ฉด ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ „๊นŒ์ง€ ์ด๋ฅผ ๊ธฐ๋‹ค๋ ค์„œ ์ตœ์ ์˜ ๋™์‹œ์„ฑ์„ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Trivial resolvers

์ด์ œ Human ๊ฐ์ฒด๊ฐ€ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•ด์กŒ์„ ๋•Œ GraphQL์€ ๋‹ค์Œ field์— ๋Œ€ํ•œ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

Human: {
  name(obj, args, context, info) {
    return obj.name
  }
}

GraphQL server๋Š” type system์— ์˜ํ•ด ๋‹ค์Œ์— ํ•  ์ž‘์—…์ด ์ด๋ฏธ ๊ฒฐ์ •๋˜์–ด ์žˆ๋‹ค. Query type์—์„œ human field์— ์•„๋ฌด๊ฒƒ๋„ ๋ฐ˜ํ™˜๋˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ๋„ GraphQL์€ ๋‹ค์Œ์— resolveํ•  field๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์•Œ๊ณ  ์žˆ๋‹ค.

Human field์—์„œ name์„ resolveํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋‹ค์†Œ ์ง๊ด€์ ์ด๋‹ค. name resolver๊ฐ€ ๋ถˆ๋ ค์ง€๊ณ  obj ์ธ์ž์— Human ๊ฐ์ฒด๊ฐ€ ๋„˜์–ด๊ฐ„๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ GraphQL ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ด์ •๋„์˜ resolver๋Š” ๋”ฐ๋กœ ์ •์˜ํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๋งŒ์•ฝ resolver๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฐ˜ํ™˜๋œ object์—์„œ ๋™์ผํ•œ ์ด๋ฆ„์˜ field๋ฅผ ์ฝ์–ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.(์ด ๋ถ€๋ถ„์€ Apollo Server๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ์ผ์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค.)

Scalar coercion

name field๊ฐ€ resolve๋˜๋Š” ๋™์•ˆ์— appearsIn๊ณผ starships field๊ฐ€ ๋™์‹œ์— resolve๋œ๋‹ค. appearsIn field ๋˜ํ•œ trivial resolver๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค.

Human: {
  appearsIn(obj) {
    return obj.appearsIn // returns [ 4, 5, 6 ]
  }
}

type system์€ appearsIn์ด Enum values๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ํ•จ์ˆ˜๋Š” ์ˆซ์ž๋“ค์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ๋‹ค.

์œ„์˜ ์˜ˆ์ œ๊ฐ€ scalar coercion์ด๋‹ค. type system์€ ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฐ’๊ณผ API contract์— ๋”ฐ๋ผ resolver ํ•จ์ˆ˜์— ์˜ํ•ด ์ ์ ˆํ•œ ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด ๊ฒฝ์šฐ ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ Enum์€ ๋‚ด๋ถ€์ ์œผ๋กœ 4, 5, 6์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

List resolvers

Human: {
  starships(obj, args, context, info) {
    return obj.starshipIDs.map(
      id => context.db.loadStarshipByID(id).then(
        shipData => new Starship(shipData)
      )
    )
  }
}

starships field๋Š” Promise์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. Human ๊ฐ์ฒด๋Š” Starships์˜ id์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” ์‹ค์ œ Startship ๊ฐ์ฒด์— id๋กœ ์ ‘๊ทผํ•ด์„œ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค.

GraphQL์€ ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€์ง€ ์ „๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.

Producing the result

leaf์—์„œ ๋ฐ˜ํ™˜ํ•œ ๊ฐ’๋“ค์ด root๊นŒ์ง€ ํƒ€๊ณ  ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ key-value๋กœ mapping๋˜์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ชจ์—ฌ์ง„ ๊ฒฐ๊ณผ๋Š” ์š”์ฒญ query์™€ ๋™์ผํ•œ ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.

์ถœ์ฒ˜

๐Ÿ”— https://graphql.org/learn/execution/

Clone this wiki locally