Adapters

Adapters

WebSockets ๋ชจ๋“ˆ์€ ํ”Œ๋žซํผ์— ๊ตฌ์• ๋ฐ›์ง€ ์•Š์œผ๋ฏ€๋กœ WebSocketAdapter ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž์‹ ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (๋˜๋Š” ๊ธฐ๋ณธ ๊ตฌํ˜„)๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋‹ค์Œ ํ‘œ์— ์„ค๋ช…๋œ ๋ช‡๊ฐ€์ง€ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ•์ œ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

create

์ „๋‹ฌ๋œ ์ธ์ˆ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์†Œ์ผ“ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

bindClientConnect

ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋“œ

bindClientDisconnect

ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ๋Š๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”์ธ๋“œํ•ฉ๋‹ˆ๋‹ค (์„ ํƒ ์‚ฌํ•ญ *).

bindMessageHandlers

๋“ค์–ด์˜ค๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํ•ด๋‹น ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๊ธฐ์— ๋ฐ”์ธ๋”ฉ

close

์„œ๋ฒ„ ์ธ์Šคํ„ด์Šค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค

Extend socket.io

socket.io ํŒจํ‚ค์ง€๋Š” IoAdapter ํด๋ž˜์Šค์— ์‹ธ์—ฌ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋Œ‘ํ„ฐ์˜ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์„ ํ–ฅ์ƒ ์‹œํ‚ค๋ ค๋ฉด ์–ด๋–ป๊ฒŒํ•ฉ๋‹ˆ๊นŒ? ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ธฐ์ˆ  ์š”๊ตฌ ์‚ฌํ•ญ์—๋Š” ์›น ์„œ๋น„์Šค์˜ ์—ฌ๋Ÿฌ ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์ธ์Šคํ„ด์Šค์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ธŒ๋กœ๋“œ ์บ์ŠคํŠธํ•˜๋Š” ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด IoAdapter๋ฅผ ํ™•์žฅํ•˜๊ณ  ์ƒˆ๋กœ์šด socket.io ์„œ๋ฒ„๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๋Š” ๋‹จ์ผ ๋ฐฉ๋ฒ•์„ ์žฌ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋จผ์ € ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜์‹ญ์‹œ์˜ค.

$ npm i --save socket.io-redis

ํŒจํ‚ค์ง€๊ฐ€ ์„ค์น˜๋˜๋ฉด RedisIoAdapterํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { IoAdapter } from '@nestjs/platform-socket.io';
import * as redisIoAdapter from 'socket.io-redis';

const redisAdapter = redisIoAdapter({ host: 'localhost', port: 6379 });

export class RedisIoAdapter extends IoAdapter {
  createIOServer(port: number, options?: any): any {
    const server = super.createIOServer(port, options);
    server.adapter(redisAdapter);
    return server;
  }
}

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒˆ๋กœ ์ƒ์„ฑ๋œ Redis ์–ด๋Œ‘ํ„ฐ๋กœ ์ „ํ™˜ํ•˜๋ฉด๋ฉ๋‹ˆ๋‹ค.

const app = await NestFactory.create(ApplicationModule);
app.useWebSocketAdapter(new RedisIoAdapter(app));

Ws library

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋˜ ๋‹ค๋ฅธ ์–ด๋Œ‘ํ„ฐ๋Š” WsAdapter์ธ๋ฐ, ์ด๋Š” ์ฐจ๋ก€๋กœ ํ”„๋ ˆ์ž„ ์›Œํฌ ์‚ฌ์ด์˜ ํ”„๋ก์‹œ์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๋ฉฐ ๋น ๋ฅด๊ณ  ์ฒ ์ €ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ๋œ ws ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ด ์–ด๋Œ‘ํ„ฐ๋Š” ๊ธฐ๋ณธ ๋ธŒ๋ผ์šฐ์ € WebSocket๊ณผ ์™„๋ฒฝํ•˜๊ฒŒ ํ˜ธํ™˜๋˜๋ฉฐ socket.io ํŒจํ‚ค์ง€๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฆ…๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„, ์ฆ‰์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ํ›จ์”ฌ ๋” ์ ์Šต๋‹ˆ๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๊ผญ ํ•„์š”ํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

ws๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋จผ์ € ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค :

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

ํŒจํ‚ค์ง€๊ฐ€ ์„ค์น˜๋˜๋ฉด ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const app = await NestFactory.create(ApplicationModule);
app.useWebSocketAdapter(new WsAdapter(app));

info ํžŒํŠธ WsAdapter๋Š”@nestjs/platform-ws์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

Advanced (custom adapter)

๋ฐ๋ชจ ๋ชฉ์ ์œผ๋กœ ws ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ํ†ตํ•ฉํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์–ธ๊ธ‰ํ•œ ๋ฐ”์™€ ๊ฐ™์ด ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์–ด๋Œ‘ํ„ฐ๋Š” ์ด๋ฏธ ์ž‘์„ฑ๋˜์—ˆ์œผ๋ฉฐ @nestjs/platform-ws ํŒจํ‚ค์ง€์—์„œ WsAdapter ํด๋ž˜์Šค๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ๋‹จ์ˆœํ™”๋œ ๊ตฌํ˜„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@@filename(ws-adapter)
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 readonly app: INestApplicationContext) {}

  create(port: number, options: any = {}): any {
    return new ws.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();
  }
}

info ํžŒํŠธ ws ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋ ค๋ฉด ์ž์ฒด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋Œ€์‹  ๋‚ด์žฅWsAdapter๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ useWebSocketAdapter()๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •์˜ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@@filename(main)
const app = await NestFactory.create(ApplicationModule);
app.useWebSocketAdapter(new WsAdapter(app));

Example

WsAdapter๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‹ค์ œ ์˜ˆ์ œ๋Š” ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค.

Last updated

Was this helpful?