real-time(채팅과 같은 실시간 어플리케이션) 구현을 하고 싶을 때..
type Query
와 비슷한 "READ" operation이지만 오래 지속(long-lasting).
웹소켓에 기반(request-response 형태가 아닌 publish-subscribe 형태).
graphQL의 type
에는 쉽게 사용할 수 있는 Query
, Mutation
외에도 type Subscription
이 있다.
Query, Mutation
의 경우 client와 server 사이에서 요청과 응답이 이루어지는 http 프로토콜을 사용하는데, http 위에서 실시간으로 요청과 응답이 연속적으로 이루어진다면 서버에는 부담이 가고, 비용이 커질 수 있다.
따라서 Subscription
은 web-socket 프로토콜 위에서 동작한다.
단방향 프로토콜 http와 달리 web-socket 프로토콜은 애초에 클라이언트와 서버간 실시간 상호작용을 위해 양방향으로 설계된 프로토콜로 web-socket 프로토콜 위에서 클라이언트와 서버는 연결이 끊어지지 않고 연속적으로 요청과 응답이 이루어질 수 있다.
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
}
type Subscription {
roomUpdates: Message
}
export default {
Subscription: {
roomUpdates: {
subscribe: () => pubsub.asyncIterator("NEW_MESSAGE"),
},
},
};
"NEW_MESSAGE"
는 문자열 형태로, 발생시킬 이벤트명(triggerName)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`);
});
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/