본문 바로가기

개발바닥/GraphQL

[개발] GraphQL - Apollo Link 제어

안녕하세요 devport 입니다. 오늘은 Apollo Link 라이브러리를 이용해서 GraphQL로 CRUD시에 매칭되는 로딩 및 완료 팝업 문구를 띄울수 있는 방법에 대해서 알아보도록 하겠습니다. 

 

Apollo Link?

Apollo Link 라이브러리는 Apollo Client와 GraphQL 서버 사이의 데이터 흐름을 사용자가 정의 할 수 있습니다.

 

GraphQL의 Query 또는 Mutation을 수행할때에 사용자가 정의한 Link가 순차적으로 실행되며 Link를 통해 해당 데이터를 제어할 수 있도록 구현도 할 수 있습니다.

Apollo Client는 기본적으로 사용되는 Apollo Link 중 HttpLink를 통해서 원격서버에 요청 작업을 전달합니다.

HttpLink 뿐만 아니라 다양한 Link들이 존재합니다. Apollo 공식사이트에서 설명하는 방법 및 오픈소스(github)에서 다양한 형식의 Link들을 찾아 볼 수 있습니다.

Apollo Link 라이브러리을 이용해서 에러 및 로딩 처리를 제어하는 부분에 대해서 어떻게 구현하는지 알아보도록 하겠습니다.

Apollo Link 설정

저는 이번 포스팅에서 3개의 Link를 거쳐 Graphql 서버에 전달되는 부분에 대한 처리 로직을 설계하였습니다.

 

import ApolloClient from 'apollo-client'
// Apollo Link 모듈
import { ApolloLink } from 'apollo-link'
import { HttpLink } from 'apollo-link-http'
import { onError } from 'apollo-link-error'
// 기타 모듈
import _ from 'lodash'

const errorLink = function () {}
const loadingLink = function () {}
const httpLink = new HttpLink({
    uri: 'http://localhost:3000/graphql'
})

const link = ApolloLink.from([
    errorLink,
    loadingLink,
    networkLink
])

const apolloClient = new ApolloClient({
    link: link,
    cache: new InMemoryCache({})
})

Query 및 Mutation이 요청 및 응답시에 errorLink -> loadingLink -> HttpLink가 순차적으로 실행되도록 선언하였습니다. 이제 errorLink와 loadingLink를 어떻게 구현하였는지 살펴 보도록 하겠습니다.

 

errorLink 구현

errorLink는 apollo-link-error 모듈을 통해 GraphQL 또는 Network에서 발생하는 에러를 처리할 때 사용하도록 구현하였습니다.

 

const onErrorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    let msg = ''
    graphQLErrors.map(({ message, locations, path }) => {
      msg += `${message}\n`
    })
    window.$notify.error(msg)
  }
})

저는 이 Link로 에러가 발생할 경우 GUI 에 출력 될 수 있도록 하였습니다.

loadingLink 구현

loadingLink는 새로운 사용자 정의 Link를 추가 하여 요청 및 응답처리를 각기 다르게 처리 가능하도록 구현하였습니다.

const dictLoading = {
  Read: '가져오는 중...',
  Create: '등록 중...',
  Update: '변경 중...',
  Delete: '삭제 중...'
}
const dictMsg = {
  Read: '검색 되었습니다.',
  Create: '추가 되었습니다.',
  Update: '수정 되었습니다.',
  Delete: '삭제 되었습니다.'
}

const loadingLink = new ApolloLink((operation, forward) => {
  const opName = _.get(operation, 'operationName', '')
  if (dictLoading[opName]) window.$loader.show({ state: dictLoading[opName] })
  return forward(operation).map((data) => {
    if (_.has(data, 'errors')) return data
    if (dictMsg[opName]) {
      window.$notify.info(dictMsg[opName])
    }
    window.$loader.hide()
    return data
  })
})

전송, 응답시에 operation의 이름을 매치하여 로딩 문구가 출력되며 응답이 올 경우 결과 팝업이 출력되도록 구현하였습니다.

forward 함수는 GraphQL 요청에 대한 응답시에 호출 됩니다.

// 조회 Query
gql`
  query Read {
    allBooks {
      id
      title
      author
  }
`
// 추가 Mutation
gql`
  mutation Create ($title: String, $author){
  	createBook(title: $title, author: $author) {
      id
      title
      author
    }
  }
`
// 수정 Mutation
gql`
  mutation UPDATE ($id: String, $input: inputBook) {
    updateBook(id: $id, input: $input) {
      id
      title
      author
    }
  }
`
// 삭제 Mutation
gql`
  mutation DELETE ($id: String) {
    deleteBook(id: $id) {
      id
      title
      author
    }
  }
`

operation의 이름은 Query 및 Mutation의 이름이며 CRUD 형식인 정보와 매치 되는 문구가 출력될 수 있도록 처리를 하였습니다.

 

책을 추가할때 팝업 메시지
추가 완료시에 출력되는 팝업