Apollo, GraphQL 알아보기

휴먼스케이프

안녕하세요. 휴먼스케이프 Hugh 입니다.

오늘은 Apollo와 GraphQL에 대해서 알아보도록 하겠습니다.

포스팅의 내용은 아래 순서로 구성됩니다.

Apollo, GraphQL이란?

apollo client 를 이용한 data fetch

sample apollo server 코드 살펴보기

Apollo, GraphQL이란?

GraphQL은 우리가 알고 있는 SQL과 같은 쿼리 언어입니다. 그리고 Apollo는 GraphQL을 편하게 사용할 수 있도록 도와주는 라이브러리 입니다.

Apollo는 client와 server 모두에서 사용이 가능합니다. 공식 홈페이지에서 apollo client는 상태관리 라이브러리로 react redux를, apolo server는 REST API 를 대체할 수 있을 것이라고 소개하고 있습니다.

apollo client는 “Data management shouldn’t have to be so difficult!” 라고 이야기를 하고 있고 충분히 공감이 가는 말이지만, 과연 어떨지 지금부터 겪어보겠습니다.

apollo client 를 이용한 data fetching

appollo client 실습을 react 환경에서 하기 위해 react app 을 만들고, 다음을 설치합니다.

npm install apollo-boost @apollo/react-hooks graphql

apollo-boost: apollo client를 사용하기 위한 것 들의 패키징

apollo/react-hooks: react view layer 에서 사용할 수 있는 hooks 를 제공

graphql: graphql 구문을 해석

react app 을 만들고 위 패키지들을 설치했다면, 준비가 끝났습니다. 실습에서는 apollo 에서 제공하는 CodeSandbox 서버를 이용합니다.

위에서 준비된 서버로 데이터를 요청해보겠습니다.

먼저 아래와 같이 apollo client instance를 생성합니다. 파라미터의 uri는 서버의 주소이고, endpoint를 따로 지정하지 않는다면 /graphql이 됩니다. 아래 예제에서는 파라미터로 uri만 전달했지만, cache 를 어떻게 구성할지를 비롯하여 많은 옵션들을 파라미터로 받을 수 있도록 되어 있습니다.

이제 instance 생성이 되었으므로 데이터를 요청할 수 있습니다. 먼저 순수 자바스크립트를 이용하여 아래와 같이 데이터를 요청해 봅니다.

다음은 react app 에서 요청을 해보겠습니다. react app에서 요청을 하기 위해서는 @apollo/react-hooks 의 ApolloProvider로 앱을 감쌉니다. context api 에서 provier로 앱을 감싸던 형태와 비슷합니다. 이제 앱 어디서든 서버로 데이터 요청이 가능한 형태가 되었습니다.

서버로 데이터를 요청할 때는 @apollo/react-hooks 에서 제공하는 hooks인 useQuery를 사용합니다. 저는 파일을 분리하지 않고 app.js 에 컴포넌트를 하나 추가하여 아래와 같이 바로 사용하였습니다. 쿼리는 client에서 정의되는데 apollo-boost의 gql함수를 사용하고 실행할 쿼리문은 ``로 감싸주어야 합니다.

예제에서는 useQuery hooks의 리턴 값 중 loading, error, data 세 가지를사용했지만 더 많은 리턴 값들이 있어 상황에 따라 다양하게 사용이 가능 할 것으로 보여집니다. 예제에서 사용된 세 가지 리턴 값은 UI에 로딩중임을 표시, 에러가 발생했을 경우 핸들링, 데이터 렌더링 세 경우의 처리가 가능하도록 해줍니다.

위 예제의 쿼리에서는 currency를 USD라는 고정된 값으로 전달했지만, useQuery의 두 번째 인자에 variables를 사용하여 쿼리에 변수를 전달하여 다양하게 사용도 가능합니다.

우리가 위에서 요청한 데이터는 물론, apollo client 요청하는 모든 데이터는 자동으로 캐싱이 됩니다. 자동으로 캐싱이 되기때문에 우리는 이 데이터가 최신의 상태인지 체크를 할 수 있어야 합니다. 이를 위해서 apollo client에서는 두 가지 기능을 지원합니다. polling과 refetching인데요. 구현하는 방법은 아래와 같이 매우 간단합니다.

useQuery hooks에 옵션으로 pollInterval을 제공하여 주기적으로 요청이 가능하도록 지원합니다. 그리고 주기적인 요청을 컨트롤 할 수 있도록 startPolling, stopPolling 함수도 지원해주고 있습니다.

