Apollo-server Subscriptions 개발환경설정

용상윤·2021년 6월 15일
0

Subscriptions?

  • real-time(채팅과 같은 실시간 어플리케이션) 구현을 하고 싶을 때..

  • type Query와 비슷한 "READ" operation이지만 오래 지속(long-lasting).

  • 웹소켓에 기반(request-response 형태가 아닌 publish-subscribe 형태).

web socket

graphQL의 type에는 쉽게 사용할 수 있는 Query, Mutation
외에도 type Subscription 이 있다.

Query, Mutation의 경우 client와 server 사이에서 요청과 응답이 이루어지는 http 프로토콜을 사용하는데, http 위에서 실시간으로 요청과 응답이 연속적으로 이루어진다면 서버에는 부담이 가고, 비용이 커질 수 있다.

따라서 Subscription 은 web-socket 프로토콜 위에서 동작한다.

단방향 프로토콜 http와 달리 web-socket 프로토콜은 애초에 클라이언트와 서버간 실시간 상호작용을 위해 양방향으로 설계된 프로토콜로 web-socket 프로토콜 위에서 클라이언트와 서버는 연결이 끊어지지 않고 연속적으로 요청과 응답이 이루어질 수 있다.


PubSub 엔진

publish-subscribe 모델 구현을 위해 pubsub 엔진이 필요하다.

(apollo-server에서 구현하는 것이긴 하지만.. 대규모 프로젝트나 상업용으로는 적합하지 않음.)

import { PubSub } from "apollo-server-express";
//import { PubSub } from "apollo-server";

const pubsub = new PubSub();

server를 구동하고 console.log(pubsub) 을 해보면 다음과 같은 객체임을 알 수 있다.

PubSub {
  ee: EventEmitter {
    _events: [Object: null prototype] {},
    _eventsCount: 0,
    _maxListeners: undefined,
    [Symbol(kCapture)]: false
  },
  subscriptions: {},
  subIdCounter: 0
}

스키마 & 리졸버 정의

schema

type Subscription {
    roomUpdates: Message
  }

resolvers

export default {
  Subscription: {
    roomUpdates: {
      subscribe: () => pubsub.asyncIterator("NEW_MESSAGE"),
    },
  },
};
  • "NEW_MESSAGE" 는 문자열 형태로, 발생시킬 이벤트명(triggerName)

server 설정

Subscriptions 은 http 대신에 WebSocket을 사용하므로
server를 분리시켜줘야 한다.


import http from "http";
import express from "express";
import { ApolloServer } from "apollo-server-express";
...

const PORT = 4000;
const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => {
    // ws에는 HTTP HEADERS가 존재하지 않기 때문에 if문 추가
    if(req){
      return {
        ...
      };
    }
  },
});

const app = express();
apolloServer.applyMiddleware({ app });

//app이 http위에서 동작하도록
const httpServer = http.createServer(app);
//subscription을 실행시키도록
apolloServer.installSubscriptionHandlers(httpServer);

//http에서 server실행
httpServer.listen(PORT, () => {
  console.log(`🚀 Server ready at http://localhost:${PORT}/graphql`);
});

이벤트를 발생시킬 mutation

subscription을 구현하고자 하는 mutation이 sendMessage 라고 가정하면..

sendMessage.resolvers.js

const resolverFn = async (parent, args, context) => {
  ...
  pubsub.publish("NEW_MESSAGE", { roomUpdates: args });
  return postController.createPost(args);
  ...
};

해당 mutation의 resolvers에 pubsub엔진을 실행시켜야 한다.


참고

Apollo DOCS
https://www.apollographql.com/docs/apollo-server/data/subscriptions/

[GraphQL] Apollo Server로 Subscription 구현
https://www.daleseo.com/graphql-apollo-server-subscriptions/

Web Socket 이란?
https://edu.goorm.io/learn/lecture/557/%ED%95%9C-%EB%88%88%EC%97%90-%EB%81%9D%EB%82%B4%EB%8A%94-node-js/lesson/174379/web-socket%EC%9D%B4%EB%9E%80

profile
달리는 중!

0개의 댓글