Next.js 에서 Apollo Client(GraphQL) 사용하기

aeong98·2022년 8월 11일
2
post-thumbnail

Next.js + Apollo Client (GraphQL) 세팅

💡 Apollo Client 가 무엇인지 알아본후, Next.js 에 Apollo Client 를 세팅하고 graphQL 을 사용할 수 있는 방법을 알아봅시다

Apollo Client 란?


Apollo Client 는 GraphQl 을 사용해 로컬 및 원격 데이터를 모두 관리할 수 있는 Javascript 용 상태관리 라이브러리입니다. UI 를 자동으로 업데이트하면서 애플리케이션 데이터를 가져오고, 캐시를 수정합니다. (공식문서 설명)

Apollo 는 GraphQL 을 사용한다면 거의 필수적으로 사용하는 상태관리 플랫폼이기도 합니다. 다른 선택지로 Facebook 에서 만든 Relay가 있지만, 학습 비용이 높고 React 계열만 지원한다는 단점이 있습니다.

그에 반해 Apollo 는 유연하고 러닝 거브가 높지 않을 뿐더러 Frontend 프레임워크 삼대장인 React, Angular, Vue 를 동시에 지원한다는 점에서 인기가 높습니다.

1.특징

  • Queries and Mutations : GraphQL 의 읽기와 쓰기 방법
  • Caching Overview : 이미 불러왔던 데이터 캐시, 네트워크 요청 횟수 최적화 가능
    - Managing local state : 서버상태와, 로컬 상태를 동시에 관리할 수 있음.
  • Basic HTTP nextworking : HTTP 통신 세팅 지원 (헤더 옵션, 쿠키 등 사용 가능)
  • Designed for modern React : React 용 Hook 사용 가능 (useQuery, useMutation)

2.캐시 작동원리

Apollo Client 는 GraphQL 쿼리 결과를 정규화된 로컬 인메모리 캐시에 저장합니다. 이를 통해 동일한 요청에 대해서는 새로운 네트워크 요청을 보내지 않고도, 이미 캐시된 데이터에 대한 쿼리에 거의 즉시 응답할수 있습니다.

  • 정규화된 로컬 인메모리 캐시? Apollo Client 의 InMemoryCache 는 데이터를 서로 참조할 수 있는 평면 객체 조회 테이블로 저장합니다. → 개체 필드를 다른 개체를 참조하는 구조로 평면화 합니다.
    		// before
    {
      "__typename": "Person",
      "id": "cGVvcGxlOjE=",
      "name": "Luke Skywalker",
      "homeworld": {
        "__typename": "Planet",
        "id": "cGxhbmV0czox",
        "name": "Tatooine"
      }
    }
    		// after
    {
      "__typename": "Person",
      "id": "cGVvcGxlOjE=",
      "name": "Luke Skywalker",
      "homeworld": {
        "__ref": "Planet:cGxhbmV0czox"
      }
    }
    위와 같이 homeworld 필드에 적절한 정규화된 Planet 개체가 참조에 포함됩니다.

데이터가 메모리에 없을 때

데이터가 메모리에 있을 때

Apollo Client 사용방법


위에서 알아본것과 같이 Apollo Client 는 GraphQL API 을 호출할 수 있게 하는 도구이자, 캐시를 이용한 상태관리 라이브러리이기도 합니다. 최근에는 useQuery, useMutation 과 같은 hook API 를 제공해 좀더 선언적으로 비동기처리를 할 수 있게 됐습니다. Next.js 에 apollo client 를 설치하고 사용하는 방법에 대해 알아봅시다.

1. 의존성 설치

yarn add @apollo/client graphql

// typescript 설치
yarn add -D @types/graphql

2. ApolloClient 초기화

// ./apollo-client.ts

import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
    uri: "https://countries.trevorblades.com",
    cache: new InMemoryCache(),
});

export default client;

프로젝트의 루트에 apollo-client.ts 라는 파일 생성하고, 연결할 엔드포인트를 적어 ApolloClient 객체를 초기화해줍니다. 여기서 사용한 엔드포인트는 GraphQL 연습을 위한 공개 API 입니다.

3. Next.js 에 Apollo Client 연결

위에서 생성한 ApolloClient 객체를 Next.js(React.js) 에 연결합니다. 앱 내에서 특정 컴포넌트만 GraphQL API 호출이 필요한 경우가 아닌 이상, 모든 컴포넌트에서 ApolloClient 를 사용할 수 있도록 설정하는 것이 일반적이라고 합니다. Next.js 의 루트 파일은 _app.tsx 이기 때문에, 이 파일에서 <ApolloProvider> 로 앱의 최상위 컴포넌트를 감싸고, client 객체를 주입해줍니다.

import { ApolloProvider } from "@apollo/client";
import client from "../apollo-client";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <>
      <Head>
        <title>Apollo Test</title>
        <meta charSet="utf-8"></meta>
      </Head>
      <ApolloProvider client={client}>
        <Component {...pageProps} />
      </ApolloProvider>
    </>
  );
}

export default MyApp;

4. GraphQL API 호출

apolli client 라이브러리는 react hook 방식으로 GraphQL 을 호출할 수 있도록 useQuery, useMutation Apollo Hooks 을 제공합니다. 그리고 라이브러리에서 제공하는 gql 이라는 template literal tag 를 사용해 일반 자바스크립트 문자열을 GraphQL 구문으로 바꿔줍니다.

데이터 읽기

데이터를 읽어올 때는 useQuery 을 사용합니다. useQuery 함수는 응답 데이터 (data) 뿐만 아니라, 로딩 여부(loading)오류 데이터 (error)까지 함께 리턴하기 때문에 아래와 같이 비동기처리와 예외처리를 쉽게 할 수 있습니다.

