오브젝트 타입
GraphQL Object
는 필드라는 단위로 구성되어 있으며, 클래스와 유사한 성질을 가지고 있습니다. 예를 들어 bookType
이라는 오브젝트 타입을 정의하는 스키마 정의 문법(SDL
)을 생각해보겠습니다.
이것은 다음 타입스크립트 문법으로 표현될 수 있습니다.
// TypeScript.
const bookType: GraphQLObjectType = new GraphQLObjectType({
name: "bookType",
fields: {
title: {
type: GraphQLString
},
author: {
type: GraphQLString
}
}
});
bookType
이라는 오브젝트 타입은 생성되었지만 아직은 사용할 수 없습니다. 이것은 말 그대로 타입
에 불과하기 때문에 실제 데이터가 주어져야 각 필드의 값을 결정(resolve
)할 수 있기 때문입니다. 클라이언트가 bookType
에 title
이 있다는 것을 알고 제목을 가져오려 했지만, 실제 데이터가 없다면 어떤 정보도 얻을 수 없겠죠.
데이터를 결정하는 방법에는 2가지가 있습니다.
- 타입에서 리졸브
- 객체에서 리졸브
타입에서 결정 :
아래의 코드는 Type
수준에서 각 필드의 값을 resolve
를 통해 결정하고 있습니다. title
의 값은 동물농장으로 고정되었으며, author
의 값도 조지오웰로 고정되었습니다. 타입이 결정한 것이기에 이외의 다른 값을 가질 수 없습니다.
//Typescript.
const bookType: GraphQLObjectType = new GraphQLObjectType({
name: "bookType",
fields: {
title: {
type: GraphQLString,
resolve: () => "동물농장"
},
author: {
type: GraphQLString,
resolve: () => "조지 오웰"
}
}
});
객체에서 결정 :
Type
이 직접 필드값을 결정하지 않았다면, 반환된 데이터에서 동일한 프로퍼티의 값
으로 리졸브합니다. 아래의 코드에서 libraryType
은 bookType[]
을 반환한다고 선언하고 동일한 구조의 데이터를 반환했기 때문에, 제목이 각각 동물농장
과 1984
인 bookType
이 반환됩니다.
//Typescript
const bookType: GraphQLObjectType = new GraphQLObjectType({
name: "bookType",
fields: {
title: {
type: GraphQLString
},
author: {
type: GraphQLString
}
}
});
const libraryType: GraphQLObjectType = new GraphQLObjectType({
name: "libraryType",
fields: {
books: {
type: new GraphQLList(bookType),
resolve: () => {
return [
{ title: "동물농장", author: "조지 오웰" },
{ title: "1984년", author: "조지 오웰" }
];
}
}
}
});
루트 오브젝트
사용자가 처음으로 접근할 수 있는 오브젝트 타입을 루트 오브젝트
라고 부릅니다. 예를 들어, 내 방의 문
을 열기 위해서는 내 집의 문
을 먼저 열어야 하죠? 또 그 전에는 아파트 정문
또는 아파트 후문
을 먼저 열어야 할겁니다.
내 방의 문
에 바로 갈 수 없기 때문에, 아파트 정문
또는 아파트 후문
부터 시작해야 하죠. 이런 시작지점
의 역할을 하는 오브젝트를 루트 오브젝트
라고 부릅니다.
루트의 필드는 특별하다
루트 오브젝트는 말 그대로 서버의 시작점(Entry-Point
)이기 때문에, 루트 오브젝트의 필드는 오퍼레이션의 이름
을 갖습니다.
const root: GraphQLObjectType = new GraphQLObjectType({
name: "root",
fields: {
allBooks: {
...
},
findBook: {
},
addBook: {
...
},
delBook: {
...
},
editBook: {
...
},
}
});
쿼리와 뮤테이션
루트 오브젝트에서 각 필드의 resolve
는 데이터베이스를 수정하거나 외부변수를 조작하는 등의 사이드 이펙트(Side-Effect
)를 발생시킬 수 있습니다. 이 때, 사이드 이펙드가 없는 필드를 쿼리
라고 부르고, 사이드 이펙트를 동반하는 필드를 뮤테이션
이라고 부릅니다.
데이터를 조회하는 필드 (Query) :
단순히 데이터를 조회하는 것은 사이드 이펙트가 없으므로, 이 필드는 쿼리로 생각할 수 있습니다. allBooks
나 findBook
같은 필드가 이에 해당합니다.
데이터를 수정하는 필드 (Mutation) :
데이터베이스를 수정, 추가, 삭제하는 것은 사이드 이펙트이므로, 이 필드는 뮤테이션으로 생각할 수 있습니다. addBook
, delBook
, editBook
이 이에 해당합니다.
쿼리 오브젝트, 뮤테이션 오브젝트
다만 GraphQL.JS
는 쿼리만 모아놓은 오브젝트와, 뮤테이션만 모아놓은 오브젝트를 나누어 설계하는 방식을 취합니다.
//Typescript
const schemaObject: GraphQLSchema = new GraphQLSchema({
query: queryObject, // 필수
mutation: mutationObject // 선택
});
Express에 부착하기
위에서 생성된 스키마를 express-graphql
모듈에 넘겨서 graphql middleware
를 생성할 수 있으며, 이것을 express
에 넘길 수 있습니다.
//Typescript
import express from "express";
import expressGraphQL from "express-graphql";
import querySchema from "./querySchema";
const app = express();
app.use(
"/",
expressGraphQL({
schema: schemaObject,
graphiql: true
})
);
graphiql: true
로 설정하면 playground
페이지도 함께 만들어집니다.
'# GraphQL > GraphQL.js' 카테고리의 다른 글
타입스크립트 GraphQL 재귀호출 recursion (0) | 2019.12.15 |
---|---|
타입스크립트 GraphQL Resolve Parameter 정보 (0) | 2019.12.15 |
타입스크립트 GraphQL Input Object Type 클래스 (0) | 2019.12.14 |
타입스크립트 GraphQL Union Type 클래스 (0) | 2019.12.14 |
타입스크립트 GraphQL Interface Type 클래스 (0) | 2019.12.14 |