Execution
유효성을 검사한 뒤 GraphQL 쿼리는 GraphQL 서버에 의해 실행되는데, 이 서버는 요청된 쿼리와 같은 형태로 결과를 리턴한다. 일반적으로 이는 JSON 이다.
GraphQL 은 타입 시스템이 없는 쿼리는 실행할 수 없는데, 예시를 통해 살펴보자. 아래 예시들은 지금까지 써왔던 예시들의 일부이다.
type Query { human(id: ID!): Human } type Human { name: String appearsIn: [Episode] starships: [Starship] } enum Episode { NEWHOPE EMPIRE JEDI } type Starship { name: String }
쿼리가 실행될 때 어떤 일들이 발생하는지 알아보기 위하여 예시를 보자.
GraphQL 쿼리의 각 필드를 이전의 타입을 다음 타입으로 돌려주는 함수라고 생각하면 된다. 실제로 이는 GrapQL이 동작하는 원리와 같다. 각 타입의 각 필드는 resolver 이라고 불리는 함수로 뒷받침된다.
필드가 문자열, 숫자와 같이 스칼라 값을 생성한다면, 실행은 거기서 완료된다. 그러나 필드가 객체 값을 리턴한다면, 쿼리는 해당 객체에 적용되는 또 다른 필드를 포함하게 될 것이다. 이것은 해당 값이 스칼라 값에 미칠때까지 계속된다. GraphQL 쿼리는 언제나 스칼라 값에서 종료된다.
Root fields & resolvers
모든 GraphQL 서버에서 가장 위의 레벨은 GraphQL API 의 가능한 모든 엔트리 포인트를 나타낸다. 이는 Root Query 라고 불린다.
아래 예시에서 Query 타입은 human 이라는 필드를 제공하는데, 이는 id 를 argument로 받는다. 이 필드의 resolver 함수는 데이터베이스에 접근하여 Human 객체를 구성한 뒤 리턴한다.
Query: { human(obj, args, context, info) { return context.db.loadHumanByID(args.id).then( userData => new Human(userData) ) } }
이 예시는 자바스크립으로 작성되었느나 GraphQL 서버는 다양한 언어들로 만들어 질 수 있다. resolver 함수는 4가지 argument 를 받는다.
obj
args
context
info
GraphQLR resolver info 는 여기를 참고
Asynchronous resolvers - 비동기적 resolvers
아래 resolver 함수에서 무슨 일이 발생하고 있는지 봐보자.
human(obj, args, context, info) { return context.db.loadHumanByID(args.id).then( userData => new Human(userData) ) }
context 는 데이터베이스에 접근할 수 있는 권한을 제공한다. 이는 GraphQL 쿼리에서 argument 로 제공된 id에 대한 사용자를 로드하는데 사용된다. 데이터베이스에서 로딩하는 과정이 비동기적 작업이므로 이는 Promise 를 리턴한다.
자바스크립트에서 Promise 는 비동기 값으로 작업하는데 사용되는데, 다른 언어에서도 비슷한 컨셉이 있긴 하다. 데이터베이스가 값을 반환하면, 우리는 Human 객체를 구성한 뒤 리턴할 수 있다.
resolver 함수는 Promise 를 알아야 하는 반면에, GraphQL 쿼리는 이를 알 필요가 없다. 이것은 단순히 name 필드에 대해서 물어볼 수 있는 무언가를 human 필드가 리턴하기를 예상한다.
Trivial resolvers
이제 human객체가 사용 가능하므로, GraphQL 요청된 필드를 실행할 수 있다.
Human: { name(obj, args, context, info) { return obj.name } }
GraphQL 서버는 타입 시스템에 의해 동작하는데 이는 다음에 무엇을 할 지 결정하는데 사용된다. 심지어 human 필드가 어떠한 값이던 리턴하기 이전에, GraphQL 은 다음 단계에 대해서 알고 있다. 이 다음 단계는 Human 타입이 가지고 있는 필드를 resolve 하는 것인데, 이는 타입 시스템이 human 필드가 Human 을 리턴할 것이라는 것을 말해주기 때문이다.
이름을 resolve 하는 것은 이 경우에는 직관적이다. 이름 resolver 함수가 불리우고 obj 는 이전 필드에서 반환된 new Human 객체이다. 이 경우에는 우리는 Human
객체가 읽고 리턴할 수 있는 name 이라는 속성을 가질 것으로 예상한다.
실제로, 많은 GraphQL 라이브러리는 이 정도로 간단한 resolver 를 생략할 수 있도록 할 것이다. 그리고 만약에 resolver 가 필드에서 제공되지 않는다면, 같은 이름의 속성이 읽어지고 리턴될 것이다.
module Types UserType = GraphQL::ObjectType.define do name 'User' field :id, !types.ID field :email, !types.String # 예를 들어 여기에서는 따로 resolver 생략하였지만 email 필드를 자동적으로 반환 end end
Scalar coercion
Human: { appearsIn(obj) { return obj.appearsIn // returns [ 4, 5, 6 ] } }
우리의 타입 시스템이 appearsIn 이 Enum 값들을 리턴할 것이라고 주장하였지만 해당 함수는 숫자들을 리턴한다. 그러나 실제로 결과를 보면 적당한 enum 값들이 리턴된 것을 볼 수 있다. 무슨 일이 일어난걸까?
이것은 스칼라 coercion 의 예시이다. 타입 시스템은 무엇을 예상해야 되는지 알고 있고, resolver 함수에 의해 리턴된 값들을 API에 맞게 변환한다. 이 경우에는, GraphQL 타입 시스템에서는 Enum 값으로 표현이 되지만 서버에 정의된 Enum 값들이 내부적으로는 4,5,6 과 같은 숫자일 것이다.
List resolvers
우리는 필드가 리스트를 리턴할 때 어떤일이 일어나는지 위의 appearsIn 필드를 통해 보았다. 이것은 enum 값들의 리스트를 반환하였는데, 타입 시스템의 예상대로 각 아이템이 적당한 enum 값으로 강제 변환되었다. 그러면 starships 필드를 resolve 할때는 무슨 일이 발생할까?
Human: { starships(obj, args, context, info) { return obj.starshipIDs.map( id => context.db.loadStarshipByID(id).then( shipData => new Starship(shipData) ) ) } }
이 필드에 해당하는 resolver 는 단순히 하나의 Promise 만 리턴하는 것이 아니라 Promise 리스트를 리턴한다. Human 객체는 그들이 조종한 Starship id 들의 리스트를 가지고 있는데, 우리는 실제 Starships 객체를 가져오기 위해서 모든 id 를 로드해야 한다.
GraphQL will wait for all of these Promises concurrently before continuing, and when left with a list of objects, it will concurrently continue yet again to load the name
field on each of these items. -?
Producing the result
각 필드가 해결되면서, 결과 값은 필드명(혹은 alias)과 함께 key-value 로 맵핑된다. 이 과정은 쿼리의 최하단 leaf 필드부터 root 쿼리타입까지 쭉 올라오면서 진행된다. 이는 기존의 쿼리와 같은 형태의 구조를 제공하고, 이는 이를 요청한 클라이언트 측에 일반적으로 JSON 형태로 반환된다.
위의 resolver 함수들이 어떻게 결과를 생성했는지 보자.
* 해당 글은 번역기 돌리다가 크롬 번역기 말도 안되는 해석에 지친 본인이 나중에 참고할 의도로 대충대충 발로 해석한 것이니 참고용으로만 사용하시길 바랍니다.
* 출처: https://graphql.github.io/learn/execution/
'배움의 즐거움 > 프로그래밍' 카테고리의 다른 글
(7) Graphql-ruby - 스키마 정의 (0) | 2018.12.31 |
---|---|
(6) GraphQL - 인트로스펙션 (0) | 2018.12.31 |
(4) GraphQL - 유효성 검사 (0) | 2018.12.31 |
(3) GraphQL - 스키마와 타입 (0) | 2018.12.31 |
(2) GraphQL - 쿼리와 뮤테이션 (1) | 2018.12.31 |