diff --git a/server/src/workspace/schemas/workspace.schema.ts b/server/src/workspace/schemas/workspace.schema.ts index 1b9852ce..a6f5c7a8 100644 --- a/server/src/workspace/schemas/workspace.schema.ts +++ b/server/src/workspace/schemas/workspace.schema.ts @@ -214,7 +214,7 @@ export class Workspace { @Prop({ type: [Page], default: [] }) pageList: Page[]; - @Prop({ type: Map, default: new Map() }) + @Prop({ type: Object, default: {} }) authUser: Map; @Prop({ default: Date.now }) diff --git a/server/src/workspace/workspace.gateway.ts b/server/src/workspace/workspace.gateway.ts index 89e2e7f1..53834d2b 100644 --- a/server/src/workspace/workspace.gateway.ts +++ b/server/src/workspace/workspace.gateway.ts @@ -116,14 +116,13 @@ export class WorkspaceGateway implements OnGatewayInit, OnGatewayConnection, OnG const workspaces = await this.workSpaceService.getUserWorkspaces(userId); const userInfo = await this.authService.getProfile(userId); let NewWorkspaceId = ""; - if (userId === "guest") { client.join("guest"); NewWorkspaceId = "guest"; } else if (workspaces.length === 0) { const workspace = await this.workSpaceService.createWorkspace( userId, - `${userInfo.name}의 Workspace`, + `${userInfo === null ? "guest" : userInfo.name}의 Workspace`, ); client.join(workspace.id); NewWorkspaceId = workspace.id; @@ -176,7 +175,7 @@ export class WorkspaceGateway implements OnGatewayInit, OnGatewayConnection, OnG }; }); - this.logger.log(`Sending workspace list to client ${client.id}`); + this.logger.log(`Sending workspace list to client(user.id): ${client.data.userId}`); client.emit("workspace/list", workspaceList); this.SocketStoreBroadcastWorkspaceConnections(); }, 100); @@ -454,6 +453,7 @@ export class WorkspaceGateway implements OnGatewayInit, OnGatewayConnection, OnG page: newPage.serialize(), } as RemotePageCreateOperation; client.emit("create/page", operation); + this.emitOperation(client.id, workspaceId, "create/page", operation, batch); } catch (error) { this.logger.error( @@ -658,7 +658,6 @@ export class WorkspaceGateway implements OnGatewayInit, OnGatewayConnection, OnG `Block Update 연산 수신 - Client ID: ${clientInfo?.clientId}, Data:`, JSON.stringify(data), ); - console.log(data); const { workspaceId } = client.data; const currentPage = await this.workSpaceService.getPage(workspaceId, data.pageId); if (!currentPage) { diff --git a/server/src/workspace/workspace.service.ts b/server/src/workspace/workspace.service.ts index d749b146..8405f99a 100644 --- a/server/src/workspace/workspace.service.ts +++ b/server/src/workspace/workspace.service.ts @@ -57,23 +57,28 @@ export class WorkSpaceService implements OnModuleInit { // room의 연결된 클라이언트 수 확인 const room = this.server.sockets.adapter.rooms.get(roomId); const clientCount = room ? room.size : 0; - // 연결된 클라이언트가 없으면 DB에 저장하고 메모리에서 제거 if (clientCount === 0) { - const serializedWorkspace = workspace.serialize(); + const serializedData = workspace.serialize(); + // 스키마에 맞게 데이터 변환 + const workspaceData = { + id: roomId, + name: workspace.name, + pageList: serializedData.pageList, + authUser: serializedData.authUser, + updatedAt: new Date(), + }; bulkOps.push({ updateOne: { filter: { id: roomId }, - update: { $set: { ...serializedWorkspace } }, + update: { $set: workspaceData }, upsert: true, }, }); - this.workspaces.delete(roomId); this.logger.log(`Workspace ${roomId} will be saved to DB and removed from memory`); } } - // DB에 저장할 작업이 있으면 한 번에 실행 if (bulkOps.length > 0) { await this.workspaceModel.bulkWrite(bulkOps, { ordered: false }); @@ -97,6 +102,9 @@ export class WorkSpaceService implements OnModuleInit { // DB에서 찾기 const workspaceJSON = await this.workspaceModel.findOne({ id: workspaceId }); + if (!workspaceJSON) { + throw new Error(`workspaceJson ${workspaceId} not found`); + } const workspace = new CRDTWorkSpace(); if (workspaceJSON) { @@ -172,10 +180,11 @@ export class WorkSpaceService implements OnModuleInit { if (!workspaceData) { throw new Error(`Workspace with id ${workspaceId} not found`); } - // authUser Map에서 모든 유저 ID를 배열로 변환하여 반환 // authUser는 Map 형태로 userId와 role을 저장하고 있음 - return Array.from(workspaceData.authUser.keys()); + // return Array.from(workspaceData.authUser.keys()); + const members = await this.userModel.find({ workspaces: workspaceId }).select("id"); + return members.map((member) => member.id); } catch (error) { this.logger.error(`Failed to get workspace members: ${error.message}`); throw error; @@ -185,9 +194,9 @@ export class WorkSpaceService implements OnModuleInit { async createWorkspace(userId: string, name: string): Promise { const newWorkspace = await this.workspaceModel.create({ name, - authUser: new Map([[userId, "owner"]]), // 올바른 형태 + authUser: { [userId]: "owner" }, }); - + // newWorkspace.authUser[userId] // 유저 정보 업데이트 await this.userModel.updateOne({ id: userId }, { $push: { workspaces: newWorkspace.id } }); @@ -237,22 +246,28 @@ export class WorkSpaceService implements OnModuleInit { const workspaces = await this.workspaceModel.find({ id: { $in: user.workspaces }, }); - - const workspaceList = workspaces.map((workspace) => { - const room = this.getServer().sockets.adapter.rooms.get(workspace.id); - return { - id: workspace.id, - name: workspace.name, - role: workspace.authUser.get(userId) || "editor", - memberCount: workspace.authUser.size, - activeUsers: room ? room.size : 0, - }; - }); - + const workspaceList = await Promise.all( + workspaces.map(async (workspace) => { + const room = this.getServer().sockets.adapter.rooms.get(workspace.id); + const role = workspace.authUser[userId] || "editor"; + + // users 컬렉션에서 멤버 수 조회 + const memberCount = await this.userModel.countDocuments({ + workspaces: workspace.id, + }); + + return { + id: workspace.id, + name: workspace.name, + role, + memberCount, + activeUsers: room?.size || 0, + }; + }), + ); return workspaceList; } - // 워크스페이스에 유저 초대 async inviteUserToWorkspace( ownerId: string, workspaceId: string, @@ -264,18 +279,22 @@ export class WorkSpaceService implements OnModuleInit { throw new Error(`Workspace with id ${workspaceId} not found`); } - // 권한 확인 - if (!workspace.authUser.has(ownerId) || workspace.authUser.get(ownerId) !== "owner") { + // 권한 확인 - 객체의 속성 접근 방식으로 변경 + if (!(ownerId in workspace.authUser) || workspace.authUser[ownerId] !== "owner") { throw new Error(`User ${ownerId} does not have permission to invite users to this workspace`); } - // 워크스페이스에 유저 추가 - if (!workspace.authUser.has(invitedUserId)) { - workspace.authUser.set(invitedUserId, "editor"); + // 워크스페이스에 유저 추가 - 객체 속성 할당 방식으로 변경 + if (!(invitedUserId in workspace.authUser)) { + // 일반 객체 업데이트 + workspace.authUser[invitedUserId] = "editor"; await workspace.save(); // 유저 정보 업데이트 - await this.userModel.updateOne({ id: invitedUserId }, { $push: { workspaces: workspaceId } }); + await this.userModel.updateOne( + { id: invitedUserId }, + { $addToSet: { workspaces: workspaceId } }, + ); } } }