본문 바로가기
개발정리 (nodeJS)

[nodeJS] Node.js에서 GraphQL 서브스크립션 구현하기

by 할리갈리0 2024. 9. 23.

GraphQL 서브스크립션은 클라이언트와 서버 간의 실시간 데이터 업데이트를 가능하게 해주는 강력한 기능입니다. 이를 통해 클라이언트는 서버로부터 실시간으로 데이터 변경 사항을 받아볼 수 있으며, 실시간 채팅, 라이브 데이터 피드, 알림 시스템 등 다양한 애플리케이션에 활용할 수 있습니다.

 

1. GraphQL 서브스크립션이란 무엇인가?

[GraphQL 서브스크립션의 개요]

GraphQL의 세 가지 기본 작업 중 하나로, 데이터의 실시간 업데이트를 클라이언트에게 푸시(push) 방식으로 제공하는 기능.

쿼리(Query)와 변형(Mutation)은 클라이언트가 요청을 보내고 서버가 응답을 반환하는 단방향 통신 방식.

서브스크립션은 양방향 통신을 지원하여 서버가 클라이언트에 데이터를 푸시 가능.

 

[GraphQL 서브스크립션의 주요 특징]

  • 실시간 데이터 전송: 서버는 데이터의 변화가 발생할 때마다 클라이언트에게 실시간으로 알림 전송
  • WebSocket을 이용한 양방향 통신: 서브스크립션은 WebSocket을 통해 서버와 클라이언트 간의 지속적인 연결을 유지하여 양방향 통신 가능.
  • 효율적인 업데이트: 클라이언트는 필요한 데이터만 구독(subscription)이 가능하여, 효율적으로 실시간 업데이트 간으

[서브스크립션 사용 이유]

  • 실시간 애플리케이션: 채팅 애플리케이션, 실시간 피드, 라이브 대시보드 등 실시간으로 데이터가 업데이트되어야 하는 애플리케이션에 적합.
  • 알림 시스템: 서버에서 이벤트가 발생할 때 클라이언트에게 즉시 알림 가능.
  • 동적 데이터: 주식 가격, 실시간 센서 데이터 등 동적으로 변경되는 데이터를 효율적으로 처리 가능

 

2. GraphQL 서브스크립션을 위한 프로젝트 설정

[nodeJS] Node.js로 GraphQL API 구현하기” 에서 사용한 프로젝트를 확장하여 서브스크립션 구현하기 때문에 필요한 패키지만 추가로 설치.

  • graphql-ws: GraphQL 서브스크립션을 위한 서버 및 클라이언트 라이브러리
  • ws: WebSocket 서버를 구현하기 위한 Node.js 라이브러리
npm install graphql-ws ws

 

이전 프로젝트 구조에서 새로운 파일을 추가하여 서브스크립션 기능을 구현

  • pubsub.js: GraphQL 서브스크립션을 위한 PubSub 인스턴스를 설정하는 파일
graphql-nodejs/
├── src/
│   ├── schema.js
│   ├── resolvers.js
│   ├── server.js
│   └── pubsub.js  // 새로 추가된 파일
├── package.json
└── node_modules/

 

3. GraphQL 서브스크립션 스키마 및 리졸버 정의

GraphQL 서브스크립션은 이벤트 기반 시스템으로, 데이터가 변경될 때마다 이벤트를 발행(publish)하고 클라이언트는 이벤트를 구독(subscribe)하는 방식

src/pubsub.js 파일에서 PubSub 인스턴스를 설정

// src/pubsub.js
import { PubSub } from 'graphql-ws';

const pubsub = new PubSub();
export default pubsub;

 

서브스크립션을 구현하기 위해 기존 스키마에 새로운 서브스크립션 타입을 추가.

새로운 Subscription 타입을 추가하고, userAdded라는 서브스크립션 필드를 정의

// src/schema.js
import { buildSchema } from 'graphql';

const schema = buildSchema(`
  type Query {
    hello: String
    getUser(id: ID!): User
  }

  type User {
    id: ID
    name: String
    email: String
  }

  type Mutation {
    createUser(name: String!, email: String!): User
  }

  type Subscription {
    userAdded: User
  }
`);