useQuery 의 첫번째 인자로 gql 로 작성한 쿼리를 넘겨줍니다.

import React from "react";
import { useQuery, gql } from "@apollo/client";

interface Country {
  code: string;
  emoji: string;
  name: string;
}

interface CountryData {
  countries: Country[];
}

const GET_COUNTRIES = gql`
  query Countries {
    countries {
      code
      name
      emoji
    }
  }
`;

// GRAPHQL READ
export function ApolloRead() {
  const { data, loading, error } = useQuery<CountryData>(GET_COUNTRIES);

  if (loading) {
    return <h2>로딩중</h2>;
  }

  if (error) {
    return <h1>에러 발생</h1>;
  }

  const countries = data?.countries.slice(0, 4);

  return (
    <div>
      {countries?.map((country: Country, idx: number) => (
        <div key={`country-${idx}`}>
          {country.code} / {country.emoji} / {country.name}
        </div>
      ))}
    </div>
  );
}

GraphQL 에 변수 넘기기

하지만 대부분의 경우에는 위처럼, 정적으로 리스트만 불러오는 경우는 많지 않습니다. 대부분의 애플리케이션에서 필드를 불러오기 위한 인수는 동적입니다. graphQL 공식 문서에 따르면, 이러한 동적 인수를 query string 에 직접 전달하는 것은 좋은 방법이 아니라고 합니다.

대신 graphQL 은 쿼리 동적 값을 넘기기 위해 variables 를 사용합니다.

  1. query 의 정적 값을 $variableName 으로 대치
  2. $varaibleName 을 query 에서 사용할 variables 로 선언
  3. variableName :value 를 넘기기.
// query
hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

//variables
{
  "episode": "JEDI"
}

출처 : GraphQL 공식문서

이를 apollo client 를 사용한 React.js 에 적용해보면 아래와 같이 작성할 수 있습니다.

그 전에 앞서 우리가 사용하는 API 의 모습은 아래와 같습니다. GraphQL 스키마 시각화를 위해 https://studio.apollographql.com/sandbox/explorer 를 사용했습니다.

  1. countries 데이터 안에는 위에 보이는 것 과 같은 fields 이 있습니다.

  1. 그리고 인자로는 filter 를 넘길 수 있습니다.

  1. filter 에 넣을 수 있는 input fields 로는 code, currency, continent 가 있습니다. 우리는 그중에서 code 필드에 필터링을 걸어보겠습니다.

  1. code 에 필터링 옵션을 나타내는 인자에도 여러가지가 있는 것 같습니다. 그중에서 우리는 eq 를 사용해봅시다.

위 정보들을 조합해서 code 가 “AD” 인 countries 의 emoji, name, code 필드를 불러오자 라는 쿼리를 작성하면 아래와 같이 작성할 수 있습니다.

이제 graphQL 쿼리를 만들어봤으니, 실제 Next.js 프로젝트에 적용해봅시다. useQuery 의 두번째 인자로는 variables 객체를 넘겨줄 수 있습니다. 이렇게 variables 인자를 통해, 동적인 쿼리 조회가 가능합니다.

import React from "react";
import { useQuery, gql } from "@apollo/client";

interface Country {
  code: string;
  emoji: string;
  name: string;
}

interface CountryData {
  countries: Country[];
}

const GET_COUNTRIES = gql`
  query Countries($filter: CountryFilterInput) {
    countries(filter: $filter) {
      code
      name
      emoji
    }
  }
`;

// GRAPHQL READ_BYFILTER
export function ApolloReadById() {
  const { data, loading, error } = useQuery<CountryData>(GET_COUNTRIES, {
    variables: {
      filter: {
        code: {
          eq: "AD",
        },
      },
    },
  });

  if (loading) {
    return <h2>로딩중</h2>;
  }

  if (error) {
    return <h1>에러 발생</h1>;
  }

  const countries = data?.countries.slice(0, 4);

  //   https://studio.apollographql.com/sandbox/explorer
  return (
    <div>
      <h1>필터링 건 데이터</h1>
      {countries?.map((country: Country, idx: number) => (
        <div key={`country-${idx}`}>
          {country.code} / {country.emoji} / {country.name}
        </div>
      ))}
    </div>
  );
}

마무리


GraphQL을 사용하면, 하나의 엔드포인트로 데이터 스키마를 한꺼번에 관리할 수 있어 자원과 용도별로 엔드포인트를 나누는 기존의 RESTAPI 를 개발하기 위한 백엔드의 공수를 줄일 수 있다는 점이 가장 큰 장점인 것 같습니다.

또한, 원하는 데이터 필드만 클라이언트에서 주도적으로 요청할 수 있으며, Apollo 가 자체적으로 캐싱을 지원하기 때문에 HTTP 요청 횟수(원하는 정보를 하나의 쿼리에 모두 담아 요청 가능)와, 사이즈를 줄일 수 있습니다. (필요한 정보만 부분적으로 요청가능)

하지만 아직 File 전송 등 text 만으로 하기 힘든 내용들을 처리하기 복잡하고, 재귀적인 query 가 불가능하다는 단점이 있다고 합니다. 그럼에도, 간단한 CRUD 로직에는 아주 강력한 퍼포먼스를 내기 때문에, 간단한 어드민이나 블로그를 제작하는 분들에게는 좋은 선택지가 될 수 있을 것 같습니다. (우리 사이드프로젝트에도^^)

단점 정보 출처

참고


profile
프린이탈출하자

0개의 댓글