From 3bd5503dc8d46e296d2f7a300cdf8e822c03c1bf Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 20:53:37 +0900 Subject: [PATCH 1/8] =?UTF-8?q?refactor:=20`room-socket`=20=EC=84=9C?= =?UTF-8?q?=EB=B8=8C=20=EB=AA=A8=EB=93=88=EC=9D=84=20`websocket`=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 분리하면서 아예 공식 모듈로 승격 - 스터디 세션, 시그널링 서버 핸들러보다 더 상위의 기초적인 서버 설정과 서버 인스턴스를 맡는 전역 게이트웨이 --- .../websocket.entity.ts} | 2 +- backend/src/websocket/websocket.gateway.ts | 51 +++++++++++++++++++ backend/src/websocket/websocket.module.ts | 15 ++++++ .../websocket.repository.ts} | 10 ++-- .../websocket.service.ts} | 23 ++++++--- 5 files changed, 87 insertions(+), 14 deletions(-) rename backend/src/{room/socket/room-socket.entity.ts => websocket/websocket.entity.ts} (81%) create mode 100644 backend/src/websocket/websocket.gateway.ts create mode 100644 backend/src/websocket/websocket.module.ts rename backend/src/{room/socket/room-socket.repository.ts => websocket/websocket.repository.ts} (81%) rename backend/src/{room/socket/room-socket.service.ts => websocket/websocket.service.ts} (60%) diff --git a/backend/src/room/socket/room-socket.entity.ts b/backend/src/websocket/websocket.entity.ts similarity index 81% rename from backend/src/room/socket/room-socket.entity.ts rename to backend/src/websocket/websocket.entity.ts index 806f051e..a6f3b482 100644 --- a/backend/src/room/socket/room-socket.entity.ts +++ b/backend/src/websocket/websocket.entity.ts @@ -1,7 +1,7 @@ import { Entity, Field, Schema } from "nestjs-redis-om"; @Schema("socket") -export class RoomSocketEntity extends Entity { +export class WebsocketEntity extends Entity { @Field({ type: "string", indexed: true }) id: string; diff --git a/backend/src/websocket/websocket.gateway.ts b/backend/src/websocket/websocket.gateway.ts new file mode 100644 index 00000000..a9b88cd9 --- /dev/null +++ b/backend/src/websocket/websocket.gateway.ts @@ -0,0 +1,51 @@ +import { Logger } from "@nestjs/common"; +import { Server, Socket } from "socket.io"; +import { + OnGatewayConnection, + OnGatewayDisconnect, + OnGatewayInit, + WebSocketGateway, + WebSocketServer, +} from "@nestjs/websockets"; +import Redis from "ioredis"; +import { createAdapter } from "@socket.io/redis-adapter"; +import { WebsocketService } from "@/websocket/websocket.service"; +import { WebsocketRepository } from "@/websocket/websocket.repository"; + +@WebSocketGateway() +export class WebsocketGateway implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit { + @WebSocketServer() + private server: Server; + + private logger: Logger = new Logger("Websocket"); + + public constructor( + private readonly websocketService: WebsocketService, + private readonly websocketRepository: WebsocketRepository + ) {} + + public afterInit() { + const pubClient = new Redis({ + host: process.env.REDIS_HOST, + port: parseInt(process.env.REDIS_PORT), + }); + + const subClient = pubClient.duplicate(); + + const redisAdapter = createAdapter(pubClient, subClient); + this.server.adapter(redisAdapter); + + this.websocketService.setRedisClient(pubClient); + this.websocketService.setServer(this.server); + } + + public async handleConnection(client: Socket) { + this.logger.log(`Client connected: ${client.id}`); + await this.websocketRepository.register(client); + } + + public async handleDisconnect(client: Socket) { + this.logger.log(`Client disconnected: ${client.id}`); + await this.websocketRepository.clean(client); + } +} diff --git a/backend/src/websocket/websocket.module.ts b/backend/src/websocket/websocket.module.ts new file mode 100644 index 00000000..cf3842fd --- /dev/null +++ b/backend/src/websocket/websocket.module.ts @@ -0,0 +1,15 @@ +import { Global, Module } from "@nestjs/common"; +import { WebsocketService } from "@/websocket/websocket.service"; +import { WebsocketGateway } from "@/websocket/websocket.gateway"; +import { WebsocketRepository } from "@/websocket/websocket.repository"; +import { RedisOmModule } from "nestjs-redis-om"; +import { RoomEntity } from "@/room/room.entity"; +import { WebsocketEntity } from "@/websocket/websocket.entity"; + +@Global() +@Module({ + imports: [RedisOmModule.forFeature([RoomEntity, WebsocketEntity])], + providers: [WebsocketGateway, WebsocketService, WebsocketRepository], + exports: [WebsocketService, WebsocketRepository], +}) +export class WebsocketModule {} diff --git a/backend/src/room/socket/room-socket.repository.ts b/backend/src/websocket/websocket.repository.ts similarity index 81% rename from backend/src/room/socket/room-socket.repository.ts rename to backend/src/websocket/websocket.repository.ts index ba3af2bb..8bd27e59 100644 --- a/backend/src/room/socket/room-socket.repository.ts +++ b/backend/src/websocket/websocket.repository.ts @@ -2,17 +2,17 @@ import { Injectable } from "@nestjs/common"; import { Socket } from "socket.io"; import { InjectRepository } from "nestjs-redis-om"; import { Repository } from "redis-om"; -import { RoomSocketEntity } from "@/room/socket/room-socket.entity"; +import { WebsocketEntity } from "@/websocket/websocket.entity"; @Injectable() -export class RoomSocketRepository { +export class WebsocketRepository { public constructor( - @InjectRepository(RoomSocketEntity) - private readonly socketRepository: Repository + @InjectRepository(WebsocketEntity) + private readonly socketRepository: Repository ) {} public async register(socket: Socket) { - const entity = new RoomSocketEntity(); + const entity = new WebsocketEntity(); entity.id = socket.id; entity.joinedRooms = []; await this.socketRepository.remove(entity.id); diff --git a/backend/src/room/socket/room-socket.service.ts b/backend/src/websocket/websocket.service.ts similarity index 60% rename from backend/src/room/socket/room-socket.service.ts rename to backend/src/websocket/websocket.service.ts index 46b45a21..f5207507 100644 --- a/backend/src/room/socket/room-socket.service.ts +++ b/backend/src/websocket/websocket.service.ts @@ -1,25 +1,32 @@ import { Injectable } from "@nestjs/common"; import { Server } from "socket.io"; +import Redis from "ioredis"; @Injectable() -export class RoomSocketService { +export class WebsocketService { constructor() {} private server: Server; + private redisClient: Redis; + + public setRedisClient(client: Redis) { + this.redisClient = client; + } public setServer(server: Server) { this.server = server; } - public getSocket(socketId: string) { - return this.server.sockets.sockets.get(socketId); + public getRedisClient() { + return this.redisClient; } - public async leaveRoom(socketId: string, roomId: string) { - const socket = this.server.sockets.sockets.get(socketId); - if (socket) { - await socket.leave(roomId); - } + public getServer() { + return this.server; + } + + public getSocket(socketId: string) { + return this.server.sockets.sockets.get(socketId); } public emitToRoom(roomId: string, event: string, data?: any) { From 5f0ae210843773cead97eacc2acd30ef46b71936 Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 20:54:05 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor:=20=EC=8B=9C=EA=B7=B8=EB=84=90?= =?UTF-8?q?=EB=A7=81=20=EC=84=9C=EB=B2=84=EC=9D=98=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/signaling-server/sig-server.gateway.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/backend/src/signaling-server/sig-server.gateway.ts b/backend/src/signaling-server/sig-server.gateway.ts index 470fa0ea..3a3c909a 100644 --- a/backend/src/signaling-server/sig-server.gateway.ts +++ b/backend/src/signaling-server/sig-server.gateway.ts @@ -1,28 +1,17 @@ import { WebSocketGateway, WebSocketServer, - OnGatewayConnection, - OnGatewayDisconnect, SubscribeMessage, MessageBody, } from "@nestjs/websockets"; import { Server } from "socket.io"; -import { Logger } from "@nestjs/common"; import { EMIT_EVENT, LISTEN_EVENT } from "@/signaling-server/sig-server.event"; @WebSocketGateway() -export class SigServerGateway implements OnGatewayConnection, OnGatewayDisconnect { +export class SigServerGateway { @WebSocketServer() private server: Server; - handleConnection(socket: any) { - Logger.log(`Client connected in signaling server: ${socket.id}`); - } - - handleDisconnect(socket: any) { - Logger.log(`Client disconnected signaling server: ${socket.id}`); - } - @SubscribeMessage(LISTEN_EVENT.OFFER) handleOffer( @MessageBody() From e2322b07a2efdf7c97fe06c0b4c05ef1bb5e6ea9 Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 20:54:31 +0900 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20=EC=8A=A4=ED=84=B0=EB=94=94=20?= =?UTF-8?q?=EC=84=B8=EC=85=98=20=EB=82=98=EA=B0=80=EA=B8=B0=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/room/services/room-leave.service.ts | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/backend/src/room/services/room-leave.service.ts b/backend/src/room/services/room-leave.service.ts index f24b68b3..08aa2940 100644 --- a/backend/src/room/services/room-leave.service.ts +++ b/backend/src/room/services/room-leave.service.ts @@ -1,17 +1,17 @@ import { Injectable } from "@nestjs/common"; -import { RoomSocketService } from "@/room/socket/room-socket.service"; +import { WebsocketService } from "@/websocket/websocket.service"; import { RoomRepository } from "@/room/room.repository"; import { RoomDto } from "@/room/dto/room.dto"; import { RoomHostService } from "@/room/services/room-host.service"; -import { EMIT_EVENT } from "@/room/socket/room-socket.events"; +import { EMIT_EVENT } from "@/room/room.events"; import { Socket } from "socket.io"; -import { RoomSocketRepository } from "@/room/socket/room-socket.repository"; +import { WebsocketRepository } from "@/websocket/websocket.repository"; @Injectable() export class RoomLeaveService { constructor( - private readonly socketService: RoomSocketService, - private readonly socketRepository: RoomSocketRepository, + private readonly socketService: WebsocketService, + private readonly socketRepository: WebsocketRepository, private readonly roomRepository: RoomRepository, private readonly roomHostService: RoomHostService ) {} @@ -27,23 +27,17 @@ export class RoomLeaveService { const room = await this.roomRepository.getRoom(roomId); if (!room) return; - await this.leaveSocket(socketId, room); - - if (room.host === socketId) await this.handleHostChange(socketId, room); - else this.socketService.emitToRoom(room.roomId, EMIT_EVENT.QUIT, { socketId }); - - if (!room.connectionList.length) await this.deleteRoom(socketId); - } - - private async leaveSocket(socketId: string, room: RoomDto) { - await this.socketService.leaveRoom(socketId, room.roomId); - - // TODO : 엄청 비효율적인 코드라고 생각하는데 개선할 방법 찾기 room.connectionList = room.connectionList.filter( (connection) => connection.socketId !== socketId ); + if (!room.connectionList.length) return this.deleteRoom(socketId); + await this.roomRepository.setRoom(room); + + if (room.host === socketId) return this.handleHostChange(socketId, room); + + this.socketService.emitToRoom(room.roomId, EMIT_EVENT.QUIT, { socketId }); } private async handleHostChange(socketId: string, room: RoomDto) { From 5477d1e561e0f45d07439396429de7a1ed4cf412 Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 20:55:58 +0900 Subject: [PATCH 4/8] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=84=B0=EB=94=94=20?= =?UTF-8?q?=EC=84=B8=EC=85=98=20=EC=9E=85=EC=9E=A5,=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 길어지는 로직을 아예 파일로 분리했음 - 현재로써는 로직의 수가 많지 않기에 가독성 측면에서 분리가 좋다고 판단했습니다. --- .../room-socket.events.ts => room.events.ts} | 0 ...room-socket.gateway.ts => room.gateway.ts} | 56 ++++-------- backend/src/room/room.module.ts | 16 ++-- .../src/room/services/room-create.service.ts | 63 ++++++++++++++ .../src/room/services/room-join.service.ts | 46 ++++++++++ backend/src/room/services/room.service.ts | 86 +------------------ 6 files changed, 135 insertions(+), 132 deletions(-) rename backend/src/room/{socket/room-socket.events.ts => room.events.ts} (100%) rename backend/src/room/{socket/room-socket.gateway.ts => room.gateway.ts} (54%) create mode 100644 backend/src/room/services/room-create.service.ts create mode 100644 backend/src/room/services/room-join.service.ts diff --git a/backend/src/room/socket/room-socket.events.ts b/backend/src/room/room.events.ts similarity index 100% rename from backend/src/room/socket/room-socket.events.ts rename to backend/src/room/room.events.ts diff --git a/backend/src/room/socket/room-socket.gateway.ts b/backend/src/room/room.gateway.ts similarity index 54% rename from backend/src/room/socket/room-socket.gateway.ts rename to backend/src/room/room.gateway.ts index 5c7490f9..40a9b528 100644 --- a/backend/src/room/socket/room-socket.gateway.ts +++ b/backend/src/room/room.gateway.ts @@ -1,62 +1,39 @@ import { WebSocketGateway, - WebSocketServer, - OnGatewayConnection, OnGatewayDisconnect, SubscribeMessage, - OnGatewayInit, MessageBody, ConnectedSocket, } from "@nestjs/websockets"; import "dotenv/config"; -import { Server, Socket } from "socket.io"; -import { RoomService } from "../services/room.service"; +import { Socket } from "socket.io"; +import { RoomService } from "./services/room.service"; import { Logger, UsePipes, ValidationPipe } from "@nestjs/common"; -import { EMIT_EVENT, LISTEN_EVENT } from "@/room/socket/room-socket.events"; +import { EMIT_EVENT, LISTEN_EVENT } from "@/room/room.events"; import { CreateRoomDto } from "@/room/dto/create-room.dto"; -import { RoomSocketService } from "@/room/socket/room-socket.service"; +import { WebsocketService } from "@/websocket/websocket.service"; import { JoinRoomDto } from "@/room/dto/join-room.dto"; import { RoomRepository } from "@/room/room.repository"; import { ReactionDto } from "@/room/dto/reaction.dto"; -import { RoomSocketRepository } from "@/room/socket/room-socket.repository"; -import Redis from "ioredis"; -import { createAdapter } from "@socket.io/redis-adapter"; +import { RoomLeaveService } from "@/room/services/room-leave.service"; +import { RoomCreateService } from "@/room/services/room-create.service"; +import { RoomJoinService } from "@/room/services/room-join.service"; @WebSocketGateway() -export class RoomSocketGateway implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit { - @WebSocketServer() - private server: Server; +export class RoomGateway implements OnGatewayDisconnect { + private logger: Logger = new Logger("Room Gateway"); public constructor( private readonly roomService: RoomService, - private readonly socketService: RoomSocketService, - private readonly socketRepository: RoomSocketRepository, + private readonly roomLeaveService: RoomLeaveService, + private readonly roomCreateService: RoomCreateService, + private readonly roomJoinService: RoomJoinService, + private readonly socketService: WebsocketService, private readonly roomRepository: RoomRepository ) {} - public async handleConnection(client: Socket) { - Logger.log(`Client connected: ${client.id}`); - await this.socketRepository.register(client); - } - public async handleDisconnect(client: Socket) { - Logger.log(`Client disconnected: ${client.id}`); await this.handleLeaveRoom(client); - await this.socketRepository.clean(client); - } - - public async afterInit() { - const pubClient = new Redis({ - host: process.env.REDIS_HOST, - port: parseInt(process.env.REDIS_PORT), - }); - - const subClient = pubClient.duplicate(); - - const redisAdapter = createAdapter(pubClient, subClient); - this.server.adapter(redisAdapter); - - this.roomService.setServer(this.server); } @SubscribeMessage(LISTEN_EVENT.CREATE) @@ -65,7 +42,8 @@ export class RoomSocketGateway implements OnGatewayConnection, OnGatewayDisconne @ConnectedSocket() client: Socket, @MessageBody() dto: CreateRoomDto ) { - await this.roomService.createRoom({ ...dto, socketId: client.id }); + // TODO: try - catch 로 에러 핸들링을 통해 이벤트 Emit 을 여기서 하기 + await this.roomCreateService.createRoom({ ...dto, socketId: client.id }); } @SubscribeMessage(LISTEN_EVENT.JOIN) @@ -74,12 +52,12 @@ export class RoomSocketGateway implements OnGatewayConnection, OnGatewayDisconne @ConnectedSocket() client: Socket, @MessageBody() dto: JoinRoomDto ) { - await this.roomService.joinRoom({ ...dto, socketId: client.id }); + await this.roomJoinService.joinRoom({ ...dto, socketId: client.id }); } @SubscribeMessage(LISTEN_EVENT.LEAVE) public async handleLeaveRoom(client: Socket) { - await this.roomService.leaveRoom(client); + await this.roomLeaveService.leaveRoom(client); } @SubscribeMessage(LISTEN_EVENT.FINISH) diff --git a/backend/src/room/room.module.ts b/backend/src/room/room.module.ts index e58dd357..cd1a0574 100644 --- a/backend/src/room/room.module.ts +++ b/backend/src/room/room.module.ts @@ -1,27 +1,27 @@ import { Module } from "@nestjs/common"; import { RoomService } from "./services/room.service"; -import { RoomSocketGateway } from "./socket/room-socket.gateway"; +import { RoomGateway } from "./room.gateway"; import { RoomRepository } from "./room.repository"; import { RoomController } from "./room.controller"; import { RedisOmModule } from "nestjs-redis-om"; import { RoomEntity } from "./room.entity"; import { RoomLeaveService } from "@/room/services/room-leave.service"; -import { RoomSocketService } from "@/room/socket/room-socket.service"; import { RoomHostService } from "@/room/services/room-host.service"; -import { RoomSocketRepository } from "@/room/socket/room-socket.repository"; -import { RoomSocketEntity } from "@/room/socket/room-socket.entity"; import { QuestionListRepository } from "@/question-list/question-list.repository"; +import { WebsocketModule } from "@/websocket/websocket.module"; +import { RoomCreateService } from "@/room/services/room-create.service"; +import { RoomJoinService } from "@/room/services/room-join.service"; @Module({ - imports: [RedisOmModule.forFeature([RoomEntity, RoomSocketEntity])], + imports: [RedisOmModule.forFeature([RoomEntity]), WebsocketModule], providers: [ RoomService, - RoomSocketGateway, + RoomGateway, RoomRepository, + RoomCreateService, + RoomJoinService, RoomLeaveService, - RoomSocketService, RoomHostService, - RoomSocketRepository, QuestionListRepository, ], controllers: [RoomController], diff --git a/backend/src/room/services/room-create.service.ts b/backend/src/room/services/room-create.service.ts new file mode 100644 index 00000000..9df2e787 --- /dev/null +++ b/backend/src/room/services/room-create.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from "@nestjs/common"; +import { CreateRoomInternalDto } from "@/room/dto/create-room.dto"; +import { EMIT_EVENT } from "@/room/room.events"; +import { WebsocketService } from "@/websocket/websocket.service"; +import { RoomRepository } from "@/room/room.repository"; +import { QuestionListRepository } from "@/question-list/question-list.repository"; +import { RoomJoinService } from "@/room/services/room-join.service"; +import { createHash } from "node:crypto"; +import "dotenv/config"; + +@Injectable() +export class RoomCreateService { + private static ROOM_ID_CREATE_KEY = "room_id"; + + public constructor( + private readonly roomRepository: RoomRepository, + private readonly socketService: WebsocketService, + private readonly questionListRepository: QuestionListRepository, + private readonly roomJoinService: RoomJoinService + ) {} + + public async createRoom(dto: CreateRoomInternalDto) { + const { socketId, nickname } = dto; + const roomId = await this.generateRoomId(); + const currentTime = Date.now(); + const questionListContents = await this.questionListRepository.getContentsByQuestionListId( + dto.questionListId + ); + + const roomDto = { + ...dto, + roomId, + connectionList: [], + questionListContents, + createdAt: currentTime, + host: dto.socketId, + }; + + await this.roomRepository.setRoom(roomDto); + + await this.roomJoinService.joinRoom({ roomId, socketId, nickname }); + + this.socketService.emitToRoom(roomId, EMIT_EVENT.CREATE, roomDto); + } + + // TODO: 동시성 고려해봐야하지 않을까? + private async generateRoomId() { + const client = this.socketService.getRedisClient(); + + const idString = await client.get(RoomCreateService.ROOM_ID_CREATE_KEY); + + let id: number; + if (idString && !isNaN(parseInt(idString))) { + id = await client.incr(RoomCreateService.ROOM_ID_CREATE_KEY); + } else { + id = parseInt(await client.set(RoomCreateService.ROOM_ID_CREATE_KEY, "1")); + } + + return createHash("sha256") + .update(id + process.env.SESSION_HASH) + .digest("hex"); + } +} diff --git a/backend/src/room/services/room-join.service.ts b/backend/src/room/services/room-join.service.ts new file mode 100644 index 00000000..32d41eff --- /dev/null +++ b/backend/src/room/services/room-join.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from "@nestjs/common"; +import { EMIT_EVENT } from "@/room/room.events"; +import { WebsocketService } from "@/websocket/websocket.service"; +import { RoomRepository } from "@/room/room.repository"; +import { RoomDto } from "@/room/dto/room.dto"; +import { JoinRoomInternalDto } from "@/room/dto/join-room.dto"; +import { WebsocketRepository } from "@/websocket/websocket.repository"; + +@Injectable() +export class RoomJoinService { + public constructor( + private readonly roomRepository: RoomRepository, + private readonly socketService: WebsocketService, + private readonly socketRepository: WebsocketRepository + ) {} + + public async joinRoom(dto: JoinRoomInternalDto) { + const { roomId, socketId, nickname } = dto; + + const room = await this.roomRepository.getRoom(roomId); + const socket = this.socketService.getSocket(socketId); + + if (!socket) throw new Error("Invalid Socket"); + + if (room.roomId === null) throw new Error("Redis: RoomEntity Entity type error"); + + if (this.isFullRoom(room)) return socket.emit(EMIT_EVENT.FULL, {}); + + socket.join(roomId); + await this.socketRepository.joinRoom(socket.id, roomId); + room.connectionList.push({ + socketId, + createAt: Date.now(), + nickname, + }); + + await this.roomRepository.setRoom(room); + + // TODO: 성공 / 실패 여부를 전송하는데 있어서 결과에 따라 다르게 해야하는데.. 어떻게 관심 분리를 할까? + this.socketService.emitToRoom(roomId, EMIT_EVENT.JOIN, room); + } + + private isFullRoom(room: RoomDto): boolean { + return room.maxParticipants <= room.connectionList.length; + } +} diff --git a/backend/src/room/services/room.service.ts b/backend/src/room/services/room.service.ts index 880cb817..012ea881 100644 --- a/backend/src/room/services/room.service.ts +++ b/backend/src/room/services/room.service.ts @@ -1,30 +1,10 @@ import { Injectable } from "@nestjs/common"; import { RoomStatus } from "@/room/room.entity"; -import { Server, Socket } from "socket.io"; -import { CreateRoomInternalDto } from "@/room/dto/create-room.dto"; -import { generateRoomId } from "@/utils/generateRoomId"; -import { EMIT_EVENT } from "@/room/socket/room-socket.events"; -import { RoomSocketService } from "@/room/socket/room-socket.service"; -import { RoomLeaveService } from "@/room/services/room-leave.service"; import { RoomRepository } from "@/room/room.repository"; -import { RoomDto } from "@/room/dto/room.dto"; -import { JoinRoomInternalDto } from "@/room/dto/join-room.dto"; -import { RoomSocketRepository } from "@/room/socket/room-socket.repository"; -import { QuestionListRepository } from "@/question-list/question-list.repository"; @Injectable() export class RoomService { - public constructor( - private readonly roomRepository: RoomRepository, - private readonly roomLeaveService: RoomLeaveService, - private readonly socketService: RoomSocketService, - private readonly socketRepository: RoomSocketRepository, - private readonly questionListRepository: QuestionListRepository - ) {} - - public setServer(server: Server) { - this.socketService.setServer(server); - } + public constructor(private readonly roomRepository: RoomRepository) {} public async getPublicRoom() { const rooms = await this.roomRepository.getAllRoom(); @@ -33,70 +13,6 @@ export class RoomService { .sort((a, b) => b.createdAt - a.createdAt); } - public async leaveRoom(socket: Socket) { - return this.roomLeaveService.leaveRoom(socket); - } - - public async createRoom(dto: CreateRoomInternalDto) { - const roomId = generateRoomId(); - const currentTime = Date.now(); - - const roomDto = { - ...dto, - roomId, - connectionList: [ - { - socketId: dto.socketId, - createAt: currentTime, - nickname: dto.nickname, - }, - ], - questionListContents: await this.questionListRepository.getContentsByQuestionListId( - dto.questionListId - ), - createdAt: currentTime, - host: dto.socketId, - }; - await this.roomRepository.setRoom(roomDto); - - const socket = this.socketService.getSocket(dto.socketId); - socket.join(roomId); - await this.socketRepository.joinRoom(socket.id, roomId); - this.socketService.emitToRoom(roomId, EMIT_EVENT.CREATE, roomDto); - } - - public async joinRoom(dto: JoinRoomInternalDto) { - const { roomId, socketId, nickname } = dto; - - const room = await this.roomRepository.getRoom(roomId); - const socket = this.socketService.getSocket(socketId); - - if (!socket) throw new Error("Invalid Socket"); - - if (room.roomId === null) throw new Error("Redis: RoomEntity Entity type error"); - - // TODO : 에러를 보내기 -> Exception 생성해서 분류 - if (this.isFullRoom(room)) return socket.emit(EMIT_EVENT.FULL, {}); - - // TODO : join room 하는 단계가 3가지가 같이 혼재 -> 이것만 따로 묶어보기? - socket.join(roomId); - await this.socketRepository.joinRoom(socket.id, roomId); - room.connectionList.push({ - socketId, - createAt: Date.now(), - nickname, - }); - - await this.roomRepository.setRoom(room); - - // TODO: 성공 / 실패 여부를 전송하는데 있어서 결과에 따라 다르게 해야하는데.. 어떻게 관심 분리를 할까? - this.socketService.emitToRoom(roomId, EMIT_EVENT.JOIN, room); - } - - private isFullRoom(room: RoomDto): boolean { - return room.maxParticipants <= room.connectionList.length; - } - public async finishRoom(roomId: string) { await this.roomRepository.removeRoom(roomId); return roomId; From a9c189712da5a68b1dc2f1f588c4899298929ef8 Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 20:56:44 +0900 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20=EC=97=AC=EB=9F=AC=20WAS=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=8A=A4=ED=84=B0=EB=94=94=EC=84=B8=EC=85=98?= =?UTF-8?q?=EC=9D=84=20=EC=9E=98=EB=AA=BB=20=EC=83=9D=EC=84=B1=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `backend/src/room/services/room-create.service.ts` 해당 파일에서 새롭게 `redisClient` 를 받아와서 새로운 roomId 값을 생성하도록 개선 --- backend/src/utils/generateRoomId.ts | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 backend/src/utils/generateRoomId.ts diff --git a/backend/src/utils/generateRoomId.ts b/backend/src/utils/generateRoomId.ts deleted file mode 100644 index c808594a..00000000 --- a/backend/src/utils/generateRoomId.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createHash } from "node:crypto"; -import "dotenv/config"; - -let id = parseInt(process.env.SESSION_HASH_START); -export function generateRoomId() { - return createHash("sha256") - .update(id++ + process.env.SESSION_HASH) - .digest("hex"); -} \ No newline at end of file From b9fe581474dfd27a1b406aad7b76c9f2ac535a44 Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 21:10:10 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20`create=5Froom`=20=EC=8B=9C,=20`join?= =?UTF-8?q?`=20emit=EA=B9=8C=EC=A7=80=20=EB=B0=9B=EB=8A=94=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/room/services/room-create.service.ts | 2 +- backend/src/room/services/room-join.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/room/services/room-create.service.ts b/backend/src/room/services/room-create.service.ts index 9df2e787..74f2c8c7 100644 --- a/backend/src/room/services/room-create.service.ts +++ b/backend/src/room/services/room-create.service.ts @@ -38,7 +38,7 @@ export class RoomCreateService { await this.roomRepository.setRoom(roomDto); - await this.roomJoinService.joinRoom({ roomId, socketId, nickname }); + await this.roomJoinService.joinRoom({ roomId, socketId, nickname }, true); this.socketService.emitToRoom(roomId, EMIT_EVENT.CREATE, roomDto); } diff --git a/backend/src/room/services/room-join.service.ts b/backend/src/room/services/room-join.service.ts index 32d41eff..f242b15f 100644 --- a/backend/src/room/services/room-join.service.ts +++ b/backend/src/room/services/room-join.service.ts @@ -14,7 +14,7 @@ export class RoomJoinService { private readonly socketRepository: WebsocketRepository ) {} - public async joinRoom(dto: JoinRoomInternalDto) { + public async joinRoom(dto: JoinRoomInternalDto, isCreate: boolean = false) { const { roomId, socketId, nickname } = dto; const room = await this.roomRepository.getRoom(roomId); @@ -37,7 +37,7 @@ export class RoomJoinService { await this.roomRepository.setRoom(room); // TODO: 성공 / 실패 여부를 전송하는데 있어서 결과에 따라 다르게 해야하는데.. 어떻게 관심 분리를 할까? - this.socketService.emitToRoom(roomId, EMIT_EVENT.JOIN, room); + if (!isCreate) this.socketService.emitToRoom(roomId, EMIT_EVENT.JOIN, room); } private isFullRoom(room: RoomDto): boolean { From ca2f744950fc2ae5df6b7062ae22927d37d25501 Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 21:31:39 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=EB=A7=88=EC=A7=80=EB=A7=89=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EA=B0=80=20=EB=B0=A9=20=ED=87=B4?= =?UTF-8?q?=EC=9E=A5=20=EC=8B=9C=20=EC=82=AD=EC=A0=9C=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8D=98=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/room/services/room-leave.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/room/services/room-leave.service.ts b/backend/src/room/services/room-leave.service.ts index 08aa2940..aaafbd1c 100644 --- a/backend/src/room/services/room-leave.service.ts +++ b/backend/src/room/services/room-leave.service.ts @@ -31,7 +31,7 @@ export class RoomLeaveService { (connection) => connection.socketId !== socketId ); - if (!room.connectionList.length) return this.deleteRoom(socketId); + if (!room.connectionList.length) return this.deleteRoom(room.roomId); await this.roomRepository.setRoom(room); From 99ee9098146788240a17aec1756502797fc0cff4 Mon Sep 17 00:00:00 2001 From: Chanwoo Kim Date: Mon, 25 Nov 2024 21:31:54 +0900 Subject: [PATCH 8/8] =?UTF-8?q?chore:=20docker=20compose=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이제 redis-stack을 사용 --- docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9370a4b2..93525498 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,10 +17,10 @@ services: - ./mysql-data:/var/lib/mysql redis: - image: redis:latest - container_name: redis + image: redis/redis-stack:latest ports: - - '6379:6379' - command: redis-server --port 6379 + - "6379:6379" # Redis port + - "8001:8001" # RedisInsight port volumes: - - ./redis-data:/data \ No newline at end of file + - redis_data:/data + restart: unless-stopped \ No newline at end of file