Chapter1. GraphQL
1-1 GraphQL
1-2 GraphQL VS REST APIChapter2. GraphQL 구조
Facebook이 개발한 오픈 소스로 제공된 API를 위한 쿼리 언어
Query Language 중에서도 Server API 를 통해 정보를 주고받기 위해 사용하는 Query Language
GraphQL로 그래프를 순회하기 위해 도서관의 도서 목록 시스템을 구축한다고 가정한다. 각 책에는 최소 1명의 저자가 있고, 최소한 한 권의 책을 같이 쓴 공동저자도 있을 것이다.
query {
책(ISBN이 "9780674430006") {
책 이름
저자 {
이름
}
}
}
이 방식으로 서버에 요청을 보내고,
서버가 쿼리를 해결하면 다음 쿼리 결과를 반환한다.
{
책 : {
책 이름 : "GraphQL은 어렵지 않다",
저자 : [
{ 이름 : "김코딩"},
{ 이름 : "박해커"},
]
}
}
이것을 그래프 관점에서 보면 다음과 같다.
1. ISBN 번호를 사용하여 선택한 "Book" 노드에서 시작
2. GraphQL 중첩된 각 필드로 표시된 간선을 따라 그래프를 탐색, 즉 쿼리 내 중첩된 "title" 필드를 통해 책 제목이 있는 노드로 이동
3. "authors"로 레이블이 지정된 "Book"의 간선을 따라가 "author"노드 가져오고 각 저자의 "name" 얻어옴
GraphQL로 쿼리한 것을 트리 구조로 나타내면,
- 쿼리가 반환하면 각 정보에는 해당 정보를 얻기 위해 따라간 GraphQL 쿼리의 필드로 구성된 연결 쿼리 정보가 있다.
- GraphQL 쿼리의 필드(ex. book, Authors, name)는 원하는 결과를 얻기 위해 그래프 간선을 지정한다.
즉, GraphQL은 그래프를 탐색하여 쿼리 결과 트리를 생성하는 쿼리 언어다.
REST API | GraphQL | |
---|---|---|
Resource에 대한 형태 정의와 데이터 요청 방법 | 연결 | 분리 |
Resource의 크기와 형태 | 서버에서 결정 | Resource에 대한 정보만 정의 크기와 형태는 클라이언트 단에서 요청시 결정 |
Resource / 작업유형 | URI / Method | GraphQL Schema / Query, Mutation 타입 |
여러 Resource에 접근할 때 | 여러 번의 요청 필요 | 한 번의 요청 |
요청 처리 | 엔드포인트에 정의된 핸들링 함수를 호출하여 처리 | 요청 받은 각 필드에 대한 resolver를 호출하여 처리 |
/graphql
이라는 하나의 endpoint로 요청을 받고 이에 따라 query , mutation을 resolver함수로 전달해 요청 응답.POST
메소드를 사용한다.// hero의 name과 friends 같이 쿼리
{
hero {
name
# 이런 식으로 GraphQL 내에서 주석 작성도 가능
friends {
name
}
}
}
//-------------------//
// 쿼리를 실행했을 때의 결과
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [ // 배열을 반환한다.
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
{
human(id: "1000") {
name
height
}
}
// id가 1000인 human의 name과 height를 쿼리
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
// 쿼리 결과
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
// 이렇게 query keyword와 query name을 작성.
query
: 오퍼레이션 타입 mutation
, subscription
, describes
등이 있다.지금까지 고정된 인수를 받아 쿼리했지만, 실제론 동적으로 인수를 받아 쿼리하는 게 대다수다.
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
// 변수를 써서 작성된 쿼리
!
를 붙일 수도 있음.POST
,PUT
요청으로 데이터를 수정하는 것처럼,mutation
로 서버 측 데이터를 수정한다. mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
type Character {
name: String!
appearsIn: [Episode!]!
}
Character
: GraphQL 객체 타입, 즉 필드가 있는 타입name
과 appearIn
: Character
타입의 필드Character
타입 어디서든 사용할 수 있다.String
: 내장된 스칼라 타입 중 하나. (스칼라 타입: ID, Int)!
: Non-Null. 서버는 null 값이 아닌 값을 반환할 것을 기대[ ]
: 배열. 배열에도 !
가 붙을 수 있다. !
이 뒤에 붙어 있어 null 값을 허용하지 않으므로 항상 0개 이상의 요소를 포함한 배열을 기대할 수 있다.
- 요청에 대한 응답을 결정해주는 함수
- Query, Mutation, Subscription과 같은 타입의 실제 일하는 방식 즉 로직을 작성한다.
- 스키마를 정의하면 그 스키마 필드에 사용되는 함수의 실제 행동을 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 () => {
...
}
}
};