[GraphQL #5] GraphQL 클라이언트
Web Frontend Developer

[GraphQL #5] GraphQL 클라이언트

해당 포스팅은 프로그래밍 인사이트에서 출판한 <웹 앱 API 개발을 위한 GraphQL> (이브 포셀로, 알렉스 뱅크스 저)을 바탕으로 작성한 글임을 먼저 밝힙니다.

이번 포스팅에서는 클라이언트에서 GraphQL을 사용하는 방법에 대한 내용을 포스팅 해보려고 한다. 예를 들어 cURL을 사용한다고 했을 때 다음과 같은 값들을 요청 보낼 때 사용할 수 있다.

  • 쿼리 : {totalPhotos, totalUsers}
  • GraphQL 엔드포인트 : http://localhost:4000/graphql
  • 콘텐츠 타입 : Content-Type: application/json

이렇게 요청을 보내면 JSON 형태로 다음과 같은 형태의 결과를 받을 수 있다.

cURL이 아닌 다른 HTTP 요청 수단도 물론 사용할 수 있다. 브라우저에서 동작하는 간단한 클라이언트를 fetch를 사용해서 만들어 본 예제이다.

이렇게 데이터를 가져온 값은 위의 값과 동일하다. fetch 이외에도 graphql-request와 같은 프레임워크를 사용할 수도 있다. graphql-request는 fetch 요청을 프로미스로 감싸, 이를 GraphQL 서버에 요청을 보내서 사용한다. 물론 이러한 방법들을 사용할 수도 있지만 가장 대중적이고 강력한 방법을 지금부터 알아보도록 한다.

 

아폴로 클라이언트(Apollo-client)

REST를 사용하면 캐시를 다루기 쉽다는 장점이 있다. 요청에 대한 응답 데이터를 캐시에 저장할 수 있는데, 요청을 보낼 때 사용한 URL 위에 저장을 하기 때문이다. 이에 반해서 GraphQL은 캐싱이 살짝 더 까다롭다. GraphQL의 라우트는 여러 개가 아니며, 하나의 엔드포인트에 전달이 되며 응답도 하나로 전달이 된다. 따라서 요청 URL 라우트 하위에 데이터 캐싱을 쉽게 할 수가 없다.

여러가지 툴이 쓰이고 있지만 가장 많이 쓰이고 있는 솔루션 중 하나는 아폴로 클라이언트이다. 이 솔루션은 캐싱, Optimistic UI 업데이트 등의 목표를 달성할 수 있게 도와주며, 클라이언트에서 서버로 요청을 보내고 받는 것에 특화되어 있다. 아폴로 링크를 같이 사용해 네트워크 요청을 처리하고, 아폴로 캐시로 모든 캐시 작업을 처리한다. 아래의 예제는 리액트에서 사용을 하지만 다른 라이브러리에서도 얼마든지 사용할 수 있다.

위의 예제는 client.query({query})를 호출해 클라이언트로 AST(추상 구문 트리)를 보낸다. 이 메서드는 프로미스를 반환하게 된다. 쿼리를 GraphQL 서비스로 HTTP 요청을 보내고 서비스에서 반환하는 데이터를 처리한다.

그리고 클라이언트는 GrpahQL 서비스로 보내는 네트워크를 다루는 일 말고도 응답으로 로컬 메모리로 캐싱하는 일도 한다. 클라이언트에서 관리하는 로컬 객체에 캐싱을 하기 때문에 다음 번에 이 데이터에 대한 쿼리를 클라이언트에서 보내면 GraphQL 서비스로 네트워크 요청을 보내는 대신 캐시에서 해당 데이터를 읽어들인다. 

Query 컴포넌트

아폴로 클라이언트를 사용할 때는 데이터를 받아 와 React UI로 불러오는 쿼리를 다루어야 한다. 데이터를 받아 로딩 상태를 관리하고 UI를 업데이트 하는 일은 Query 컴포넌트에서 담당한다. ApolloProvider로 전역 스코프를 감싼 뒤에 Query 컴포넌트를 원하는 곳에 사용해 주면 된다.

Query 컴포넌트 내의 응답 객체는 로딩여부(loading), 페이지네이션(pagenation), 리페칭(refetching), 폴링(polling) 등의 유틸리티 함수를 가지고 있다. 예를 들면 refetch 함수를 사용하면 계속해서 다시 데이터를 불러올 수 있는 이벤트를 만들어서 사용할 수 있다.

Mutation 컴포넌트

GraphQL 서비스로 뮤테이션 요청을 보낼 때는 Mutation 컴포넌트를 사용하면 된다. 뮤테이션을 만들고 뮤테이션 컴포넌트에서 사용하는 예제를 살펴보도록 하자.

쿼리 컴포넌트 처럼 Mutation 컴포넌트에는 mutation 프로퍼티를 부여한다. 여기서는 variables 프로퍼티도 사용하는데 뮤테이션에 필요한 쿼리 변수를 전달할 때 사용한다. Mutation 컴포넌트는 addFakeUsers라는 함수를 사용하는데, 이 함수를 호출하여 뮤테이션 요청을 보낸다. 이 때 버튼을 누르면 뮤테이션 요청이 API로 전송된다.

 

캐시

네트워크 요청을 최소화하는 것은 두말할 필요 없이 중요하다. 이러한 네트워크 요청을 최소화 하려면 아폴로 캐시를 어떻게 설정해야 하는지 살펴보도록 하자.

기본적으로 아폴로 클라이언트는 로컬 자바스크립트 변수로 데이터를 저장한다. 그리고 클라이언트를 만들 때 마다 캐시가 생성된다. 이 때 fetchPolicy를 사용하면 아폴로 클라이언트가 데이터를 찾아보는 장소를 지정할 수 있다. 다음과 같은 옵션이 있다.

  • cache-first(default) : 클라이언트는 캐시 내부만 들어다 본다. 쿼리에서 처리해야 할 데이터가 캐시에 없다면 GraphQL 서비스로 네트워크 요청이 가게 된다. 
  • cache-only : 클라이언트로 하여금 캐시만 보도록 강제하여 네트워크 요청은 보내지 않는다. 데이터가 캐시에 없으면 에러를 던진다.
  • cache-and-network : 요청 즉시 캐시를 우선으로 쿼리 처리 시도가 이루어지며, 캐시 안의 데이터와는 별개로 최신 데이터를 가져오기 위해 네트워크 요청도 항상 추가로 발생한다.
  • network-only : 쿼리를 처리할 때 네트워크 요청만 사용한다.
  • no-cache : 항상 네트워크 요청을 사용해 데이터를 처리하고 응답 결과를 캐싱하지 않는다.

클라이언트에 캐시를 보관할 수도 있다. InMemoryCache 생성자를 사용해 캐시 인스턴스를 만든다. 그리고 apollo-cache-persist 와 같은 패키지를 사용하면 캐시가 바뀔 때마다 로컬 저장소에 변경 내역을 저장하는 함수가 포함되어 있다. 그리고 어플리케이션 시작 시 localStorage에 존재 여부를 체크해 이미 캐시가 존재하는지 확인하는 코드를 작성해 주면 다음과 같이 표현해 볼 수 있다.

 

이번 포스팅에서는 GraphQL을 클라이언트에서 어떻게 사용하는지에 대해서 공부한 내용을 정리해 보았다. 이렇게 다섯 개의 GraphQL 포스팅을 통해 GraphQL을 처음 공부하시는 분들이 이해하는데 조금이라도 도움이 되기를 바라는 마음으로 연재 글을 마무리 하고자 한다.