GraphQL Subscriptions
GraphQL Subscriptions?
- 특정 이벤트가 발생할때 서버가 클라이언트로 데이터를 보낼 수 있도록 GraphQL에서 제공하는 기능이다.
- 서버와 클라이언트가 실시간 연동을 하게끔 하기 위해 웹소켓을 기반으로 한다.
subscriptions
을 사용함으로써, 일반적인 흔히 사용되는 request
- response
통신에서 벗어날 수 있다. 대신 클라이언트에서 원하는 이벤트를 지정하여 서버와의 연결을 지속할 수 있다. 이후 해당 이벤트가 발생할때마다 서버는 클라이언트에 데이터를 전달한다.
- 결론적으로, 실시간 데이터 통신을 위해 사용된다.(알람, 푸시, 배송상태 등에 적용할 수 있다.)
Setting with Apollo client
- subscriptions 설정을 위해서는
graph-ws
, subscriptions-transport-ws
두가지가 사용되지만 후자의 경우 유지보수가 액티브하게 되지 않으므로 전자를 추천한다고 한다. 하지만 HOW TO GRAPHQL 튜토리얼 흐름에 맞춰 후자의 라이브러리로 진행해보겠다.
Using
라이브러리 설치
yarn add subscriptions-transport-ws
ApolloClient instance 수정
- 웹소켓을 사용할 때
subscription
의 endpoint
에서는 http
대신 ws
을 사용하게 된다.
split
은 특정 미들웨어 링크로 라우트하는데 사용된다.
// src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import "src/styles/index.css";
import App from "./components/App";
import { split } from "@apollo/client";
import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
import {
ApolloProvider,
ApolloClient,
createHttpLink,
InMemoryCache,
} from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { AUTH_TOKEN } from "./constants";
const httpLink = createHttpLink({
uri: "http://localhost:4000",
});
// 웹소켓 연결을 위한 wslLink 추가
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/graphql`,
options: {
reconnect: true,
connectionParams: {
authToken: localStorage.getItem(AUTH_TOKEN),
},
},
});
// 특정 미들웨어 링크로 라우트하는데 split이 사용됨
// 첫번째 인자가 true일 경우(즉 작업 타입이 query, mutation, subscription중 subscription일때), 두번째 인자로 주어진 링크에 라우팅: wsLink(웹소켓 통신)
// 첫번째 인자가 false일 경우, 세번째 인자로 주어진 링크에 라우팅: httpLink(일반 통신)
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscription";
},
wsLink,
httpLink
);
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
document.getElementById("root")
);
subscription 지정 및 사용
// feed list query
export const FEED_QUERY = gql`
query GetFeedListQuery {
...
}
`;
// feed subscription
export const NEW_LINKS_SUBSCRIPTION = gql`
subscription {
...
}
`;
// get data using subscription
const LinkList = () => {
const { data, loading, error, subscribeToMore } = useQuery(FEED_QUERY);
subscribeToMore({
document: NEW_LINKS_SUBSCRIPTION,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) {
return prev;
}
const newLink = subscriptionData.data.newLink;
const exists = prev.feed.links.find(({ id }) => id === newLink.id);
if (exists) {
return prev;
}
return Object.assign({}, prev, {
feed: {
links: [newLink, ...prev.feed.links],
count: prev.feed.links.length + 1,
__typename: prev.feed.__typename,
},
});
},
});
return ...
};
결과
mutation
으로 list가 실시간 추가됨을 통해, subscriptions
기능이 정상적으로 작동하고 있음을 알 수 있다.
Reference