From 5277f7fb92da02a073a3ea8850be146f2e979705 Mon Sep 17 00:00:00 2001 From: Ss0Mae Date: Tue, 4 Feb 2025 17:20:35 +0900 Subject: [PATCH 01/35] =?UTF-8?q?[Fix]=20=EB=94=94=EB=B2=84=EA=B9=85=20?= =?UTF-8?q?=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/notification/notification.controller.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/modules/notification/notification.controller.ts b/src/modules/notification/notification.controller.ts index b077c79..4df479e 100644 --- a/src/modules/notification/notification.controller.ts +++ b/src/modules/notification/notification.controller.ts @@ -43,12 +43,6 @@ export class NotificationsController { }); } - console.log(`✅ SSE 연결 성공 - 사용자 ${userId}`); - - req.on('close', () => { - console.log(`❌ 사용자 ${userId}와의 SSE 연결 종료`); - }); - const unreadNotifications = await this.notificationsService.getUnreadNotifications(userId); From a0c9b9cfe682bb28445eedf89c0814ba031b8191 Mon Sep 17 00:00:00 2001 From: Ss0Mae Date: Tue, 4 Feb 2025 17:20:41 +0900 Subject: [PATCH 02/35] =?UTF-8?q?[Fix]=20=EB=94=94=EB=B2=84=EA=B9=85=20?= =?UTF-8?q?=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/notification.service.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/modules/notification/notification.service.ts b/src/modules/notification/notification.service.ts index 3f68c75..dff84c5 100644 --- a/src/modules/notification/notification.service.ts +++ b/src/modules/notification/notification.service.ts @@ -25,9 +25,6 @@ export class NotificationsService { message, }, }); - - console.log('✅ 알림 생성 완료:', createdNotification); - return { notificationId: createdNotification.id, // `id`를 `notificationId`로 변경 ...createdNotification, @@ -60,11 +57,6 @@ export class NotificationsService { }, }); - console.log( - '📥 [getUnreadNotifications] DB 조회 결과:', - unreadNotifications - ); - // 2. 데이터를 변환하여 반환 const transformedNotifications = unreadNotifications.map(notification => { const transformedNotification = { @@ -80,18 +72,8 @@ export class NotificationsService { profileUrl: notification.sender.profile_url, // `profile_url` -> `profileUrl` }, }; - - console.log( - '🔧 [getUnreadNotifications] 변환된 알림:', - transformedNotification - ); return transformedNotification; }); - - console.log('📤 [getUnreadNotifications] 최종 반환 데이터:', { - notifications: transformedNotifications, - }); - return { notifications: transformedNotifications, }; From 0377fdef8e3677e383a0bd45a35efac67195f622 Mon Sep 17 00:00:00 2001 From: Ss0Mae Date: Tue, 4 Feb 2025 17:20:55 +0900 Subject: [PATCH 03/35] =?UTF-8?q?[Fix]=20=EB=94=94=EB=B2=84=EA=B9=85?= =?UTF-8?q?=EB=AC=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/project/project.service.ts | 320 +++++++++++++------------ 1 file changed, 164 insertions(+), 156 deletions(-) diff --git a/src/modules/project/project.service.ts b/src/modules/project/project.service.ts index 83e0295..b2ad196 100644 --- a/src/modules/project/project.service.ts +++ b/src/modules/project/project.service.ts @@ -130,108 +130,112 @@ export class ProjectService { } async createProject(createProjectDto: CreateProjectDto, userId: number) { - const { - title, - content, - role, - hub_type, - start_date, - duration, - work_type, - recruiting, - skills, - detail_roles, - } = createProjectDto; - - const thumbnailUrl = await this.getThumbnailUrl(content); - // 프로젝트 생성 - const project = await this.prisma.projectPost.create({ - data: { + try { + const { title, content, role, hub_type, - start_date: new Date(start_date), + start_date, duration, work_type, recruiting, - thumbnail_url: thumbnailUrl, - user_id: userId, // userId를 사용하여 사용자 식별 - }, - }); + skills, + detail_roles, + } = createProjectDto; - // 태그 저장 (skills) - const tags = []; - for (const skill of skills) { - const tag = await this.prisma.projectTag.upsert({ - where: { name: skill }, - create: { name: skill }, - update: {}, - }); - - await this.prisma.projectPostTag.create({ + const thumbnailUrl = await this.getThumbnailUrl(content); + // 프로젝트 생성 + const project = await this.prisma.projectPost.create({ data: { - post_id: project.id, - tag_id: tag.id, + title, + content, + role, + hub_type, + start_date: new Date(start_date), + duration, + work_type, + recruiting, + thumbnail_url: thumbnailUrl, + user_id: userId, // userId를 사용하여 사용자 식별 }, }); - tags.push(tag.name); // 생성된 태그 추가 - } + // 태그 저장 (skills) + const tags = []; + for (const skill of skills) { + const tag = await this.prisma.projectTag.upsert({ + where: { name: skill }, + create: { name: skill }, + update: {}, + }); - const roleMapping: Record = { - Programmer: 1, - Artist: 2, - Designer: 3, - }; + await this.prisma.projectPostTag.create({ + data: { + post_id: project.id, + tag_id: tag.id, + }, + }); - // 매핑된 role_id 가져오기 - const saveRoleId = roleMapping[role]; + tags.push(tag.name); // 생성된 태그 추가 + } - // 모집단위 저장 (detail_roles) - const roles = []; - for (const detail_role of detail_roles) { - const role = await this.prisma.detailRole.upsert({ - where: { name: detail_role }, - create: { name: detail_role, role_id: saveRoleId }, - update: {}, - }); + const roleMapping: Record = { + Programmer: 1, + Artist: 2, + Designer: 3, + }; - await this.prisma.projectDetailRole.create({ - data: { - post_id: project.id, - detail_role_id: role.id, - }, - }); + // 매핑된 role_id 가져오기 + const saveRoleId = roleMapping[role]; - roles.push(role.name); // 생성된 모집단위 추가 - } + // 모집단위 저장 (detail_roles) + const roles = []; + for (const detail_role of detail_roles) { + const role = await this.prisma.detailRole.upsert({ + where: { name: detail_role }, + create: { name: detail_role, role_id: saveRoleId }, + update: {}, + }); - // 결과 반환 - return { - message: { - code: 201, - text: '프로젝트 생성에 성공했습니다', - }, - project: { - projectId: project.id, - title: project.title, - content: project.content, - thumbnailUrl: project.thumbnail_url, - role: project.role, - hubType: project.hub_type, - startDate: project.start_date, - duration: project.duration, - workType: project.work_type, - status: project.recruiting ? 'OPEN' : 'CLOSED', - viewCount: project.view, - applyCount: 0, - bookmarkCount: 0, - createdAt: project.created_at, - skills: tags, - detailRoles: roles, - }, - }; + await this.prisma.projectDetailRole.create({ + data: { + post_id: project.id, + detail_role_id: role.id, + }, + }); + + roles.push(role.name); // 생성된 모집단위 추가 + } + + // 결과 반환 + return { + message: { + code: 201, + text: '프로젝트 생성에 성공했습니다', + }, + project: { + projectId: project.id, + title: project.title, + content: project.content, + thumbnailUrl: project.thumbnail_url, + role: project.role, + hubType: project.hub_type, + startDate: project.start_date, + duration: project.duration, + workType: project.work_type, + status: project.recruiting ? 'OPEN' : 'CLOSED', + viewCount: project.view, + applyCount: 0, + bookmarkCount: 0, + createdAt: project.created_at, + skills: tags, + detailRoles: roles, + }, + }; + } catch (err) { + console.log(err); + } } async getPopularProjectsThisWeek() { @@ -317,90 +321,94 @@ export class ProjectService { } async getProjectDetail(userId: number, numProjectId: number) { - // 조회수 증가 - await this.prisma.projectPost.update({ - where: { id: numProjectId }, - data: { - view: { - increment: 1, // view 값을 1 증가 + try { + // 조회수 증가 + await this.prisma.projectPost.update({ + where: { id: numProjectId }, + data: { + view: { + increment: 1, // view 값을 1 증가 + }, }, - }, - }); + }); - // 프로젝트 상세 정보 조회 - const project = await this.prisma.projectPost.findUnique({ - where: { id: numProjectId }, - include: { - Tags: { - select: { - tag: { - select: { name: true }, + // 프로젝트 상세 정보 조회 + const project = await this.prisma.projectPost.findUnique({ + where: { id: numProjectId }, + include: { + Tags: { + select: { + tag: { + select: { name: true }, + }, }, }, - }, - Details: { - select: { - detail_role: { - select: { name: true }, + Details: { + select: { + detail_role: { + select: { name: true }, + }, }, }, - }, - user: { - select: { - id: true, - name: true, - nickname: true, - profile_url: true, - introduce: true, - role: true, + user: { + select: { + id: true, + name: true, + nickname: true, + profile_url: true, + introduce: true, + role: true, + }, + }, + Applications: { + select: { id: true }, // 지원 데이터를 가져옴 }, }, - Applications: { - select: { id: true }, // 지원 데이터를 가져옴 - }, - }, - }); + }); - if (!project) { - throw new NotFoundException('프로젝트를 찾을 수 없습니다.'); - } + if (!project) { + throw new NotFoundException('프로젝트를 찾을 수 없습니다.'); + } - // 사용자가 작성자인지 여부 확인 - const isOwnConnectionHub = project.user.id === userId; + // 사용자가 작성자인지 여부 확인 + const isOwnConnectionHub = project.user.id === userId; - // 데이터 반환 - return { - message: { - code: 200, - text: '프로젝트 상세 조회에 성공했습니다', - }, - project: { - projectId: project.id, - title: project.title, - content: project.content, - role: project.role, - hubType: project.hub_type, - startDate: project.start_date, - duration: project.duration, - workType: project.work_type, - status: project.recruiting ? 'OPEN' : 'CLOSED', - skills: project.Tags.map(t => t.tag.name), - detailRoles: project.Details.map(d => d.detail_role.name), - viewCount: project.view, // 이미 증가된 view 값을 사용 - bookmarkCount: project.saved_count, - applyCount: project.Applications.length, - createdAt: project.created_at, - manager: { - userId: project.user.id, - name: project.user.name, - nickname: project.user.nickname, - role: project.user.role.name, - profileUrl: project.user.profile_url, - introduce: project.user.introduce ? project.user.introduce : null, + // 데이터 반환 + return { + message: { + code: 200, + text: '프로젝트 상세 조회에 성공했습니다', }, - }, - isOwnConnectionHub, - }; + project: { + projectId: project.id, + title: project.title, + content: project.content, + role: project.role, + hubType: project.hub_type, + startDate: project.start_date, + duration: project.duration, + workType: project.work_type, + status: project.recruiting ? 'OPEN' : 'CLOSED', + skills: project.Tags.map(t => t.tag.name), + detailRoles: project.Details.map(d => d.detail_role.name), + viewCount: project.view, // 이미 증가된 view 값을 사용 + bookmarkCount: project.saved_count, + applyCount: project.Applications.length, + createdAt: project.created_at, + manager: { + userId: project.user.id, + name: project.user.name, + nickname: project.user.nickname, + role: project.user.role.name, + profileUrl: project.user.profile_url, + introduce: project.user.introduce ? project.user.introduce : null, + }, + }, + isOwnConnectionHub, + }; + } catch (err) { + console.log(err); + } } async applyToProject(userId: number, projectId: number) { From 1da0094d8521672b966bf1c6b07fc4a6910775d2 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Tue, 4 Feb 2025 17:45:14 +0900 Subject: [PATCH 04/35] =?UTF-8?q?[Fix]=20=ED=94=BC=EB=93=9C=EB=B3=84=20?= =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20=EB=9D=BC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BB=A4=EC=84=9C=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/feed/feed.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feed/feed.service.ts b/src/feed/feed.service.ts index 6fa260e..5529928 100644 --- a/src/feed/feed.service.ts +++ b/src/feed/feed.service.ts @@ -46,8 +46,8 @@ export class FeedService { const result = await this.prisma.feedPost.findMany({ orderBy: { id: 'desc' }, where: { - ...(cursor ? { id: { lt: cursor } } : {}), // cursor 조건 추가 (옵셔널) ...(feedTagIds ? { id: { in: feedTagIds } } : {}), // 태그 조건 추가 (옵셔널) + ...(cursor ? { id: { lt: cursor } } : {}), // cursor 조건 추가 (옵셔널) }, take: limit, From e6ecbbc09ca194b90e1096c31e509e2ee0ee0b83 Mon Sep 17 00:00:00 2001 From: Ss0Mae Date: Tue, 4 Feb 2025 17:55:39 +0900 Subject: [PATCH 05/35] =?UTF-8?q?[Fix]=20=EB=94=94=EB=B2=84=EA=B9=85?= =?UTF-8?q?=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/notification/notification.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/notification/notification.service.ts b/src/modules/notification/notification.service.ts index dff84c5..1c21557 100644 --- a/src/modules/notification/notification.service.ts +++ b/src/modules/notification/notification.service.ts @@ -36,8 +36,6 @@ export class NotificationsService { } async getUnreadNotifications(userId: number) { - console.log(`🔍 [getUnreadNotifications] 시작 - userId: ${userId}`); - // 1. 읽지 않은 알림 조회 const unreadNotifications = await this.prisma.notification.findMany({ where: { From 37b5588679a7da15a5b3b4e19a89224da7c54e6b Mon Sep 17 00:00:00 2001 From: Ss0Mae Date: Wed, 5 Feb 2025 13:08:30 +0900 Subject: [PATCH 06/35] =?UTF-8?q?[Fix]=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/notification/notification.controller.ts | 5 +++++ src/modules/notification/notification.service.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/notification/notification.controller.ts b/src/modules/notification/notification.controller.ts index 4df479e..3121b6f 100644 --- a/src/modules/notification/notification.controller.ts +++ b/src/modules/notification/notification.controller.ts @@ -43,6 +43,11 @@ export class NotificationsController { }); } + console.log(`✅ SSE 연결 성공 - 사용자 ${userId}`); + req.on('close', () => { + console.log(`❌ 사용자 ${userId}와의 SSE 연결 종료`); + }); + const unreadNotifications = await this.notificationsService.getUnreadNotifications(userId); diff --git a/src/modules/notification/notification.service.ts b/src/modules/notification/notification.service.ts index 1c21557..3ac6ce2 100644 --- a/src/modules/notification/notification.service.ts +++ b/src/modules/notification/notification.service.ts @@ -58,7 +58,7 @@ export class NotificationsService { // 2. 데이터를 변환하여 반환 const transformedNotifications = unreadNotifications.map(notification => { const transformedNotification = { - notificationId: notification.id, // + notificationId: notification.id, userId: notification.userId, senderId: notification.senderId, type: notification.type, From 6e62e6f3ef84a48d404c945aea74879d38cdd7e5 Mon Sep 17 00:00:00 2001 From: Ss0Mae Date: Wed, 5 Feb 2025 13:42:09 +0900 Subject: [PATCH 07/35] =?UTF-8?q?[Fix]=20DB=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ff183b7..bec96d2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -229,7 +229,7 @@ model ProjectPost { id Int @id @default(autoincrement()) user_id Int title String - content String @db.Text + content String @db.LongText thumbnail_url String? role String start_date DateTime From 7faaa71c7f56dcdfa5ee978b65c25c60640d02cd Mon Sep 17 00:00:00 2001 From: Ss0Mae Date: Wed, 5 Feb 2025 13:52:16 +0900 Subject: [PATCH 08/35] =?UTF-8?q?[Fix]=20DB=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/project/dto/CreateProject.dto.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/project/dto/CreateProject.dto.ts b/src/modules/project/dto/CreateProject.dto.ts index 1f7682f..6278ceb 100644 --- a/src/modules/project/dto/CreateProject.dto.ts +++ b/src/modules/project/dto/CreateProject.dto.ts @@ -14,7 +14,6 @@ export class CreateProjectDto { title: string; @IsString() - @Length(1, 500) content: string; @IsString() From 54434ca8b9d799730586100156e02e84e44e9b32 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Mon, 10 Feb 2025 17:29:00 +0900 Subject: [PATCH 09/35] =?UTF-8?q?[Refactor]=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=AA=85=EC=8B=9C=20=EA=B5=AC=EC=B2=B4=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 3e6dcb2..38065d6 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -22,7 +22,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { // 유저 소켓 접속 async handleConnection(client: Socket) { - const userId = +client.handshake.query.userId; + const userId = Number(client.handshake.query.userId); client.data.userId = userId; // userId 넘버로 저장 // 유저 온라인 -> DB에 저장 From 363e1cc719a415d6a10b626ab216a945297e5db1 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 12 Feb 2025 22:45:56 +0900 Subject: [PATCH 10/35] =?UTF-8?q?[Refactor]=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EC=97=90=20=ED=83=80=EC=9E=85=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 54cf5ca..27cbe4f 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -566,14 +566,18 @@ export class ChatService { }; } - async increaseReadCount(messageId) { + async increaseReadCount(messageId: number) { await this.prisma.message.update({ where: { id: messageId }, data: { read_count: { increment: 1 } }, }); } - async setLastMessageId(userId, channelId, lastMessageId) { + async setLastMessageId( + userId: number, + channelId: number, + lastMessageId: number + ) { const exist = await this.prisma.last_message_status.findFirst({ where: { user_id: userId, channel_id: channelId }, }); @@ -595,7 +599,7 @@ export class ChatService { } // 라스트 메세지 id 조회 - async getLastMessageId(userId, channelId) { + async getLastMessageId(userId: number, channelId: number) { const lastMessageId = await this.prisma.last_message_status.findFirst({ where: { user_id: userId, From 189922661039236b4830feb014c4e5e23d8e80d7 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 12 Feb 2025 23:01:17 +0900 Subject: [PATCH 11/35] =?UTF-8?q?[Feat]=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 27cbe4f..3dcd164 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -4,12 +4,14 @@ import { GetMessageDto } from './dto/getMessage.dto'; import { SearchMessageDto } from './dto/serchMessage.dto'; import { S3Service } from '@src/s3/s3.service'; import * as fileType from 'file-type'; +import { NotificationsService } from '@src/modules/notification/notification.service'; @Injectable() export class ChatService { constructor( private readonly prisma: PrismaService, - private readonly s3: S3Service + private readonly s3: S3Service, + private readonly notificationsService: NotificationsService ) {} // 온라인 유저 DB에 저장 @@ -651,4 +653,36 @@ export class ChatService { return result; } + + // 알림 생성 및 전송 + async handleChatNotices( + sender, + targetUserId: number, + type: string, + message: string + ) { + // 알림 생성 및 DB에 저장 + const createdNotification = + await this.notificationsService.createNotification( + targetUserId, + sender.userId, + type, + message + ); + + // 전송할 알림 데이터 객체 + const notificationData = { + notificationId: createdNotification.notificationId, // 포함된 notificationId + type: 'privateChat', + message, + senderNickname: sender.nickname, + senderProfileUrl: sender.profileUrl, + }; + + // SSE를 통해 실시간 알림 전송 + this.notificationsService.sendRealTimeNotification( + targetUserId, + notificationData + ); + } } From a13e993bc4aa4fc52f580525617074b967a7343a Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 12 Feb 2025 23:02:33 +0900 Subject: [PATCH 12/35] =?UTF-8?q?[Refactor]=20=EA=B0=9C=EC=9D=B8=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=83=9D=EC=84=B1=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=95=B8=EB=93=A4=EB=9F=AC=EB=A1=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.gateway.ts | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 38065d6..da94033 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -90,33 +90,15 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { console.log(`User ${userId2} is not connected.`); } - // 알람 기능 + // 알림 const sender = await this.chatService.getSenderProfile(userId1); - const message = `${sender.nickname}님과의 개인 채팅방이 생성되었습니다.`; - // 알람 DB에 저장 - const createdNotification = - await this.notificationService.createNotification( - userId2, - userId1, - 'privateChat', - message - ); - - // 전송할 알림 데이터 객체 - const notificationData = { - notificationId: createdNotification.notificationId, // 포함된 notificationId - type: 'privateChat', - message, - senderNickname: sender.nickname, - senderProfileUrl: sender.profileUrl, - }; - - // SSE를 통해 실시간 알림 전송 - this.notificationService.sendRealTimeNotification( + await this.chatService.handleChatNotices( + sender, userId2, - notificationData + 'privateChat', + message ); // 클라이언트에 채널id 전달 From 4b420a630b15efda64d3bb5fbe43e7bd81739a0c Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 12 Feb 2025 23:05:42 +0900 Subject: [PATCH 13/35] =?UTF-8?q?[Refactor]=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EC=83=9D=EC=84=B1=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=95=B8=EB=93=A4=EB=9F=AC=EB=A1=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.gateway.ts | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index da94033..3b9ff3d 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -155,31 +155,16 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { console.log('모든 유저가 오프라인 상태입니다.'); } + // 알림 const sender = await this.chatService.getSenderProfile(userId); const message = `${sender.nickname}님이 단체 채팅방을 생성했습니다.`; groupMemberIds.forEach(async memberId => { - const createdNotification = - await this.notificationService.createNotification( - memberId, - userId, - 'groupChat', - message - ); - - // 전송할 알림 데이터 객체 - const notificationData = { - notificationId: createdNotification.notificationId, // 포함된 notificationId - type: 'groupChat', - message, - senderNickname: sender.nickname, - senderProfileUrl: sender.profileUrl, - }; - - // SSE를 통해 실시간 알림 전송 - this.notificationService.sendRealTimeNotification( + await this.chatService.handleChatNotices( + sender, memberId, - notificationData + 'groupChat', + message ); }); From 77d1ac58f01e74508edbdae47e61c67c33ab5a4b Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 12 Feb 2025 23:07:49 +0900 Subject: [PATCH 14/35] =?UTF-8?q?[Refactor]=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EC=95=8C=EB=A6=BC=20=ED=95=B8=EB=93=A4=EB=9F=AC=EB=A1=9C=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.gateway.ts | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 3b9ff3d..85521c1 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -270,7 +270,6 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { date, readCount: messageData.read_count, }; - console.log(sendData); // 오프라인 유저들에게 알람 const offlineUsers = await this.chatService.getChannelOfflineUsers( @@ -281,23 +280,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { const message = '새로운 메세지가 있습니다.'; offlineUsers.forEach(async id => { - const createdNotification = - await this.notificationService.createNotification( - id, - userId, - 'groupChat', - message - ); - - const notificationData = { - notificationId: createdNotification.notificationId, // 포함된 notificationId - type: 'groupChat', - message, - senderNickname: user.nickname, - senderProfileUrl: user.profileUrl, - }; - - this.notificationService.sendRealTimeNotification(id, notificationData); + await this.chatService.handleChatNotices(user, id, 'message', message); }); } From fec276cf89aa98864a04cf3e1a8b616b2a80c7a7 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 13 Feb 2025 23:18:49 +0900 Subject: [PATCH 15/35] =?UTF-8?q?[Fix]=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95;?= =?UTF-8?q?=20=EC=BB=A4=EC=84=9C=EA=B0=92=20=EC=97=86=EC=9D=84=20=EC=8B=9C?= =?UTF-8?q?(=EC=B2=AB=20=EA=B2=80=EC=83=89=20=EC=8B=9C)=20=EC=BB=A4?= =?UTF-8?q?=EC=84=9C=EB=A5=BC=20=EC=B1=84=EB=84=90=20=EB=A7=88=EC=A7=80?= =?UTF-8?q?=EB=A7=89=20=EB=A9=94=EC=84=B8=EC=A7=80=EC=9D=98=20id=20+=201?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EC=A0=95=ED=95=B4=20=EB=A7=88=EC=A7=80?= =?UTF-8?q?=EB=A7=89=20=EB=A9=94=EC=84=B8=EC=A7=80=EB=8A=94=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=95=88=EB=90=98=EB=8A=94=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 3dcd164..3b6af14 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -394,7 +394,7 @@ export class ChatService { select: { id: true }, }); direction = 'backward'; - cursor = res.id; + cursor = res.id + 1; } // 키워드에 해당하는 메세지id 검색 From 7ed33057f0a75157849b2b98525a438576e87e0c Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 19 Feb 2025 22:19:34 +0900 Subject: [PATCH 16/35] =?UTF-8?q?[Feat]=20=EC=B1=84=EB=84=90=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20direction?= =?UTF-8?q?=20=EC=83=81=EA=B4=80=EC=97=86=EC=9D=B4=20prev,=20next=20?= =?UTF-8?q?=EB=91=98=EB=8B=A4=20=EC=A0=84=EB=8B=AC=20=EB=B0=8F=20prev=20/?= =?UTF-8?q?=20next=20=EC=9C=84=EC=B9=98=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 3b6af14..b387cee 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -352,14 +352,11 @@ export class ChatService { !cursor || direction == 'backward' ? data.reverse() : data; // 커서 - const cursors = - direction == 'backward' - ? { prev: data[0] ? data[0].messageId : null } - : { - next: data[data.length - 1] - ? data[data.length - 1].messageId - : null, - }; + const cursors = { + next: data[0] ? data[0].messageId : null, + + prev: data[data.length - 1] ? data[data.length - 1].messageId : null, + }; // 응답 메세지 const message = { From 523f967d930d658622efe659b62d69ad1eae4b07 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 19 Feb 2025 22:25:32 +0900 Subject: [PATCH 17/35] =?UTF-8?q?[Feat]=20GetMessageDto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95;=20cursor=20=EB=8C=80=EC=8B=A0=20prev,=20next=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/dto/getMessage.dto.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/chat/dto/getMessage.dto.ts b/src/chat/dto/getMessage.dto.ts index 4da4eb2..da83010 100644 --- a/src/chat/dto/getMessage.dto.ts +++ b/src/chat/dto/getMessage.dto.ts @@ -15,7 +15,11 @@ export class GetMessageDto { @IsOptional() @Type(() => Number) - cursor?: number; + prev?: number; + + @IsOptional() + @Type(() => Number) + next?: number; @IsString({ message: 'direction은 문자타입으로 주어져야 합니다' }) @IsNotEmpty({ message: 'direction을 입력해주세요' }) From 34d48bc5f03a7a554ce8edb9b05bdd7a75a1d695 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 19 Feb 2025 22:43:28 +0900 Subject: [PATCH 18/35] =?UTF-8?q?[Feat]=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C/=EA=B2=80=EC=83=89=EC=97=90=20cursor=20?= =?UTF-8?q?=EB=8C=80=EC=8B=A0=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EB=A1=9C=20prev,=20next=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index b387cee..2781b3b 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -308,10 +308,13 @@ export class ChatService { getMessageDto: GetMessageDto ) { try { - const { cursor, limit, direction } = getMessageDto; + const { prev, next, limit, direction } = getMessageDto; // 권한 확인 await this.confirmAuth(userId, channelId); + let cursor; + if (direction == 'forward' && prev) cursor = prev; + else if (direction == 'backward' && next) cursor = next; // 메세지 데이터 조회 const result = await this.prisma.message.findMany({ orderBy: { @@ -378,9 +381,11 @@ export class ChatService { searchMessageDto: SearchMessageDto ) { try { - const { limit, keyword } = searchMessageDto; - let { cursor, direction } = searchMessageDto; - + const { prev, next, limit, keyword } = searchMessageDto; + let { direction } = searchMessageDto; + let cursor; + if (direction == 'forward' && prev) cursor = prev; + else if (direction == 'backward' && next) cursor = next; // 권한 확인 await this.confirmAuth(userId, channelId); @@ -439,9 +444,9 @@ export class ChatService { // 무한 스크롤용 커서 데이터 const cursors = { // backward 무한스크롤 요청 커서 - prev: forwardIds.length ? forwardIds[0] : null, + next: forwardIds.length ? forwardIds[0] : null, // forward 무한스크롤 요청 커서 - next: backwordIds.length ? backwordIds[backwordIds.length - 1] : null, + prev: backwordIds.length ? backwordIds[backwordIds.length - 1] : null, // 검색 메세지 아이디 커서 search, }; From 1a29fa216b8aa432203ccd6066af1024a9f83c62 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 20 Feb 2025 11:29:19 +0900 Subject: [PATCH 19/35] =?UTF-8?q?[Feat]=20SearchMessageDto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95;=20cursor=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/dto/serchMessage.dto.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/chat/dto/serchMessage.dto.ts b/src/chat/dto/serchMessage.dto.ts index 2b3b1a1..6827342 100644 --- a/src/chat/dto/serchMessage.dto.ts +++ b/src/chat/dto/serchMessage.dto.ts @@ -1,7 +1,12 @@ -import { IsNotEmpty } from 'class-validator'; +import { IsNotEmpty, IsOptional } from 'class-validator'; import { GetMessageDto } from './getMessage.dto'; +import { Type } from 'class-transformer'; export class SearchMessageDto extends GetMessageDto { + @IsOptional() + @Type(() => Number) + cursor?: number; + @IsNotEmpty({ message: '검색어를 입력해주세요' }) keyword: any; } From 15aa94a4a00bef8d54f81d9fe01ec819c370761a Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 20 Feb 2025 11:29:35 +0900 Subject: [PATCH 20/35] =?UTF-8?q?[Fix]=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EC=8B=9C=20=EC=BB=A4=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 2781b3b..339571a 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -381,11 +381,9 @@ export class ChatService { searchMessageDto: SearchMessageDto ) { try { - const { prev, next, limit, keyword } = searchMessageDto; - let { direction } = searchMessageDto; - let cursor; - if (direction == 'forward' && prev) cursor = prev; - else if (direction == 'backward' && next) cursor = next; + const { limit, keyword } = searchMessageDto; + let { cursor, direction } = searchMessageDto; + // 권한 확인 await this.confirmAuth(userId, channelId); From 7a1a268ae5f6fa6a90d442ac3808f9088ced882b Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Fri, 28 Feb 2025 17:41:42 +0900 Subject: [PATCH 21/35] =?UTF-8?q?[Refactor]=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EA=B2=80=EC=83=89=EA=B0=92=20=EC=97=86=EC=9D=84=20=EC=8B=9C?= =?UTF-8?q?=20404=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 339571a..1d60e36 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -411,8 +411,10 @@ export class ChatService { }); if (!keywordMessage) { - const message = { code: 404, text: '메세지를 찾을 수 없습니다' }; - return { message }; + throw new HttpException( + '메세지를 찾을 수 없습니다', + HttpStatus.NOT_FOUND + ); } // 키워드 메세지 커서 설정 @@ -456,7 +458,7 @@ export class ChatService { return { messages, cursors, message }; } catch (err) { - return err; + throw err; } } From a682f44de594c3fd29a8927a44cf1d20e7fb29b6 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 5 Mar 2025 03:18:22 +0900 Subject: [PATCH 22/35] =?UTF-8?q?[Refactor]=20broadcastChannelJoined=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=88=98=EC=8B=A0=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 85521c1..b8eddbf 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -214,7 +214,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { // 클라이언트에 채널 객체 전달 client.emit('channelJoined', channel); - this.server.to(channelId.toString()).emit('broadcastChannelJoined'); + client.broadcast.to(channelId.toString()).emit('broadcastChannelJoined'); } // 메세지 송수신 From 3a5f04ced3acdac9b34ab98ad8ce62c201a41366 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 5 Mar 2025 03:21:00 +0900 Subject: [PATCH 23/35] =?UTF-8?q?[Refactor]=20=EB=B9=84=EB=8F=99=EA=B8=B0?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 1d60e36..5c58730 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -514,8 +514,8 @@ export class ChatService { user_id: userId, }, }); - const userData = this.getSenderProfile(userId); - const nickname = (await userData).nickname; + const userData = await this.getSenderProfile(userId); + const nickname = userData.nickname; const data = { type: 'exit', From abd93d120d17fd5842bd03ba18fabdd44bd57235 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Wed, 5 Mar 2025 03:28:57 +0900 Subject: [PATCH 24/35] =?UTF-8?q?[Feat]=20exitChannel=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=9C=EC=83=9D=20=EC=8B=9C=20=EB=82=98=EA=B0=84?= =?UTF-8?q?=20=EC=9C=A0=EC=A0=80=EC=9D=98=20last=5Fmessage=5Fid=20=20~=20?= =?UTF-8?q?=EA=B0=80=EC=9E=A5=20=EC=B5=9C=EA=B7=BC=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20read=5Fcount=20=EA=B0=90=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 5c58730..31e1774 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -528,6 +528,13 @@ export class ChatService { data, }); + const lastMessage = await this.getLastMessageId(userId, channelId); + + await this.prisma.message.updateMany({ + where: { id: { gte: lastMessage.last_message_id } }, + data: { read_count: { decrement: 1 } }, + }); + return { userId, type: msg.type, From c8b12a885dd116c6c4f0b9238661228cad64fcca Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 6 Mar 2025 14:45:24 +0900 Subject: [PATCH 25/35] =?UTF-8?q?[Fix]=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EC=B1=84=ED=8C=85=20=EB=82=98=EA=B0=88=EB=95=8C=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EB=A6=AC=EB=93=9C=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=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/chat/chat.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 31e1774..a10f98c 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -531,8 +531,8 @@ export class ChatService { const lastMessage = await this.getLastMessageId(userId, channelId); await this.prisma.message.updateMany({ - where: { id: { gte: lastMessage.last_message_id } }, - data: { read_count: { decrement: 1 } }, + where: { id: { gt: lastMessage.last_message_id || 0 } }, + data: { read_count: { increment: 1 } }, }); return { From ea85400a7ad0a0aaab12a008df96d6694ed54554 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 6 Mar 2025 15:00:52 +0900 Subject: [PATCH 26/35] =?UTF-8?q?[Fix]=20=EC=A6=9D=EA=B0=80=20->=20?= =?UTF-8?q?=EA=B0=90=EC=86=8C=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index a10f98c..52f166e 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -532,7 +532,7 @@ export class ChatService { await this.prisma.message.updateMany({ where: { id: { gt: lastMessage.last_message_id || 0 } }, - data: { read_count: { increment: 1 } }, + data: { read_count: { decrement: 1 } }, }); return { From bd036d4db5191f3a1db18349870550cbd10be8cd Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 6 Mar 2025 17:02:05 +0900 Subject: [PATCH 27/35] =?UTF-8?q?[Fix]=20=EB=A6=AC=EB=93=9C=20=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 52f166e..c866a98 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -531,7 +531,10 @@ export class ChatService { const lastMessage = await this.getLastMessageId(userId, channelId); await this.prisma.message.updateMany({ - where: { id: { gt: lastMessage.last_message_id || 0 } }, + where: { + id: { lt: lastMessage.last_message_id }, + channel_id: channelId, + }, data: { read_count: { decrement: 1 } }, }); From 07e02ec4d7b94b067d4f3537e07106c859642d67 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 6 Mar 2025 17:02:18 +0900 Subject: [PATCH 28/35] =?UTF-8?q?[Feat]=20DTO=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/dto/getMessage.dto.ts | 6 +----- src/chat/dto/serchMessage.dto.ts | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/chat/dto/getMessage.dto.ts b/src/chat/dto/getMessage.dto.ts index da83010..4da4eb2 100644 --- a/src/chat/dto/getMessage.dto.ts +++ b/src/chat/dto/getMessage.dto.ts @@ -15,11 +15,7 @@ export class GetMessageDto { @IsOptional() @Type(() => Number) - prev?: number; - - @IsOptional() - @Type(() => Number) - next?: number; + cursor?: number; @IsString({ message: 'direction은 문자타입으로 주어져야 합니다' }) @IsNotEmpty({ message: 'direction을 입력해주세요' }) diff --git a/src/chat/dto/serchMessage.dto.ts b/src/chat/dto/serchMessage.dto.ts index 6827342..2b3b1a1 100644 --- a/src/chat/dto/serchMessage.dto.ts +++ b/src/chat/dto/serchMessage.dto.ts @@ -1,12 +1,7 @@ -import { IsNotEmpty, IsOptional } from 'class-validator'; +import { IsNotEmpty } from 'class-validator'; import { GetMessageDto } from './getMessage.dto'; -import { Type } from 'class-transformer'; export class SearchMessageDto extends GetMessageDto { - @IsOptional() - @Type(() => Number) - cursor?: number; - @IsNotEmpty({ message: '검색어를 입력해주세요' }) keyword: any; } From b289d1f61bca4d9aea537eecda5a1cce0bad5fd4 Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 6 Mar 2025 17:23:44 +0900 Subject: [PATCH 29/35] =?UTF-8?q?[Refactor]=20=EC=B1=84=EB=84=90=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index c866a98..9ac7bf7 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -308,13 +308,10 @@ export class ChatService { getMessageDto: GetMessageDto ) { try { - const { prev, next, limit, direction } = getMessageDto; + const { cursor, limit, direction } = getMessageDto; // 권한 확인 await this.confirmAuth(userId, channelId); - let cursor; - if (direction == 'forward' && prev) cursor = prev; - else if (direction == 'backward' && next) cursor = next; // 메세지 데이터 조회 const result = await this.prisma.message.findMany({ orderBy: { @@ -354,12 +351,14 @@ export class ChatService { const messages = !cursor || direction == 'backward' ? data.reverse() : data; - // 커서 - const cursors = { - next: data[0] ? data[0].messageId : null, - - prev: data[data.length - 1] ? data[data.length - 1].messageId : null, - }; + const cursors = + direction == 'backward' + ? data[data.length - 1] + ? data[data.length - 1].messageId + : null + : data[0] + ? data[0].messageId + : null; // 응답 메세지 const message = { From 67c21c33e5c95e86ee3c0d2539c71a9e386a5c9e Mon Sep 17 00:00:00 2001 From: Dong Hyun Date: Thu, 6 Mar 2025 17:24:30 +0900 Subject: [PATCH 30/35] =?UTF-8?q?[Refactor]=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EA=B2=80=EC=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.service.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 9ac7bf7..3b2b8da 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -441,14 +441,7 @@ export class ChatService { const messages = await this.getMessageById(ids); // 무한 스크롤용 커서 데이터 - const cursors = { - // backward 무한스크롤 요청 커서 - next: forwardIds.length ? forwardIds[0] : null, - // forward 무한스크롤 요청 커서 - prev: backwordIds.length ? backwordIds[backwordIds.length - 1] : null, - // 검색 메세지 아이디 커서 - search, - }; + const cursors = search; const message = { code: 200, From 4e154ceedcd533b8d834aa588b7275ac3ceeac19 Mon Sep 17 00:00:00 2001 From: JaeHye0k <55015406+JaeHye0k@users.noreply.github.com> Date: Wed, 12 Mar 2025 21:19:25 +0900 Subject: [PATCH 31/35] =?UTF-8?q?[Refactor]=20cursors=20->=20cursor=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20cursors=20=EC=82=BC=ED=95=AD?= =?UTF-8?q?=20=EC=97=B0=EC=82=B0=EC=9E=90=20=EC=A1=B0=EA=B1=B4=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EA=B2=B0=EA=B3=BC=20=20=EC=88=9C=EC=84=9C?= =?UTF-8?q?=20=EB=B0=98=EB=8C=80=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.gateway.ts | 1 + src/chat/chat.service.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index b8eddbf..a083f90 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -317,6 +317,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { } ) { const { userId, channelId, messageId } = data; + // 이미 읽은 메시지는 카운트되지 않도록 하는 로직 필요 await this.chatService.increaseReadCount(messageId); await this.chatService.setLastMessageId(userId, channelId, messageId); this.server.to(data.channelId.toString()).emit('readCounted', messageId); diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 3b2b8da..403d25b 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -353,11 +353,11 @@ export class ChatService { const cursors = direction == 'backward' - ? data[data.length - 1] - ? data[data.length - 1].messageId - : null - : data[0] + ? data[0] ? data[0].messageId + : null + : data[data.length - 1] + ? data[data.length - 1].messageId : null; // 응답 메세지 @@ -367,7 +367,7 @@ export class ChatService { }; // 응답데이터 {메세지데이터, 커서, 응답메세지} - return { messages, cursors, message }; + return { messages, cursor: cursors, message }; } catch (err) { return err.message; } @@ -448,7 +448,7 @@ export class ChatService { message: '데이터 패칭 성공', }; - return { messages, cursors, message }; + return { messages, cursor: cursors, message }; } catch (err) { throw err; } From 26d9d301f7dd06be0ab23d1df8b402404675f19c Mon Sep 17 00:00:00 2001 From: JaeHye0k <55015406+JaeHye0k@users.noreply.github.com> Date: Wed, 12 Mar 2025 21:48:21 +0900 Subject: [PATCH 32/35] =?UTF-8?q?[Refactor]=20data.reverse=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이미 DB에서 조회할 때 orderBy 를 통해 정렬을 수행하므로 다시 뒤집을 필요가 없다고 판단하여 제거. --- src/chat/chat.service.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 403d25b..7efd540 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -316,7 +316,7 @@ export class ChatService { const result = await this.prisma.message.findMany({ orderBy: { // 커서값이 없다면(초기요청) direction 상관없이 desc 정렬 - id: cursor ? (direction == 'forward' ? 'asc' : 'desc') : 'desc', + id: cursor || direction === 'backward' ? 'desc' : 'asc', }, where: cursor ? { @@ -345,19 +345,15 @@ export class ChatService { }); // 메세지 데이터 양식화 - const data = await this.getMessageObj(result); - - // 메세지 데이터, 메세지 id순 오름차순 정렬 - const messages = - !cursor || direction == 'backward' ? data.reverse() : data; + const messages = await this.getMessageObj(result); const cursors = direction == 'backward' - ? data[0] - ? data[0].messageId + ? messages[messages.length - 1] + ? messages[messages.length - 1].messageId : null - : data[data.length - 1] - ? data[data.length - 1].messageId + : messages[0] + ? messages[0].messageId : null; // 응답 메세지 From 6473742d8533f3835e75f895bcc5ff0ff99e099a Mon Sep 17 00:00:00 2001 From: JaeHye0k <55015406+JaeHye0k@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:29:01 +0900 Subject: [PATCH 33/35] =?UTF-8?q?[Refactor]=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/chat.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index a083f90..21bde86 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -214,6 +214,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { // 클라이언트에 채널 객체 전달 client.emit('channelJoined', channel); + console.log(client.id); client.broadcast.to(channelId.toString()).emit('broadcastChannelJoined'); } @@ -317,7 +318,6 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { } ) { const { userId, channelId, messageId } = data; - // 이미 읽은 메시지는 카운트되지 않도록 하는 로직 필요 await this.chatService.increaseReadCount(messageId); await this.chatService.setLastMessageId(userId, channelId, messageId); this.server.to(data.channelId.toString()).emit('readCounted', messageId); From 72ad6f03583b6f9ca4cba2fb8597faa935d19e3d Mon Sep 17 00:00:00 2001 From: JaeHye0k <55015406+JaeHye0k@users.noreply.github.com> Date: Thu, 13 Mar 2025 19:51:43 +0900 Subject: [PATCH 34/35] =?UTF-8?q?[Refactor]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=86=8C=EC=BC=93=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit readCounted 이벤트 emit 시 messageId 불필요함 --- src/chat/chat.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 21bde86..24b24db 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -320,6 +320,6 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { const { userId, channelId, messageId } = data; await this.chatService.increaseReadCount(messageId); await this.chatService.setLastMessageId(userId, channelId, messageId); - this.server.to(data.channelId.toString()).emit('readCounted', messageId); + this.server.to(data.channelId.toString()).emit('readCounted'); } } From 03bf61ff4d77abc6ed3e784a899a51ef2720d551 Mon Sep 17 00:00:00 2001 From: JaeHye0k <55015406+JaeHye0k@users.noreply.github.com> Date: Fri, 14 Mar 2025 11:56:09 +0900 Subject: [PATCH 35/35] =?UTF-8?q?[Fix]=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 유저가 채팅방에 한 번도 입장하지 않은 상태에서 채팅방을 나갈 경우 lastMessage 가 null 이기 때문에 에러가 발생함. --- src/chat/chat.gateway.ts | 2 +- src/chat/chat.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 24b24db..8aef61e 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -304,7 +304,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { // DB에서 유저 삭제 + 채널 탈퇴 메세지 DB 저장 후 반환 const leaveMessage = await this.chatService.deleteUser(userId, channelId); - this.server.to(channelId.toString()).emit('message', leaveMessage); + client.broadcast.to(channelId.toString()).emit('message', leaveMessage); } // 메세지 실시간 읽음처리 diff --git a/src/chat/chat.service.ts b/src/chat/chat.service.ts index 7efd540..3dbad05 100644 --- a/src/chat/chat.service.ts +++ b/src/chat/chat.service.ts @@ -520,7 +520,7 @@ export class ChatService { await this.prisma.message.updateMany({ where: { - id: { lt: lastMessage.last_message_id }, + id: { lt: lastMessage?.last_message_id || 0 }, channel_id: channelId, }, data: { read_count: { decrement: 1 } },