refetching은 주기적으로 동작하는 polling과는 반대로 특정 액션에 반응하여 동작하는 방식으로 구성됩니다. useQuery hooks의 리턴값 중 하나로 함수형태로 제공되어 원하는 곳에서 간단하게 사용이 가능합니다.

그리고 또 useQuery hooks는 loading state를 제공해주는데요. 디폴트 캐싱으로 인해서 처음 데이터를 fetch할 때를 제외하고는 loading indicator를 확인하기 어렵습니다. 하지만 우리는 매 데이터를 불러올 때 마다 loading indicator를 확인하고 싶은데요. 이를 위해서 networkStatus state와 notifyOnNetworkStatusChange 옵션을 이용하여 해결할 수 있습니다. networkStatus 는 1~8까지 있는데 4번이 refetch를 나타냅니다.

앞에서 소개한 useQuery는 컴포넌트가 마운트되고 렌더링 될 때 자동으로 실행되는데, 이와 다르게 특정 액션을 취했을 때 쿼리를 요청하고 싶다면 useLazyQuery hooks를 사용할 수도 있습니다.

여기까지 react app 에서 서버로 데이터를 어떻게 요청하는지 살펴보았습니다.

그럼 이쯤 되면 서버는 어떻게 구성되어 있을까 궁금해집니다.

sample apollo server 코드 살펴보기

apollo 에서 제공하는 codesandbox에 구성되어 있는 서버 코드를 살펴보도록 하겠습니다.

우리가 useQuesry hooks를 이용해서 데이터를 요청했던 스키마 정의는 다음과 같습니다. ExchangeRate라는 타입의 schema가 정의되어 있고, rates(…)라는 쿼리가 정의되어 있습니다. client는 ExchangeRate의 schema에 있는 필드를 query에서 제공하는 rate(…)을 사용하여 조회할 수 있습니다. query에는 api를 추가하듯 필요한 것이 있다면 추가하여 사용하면 됩니다.

아래의 좀 더 다양한 schema 구성을 보면서 기존에 사용하던 환경에서의 방식과 어떻게 다른지 각 자 생각해보면 좋을 것 같습니다.

우리 예제에서는 query의 타입은 rates 하나만 정의되어 있었는데요. 아래의 query은 좀 더 다양한 query 타입을 담고 있어, 우리가 기존에 api를 작성하던 방식과 비교하여 살펴볼 수 있을 것 같습니다.

지금까지 서버로부터 데이터를 요청하여 읽기만 하는 부분을 살펴보았는데요. 서버의 데이터 업데이트를 요청할 때는 query가 아닌, mutation 이라는 타입을 정의해주어야 합니다. mutation의 정의도 query와 비슷하게 구성됩니다. 그리고 query와 마찬가지로 리턴을 우리가 원하는 형태로 구성할 수 있습니다. 문서에서는 리턴도 타입을 정의해주고 항상 그 타입으로 리턴을 통일하는 것을 추천하고 있습니다.

여기까지는 서버에서 요청을 받기 위해 query 타입을 정의하였고, 데이터를 담기 위해 schema 구성을 하였습니다. 그렇다면 우리가 예제에서 호출한 rates(…) query가 수행할 로직은 어디에 있는걸까요? 바로 수행을 담당하는 resolver라는 함수에 있습니다. resolver라는 함수의 원형은 다음과 같습니다.

fieldName: (parent, args, context, info) => data;

resolver는 위와 같이 생겼는데요.

parent: 부모 resolver가 리턴한 값으로 연쇄적으로 resolver 함수를 사용할 때 사용됩니다.

args: client에서 보내준 파라미터입니다.

context: 모든 resolver들이 공유하는 오브젝트로 로그인, 권한과 같은 정보를 가지고 있을 수 있습니다.

info: 작업의 실행 상태에 대한 정보를 담고 있습니다.

위에서 resolver 함수의 원형에 대해 살펴보았고, 우리의 요청을 수행하는 resolver를 살펴보겠습니다. 우리가 사용하고 있는 예제의 서버에서는 데이터베이스가 아닌, 외부 api에서 제공하는 데이터를 샘플 dataSource로 사용하고 있어 아래와 같은 로직을 수행하고 있는 걸 볼 수 있습니다.

여기까지 간략하게 tutorial을 진행하면서 client와 server의 기본 구성에 대해 알아보았습니다. 다음 시간에는 우리가 직접 구성한 dataSouce를 활용하여 다양한 쿼리를 사용해보며 Apollo, GraphQL에 대해 알아보는 시간을 갖도록 하겠습니다.

감사합니다.

Get to know us better! Join our official channels below.

Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io

기업문화 엿볼 때, 더팀스

로그인

/