서브스크립션과 실시간 알림: GraphQL 구현 요약
GraphQL Subscriptions을 사용해 Node.js 환경에서 실시간 알림을 구성하는 핵심 개념과 구현 예제, 확장 전략을 정리한 설명서
목차
개요
실시간 기능은 사용자 경험을 한층 끌어올린다. GraphQL Subscriptions는 서버에서 클라이언트로 이벤트를 푸시하는 표준 방식이다. 이 글은 GraphQL Subscriptions를 Node.js 환경에서 구현하면서 고려할 점과 예제를 단계별로 설명한다. 초보자도 이해하기 쉽도록 핵심 개념을 먼저 정리한 뒤 코드 예제를 제공한다.
핵심 개념
Subscriptions는 클라이언트가 특정 이벤트 스트림에 연결하여 실시간으로 데이터를 받는 메커니즘이다. 일반적인 요청/응답 흐름과 달리 연결이 유지된다. 이를 위해 WebSocket 기반의 실시간 프로토콜이 주로 사용된다.
주요 요소
- 스키마 정의: subscription 타입과 필드 선언
- 퍼블리셔/구독자 패턴: 이벤트 발행과 구독자 알림
- 실시간 통신: WebSocket 연결 관리
- 스케일링: 여러 인스턴스 간 이벤트 전파
사전 준비
다음 환경을 가정한다.
- Node.js 16+
- Apollo Server 또는 비슷한 GraphQL 서버
- graphql, graphql-subscriptions, graphql-ws 또는 subscriptions-transport-ws
이 글에서는 예제로 "GraphQL Subscriptions Node.js" 환경을 기준으로 설명하며, 실제 코드에서는 Apollo 기반 설정을 사용한다.
간단한 구현 흐름
작업 흐름은 다음과 같다.
- 스키마에 subscription 타입 추가
- 서버에서 PubSub(퍼브섭) 인스턴스 생성
- 이벤트가 발생하면 publish 호출
- 클라이언트가 subscription 쿼리로 실시간 데이터 수신
코드 예제
아래 예제는 Apollo Server와 graphql-ws를 이용한 기본 구성이다. 실제 서비스 환경에서는 인증과 스케일링 구성이 추가로 필요하다. 코드 블록은 JSX 형태의 문법을 포함할 경우 < 및 >로 변환했다.
서버 설정 (예제)
const { createServer } = require('http')
const { execute, subscribe } = require('graphql')
const { makeExecutableSchema } = require('@graphql-tools/schema')
const { useServer } = require('graphql-ws/lib/use/ws')
const { WebSocketServer } = require('ws')
const { ApolloServer } = require('apollo-server-express')
const express = require('express')
const { PubSub } = require('graphql-subscriptions')
const typeDefs = `
type Message { id: ID!, content: String! }
type Query { _: Boolean }
type Mutation { sendMessage(content: String!): Message }
type Subscription { messageSent: Message }
`
const pubsub = new PubSub()
const MESSAGE_TOPIC = 'MESSAGE_TOPIC'
const resolvers = {
Query: {},
Mutation: {
sendMessage: (_, { content }) => {
const msg = { id: Date.now().toString(), content }
pubsub.publish(MESSAGE_TOPIC, { messageSent: msg })
return msg
}
},
Subscription: {
messageSent: {
subscribe: () => pubsub.asyncIterator([MESSAGE_TOPIC])
}
}
}
const schema = makeExecutableSchema({ typeDefs, resolvers })
async function start() {
const app = express()
const httpServer = createServer(app)
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql'
})
useServer({ schema, execute, subscribe }, wsServer)
const server = new ApolloServer({ schema })
await server.start()
server.applyMiddleware({ app })
httpServer.listen(4000, () => {
console.log('Server ready at http://localhost:4000/graphql')
})
}
start()
클라이언트 구독 예제
import { createClient } from 'graphql-ws'
import { subscribe, gql } from '@apollo/client'
const wsClient = createClient({ url: 'ws://localhost:4000/graphql' })
const SUBSCRIPTION = gql`subscription { messageSent { id content } }`
// pseudo-code for subscribing
wsClient.subscribe({ query: SUBSCRIPTION.loc.source.body }, {
next: (data) => console.log('받은 메시지', data),
error: (err) => console.error(err),
complete: () => console.log('complete')
})
실무 팁과 주의사항
- 인증: WebSocket 연결 시 토큰을 검증해야 한다. 연결 초기 단계의 컨텍스트에서 처리한다.
- 에러 처리: 구독 중 발생한 예외를 적절히 클라이언트에 전달한다.
- 리소스 관리: 많은 연결이 동시 유지되면 메모리와 소켓 한계에 도달할 수 있다.
- 스케일링: 다중 인스턴스 환경에서는 Redis, NATS, Kafka 같은 중앙 퍼브섭을 사용해 이벤트를 전파한다.
확장과 운영
개발 단계에서는 in-memory PubSub가 편리하다. 그러나 프로덕션에서는 외부 메시지 브로커가 필요하다. 예를 들어 Redis를 사용하면 여러 서버 인스턴스가 동일한 토픽을 구독하고 이벤트를 공유할 수 있다. 이와 같은 전략은 "실시간 알림 GraphQL Node" 환경에서 높은 가용성을 확보하는 데 필수적이다.
결론
이 글에서는 GraphQL Subscriptions의 핵심 개념과 Node.js 환경에서의 기본 구현 방법을 다뤘다. 예제 코드는 Apollo와 graphql-ws 기반이며, 실제 서비스에서는 인증과 스케일링을 추가해야 한다. 또한, "Apollo subscriptions 예제"나 "GraphQL Subscriptions Node.js" 관련 검색어로 더 많은 실무 사례를 찾아볼 수 있다. 기본 흐름을 이해하면 실시간 알림 기능을 안정적으로 설계할 수 있다.