NestJS WebSockets - Adapters

GGAE99·2023년 11월 22일
0

NestJS 공식 문서

목록 보기
33/33
post-thumbnail

Adapters

웹소켓 모듈은 플랫폼에 독립적이기 때문에 WebSocketAdapter 인터페이스를 사용하여 자체 라이브러리를 가져올 수 있습니다. 이 인터페이스는 다음 표에 설명된 몇 가지 메서드를 구현하도록 강제합니다.

  • create: 전달된 인수를 기반으로 소켓 인스턴스를 생성합니다.
  • bindClientConnect: 클라이언트 연결 이벤트를 바인딩합니다.
  • bindClientDisconnect: 클라이언트 연결 해제 이벤트를 바인딩합니다 (선택 사항).
  • bindMessageHandlers: 들어오는 메시지를 해당 메시지 핸들러에 바인딩합니다.
  • close: 서버 인스턴스를 종료합니다.

Extend socket.io

socket.io 패키지는 IoAdapter 클래스로 래핑되어 있습니다. 어댑터의 기본 기능을 향상시키고 싶다면 IoAdapter를 확장할 수 있습니다.

여러 로드 밸런싱된 인스턴스에서 socket.io를 사용하려면 Redis 어댑터를 사용해야 합니다. 필요한 패키지를 설치하고 RedisIoAdapter 클래스를 만듭니다.

$ npm i --save redis socket.io @socket.io/redis-adapter

그런 다음 RedisIoAdapter 클래스를 만듭니다.

import { IoAdapter } from '@nestjs/platform-socket.io';
import { ServerOptions } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';

export class RedisIoAdapter extends IoAdapter {
  private adapterConstructor: ReturnType<typeof createAdapter>;

  async connectToRedis(): Promise<void> {
    const pubClient = createClient({ url: `redis://localhost:6379` });
    const subClient = pubClient.duplicate();

    await Promise.all([pubClient.connect(), subClient.connect()]);

    this.adapterConstructor = createAdapter(pubClient, subClient);
  }

  createIOServer(port: number, options?: ServerOptions): any {
    const server = super.createIOServer(port, options);
    server.adapter(this.adapterConstructor);
    return server;
  }
}

그런 다음 새로 만든 Redis 어댑터로 전환합니다.

//main.ts
const app = await NestFactory.create(AppModule);
const redisIoAdapter = new RedisIoAdapter(app);
await redisIoAdapter.connectToRedis();

app.useWebSocketAdapter(redisIoAdapter);

이렇게 하면 여러 로드 밸런싱된 인스턴스에서 Redis를 사용하여 socket.io를 구현할 수 있습니다.

Ws library

또 다른 사용 가능한 어댑터는 WsAdapter입니다. 이 어댑터는 프레임워크와 ws 라이브러리 간의 프록시 역할을 합니다. 이 어댑터는 네이티브 브라우저 웹소켓과 완벽하게 호환되며 socket.io 패키지보다 훨씬 빠릅니다. 불행히도, 기본으로 제공되는 기능은 상당히 적지만, sometime 그것들이 필요하지 않을 수 있을지도 모릅니다.

Hint!
ws 라이브러리는 socket.io에서 인기 있는 네임스페이스(communication channels)를 지원하지 않습니다. 그러나 이 기능을 어떻게든 흉내내기 위해 다른 경로에 여러 ws 서버를 마운트할 수 있습니다. (예: @WebSocketGateway({ path: '/users' }))

ws를 사용하려면 먼저 필요한 패키지를 설치해야 합니다.

$ npm i --save @nestjs/platform-ws

패키지를 설치한 후에는 다음과 같이 어댑터를 전환할 수 있습니다.

//main.ts
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));

Hint!
The WsAdapter is imported from @nestjs/platform-ws.

Advanced (custom adapter)

데모 목적으로 ws 라이브러리를 수동으로 통합해 보겠습니다. 어댑터는 이미 @nestjs/platform-ws 패키지에서 노출되어 있는 WsAdapter 클래스로 만들어졌습니다. 다음은 간소화된 구현 예제일 수 있습니다.

//ws-adapter.ts
import * as WebSocket from 'ws';
import { WebSocketAdapter, INestApplicationContext } from '@nestjs/common';
import { MessageMappingProperties } from '@nestjs/websockets';
import { Observable, fromEvent, EMPTY } from 'rxjs';
import { mergeMap, filter } from 'rxjs/operators';

export class WsAdapter implements WebSocketAdapter {
  constructor(private app: INestApplicationContext) {}

  create(port: number, options: any = {}): any {
    return new WebSocket.Server({ port, ...options });
  }

  bindClientConnect(server, callback: Function) {
    server.on('connection', callback);
  }

  bindMessageHandlers(
    client: WebSocket,
    handlers: MessageMappingProperties[],
    process: (data: any) => Observable<any>,
  ) {
    fromEvent(client, 'message')
      .pipe(
        mergeMap(data => this.bindMessageHandler(data, handlers, process)),
        filter(result => result),
      )
      .subscribe(response => client.send(JSON.stringify(response)));
  }

  bindMessageHandler(
    buffer,
    handlers: MessageMappingProperties[],
    process: (data: any) => Observable<any>,
  ): Observable<any> {
    const message = JSON.parse(buffer.data);
    const messageHandler = handlers.find(
      handler => handler.message === message.event,
    );
    if (!messageHandler) {
      return EMPTY;
    }
    return process(messageHandler.callback(message.data));
  }

  close(server) {
    server.close();
  }
}

그런 다음 useWebSocketAdapter() 메서드를 사용하여 사용자 정의 어댑터를 설정할 수 있습니다.

//main.ts
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));

이렇게 하면 ws 라이브러리를 사용하는 데모 어플리케이션을 만들 수 있습니다.

0개의 댓글