'facebook'에 해당되는 글 2건

  1. 2018.01.05 GraphQL Overview (graphql.js server code)
  2. 2017.11.24 GraphQL Overview

http://devcoding.tistory.com/24 슬라이드를 풀어서 써봤는데 가독성이 그리 좋진 않네요...

Graphql과 느낌이 비슷한 netflix falcor(http://devcoding.tistory.com/28)도 있으니 모르시는 분은 한번 알아두시면 좋을듯 합니다.



GraphQL Overview



Graph - 사전적으로 점과 선으로 이루어진 도형을 Graph라고 함

서로 관계가 있는 2개 또는 그 이상의 양의 상태값을 나타낸 도형

QL - SQL 비슷하다고 우선 생각하면 됨.

- 누가 만들었지?

Facebook

- 언제 만들었지?

2012년부터 내부적으로 사용하다 2015년 공개.

어떤 언어로 되어 있어?

단지 스펙이라서 구현을 하던지 언어별로 구현체가 있으니 가져다 쓰면 됨.http://graphql.org/code/

어디서 쓰고 있어?

Facebook - https://developers.facebook.com/docs/graph-api/using-graph-api)

Github - https://developer.github.com/

Why Github using GraphQL

블로그 : https://githubengineering.com/the-github-graphql-api/

  • 모든 Rest API를 제공하고 있지만 gw(integrators)의 요청을 유연하게 처리하기 힘들었다.

    GraphQL을 쓰면 신경 안써도 된다. 스키마만 업데이트 해주면 된다.

  • 때때로 response를 위해 2, 3번의 호출이 필요했다.

    GraphQL이 알아서 해준다.

  • 매개 변수에 대한 검증이 필요했다.

    매개변수에 대한 검증도 FE의 요청쿼리에 대한 validation 가능하다. eslint-plugin-graphql

  • 코드를 통해 문서를 생성하길 원했다.

    GraphiQL을 쓰면 schema에 대한 정보를 볼수 있고 Query도 실행/검증 가능

  • 요청 받을 Response에 대해서 요청자가 정의하길 원한다.

    이건 넣고 이건 빼고 할필요 없음. 요청자가 요청한 정보만 내려온다.(=overfetch 방지)

Rest API vs GraphQL HTTP상의 Endpoint 비교

REST API

엔티티별로 아래와 같은 스타일.

  • 리스트 조회 : GET /api/posts

  • 조회 : GET /api/posts/1

  • 생성 : POST /api/posts

  • 치환(수정) : PUT or PATCH /api/posts/1

  • 삭제 : DELETE /api/posts/1

GraphQL

모든 엔티티에 대해서 하나의 URL만 가짐.

  • POST /graphql

    ​-> 리스트, 조회, 생성, 수정, 삭제등

이정도면...간단하게 Database의 console에 붙어서 Query한다 생각하면 편하다.

다만 Query의 스펙을 미리 정의(=Schema) 해놓아야만 한다. Query 에 따라서 입력/조회/수정/삭제를 한다.

GraphQL Server가 할일은?

1. Node 정의



개별 node를 정의. 예 : 유저, 글, 댓글, 회사, 상품, 판매자

쉽게 이야기하면 Database의 테이블 단위

2. Node 간 관계설정



  • node에 관계 필드를 추가.- user node에 companyInfo 추가- company node에 users 추가

  • 관계 필드에 대해서 Store 연결.

    - companyInfo필드의 Type은 CompanyType- users 필드의 Type은 GraphQLList(UserType)과 같이 감싸준다.

노드(점)의 관계 필드(선)가 곧 Store 연결(=DB 조회)


3. Root Query 생성(선정).

Node중 최상위에서 조회가능한 녀석들을 분류해야한다.


Root Query?

  • REST API의 여러 조회용 END POINT의 모음이라 생각하면 이해하기 쉽다. 즉. Entry Point를 제공자가 정의해주는 것이다.

  • RootQuery는 의미적인 표현인 것이고 일반 node와 Type/동작 동일.= GraphQLObjectType

  • 다만 의미적으로 Root Query Node의 경우에는 다른 node를 참조만 하고,다른 노드로 부터 참조되지 않는다.

GraphQL로 서비스 구성 예시

DB든 외부연동서버든 처리할 데이타에 대해서는 Node 정의를 해주어야 합니다.


GraphQL로 서비스 예시 2

여기서도 GraphQL서버에서 Composite을 위해서 서버별 타입별로 Node정의를 해주어야 한다.

만약 A,B 서버에 동일하게 생긴 Node가 있더라도 관계설정이 달라질수 있으므로각각을 구분해주어야 한다.

GraphQL 사용시 주요 장점.

  • FE/Native의 요청 필드만 리턴하므로 Overfetch 방지.

  • MSA 구조에서 여러 서버의 데이타를 효율적으로 Data Integration 한다.

  • REST API에서 2~3회 요청에 걸쳐 데이타를 가져오는 경우를 간단하게 1 회 요청으로 끝낼 수 있다.(유연성 甲)# BE 개발자가 별도 Endpoint 추가하고 로직으로 풀던 문제를 쉽게 끝낼 수 있다.

전체 구조



이렇게도 나누는것도 가능은 할듯.

코드를 보면 이렇게도 가능할 것으로 생각된다.다만 아래와 같이 나눌 경우 동일 데이타에 대해서 변경과 조회가

함께 일어나므로 시점에 따라 값이 달라질수도 있는 문제가 발생 할 수 있다.

샘플코드

회사와 사람 Node

코드로 두 Node로 관계를 지어보자.

샘플을 실행시켜보려면 https://github.com/gidong-lee/graphQL_exam

샘플코드에 대해서.

  • axios는 back 단 rest api를 호출해주는 jQuery의 ajax같은 라이브러리다.(예 : Spring 개발시 RestTemplate)

  • JSON Server는 json파일을 읽어 쉽게 개발/목킹용 rest api 서버를 만들어 주는 녀석이다.입력,수정,삭제,페이징등을 모두 처리해준다.GitHub : https://github.com/typicode/json-server

User Type

const UserType = new GraphQLObjectType({
 name: 'User', // GraphQL Object Name(필수)
 fields: () => ({
   id: {type: GraphQLString},
   firstName: {type: GraphQLString},
   age: {type: GraphQLInt},
   companyId: {type: GraphQLString},
   company: {   /* 연결(user -> company) */
     type: CompanyType,
     resolve(parentValue, args) {
       console.log(parentValue, args);
       return axios.get(`${SERVER_DOMAIN}/companies/${parentValue.companyId}`)
        .then(req => req.data);
    }
  }
})
});

위의 fields.company 필드가 관계필드가 된다.

관계 필드는 곧 DB/Cache/rest server 연결(=DB 조회)을 뜻한다.

코드의 axios는 뒤단의 rest api를 호출하여 데이타를 가져오는 것이니 크게신경 안써도 됨.

Company Type


const CompanyType = new GraphQLObjectType({
 name: 'Company',
 fields: () => ({
   id: {type: GraphQLString},
   name: {type: GraphQLString},
   description: {type: GraphQLString},
   users: { /* 연결(company -> users) */
     type: new GraphQLList(UserType),
     args: {limit: {type: GraphQLInt}},
     resolve(parentValue, args) {
       const { limit = 20 } = args;
       return axios.get(
         `${SERVER_DOMAIN}/companies/${parentValue.id}/users?_limit=${limit}`
      ).then(req => req.data);
    }
  }
})
});

위의 fields.users 필드가 관계필드가 된다.

관계 필드는 곧 DB/Cache/rest server 연결(=DB 조회)을 뜻한다.

users필드의 타입이 GraphQLList(UserType)인 것을 잘 숙지하자.

Root Query

Query(조회) 시 시작될 수 있는 node 를 지정하여야 한다.따라서 node product 조회는 RootQuery 필드에 없으므로 company를 통해서만 접근 할 수 있다.


RootQuery 지정


const RootQuery = new GraphQLObjectType({
 name: 'RootQueryType',
 fields: () => ( {
   user: { /* RootQueryObject에 User연결 */
     type: UserType,
     args: {id: {type: GraphQLString}},
     resolve(parentValue, args) {
       return axios.get(`${SERVER_DOMAIN}/users/${args.id}`)
        .then(req => req.data);
    }
  },
   company: { /* RootQueryObject에 Company연결 */
     type: CompanyType,
     args: {id: {type: GraphQLString}},
     resolve(parentValue, args) {
       return axios.get(`${SERVER_DOMAIN}/companies/${args.id}`)
        .then(req => req.data);
    }
  }
})
});

관계 필드의 resolve 함수?

parentValue -> 나의 상위에서 주입해준 값.user의 Company에서 조회하여 리턴해준값

args -> Query 호출시에 적어주는 값. 예 : id, searchKeyword, limit, first, last, page, lastNum


const CompanyType = new GraphQLObjectType({
 name: 'Company',
 fields: () => ({
   id: {type: GraphQLString},
   name: {type: GraphQLString},
   description: {type: GraphQLString},
   users: {
     type: new GraphQLList(UserType),
     args: {limit: {type: GraphQLInt}},
     resolve(parentValue, args) {
       const { limit = 20 } = args;
       return axios.get(
         `${SERVER_DOMAIN}/companies/${parentValue.id}/users?_limit=${limit}`
      ).then(req => req.data);
    }
  }
})
});

GraphiQL - Graphql의 개발자도구.

작성한 Schema를 보고 Query를 작성하고 유효성을 검사하면서 테스트를 할 수 있는 good dev tool


설치버전도 있고, 서버(dev용으로) 띄울때 함께 띄울수 있다.




Query 해보기

User정보와 회사정보를 가져오는 샘플

요청


{
 user(id: "23") {
   id
   firstName
   company {
     name
  }
}
}
결과


{
 "data": {
   "user": {
     "id": "23",
     "firstName": "ko",
     "company": {
       "name": "tomorrow"
    }
  }
}
}

Company정보와 users를 가져오는 샘플

요청


{
 company(id: "1") {
   id
   name
   description
   users {
     id
     firstName
     age
  }
}
}
결과

{
 "data": {
   "company": {
     "id": "1",
     "name": "tomorrow",
     "description": "111st",
     "users": [
      {
         "id": "23",
         "firstName": "ko",
         "age": 20
      },
      {
         "id": "40",
         "firstName": "lee",
         "age": 40
      },
      {
         "id": "44",
         "firstName": "ra",
         "age": 45
      }
    ]
  }
}
}

Mutation

변경작업(추가, 수정, 삭제)

Response Type에 따라서 Query를 원하는대로 데이타를 받을 수도 있다.

Mutation 사용시 장점.

  • 사실 로직적으로 내부적으로 변경하는 로직을 있는것 뿐 조회 Query와 구조적으로는 같다.

  • Mutation query를 호출하더라도 해당 Mutation의 Return Type에 따라서 Response를 자유롭게 변경 할 수 있다.



Mutation - User 추가


const mutation = new GraphQLObjectType({
 name: 'Mutation',
 fields: {
   addUser: {
     type: UserType,
     args: {
       firstName: {type: new GraphQLNonNull(GraphQLString)},
       age: {type: new GraphQLNonNull(GraphQLInt)}     },
     resolve(parentValue, {firstName, age}) {
       return axios.post(`${SERVER_DOMAIN}/users`, {firstName, age})
        .then(res => res.data);
    }
  },
   deleteUser: {},
   editUser: {}
}
})

mutation을 이용한 유저 추가 및 생성 정보 받기

요청


mutation {
 addUser( firstName: "gu", age: 40 ) {
   id
   firstName
   age
}
}
결과


{
 "data": {
   "addUser": {
     "id": "SJ0Wox-i-",
     "firstName": "gu",
     "age": 40
  }
}
}


Mutation - User 삭제


const mutation = new GraphQLObjectType({
 name: 'Mutation',
 fields: {
   addUser: {},
   deleteUser: {
     type: UserType,
     args: {
       id: {type: new GraphQLNonNull(GraphQLString)}
    },
     resolve(parentValue, {id}) {
       return axios.delete(`${SERVER_DOMAIN}/users/${id}`)
        .then(res => res.data);
    }
  },
   editUser: {}
}
})

mutation을 이용한 유저 삭제

요청


mutation {
deleteUser(id : "SJ0Wox-i-") {
  id
}
}
결과


{
 "data": {
   "deleteUser": {
     "id": null
  }
}
}


Mutation - User 수정


const mutation = new GraphQLObjectType({
 name: 'Mutation',
 fields: {
   addUser: {},
   deleteUser: {},
   editUser: {
     type: UserType,
     args: {
       id: {type: new GraphQLNonNull(GraphQLString)},
       firstName: {type: GraphQLString},
       age: {type: GraphQLInt},
       companyId: {type: GraphQLString}
    },
     resolve(parentValue, args) {
       return axios.patch(`${SERVER_DOMAIN}/users/${args.id}`, args)
        .then(res => res.data);
    }
  }
}
})

mutation을 이용한 유저 수정(Patch)

요청

mutation {
 editUser( id: "44", firstName: "ra", age: 45) {
   id,
   firstName,
   age,
   company {
     id,
     name,
     description
  }
}
}
결과


{
 "data": {
   "editUser": {
     "id": "44",
     "firstName": "ra",
     "age": 45,
     "company": {
       "id": "1",
       "name": "tomorrow",
       "description": "111st"
    }
  }
}
}

상황극

GraphQL을 안쓸때 상황.

  • login.json에 userName이랑 image좀 추가해주세요.

  • NoticeList.json에서는 id, title, 작성자만 필요하니 나머지는 빼주세요.

  • 몇일있다가 수정일은 추가해주세요.

  • 이 데이타는 결과에 따라서 추가 Request가 필요하므로 서버에서 한번에 말아주세요.

  • 하위 호환 때문에 API 버전 변경이 필요해요.

GraphQL을 쓰게되면.

  • Root Query에 머하나 추가해주세요.

  • A 랑 B랑 릴레이션 맺어주심 안돼요?

  • mutation 추가해주세요.

  • 빨리 끝내고 커피 한 잔 하러 가시죠.(추정)

FrontEnd Client 3총사

URL


'Javascript/node.js Coding' 카테고리의 다른 글

ES6+(EcmaScript2015+) 정리(ing)  (0) 2017.12.27
Mobx with angular 정리.  (0) 2017.12.05
Netflix Falcor Overview  (0) 2017.12.03
Posted by 다인,보리아빠
,

GraphQL Overview

세미나 자료 2017. 11. 24. 13:48

GraphQL Overview 과정.


Sample Code : https://github.com/gidong-lee/graphQL_exam






Posted by 다인,보리아빠
,