카테고리 없음

nest에 socket.io 붙이기

개발하는 가오나시 2023. 3. 1. 00:50

사내 프로젝트에 게임서버와 같이 대기실과 게임방 같은 기능이 필요하여 해당 기능에 Socket 기능을 담당하게 되었다.

 

web socket기술은 혼자서 심심풀이로 6인용 캐치마인드를 만들 때 써본게 다인데.. 그거면 되려나

 

일단 nest.js 를 기반으로 소켓 서버를 띄울것이다.

 

nest new project-name

nest 명령어로 기본 세팅 설치하고~

npm i @nestjs/websockets socket.io

socket.io와 nestjs/websocket을 설치해준다.

 

src 폴더에 socket을 위한 모듈 폴더를 생성해주고 고 안에 socket.module.ts 와 socket.gateway.ts 파일을 만들어준다.

 

nestjs와 socket.io 에 관련된 글을 좀 읽어봤는데, nestjs에서 socekt.io를 사용하면 dynamic namespace를 지원한하니 등등의 이슈가 있는 것도 있고 gateway쓰지말고 그냥 쓰라고 하는 게 대부분인것 같다. 그래도 난 처음해보니까 gateway로 nestjs와 연결해봐야겠다.

 

@WebSocketGateway({
  namespace: 'localLobby',
  cors: {
    origin: ['http://localhost:3000'],
  },
})
export class gameLobbyEventGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer() nsp: Namespace; // 네임스페이스 타입선언

  afterInit() {}// 초기화 이후에 실행되는 함수

  handleConnection(@ConnectedSocket() socket: Socket) {}
  handleDisconnect(@ConnectedSocket() socket: Socket) {}
  // 소켓이 연결되고 끊길때 실행되는 메소드들
}

 

그런데 아차 내가 만드는 서비스는 서버가 지역/글로벌 이렇게 두개로 나뉜다. 그럼 결국 namespace를 기준으로 나눠서 관리하는게 편할텐데, nest.js에서는 dynamic namespace를 지원하지 않는다고 하니, gateway를 2개 만들어주는 수 밖에없다.

 

@wabSocketGateway 를 호출하는 곳에 namespace를 바꿔서 gateway파일을 하나 더 만든다. 자 이제 기능을 구현해 볼까? 했는데

 

아니 생각해보니 아무리 gateway파일이 두 개라지만 결국 두 웹 소켓 서버는 같은 동작을 할 것임이 자명하다. 그럼 gateway 파일에 똑같은 코드가 들어갈 것이고, 그럼 기능 수정하나 하라면 두 파일을 다 수정해야한다. 

 

말도 안된다. 이건 아니지

 

그래서 생각한 방법이 "어차피 nestjs module이라고 해도 js class로 만들어 졌으니까, 이거 상속받아서 사용함 되지 않을까?" 이다.

 

그렇게 defaultGateway.ts 파일을 만들고..

 

import { Logger } from '@nestjs/common';
import { ConnectedSocket, MessageBody, SubscribeMessage } from '@nestjs/websockets';
import { Namespace, Socket } from 'socket.io';

export class DefaultSocket {
  nsp: Namespace;
  socketType;
  logger: Logger;

  constructor(type: string) {
    this.logger = new Logger(`${type}Gateway`);
    this.socketType = type;
  }

  afterInit() {
    this.nsp.adapter.on('join-server', (room) => {
      console.log(`join-${this.socketType}-server`);
    });

    this.nsp.adapter.on('leave-server', (room) => {
      console.log(`leave-${this.socketType}-server`);
    });

    console.log(this.socketType + ' ready');
  }

 

아까 만들어둔 gateway 파일의 클래스에 extends 해준다. 

import {
  OnGatewayConnection,
  OnGatewayDisconnect,
  OnGatewayInit,
  WebSocketGateway,
  WebSocketServer,
} from '@nestjs/websockets';

import { Namespace, Server } from 'socket.io';
import { HttpRequestService } from 'src/httpRequest/httpRequest.service';
import { KafkaService } from 'src/kafka/kafka.service';
import { CustomRedisService } from 'src/redis/redis.service';
import { DefaultSocket } from './defalutSocket';

@WebSocketGateway(13000, {
  namespace: 'global',
  transports: ['websocket'],
  cors: {
    origin: process.env.CORS_ORIGIN.split(','),
    credentials: true,
    methods: ['GET', 'POST'],
  },
})
export class GlobalSocketGateway
  extends DefaultSocket
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  nsp: Namespace;
  server: Server;
  socketType = 'global';

  constructor(
    httpService: HttpRequestService,
    producer: KafkaService,
    redisService: CustomRedisService
  ) {
    super('global', httpService, producer, redisService);
  }
}

네임 스페이스에 대한 분기를 직접해주다보니 코드가 중복될 것 같았으나, 클래스의 특징을 살림으로 코드를 반으로 줄여버린 것이다~