다시 말하지만 graphQL은 graphQL 서버와 통신하기 위한 쿼리 언어일뿐 서버 통신 방법이 아닐뿐더러 graphQL 역시 비동기로 처리가 된다.
지난번 블로깅을 통해 graphQL 서버에 QUERY라는 요청하는 방법, 더 자세히는 문법에 대해 알아보았다.
여러가지 문법을 통해 원하는대로 요청을 보내고 원하는대로 응답을 받고 또한 여러 상황에 대해서 서버와 통신하는 graphQL만의 문법을 알아보았다.
그렇다면 실제로 서버와의 통신과정과 통신을 통한 응답을 우리가 만드는 어플리케이션에 가져올 수 있을까?
Apollo Client is a fully-featured caching GraphQL client with integrations for React, Angular, and more. It allows you to easily build UI components that fetch data via GraphQL.
출처:npmjs-apollo-client
npmjs.com에 나온 Apollo Client
에 대한 설명이다. graphQL API
를 어플리케이션 내에서 사용할 때 유용한 package들이 여러개 있는데 (예를 들면 페이스북의 relay
) Apollo Client
가 최고이며 가장 많이 사용된다고 하는데 이유들은 다음과 같다.
Apollo Client
는 개발자로 하여금 graphQL
을 통한 local과 remote 데이터 관리가 가능하게 해주는 종합적인 자바스크립트 상태관리 라이브러리 이고 (apollo의 cache 기능과 관련이 있다. 나중에 깊게 정리할 예정)등등의 이유가 있다.
하지만 개인적으로 Apollo Client
가 graphQL
과 찰떡궁합인 이유는 (비록 relay를 사용해보지 못하였지만) graphQL API
의 작동원리에 있다고 생각한다.
graphQL API
의 작동방법을 간단하게 살펴보면 결국은 fetch
이다.
이 말이 무슨 뜻이냐 하면 graphQL API
는 결국은 REST API
와 다를바 없다는 뜻이다.
REST API는 url
로 접근하여 원하는 method
를 코드로 작성하여서 서버와 통신을 한다.
graphQL
은 이와는 조금 다르게 단 하나의 endpoint
인 url
에 접근하여 원하는 방식과 데이터 필드를 query
와 mutation
을 통해서 통신한다.
하지만 조금만 더 깊게 살펴보면 graphQL API
는 기본적으로 우리가 원하는 정보를 query나 mutation으로 작성하여 Axios
의 fetch
와 함께 POST
request를 서버로 보낸다.
graphQL API
는 모두 단 하나의 endpoint
를 가진다는 차이점을 제외하면 결국 통신은 fetch
,POST
등으로 이뤄진다는 점에서 큰 차이점은 없다.
위 이미지의 빨간색 박스 두개를 보듯이 실제 graphQL 통신은 fetch
타입에 POST
method임을 알 수 있다.
즉 apollo-client와 같은 패키지 없이 graphQL 서버와 통신하기 위해서는 우리가 원하는 정보와 함께 작성한 query를 Axios의 fetch와 함께 POST request로 보내야한다. 그 방법이 존재하는지 어떻게 하는지 알지도 못하지만 매우 복잡해 보이고 번거로워보인다.
그래서 apollo-client
를 이용하면 apollo
가 직접 이런 방식으로 알아서 서버와 통신을 가능케 해주고 모든 설정을 다 자동으로 해주는 package를 제공해준다.
위의 이유들 중 가장 손꼽히는 apollo
의 장점은 cache기능으로서 불필요한 서버 통신을 최대한으로 줄여준다.
더 나아가 리액트 개발자로서 apollo-client
가 제공해주는 여러 hook
들은 나로 하여금 apollo
를 사용하지 않을 이유가 전혀 없다고 본다.
물론 react말고도 다른 javascript 프론트엔드 프레임워크에서도 사용이 가능하지만 이상하리만큼 REACT에 대한 기능 제공이 많다.
그럼 너무 감사하니 한번 기본 세팅을 적용해보자.
Apollo-client에서 사용하는 여러 페키지가 있었지만 지금은 core-package하나면 충분히 사용이 가능하게 update되었다
npm install @apollo/client graphql
요 2개의 패키지만으로도 충분히 사용 가능하다.
@apollo/client
:
이 싱글 페키지는 Apollo Client를 세팅하기 위해 필요한 사실상 거의 모든것이 들어있다.
[in-memory cache, local state management, error handling, and a React-based view layer] 등등 graphQL서버와 통신을 위해 사용하는 Apollo Client
세팅을 할 수 있게 해준다.
graphql
: 이 패키지는 GraphQL
쿼리들을 파싱하는 로직들을 제공해준다.
또한 apoll-client
자체가 커뮤니티 오픈 소스 기반으로 개발된 라이브러리라 customized되고 공식 문서에서 인증해주는 패키지들도 존재한다.
역시나 공식문서에 잘 설명이 되어있다. 특히 react관련은 더더욱 다양하고 자세한 설명이 존재한다.
공식문서에 따라 천천히 설정해보자.
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { gql } from '@apollo/client';
const client = new ApolloClient({
uri: 'endpoint of your graphQL server',
cache: new InMemoryCache()
});
client
// client에 접근하여
.query({
// query요청임을 명시해주고
query: gql`
query GetRates {
rates{
currency
}
}
`
// query에서 우리가 원하는 내용을 명시해준다.
// 이렇게 직접 명시도 가능하지만 지난번 블로그에서 정리했듯이 query내용을 직접 다른 파일에 정의해놓고 import해서 사용도 가능하다.
})
.then(result => console.log(result));
// fetch타입의 post method임으로 Promise패턴으로 접근이 가능하다.
이게 기본적인 client
설정과 그 설정에 접근하여 실제로 Apollo-client
도움으로 graphQL
서버와 통신하는 방법이다.
하지만 우리는 개발자이기에 매번 요청마다 client
를 설정하여 서버와 통신하는 방법보다 조금 더 개발자스러운 방법으로 해보자.
Provider
패턴을 통해 매번 client
를 정의할 필요없이 props
로 받아서 사용해보자.
import React from 'react';
import { render } from 'react-dom';
import { ApolloProvider } from '@apollo/client';
import { ApolloClient, InMemoryCache } from '@apollo/client';
function App() {
const client = new ApolloClient({
uri: 'https://48p1r2roz4.sse.codesandbox.io',
cache: new InMemoryCache()
});
return (
<ApolloProvider client={client}>
<div>
<h2>My first Apollo app 🚀</h2>
</div>
</ApolloProvider>
);
}
render(<App />, document.getElementById('root'));
이런식으로 Provider
패턴을 통해 Provider
에 의해 감싸진 컴포넌트들은 어느 곳에서든 client
를 props
로 받아서
서버와 통신이 가능해진다.
// getExchangeRatesQuery.js
import { gql } from '@apollo/client';
export const EXCHANGE_RATES = gql`
query GetExchangeRates {
rates {
currency
rate
}
}
`;
// ExchangeRates.js
import { useQuery} from '@apollo/client';
import {EXCHANGE_RATES} from 'getExchangeRatesQuery.js'
function ExchangeRates() {
const { loading, error, data } = useQuery(EXCHANGE_RATES);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return data.rates.map(({ currency, rate }) => (
<div key={currency}>
<p>
{currency}: {rate}
</p>
</div>
));
}
이렇게 hook
사용이 가능한 함수형 컴포넌트에서는 apollo-client
가 제공해주는 hook
으로 사용이 가능하며
class형 컴포넌트에서는 props.client
로 접근하여 사용이 가능해진다.
이번 블로그에서 client
설정 즉, apollo-client
를 설정하는 다양한 방법에 (Link, onError 등등) 대해서 함께 정리하려했다.
하지만 정말 이번주도 바쁘고 다음주도 바쁠예정이라 오늘은 블로깅이 부끄럽게 느껴질만큼 간단하지만 여기서 마무리 하려 한다.
다음 블로깅에서는 다양한 client 설정 방법과 옵션설정들에 대해서 알아보고 추가로 실제로 accessToken
과 refreshToken
을 이용한
인증 인가 부분을 개발하며 했었던 삽질과 해결 방법을 통해 Apollo-client
의 error handling에 대해서 정리할 계획인다.
개발하며 정리하고 넘어가고 싶은 부분이 정말 많은데 매번 일정에 쫒겨 정리를 못하는 모습에 반성만 하고 있는 내모습에 다시한번 반성하며 이번 블로깅은 여기서 끝.