π NestJS + Kubernetes & λ§μ΄ν¬λ‘μλΉμ€ λ°°ν¬: μ€μ κ°μ΄λ - NestJS μ€μκ° κΈ°λ₯ μ€κ³: WebSocket + GraphQL Subscriptions
π NestJS + Kubernetes & λ§μ΄ν¬λ‘μλΉμ€ λ°°ν¬: μ€μ κ°μ΄λ
9. NestJS μ€μκ° κΈ°λ₯ μ€κ³: WebSocket + GraphQL Subscriptions
νλ μΉ μ ν리μΌμ΄μ
μμλ μ€μκ° λ°μ΄ν° μ²λ¦¬κ° νμκ° λμμ΅λλ€.
NestJSλ WebSocket, GraphQL Subscriptions, Redis Pub/Sub λ±μ ν΅ν΄ λ€μν μ€μκ° κΈ°λ₯μ μμ½κ² ꡬνν μ μμ΅λλ€.
μ΄λ² κΈμμλ NestJSμμ μ€μκ° μ±ν
, μλ¦Ό, λμ보λ μ
λ°μ΄νΈμ κ°μ κΈ°λ₯μ ꡬμΆνκΈ° μν μ€μ μ λ΅μ μκ°ν©λλ€. π
β 1. μ€μκ° κΈ°λ₯μ΄ νμν μ΄μ
- μ€μκ° μ±ν , νμ ν΄, κ²μ
- λμ보λ μ§ν μλ κ°±μ
- μ€μκ° μλ¦Ό(Notification) κΈ°λ₯
- IoT λλ°μ΄μ€ μν λͺ¨λν°λ§
π‘ REST APIλ ν΄λΌμ΄μΈνΈκ° μ£ΌκΈ°μ μΌλ‘ pollingν΄μΌ νλ―λ‘ λΉν¨μ¨μ
→ WebSocket or Subscription κΈ°λ°μ push κ΅¬μ‘°λ‘ κ°μ νμ
β 2. NestJSμμ μ§μνλ μ€μκ° κΈ°μ
κΈ°μ μ€λͺ μ₯μ
WebSocket (Socket.IO) | μλ°©ν₯ ν΅μ , ν΄λΌμ΄μΈνΈ ↔ μλ² μ§μ μ°κ²° | κ°λ¨νκ³ λΉ λ¦ |
GraphQL Subscriptions | GraphQL κΈ°λ° μ€μκ° λ°μ΄ν° μ€νΈλ¦Ό | GraphQL APIμ ν΅ν© μ©μ΄ |
Redis Pub/Sub | λ€μ€ μΈμ€ν΄μ€ κ° λ©μμ§ λΈλ‘λμΊμ€νΈ | μ€μΌμΌμμμ μ 리 |
β 3. WebSocket (Socket.IO) κΈ°λ° μ€μκ° ν΅μ
β 1) μ€μΉ λ° μ€μ
npm install @nestjs/websockets @nestjs/platform-socket.io
π chat.gateway.ts
@WebSocketGateway({ cors: true })
export class ChatGateway {
@WebSocketServer() server: Server;
@SubscribeMessage('chat')
handleChat(@MessageBody() msg: string): void {
this.server.emit('chat', msg);
}
}
β 2) ν΄λΌμ΄μΈνΈ μμ (HTML + JS)
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io('http://localhost:3000');
socket.emit('chat', 'μλ
νμΈμ!');
socket.on('chat', msg => console.log('λ©μμ§:', msg));
</script>
β 4. GraphQL Subscriptions κΈ°λ° μ€μκ° μ€κ³
β 1) μ€μΉ
npm install @nestjs/graphql graphql graphql-subscriptions graphql-ws
β 2) μ€μ
π app.module.ts
GraphQLModule.forRoot({
autoSchemaFile: true,
subscriptions: {
'graphql-ws': true,
},
})
β 3) μ€μκ° μλ¦Ό μμ
π notifications.resolver.ts
@Resolver()
export class NotificationResolver {
constructor(@Inject('PUB_SUB') private pubSub: PubSub) {}
@Mutation(() => String)
sendNotification(): string {
this.pubSub.publish('notificationAdded', {
notificationAdded: 'μλ‘μ΄ μλ¦Ό!',
});
return '보λ';
}
@Subscription(() => String)
notificationAdded() {
return this.pubSub.asyncIterator('notificationAdded');
}
}
β ν΄λΌμ΄μΈνΈμμ subscription { notificationAdded } λ₯Ό ꡬλ νλ©΄ μλ²μμ μλ¦Ό push
β 5. Redis κΈ°λ° Pub/Sub μ€κ³ (λ©ν° μΈμ€ν΄μ€ λμ)
π pubsub.module.ts
const redisOptions = {
host: 'localhost',
port: 6379,
};
@Module({
providers: [
{
provide: 'PUB_SUB',
useFactory: () => new RedisPubSub({ connection: redisOptions }),
},
],
exports: ['PUB_SUB'],
})
export class PubSubModule {}
π‘ μλ²κ° μ¬λ¬ κ°μΌ λ, Redis Pub/Subμ μ¬μ©νλ©΄ λ©μμ§λ₯Ό λͺ¨λ μΈμ€ν΄μ€μ λΈλ‘λμΊμ€νΈ κ°λ₯!
β 6. μ€μ ꡬν μ κ³ λ €μ¬ν
νλͺ© λ΄μ©
μΈμ¦ | JWT λλ μΈμ κΈ°λ° μΈμ¦ μ μ© (socket.handshake) |
λ©μμ§ λ³΄μ | λ©μμ§ λ΄μ© μνΈν λλ κΆν κ²μ¦ νμ |
νμ₯μ± | Redis Pub/Sub λλ Kafka νμ© |
μ°κ²° μ κ΄λ¦¬ | WebSocket μ°κ²° μ λͺ¨λν°λ§ + μ ν μ μ© (Rate Limit) |
λ체 μ λ΅ | polling fallback → μ€λλ λΈλΌμ°μ κ³ λ € |
β 7. μ€μκ° κΈ°λ₯ + Kubernetes μ°λ ν
- Ingress μ€μ : WebSocket μ¬μ© μ nginx.ingress.kubernetes.io/backend-protocol: "WS" μΆκ° νμ
- Sticky Session: ν΄λ¬μ€ν° νκ²½μμ νΉμ μ¬μ©μ μ°κ²° μ μ§ νμ μ
- Redis Pod λ³λ μ΄μ: μ€μκ° λ©μμ§μ Redis μμ‘΄λκ° λμΌλ―λ‘ λͺ¨λν°λ§ μ€μ
β κ²°λ‘ : NestJSλ‘ μ€μκ° μμ€ν μ€κ³ λ§μ€ν°νκΈ°
β
WebSocketμ νμ©ν μ€μκ° μλ°©ν₯ ν΅μ
β
GraphQL Subscriptionsλ‘ GraphQL API λ΄ μ€μκ° κΈ°λ₯ ꡬν
β
Redisλ₯Ό ν΅ν λΈλ‘λμΊμ€νΈ λ° μ€μΌμΌμμ
β
Kubernetesμμλ μ€μκ° ν΅μ μμ μ μΌλ‘ μ΄μ κ°λ₯
λ€μ κΈμμλ NestJS νλ‘μ νΈμμ κ΄μ°°μ±(Observability)μ λμ΄λ λ°©λ² – OpenTelemetry, Distributed Tracing, λ‘κ·Έ μμ§ μ λ΅μ λ€λ£Ήλλ€! π
π λ€μ κΈ μκ³ : NestJS Observability – λ‘κ·Έ, μΆμ , μ§ν ν΅ν© μ λ΅
π λ€μ νΈ: 10. NestJS Observability: λ‘κ·Έ, λͺ¨λν°λ§, μΆμ ν΅ν© κ°μ΄λ
NestJS WebSocket,NestJS μ€μκ° κΈ°λ₯,NestJS GraphQL Subscriptions,NestJS Redis PubSub,NestJS μ€μκ° μ±ν ,NestJS μλ¦Ό κΈ°λ₯,NestJS μμΌ μ€μ ,NestJS μ€μκ° λμ보λ,NestJS μλ² νΈμ,NestJS μ€μκ° ν΅μ