export default schema;

 

서브스크립션 리졸버는 데이터가 변경될 때마다 이벤트를 발행.

사용자가 생성될 때마다 USER_ADDED 이벤트를 발행하고, userAdded 서브스크립션을 통해 이를 클라이언트에게 전달.

// src/resolvers.js
import pubsub from './pubsub.js';

const users = [];

const resolvers = {
  hello: () => 'Hello, GraphQL!',

  getUser: ({ id }) => {
    return users.find(user => user.id === id);
  },

  createUser: ({ name, email }) => {
    const user = { id: `${users.length + 1}`, name, email };
    users.push(user);
    pubsub.publish('USER_ADDED', { userAdded: user });
    return user;
  },

  userAdded: {
    subscribe: () => pubsub.asyncIterator(['USER_ADDED'])
  }
};

export default resolvers;

 

4. WebSocket 서버 설정 및 GraphQL 서브스크립션 통합

GraphQL 서브스크립션을 사용하려면 WebSocket 서버 설정 필요.

/graphql 경로에서 WebSocket 서버를 설정하고 GraphQL 서브스크립션을 처리.

// src/server.js
import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { createServer } from 'http';
import { useServer } from 'graphql-ws/lib/use/ws';
import { WebSocketServer } from 'ws';
import schema from './schema.js';
import resolvers from './resolvers.js';

const app = express();

app.use('/graphql', graphqlHTTP({
  schema,
  rootValue: resolvers,
  graphiql: { subscriptionEndpoint: `ws://localhost:4000/graphql` }, // GraphiQL 인터페이스에서 서브스크립션 사용 가능
}));

const PORT = 4000;
const server = createServer(app);

const wsServer = new WebSocketServer({
  server,
  path: '/graphql',
});

useServer({ schema, execute: resolvers }, wsServer);

server.listen(PORT, () => {
  console.log(`GraphQL 서버가 <http://localhost>:${PORT}/graphql 에서 실행 중입니다.`);
  console.log(`WebSocket 서버가 ws://localhost:${PORT}/graphql 에서 실행 중입니다.`);
});

 

5. 클라이언트에서 GraphQL 서브스크립션 사용하기

클라이언트에서 GraphQL 서브스크립션을 사용하려면 WebSocket 연결 설정 필요.

Apollo Client를 사용하는 경우 HttpLink를 통해 HTTP 요청을 처리하고, GraphQLWsLink를 통해 WebSocket 설정.

split 함수를 사용하여 쿼리 유형에 따라 HTTP 또는 WebSocket을 선택적으로 사용 가능.

// client.js (Apollo Client 설정)
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { split, HttpLink } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { getMainDefinition } from '@apollo/client/utilities';

const httpLink = new HttpLink({
  uri: '<http://localhost:4000/graphql>',
});

const wsLink = new GraphQLWsLink(createClient({
  url: 'ws://localhost:4000/graphql',
}));

const link = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink
);

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

export default client;

 

새로운 사용자가 추가될 때마다 USER_ADDED_SUBSCRIPTION을 통해 실시간 데이터를 수신하고, 수신된 데이터를 화면에 표시하는 서브스크립션.

// subscription.js
import { gql, useSubscription } from '@apollo/client';

const USER_ADDED_SUBSCRIPTION = gql`
  subscription OnUserAdded {
    userAdded {
      id
      name
      email
    }
  }
`;

const UserSubscriptionComponent = () => {
  const { data, loading, error } = useSubscription(USER_ADDED_SUBSCRIPTION);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h2>New User Added:</h2>
      <p>ID: {data.userAdded.id}</p>
      <p>Name: {data.userAdded.name}</p>
      <p>Email: {data.userAdded.email}</p>
    </div>
  );
};

export default UserSubscriptionComponent;

 

 

이번 포스팅에서는 Node.js 환경에서 GraphQL 서브스크립션을 사용하여 실시간 데이터를 제공하는 방법을 살펴보았습니다. GraphQL 서브스크립션은 실시간 애플리케이션에 적합하며, 클라이언트와 서버 간의 지속적인 데이터 동기화를 가능하게 합니다.

반응형