NestJS SocketIO - 방을 선택해서 들어가는 채팅방 만들기

 아녕하세요. 알렉스 입니다. 이번에는 NestJS와 SocketIO를 이용하여 채팅방을 만들어 보도록 하겠습니다. 웹소켓에 대한 공식 문서는 해당 링크를 참고해 주시기 바랍니다.


사진1) NestJS을 셋팅한다.

사진1에서는 프로젝트 명을 'socketIO-chat'으로 지정했습니다.



1. 소켓IO를 구현하기 위해서 필요 패키지를 설치한다.

- NestJS에 소켓IO를 구현하기 위해서 필요 패키지 2개를 설치한다. (websockets, socket.io)

$ npm i --save @nestjs/websockets @nestjs/platform-socket.io


2. NestJS CLI를 이용하여 SocketIO 코드 생성

- NestJS에서 지원해주는 CLI를 이용하여 베이스 코드를 생성한다.

$ nest g res


사진2) 베이스코드 생성

이제 실제 코드를 작성하도록 하겠습니다. 먼저 'socketio.service.ts'을 먼저 프로그래밍 하도록 하겠습니다.

// src/socketio/socketio.service.ts
import { Injectable } from '@nestjs/common';
import { CreateSocketioDto } from './dto/create-socketio.dto';

@Injectable()
export class SocketioService {
// 현재 서버에 참여하고 있는 유저와 ClientID를 참여한 방으로 나타냅니다.
roomUser = {};

// 각 방의 메세지를 저장합니다.
roomMessage = {};

// ClientId 및 유저이름
client = {};

// 대화 내용을 각 방에 맞춰서 저장합니다.
create(createSocketioDto: CreateSocketioDto, id: string) {
const message = {
name: this.client[id]['user'],
time: new Date().toISOString(),
text: createSocketioDto.text,
};
this.roomMessage[createSocketioDto.room].push(message);
return message;
}

// 해당 방에 대한 이전 기록을 반환합니다.
findAll(room: string) {
return this.roomMessage[room];
}

// 해당 방에 참여한 유저에 대한 정보를 저장합니다.
joinRoom(
room: string,
user: string,
id: string,
joinMsg: { name: string; time: string; text: string },
) {
//client Object에 clientId를 기로 유저와 방을 저장합니다.
this.client[id] = { user, room };

// 만약 방이 처음 생성 된 것이면 실행합니다.
if (!this.roomUser[room]) {
// 해당 방에 들어갈 빈 배열 생성
this.roomUser[room] = [];

// 해당 방에 들어갈 메세지 빈 배열 생성
this.roomMessage[room] = [];
}

// 해당 방에 들어갈 유저의 ClientId와 유저 이름을 배열로 push 해줍니다.
this.roomUser[room].push({ id, user });

// 해당 방에 들어갈 메세지에서 서버에서 보낸 참여자 정보 메세지를 roomMessage배열에 저장합니다.
this.roomMessage[room].push(joinMsg);

return Object.values(this.client);
}

// 현재 서버에 참여한 모든 유저정보
getAllUser() {
return this.client;
}

// ClientId를 이용하여 확인한 유저 정보
getClientInfo(id: string) {
return this.client[id];
}

// 유저가 접속이 종료시 실행할 비즈니스 로직
disconnectUser(
id: string,
disconnectMsg: { name: string; time: string; text: string },
) {
this.roomMessage[this.client[id]['room']].push(disconnectMsg);
this.roomUser[this.client[id]['room']] = this.roomUser[
this.client[id]['room']
].filter((ele) => ele.id !== id);
delete this.client[id];
return this.client;
}
}

코드1) 비즈니스 로직 코드


// src/socketio/socketio.gateway.ts
import {
ConnectedSocket,
MessageBody,
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { CreateSocketioDto } from './dto/create-socketio.dto';
import { SocketioService } from './socketio.service';

// Cors에러 방지
@WebSocketGateway({
cors: {
origin: '*',
},
})
export class SocketioGateway {
@WebSocketServer()
server: Server; // 서버 셋팅
constructor(private readonly socketioService: SocketioService) {}

// SocketIO Controller 로직
@SubscribeMessage('createSocketio')
// 유저가 메세지 생성시
async create(
@MessageBody() createSocketioDto: CreateSocketioDto,
@ConnectedSocket() client: Socket,
) {
const message = this.socketioService.create(createSocketioDto, client.id);
this.server.emit(createSocketioDto.room, message);

return message;
}

@SubscribeMessage('findAllSocketio')
findAll(
@ConnectedSocket() client: Socket,
@MessageBody('room') room: string,
) {
const res = this.socketioService.findAll(room);
console.log(`Client ${client.id} request all the chat data`);
return res;
}

@SubscribeMessage('join')
joinRoom(
@MessageBody('user') user: string,
@MessageBody('room') room: string,
@ConnectedSocket() client: Socket,
) {
console.log(`${user}(${client.id}) join to the Chat Room`);

// Client가 접속이 종료될시 동작
client.on('disconnect', () => {
const disconnectMsg = {
name: 'server',
time: new Date().toISOString(),
text: `user(${client.id}) ${user} has been disconnected`,
};
// Client의 접속 종료를 해당 방에 메세지로 올린다.
this.server.emit(room, disconnectMsg);
this.socketioService.disconnectUser(client.id, disconnectMsg);
});
const joinMsg = {
name: 'NestJS-Server',
time: new Date().toISOString(),
text: `user ${user}(${client.id}) has join the game`,
};
// Client의 접속을 해당 방에 메세지로 올린다.
this.server.emit(room, joinMsg);
return this.socketioService.joinRoom(room, user, client.id, joinMsg);
}
}

코드2) 컨트롤 로직


2개의 코드를 적용후 Vue를 이용하여 채팅앱 프런트를 구성하였다.


사진3) 로컬로 채팅창3개 실행

여기서 유저 이름과 참여할 방을 입력해야 합니다. 만약 처음 입장하는 유저이면 접속했다는 글이 가장 위쪽에 나타났을 것입니다.


사진3) 필요데이터 입력








위와 같이 영상에서 채팅 프로그램이 완성된 것을 알수 있습니다. 해당 코드는 다른 데이터 베이스에 저장된 것이 아닌 서버에 저장된 데이터 입니다. 이는 향후에 MongoDB나 다른 기타 데이터 베이스와 연동을 해야 합니다.


해당 gitHub는 아래 링크들을 참고해 주시기 바랍니다.

채팅앱 프런트(Vue) : https://github.com/Alex-Choi0/Test_SocketIO_Vue.git

채팅앱 백엔드(NestJS) : https://github.com/Alex-Choi0/NestJS_SocketIO_LocalStorage.git


댓글

이 블로그의 인기 게시물

Lesson 12_1 프로퍼티 노드(Property Node)

DAQ로 전압 측정하기-2

Nest JS URL에 있는 쿼리(Query) 읽기