순서

  • 1. 왜 쓰는지 납득을 해야 쓰지
  • 2. REST API를 GraphQL로
  • 3. Client
  • 4. 왜 이걸 써야하는거죠
  • Q & A

들어가기전에

더 자세한 내용은 구글링...! (내용은 백엔드 쪽에 거의 다 있어요 헤헤)

1. 왜 쓰는지 납득을 해야 쓰지

그동안 잘쓰고 있던 API. 한계는??
  • 플랫폼마다 조금씩 다른 쿼리,
    그때 그때 원하는 데이터만 보고싶은데...
  • 생각보다 편하다고 쓰고있었는데, 정형화된 규칙이 있을까?
    (JSON이 규칙을 가지고 있진 않음)
  • 필터는? 정렬은?
  • 페이지네이ㅅ....
  • 읍읍

그렇다면 DB... DB를 보자!!

GraphQL에서 `RDB`가 나을까 `NoSQL`이 나을까?
보통 DB에서 성능에 영향을 많이 주더라도 사용하게 되는 JOIN... 하지만 JOIN이 힘든 NoSql에서 쿼리가 급격히 늘어날수도...
RDB가 나쁠 이유는 없는데?
심지어 각각의 클라이언트에서 Depth에 대한 난이도 조절 가능...!

Depth 조절이 가능하다니 무슨 소리죠?


          module.exports = `
            type A {
              _id: String!
              Aprop1: String
              Aprop2: String
              Aprop3: [B]
            }
            type b {
              _id: String!
              Bprop1: String
              Bprop2: [C]
            }
            ...
          `
        

          module.exports = `
            type C {
              _id: String!
              Cprop1: String
              Cprop2: [D]
            }
            type D {
              _id: String!
              Dprop1: String
              Dprop2: [A]       // 두둥...!
            }
            ...
          `
        
이런 Recursive한 관계...
서비스를 하다보면 없을 순 없다 ㅠㅠ...

GraphQL의 목표

필요한 것만 정확히 물어볼 수있는 기능을 제공하며 시간이 지남에 따라 API를 쉽게 개발할 수 있도록...
GraphQL은 단독 버전 관리를 통해 기존 코드 수정없이 보다 깨끗하고 유지보수가 쉽게 사용이 가능...

2. REST API를 GraphQL로

솔직히 한번쯤은 들어봤을 GraphQL. but, 노관심

REST API

URI 중심으로 데이터의 CRUD 진행

GraphQL

QueryMutation으로 데이터의 CRUD를 진행

서버는 살짝만

하기에는... 생각보단 쉬워요! (클라이언트보단)
어떤 부분이 달라졌는지 확인해봅시다.

우선 라우팅부터

API에서 GraphQL로 경로가 바뀌었습니다.


          ...
          // server.js
          import { graphqlExpress, graphiqlExpress } from 'apollo-server-express'
          import schema from './graphql'
          app.use('/graphql', cors(), bodyParser.json(), graphqlExpress({ schema }))
          app.use('/graphiql', cors(), graphiqlExpress({ endpointURL: '/graphql' }))
          ...
        

          // graphql/index.js
          import { makeExecutableSchema } from 'graphql-tools'
          import typeDefs from './typeDefs'     // 타입 정의
          import resolvers from './resolvers'   // 정의된 타입 구현

          export default makeExecutableSchema({ typeDefs, resolvers })
        
graphql과 graphiql의 차이

typedef와 resolver

정말 단순하게도, 이 파일 2개를 만들면 서버는 끝.


          // graphql/typedef.js
          module.exports = `
            scalar Date       // 다른 타입에 대해서는 Date와 같이 scalar로 정의

            type News {       // DB에서 가져올 타입은 scalar없이 정의.
              _id: String!    // graphql의 기본 데이터 형은 String과 Int 두개
              context: String // !는 requied
              image: String
              is_ok: Int!
              crt_dt: Date!
              udt_dt: Date!
            }
            type Query {
              newsList: [News]
            }
          `
        

          // graphql/resolver.js
          import { getNewsList } from '../modules/news'

          module.exports = {
            Query: {
              newsList: () => getNewsList()
            }
          }
        

          // modules/news.js
          // API에서 쓰던 모듈과 같음
          // MongoDB 조회. 끝.
          export function getNewsList () {
            return News.find({is_ok: 1}).sort({crt_dt: -1})
          }
        

