Logo

[GraphQL] SchemaLink 사용법 - 서버없는 클라이언트

GraphQL 서버 없이도 클라이언트에서 GraphQL API를 호출할 수 있도록 도와주는 Aollo Client의 SchemaLink에 대해서 알아보겠습니다.

일반적인 Apollo Client 생성

일반적으로 Apollo Client를 사용할 때는 다음과 같이 GraphQL 서버로 HTTP 요청을 보내기 위해서 HttpLink를 사용합니다.

import { ApolloClient } from "apollo-client";
import { HttpLink } from "apollo-link-http";

const client = new ApolloClient({
  link: new HttpLink({ uri: "https://www.my-server.com/graphql" }),
  // 다른 옵션
});

client.query(/*...생략... */);

이렇게 HttpLink를 사용할 때는 반드시 연동할 GraphQL API의 endpoint를 가진 GraphQL Server가 어딘가에 떠 있어야 합니다.

Apollo Client를 이용해서 GraphQL API 호출하는 기본적인 방법은 아래 포스팅를 참고 바랍니다.

Apollo Client에는 Network Layer를 담당하는 Apollo Link라고 불리는 중요한 개념이 있습니다. 이 Apollo Link를 통해 Apollo Client는 GraphQL 요청을 어떤 과정을 거처 어떤 방식으로 처리할지에 대해서 유연하게 제어가 가능하도록 설계되어 있습니다. 이것이 ApolloClient 생성자가 link 옵션을 통해 다른 Apollo Link 객체를 받을 수 있는 이유입니다.

예를 들어, 방금 살펴본 HttpLink는 원격 GraphQL Server와 HTTP 프로토콜을 통해 데이터를 송수신하도록 구현된 Apollo Link입니다. Apollo Client는 HTTP 프로토콜이 아닌 WebSocket을 통해서 GraphQL Server와 연동할 수 있도록 WebSocketLink도 제공합니다. 또한, 이제부터 자세히 살펴볼 SchemaLink는 GraphQL Server 없이도 GraphQL API를 호출이 가능하도록 구현되어 있습니다.

SchemaLink는 원격 서버를 필요로 하는 HttpLinkWebSocketLink와 달리 단지 GraphQL 스키마만 있으면 GraphQL API를 사용할 수 있게 해줍니다. 즉, 서버 단에 두었던 GraphQL 스키마를 클라이언트 단에 옮겨놓을 수만 있다면 굳이 서버가 없이도 클라이언트 앱 개발이 가능하다는 말입니다. 이러한 SchemaLink의 특징은 클라이언트 단에서 서버를 모킹하거나 Server-sdie rendering을 구현하는데 유용하게 쓰입니다.

패키지 설치

프로젝트에 GraphQL과 Apollo Client 관련 패키지를 설치합니다. 이 중, apollo-link-link 패키지가 SchemaLink를 제공하며, graphql-tools는 스키마 생성을 위해 쓰이는 유틸리티 함수인 makeExecutableSchema()를 제공합니다.

$ npm i apollo-client apollo-cache-inmemory apollo-link-schema graphql graphql-tag graphql-tools
  • package.json
  "dependencies": {
    "apollo-cache-inmemory": "1.6.3",
    "apollo-client": "2.6.4",
    "apollo-link-schema": "1.2.3",
    "graphql": "14.4.2",
    "graphql-tag": "2.10.1",
    "graphql-tools": "4.0.5"
  }

패키지 임포트

Apollo Client를 사용하기 위해 기본적으로 필요한 ApolloClient, InMemoryCache, gql을 임포트 합니다. 추가적으로 SchemaLink를 사용하기 위해 SchemaLinkmakeExecutableSchema을 임포트해야 합니다.

import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { SchemaLink } from "apollo-link-schema";
import { makeExecutableSchema } from "graphql-tools";
import gql from "graphql-tag";

스키마 생성

GraphQL 스키마는 타입에 대한 정의(typeDefs)와 리졸버(resolvers)로 이루어집니다. 타입 정의는 GraphQL 쿼리 언어(GQL)을 통해 데이터의 형태를 나타내며, 리졸버는 그 데이터가 어떻게 만들어져야 하는지 함수로 구현합니다.

예를 들어, 간단하게 항상 "pong"이라는 문자열 타입의 데이터를 리턴하는 ping 쿼리에 대한 스키마는 다음과 같이 작성할 수 있습니다.

const typeDefs = `
  type Query {
    ping: String!
  }
`;

const resolvers = {
  Query: {
    ping: () => "pong",
  },
};

const schema = makeExecutableSchema({ typeDefs, resolvers });

여기서, graphql-tools 패키지에서 임포트한 makeExecutableSchema() 함수에 타입 정의와 리졸버를 넘기면 스키마가 만들어집니다.

클라이언트 생성

이제 SchemaLink를 사용해서 ApolloClient를 생성할 차례입니다. 위에서 생성한 스키마를 SchemaLink() 생성자의 인자로 넘긴 후에, 생성된 SchemaLink 객체를 다시 ApolloClient() 생성자에 넘겨주면 됩니다. 캐시는 가장 범용적으로 쓰이는 InMemoryCache를 사용합니다.

const client = new ApolloClient({
  link: new SchemaLink({ schema }),
  cache: new InMemoryCache(),
});

GraphQL API 호출

ApolloClientquery() 함수를 사용해서 위에서 정의한 ping 쿼리를 호출하면,

(async function () {
  const { loading, error, data } = await client.query({
    query: gql`
      query {
        ping
      }
    `,
  });

  console.log("loading:", loading);
  console.log("error:", error);
  console.log("data:", data);
})();

다음과 같이 원하는 문자열 타입의 "pong" 이라는 데이터가 리턴되는 것을 알 수 있습니다.

loadig: false
error: undefined
data: {ping: "pong"}

async/await 키워드에 대한 자세한 설명은 관련 포스팅을 참고 바랍니다.

전체 코드

마치면서

이상으로 SchemaLink를 사용해서 서버없이 클라이언트 단에서 직접 GraphQL 스키마를 만든 후 GraphQL API를 호출하는 방법에 대해서 알아보았습니다. SchemaLink에 넘길 스키마를 만들 때, 클라이언트 단에서 직접 스키마 생성하지 않고, 원격 서버에서 스키마 정보만 얻어오거나 로컬에 저장해놓은 스키마 파일을 읽어올 수도 있습니다. 이렇게 SchemaLink를 다양하게 생성하고 여기에 mock 데이터까지 추가할 수 있는데 이러한 방법에 대해서는 추후 포스팅하도록 하겠습니다.

관련 포스팅