GraphQL에서는 쿼리의 각 필드가 다른 타입을 반환하는 하나의 함수나 메소드로 볼 수 있습니다.
각 타입의 필드는 GraphQL 서버 개발자가 제공한 resolver라는 함수를 통해 처리됩니다.
필드가 처리될 때 이에 상응하는 resolver가 호출되며 다음 값을 줍니다.
만약 필드가 scalar 값을 주게되면 해당 필드의 처리는 끝난 것입니다.
하지만 만약 필드가 객체 타입을 준다면 해당 객체 타입의 필드들에 대해서 재귀적으로 resolver를 호출합니다.
이는 scalar 값을 줄 때까지 반복됩니다.
결과적으로 GraphQL에서 필드에 반환되는 값은 scalar 값입니다.
GraphQL 서버의 최상위에 위치한 타입은 모든 진입점을 보여줍니다.
이를 Root type나 Query type이라 부릅니다.
아래 예시에서는 Query type이 human이라 불리는 필드를 제공하는데 인자로 id를 받습니다.
type Query {
human(id: ID!): Human
}
type Human {
name: String
appearsIn: [Episode]
starships: [Starship]
}
human필드에 대한 resolver함수의 예시를 보겠습니다.
Query: {
human(obj, args, context, info) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
이 resolver 함수에는 데이터베이스에 접근하여 id에 해당하는 유저를 찾고 해당 Human 객체로 반환하고 있습니다.
resolver 함수의 각 인자에 대해 설명하자면
obj
이전 객체입니다. 루트 Query 타입의 필드에 대해서는 거의 사용되지 않습니다.
args
GraphQL 쿼리에서 필드에 제공된 인자를 가진 객체입니다.
context
모든 resolver에 제공되는 값입니다. 중요한 맥락 정보들을 가지고 있는데 최근에 로그인한 유저나 데이터베이스에 대한 접근과 같은 정보를 가집니다.
info
현재 쿼리와 관련된 필드별 정보와 스키마 세부 정보를 담고 있습니다.
앞서 봤던 resolver를 다시 보겠습니다.
human(obj, args, context, info) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
해당 resolver에서 context는 데이터베이스에 접근할 수 있도록 도와주고 있습니다.
이때 데이터베이스에서 접근해서 데이터를 불러오는 작업은 비동기적입니다.
이때 GraphQL은 실행 중에 해당 비동기작업이 완료될 때까지 기다리며 최적의 동시성으로 처리합니다.
Human객체가 사용가능해졌는데 GraphQL은 해당 Human 객체의 필드들에 대해서도 계속해서 resolver를 호출가능합니다.
아래는 예시입니다.
Human: {
name(obj, args, context, info) {
return obj.name
}
}
위와 같이 하면 Human객체가 반환되었을 때 그 다음에 name을 반환하기 위한 함수를 GraphQL이 알아서 호출합니다.
Scalar coercion은 스칼라 강제 변환이라는 뜻입니다.
예시를 보겠습니다.
Human: {
appearsIn(obj) {
return obj.appearsIn; // [ 4, 5, 6 ]을 반환
}
}
obj.appearsIn은 Enum값을 반환해야 합니다.
그러나 실제로는 숫자를 반환하고 있습니다.
하지만 GraphQL의 타입 시스템이 이러한 숫자를 적절한 Enum 값으로 변환할 것입니다.
즉 클라이언트는 아래와 같은 값을 받게 됩니다.
{
"data": {
"human": {
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
}
}
}
리스트에 대해서는 어떻게 resolver가 작동하는지 보겠습니다.
아래는 예시입니다.
Human: {
starships(obj, args, context, info) {
return obj.starshipIDs.map(
id => context.db.loadStarshipByID(id).then(
shipData => new Starship(shipData)
)
)
}
}
Human객체에는 Starships의 id들이 있는 리스트가 있습니다.
해당 리스트를 이용해서 Promise의 리스트를 반환합니다.
GraphQL은 해당 Promise들이 모두 완료되면 다른 필드들을 처리하기 시작합니다.
위의 모든 필드들이 resolved되면 아래와 같은 결과가 나옵니다.
{
human(id: 1002) {
name
appearsIn
starships {
name
}
}
}
결과
{
"data": {
"human": {
"name": "Han Solo",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"starships": [
{
"name": "Millenium Falcon"
},
{
"name": "Imperial shuttle"
}
]
}
}
}