3. Client

클라이언트도 서버처럼
지난 3월 발표에서 보여드린 PWA 코드의 api 호출을 GraphQL
어떻게 바꾸는지 알아봅시다.

Vue 객체에 apollo provider 주입


          // apollo-provider.js
          import Vue from 'vue'
          import VueApollo from 'vue-apollo'
          import {HttpLink} from "apollo-link-http/lib/index"
          import {ROOT_URL} from "./config"
          import {InMemoryCache} from "apollo-cache-inmemory/lib/index"

          Vue.use(VueApollo)

          // This can hold multiple apollo clients
          const apolloProvider = new VueApollo({
            defaultClient: new ApolloClient({
              link: new HttpLink({uri: `${ROOT_URL}/graphql`}),
              cache: new InMemoryCache(),
              connectToDevTools: true
            }),
            defaultOptions: {
              $loadingKey: 'loading'
            }
          })

          export default apolloProvider
        

            // main.js
            ...
            import apolloProvider from './apollo-provider'

            /* eslint-disable no-new */
            new Vue({
              el: '#app',
              router,
              provide: apolloProvider.provide(),  // Provider 주입
              store,
              template: '',
              components: { App }
            })
          
provider는 공급자.
공급자가 하나일 필요는 없음
여러개로 늘려도? 상관 없음

호출부에 대한 설정 완료! 이제는 SPC!


          // container/News.vue
          ...
          <template>
          ...
            <li v-for="(news, index) in newsList" :key="index">
              ...
            </li>
          ...
          </template>
          <script>
          import apollo from '../graphql/news'
          ...
          export default {
            apollo,   // apollo news 모듈
            data () {
              return {
                newsList: [],
                ...
              }
            },
            ...
          }
          </script>
          ...
        

          // graphql/news.js
          import gql from 'graphql-tag'

          export default {
            newsList: {
              // 가져올 column만 명시.
              query: gql`query NewsList {
                newsList {
                  _id
                  context
                  image
                }
              }`,
              result ({ data, loader, networkStatus }) {
                console.log('We got some result!')
              },
              // Error handling
              error (error) {
                console.error('We\'ve got an error!', error)
              },
              loadingKey: 'loading'
            }
          }
        

간단하게 설명했지만


          export default {
            module: {
              rules: [
                // ...
                {
                  test: /\.(graphql|gql)$/,
                  exclude: /node_modules/,
                  loader: 'graphql-tag/loader'
                }
              ]
            }
          }
        
1. 코드를 분리하려면 꼭 필요한 webpack loader 설정
2. pub/sub 모델을 넣고 소켓으로 DB 변화를 `구독` 하려면 추가해야하는 graphql-subscriptions
3. vuex에서 데이터 변화를 조금 더 쉽게 관찰할 수 있게 해줄 vue-supply
4. 기타 등등 ....

3. 단점에 대해

사실 여기서부터가 나눠보고 싶은 얘기.
페이스북에서 "write once, run anywhere"
이라는 이상에 맞추기 위해 만든게 GraphQL.
안 쓸 이유가 있을까?

주관적인(겪어봤던) 리뷰

실제로 구현하는건 훨씬 복잡한 케이스가 다수
에러 관리 (에러가 200으로 떨어지는 케이스)
실패한 요청에 대한 재시도 (pub/sub으로 해결 가능함)
하지만! 어느정도 잡혀있는 규칙에 대해선 매우 긍정적
러닝커브는 모르겠지만, 한번 익숙해지면 rest보다 낫다고 생각할 수 밖에 없음

느낀 점 & 드리고자 하는 말

대형 서비스를 기존 REST에서 GraphQL로 모두 바꾸기엔
조금은 이른게 아닐까 + 시작해볼만 하다
신규서비스라면 써보라 추천!!

Q&A

감사합니다.