사내 프로젝트에 게임서버와 같이 대기실과 게임방 같은 기능이 필요하여 해당 기능에 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);
}
}
네임 스페이스에 대한 분기를 직접해주다보니 코드가 중복될 것 같았으나, 클래스의 특징을 살림으로 코드를 반으로 줄여버린 것이다~