Skip to content

feat: 동아리별 단체 채팅 구현#209

Open
dh2906 wants to merge 36 commits intodevelopfrom
feat/group-chat-room
Open

feat: 동아리별 단체 채팅 구현#209
dh2906 wants to merge 36 commits intodevelopfrom
feat/group-chat-room

Conversation

@dh2906
Copy link
Contributor

@dh2906 dh2906 commented Feb 13, 2026

🔍 개요

  • close #이슈번호

🚀 주요 변경 내용

  • 동아리별 단체 채팅 기능을 구현했습니다.

  • 동아리를 새로 생성하면 단체 채팅방이 자동으로 만들어집니다.

  • 채팅 메시지 폴링 주기가 1초 이므로 접속 상태 TTL을 15초 -> 5초로 수정했습니다.

sequenceDiagram
    autonumber
    actor C as Client
    participant CC as ChatController (unified)
    participant US as UnifiedChatService
    participant DS as ChatService (direct)
    participant GS as GroupChatService (group)
    participant CR as ChatRoomRepository (direct)
    participant GR as GroupChatRoomRepository (group)
    Note over C,US: 1) 채팅방 목록 통합 조회 (GET /chats/rooms)
    C->>CC: GET /chats/rooms
    CC->>US: getChatRooms(userId)
    US->>DS: getChatRooms(userId)
    DS-->>US: ChatRoomsResponse (1:1)
    US->>GS: getChatRooms(userId)
    GS-->>US: GroupChatRoomsResponse (단체)
    US-->>CC: UnifiedChatRoomsResponse (병합/정렬)
    CC-->>C: 200 OK
    Note over C,US: 2) 메시지 조회 (GET /chats/rooms/{roomId}?type=DIRECT|GROUP)
    C->>CC: GET /chats/rooms/{roomId}?type=...
    CC->>US: getMessages(userId, type, roomId, page, limit)
    alt type == DIRECT
        US->>DS: getChatRoomMessages(userId, roomId, page, limit)
        DS->>CR: findById/getById + message 조회
        DS-->>US: ChatMessagesResponse
    else type == GROUP
        US->>GS: getMessagesByRoomId(roomId, userId, page, limit)
        GS->>GR: getById(roomId)
        GS-->>US: GroupChatMessagesResponse
    end
    US-->>CC: UnifiedChatMessagesResponse
    CC-->>C: 200 OK
    Note over C,US: 3) 메시지 전송 (POST /chats/rooms/{roomId}/messages?type=...)
    C->>CC: POST /chats/rooms/{roomId}/messages?type=...
    CC->>US: sendMessage(userId, type, roomId, request)
    alt type == DIRECT
        US->>DS: sendMessage(userId, roomId, request)
        DS-->>US: ChatMessageResponse
    else type == GROUP
        US->>GS: sendMessageByRoomId(roomId, userId, content)
        GS->>GR: getById(roomId)
        GS-->>US: GroupChatMessageResponse
    end
    US-->>CC: UnifiedChatMessageResponse
    CC-->>C: 200 OK
Loading

💬 참고 사항


✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

# Conflicts:
#	src/main/java/gg/agit/konect/domain/chat/controller/ChatApi.java
#	src/main/java/gg/agit/konect/domain/chat/service/ChatService.java
@dh2906 dh2906 requested a review from Copilot February 13, 2026 14:10
@dh2906 dh2906 self-assigned this Feb 13, 2026
@dh2906 dh2906 added the 기능 새로운 기능을 개발합니다. label Feb 13, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

동아리(Club) 단위의 단체 채팅을 추가하고, 기존 1:1 채팅과 단체 채팅을 단일 API에서 통합 조회/전송할 수 있도록 채팅 도메인을 direct/group/unified로 재구성한 PR입니다. 또한 메시지 폴링 주기(1초)에 맞춰 접속 상태 TTL을 단축하고, 단체 채팅 푸시 알림 전송 로직을 추가합니다.

Changes:

  • 단체 채팅용 DB 테이블/엔티티/레포지토리/서비스 추가 및 동아리 생성 시 단체 채팅방 자동 생성
  • 채팅방 목록/메시지 조회/전송 API를 DIRECT/GROUP 타입으로 통합(unified) 제공
  • Presence TTL(15s→5s) 조정 및 단체 채팅 푸시 알림 전송 기능 추가

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/main/resources/db/migration/V22__add_group_chat_tables.sql 단체 채팅 테이블 생성 및 기존 동아리/멤버 기반 초기 데이터 삽입
src/main/java/gg/agit/konect/global/code/ApiResponseCode.java 단체 채팅 접근/채팅방 미존재 응답 코드 추가
src/main/java/gg/agit/konect/domain/user/service/UserService.java direct 패키지로 이동한 채팅 모델/레포지토리 import 반영
src/main/java/gg/agit/konect/domain/notification/service/NotificationService.java 단체 채팅 푸시 알림 전송 메서드 추가 및 PresenceService import 경로 변경
src/main/java/gg/agit/konect/domain/club/service/ClubService.java 동아리 생성 시 GroupChatRoom 자동 생성
src/main/java/gg/agit/konect/domain/club/repository/ClubMemberRepository.java 동아리 가입시각 조회/동아리 멤버 userId 목록 조회 등 단체 채팅에 필요한 쿼리 추가
src/main/java/gg/agit/konect/domain/chat/unified/service/UnifiedChatService.java DIRECT/GROUP 채팅방/메시지 API 응답을 통합하여 병합/정렬/변환
src/main/java/gg/agit/konect/domain/chat/unified/service/ChatPresenceService.java 패키지 이동 및 Presence TTL 5초로 조정
src/main/java/gg/agit/konect/domain/chat/unified/enums/ChatType.java DIRECT/GROUP 타입 구분 enum 추가
src/main/java/gg/agit/konect/domain/chat/unified/dto/UnifiedChatRoomsResponse.java 통합 채팅방 리스트 응답 DTO 추가
src/main/java/gg/agit/konect/domain/chat/unified/dto/UnifiedChatRoomResponse.java 통합 채팅방 항목 DTO 추가
src/main/java/gg/agit/konect/domain/chat/unified/dto/UnifiedChatMessagesResponse.java 통합 메시지 리스트 응답 DTO 추가
src/main/java/gg/agit/konect/domain/chat/unified/dto/UnifiedChatMessageResponse.java 통합 메시지 항목 DTO 추가
src/main/java/gg/agit/konect/domain/chat/unified/controller/ChatController.java 기존 채팅 컨트롤러를 unified로 이동하고 통합 서비스 기반으로 변경
src/main/java/gg/agit/konect/domain/chat/unified/controller/ChatApi.java 통합 채팅 API 스펙에 맞게 응답/파라미터 타입 변경 및 문구 일부 수정
src/main/java/gg/agit/konect/domain/chat/group/service/GroupChatService.java 단체 채팅방 목록/메시지 조회/전송/음소거 토글 등 핵심 비즈니스 로직 추가
src/main/java/gg/agit/konect/domain/chat/group/repository/GroupChatRoomRepository.java 단체 채팅방 레포지토리 추가
src/main/java/gg/agit/konect/domain/chat/group/repository/GroupChatReadStatusRepository.java 단체 채팅 읽음 상태 레포지토리 추가
src/main/java/gg/agit/konect/domain/chat/group/repository/GroupChatNotificationSettingRepository.java 단체 채팅 알림 설정(음소거) 레포지토리 추가
src/main/java/gg/agit/konect/domain/chat/group/repository/GroupChatMessageRepository.java 단체 채팅 메시지 레포지토리 추가
src/main/java/gg/agit/konect/domain/chat/group/model/GroupChatRoom.java 단체 채팅방 엔티티 추가
src/main/java/gg/agit/konect/domain/chat/group/model/GroupChatReadStatusId.java 읽음 상태 복합키 IdClass 추가
src/main/java/gg/agit/konect/domain/chat/group/model/GroupChatReadStatus.java 읽음 상태 엔티티 추가
src/main/java/gg/agit/konect/domain/chat/group/model/GroupChatNotificationSetting.java 음소거 설정 엔티티 추가
src/main/java/gg/agit/konect/domain/chat/group/model/GroupChatMessage.java 단체 채팅 메시지 엔티티 추가
src/main/java/gg/agit/konect/domain/chat/group/dto/GroupChatRoomsResponse.java 단체 채팅방 리스트 응답 DTO 추가
src/main/java/gg/agit/konect/domain/chat/group/dto/GroupChatRoomResponse.java 단체 채팅방 ID 응답 DTO 추가
src/main/java/gg/agit/konect/domain/chat/group/dto/GroupChatMuteResponse.java 음소거 상태 응답 DTO 추가
src/main/java/gg/agit/konect/domain/chat/group/dto/GroupChatMessagesResponse.java 단체 메시지 리스트 응답 DTO 추가
src/main/java/gg/agit/konect/domain/chat/group/dto/GroupChatMessageResponse.java 단체 메시지 응답 DTO 추가
src/main/java/gg/agit/konect/domain/chat/direct/service/ChatService.java 기존 ChatService를 direct로 이동 및 import/타입 참조 갱신
src/main/java/gg/agit/konect/domain/chat/direct/repository/ChatRoomRepository.java direct 패키지로 이동 및 model import 갱신
src/main/java/gg/agit/konect/domain/chat/direct/repository/ChatMessageRepository.java direct 패키지로 이동 및 DTO/model import/JPQL 타입 갱신
src/main/java/gg/agit/konect/domain/chat/direct/model/ChatRoom.java direct 패키지로 이동
src/main/java/gg/agit/konect/domain/chat/direct/model/ChatMessage.java direct 패키지로 이동
src/main/java/gg/agit/konect/domain/chat/direct/dto/UnreadMessageCount.java direct 패키지로 이동
src/main/java/gg/agit/konect/domain/chat/direct/dto/ChatRoomsResponse.java direct 패키지로 이동 및 model import 갱신
src/main/java/gg/agit/konect/domain/chat/direct/dto/ChatRoomResponse.java direct 패키지로 이동 및 model import 갱신
src/main/java/gg/agit/konect/domain/chat/direct/dto/ChatRoomCreateRequest.java direct 패키지로 이동
src/main/java/gg/agit/konect/domain/chat/direct/dto/ChatMessagesResponse.java direct 패키지로 이동 및 model import 갱신
src/main/java/gg/agit/konect/domain/chat/direct/dto/ChatMessageSendRequest.java direct 패키지로 이동
src/main/java/gg/agit/konect/domain/chat/direct/dto/ChatMessageResponse.java direct 패키지로 이동 및 model import 갱신
Comments suppressed due to low confidence (3)

src/main/java/gg/agit/konect/domain/chat/unified/controller/ChatApi.java:72

  • Unified 엔드포인트에서 type(DIRECT|GROUP) 파라미터가 필수로 추가됐는데, Operation 설명의 에러 섹션이 아직 FORBIDDEN_CHAT_ROOM_ACCESS만 언급하고 있습니다. GROUP 타입일 때의 에러 코드(예: FORBIDDEN_GROUP_CHAT_ACCESS / NOT_FOUND_GROUP_CHAT_ROOM)와 type 파라미터 의미를 문서에 반영해 주세요.
    src/main/java/gg/agit/konect/domain/chat/unified/controller/ChatApi.java:94
  • sendMessage Operation 설명이 1:1 채팅 전제를 포함하고 있고(수신자 자동 설정 등), GROUP 타입에서는 동작이 다릅니다. type 파라미터에 따라 동작/에러가 달라지는 점을 문서에 분리해서 명확히 해 주세요.
    src/main/java/gg/agit/konect/domain/chat/unified/service/ChatPresenceService.java:18
  • Presence Redis 키가 roomId만으로 구성되도록 설계되어 있는데(상단 PREFIX/TTL 근처), direct 채팅방 id와 group 채팅방 id가 겹치면 서로의 접속 상태가 충돌할 수 있습니다. ChatType을 키에 포함시키거나(프리픽스 분리), recordPresence/isUserInChatRoom에 type 파라미터를 추가해 채팅 타입별 네임스페이스를 분리해 주세요.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 43 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

src/main/java/gg/agit/konect/domain/chat/unified/controller/ChatApi.java:69

  • getChatRoomMessages가 DIRECT/GROUP 모두를 처리하는데 Swagger 설명의 로직/에러가 1:1 채팅 기준(FORBIDDEN_CHAT_ROOM_ACCESS 등)만 적혀 있어 오해 소지가 있습니다. type=GROUP일 때의 동작과 에러 코드(FORBIDDEN_GROUP_CHAT_ACCESS/NOT_FOUND_GROUP_CHAT_ROOM 등)를 문서에 반영해 주세요.
    src/main/java/gg/agit/konect/domain/chat/unified/controller/ChatApi.java:90
  • sendMessage 설명에 “수신자는 채팅방 상대방으로 자동 설정” 등 1:1 채팅 전제를 포함하고 있어 type=GROUP 호출 시와 맞지 않습니다. 그룹 채팅 전송 시의 동작/응답 필드 차이를 Swagger 설명에 명확히 추가해 주세요.
    src/main/java/gg/agit/konect/domain/chat/unified/service/ChatPresenceService.java:18
  • Redis presence 키가 roomId/userId만으로 구성되어(예: chat:presence:room:{roomId}:user:{userId}) 1:1 채팅방 ID와 단체 채팅방 ID가 겹치면 서로의 접속 상태를 덮어쓰거나 잘못 감지할 수 있습니다. 채팅 타입(DIRECT/GROUP) 또는 테이블 구분자를 키에 포함하도록 prefix를 분리해 충돌을 방지해 주세요.
    src/main/java/gg/agit/konect/domain/chat/unified/controller/ChatApi.java:75
  • 통합 API로 바뀌면서 type 쿼리 파라미터가 필수가 되었는데(기존 클라이언트는 미전달 가능) 이는 하위 호환성을 깨는 변경입니다. 가능하면 type에 기본값(DIRECT)을 두거나 required=false 처리, 혹은 별도 엔드포인트로 분리해 호환성을 유지해 주세요.

Comment on lines +213 to +217
groupChatReadStatusRepository.findByRoomIdAndUserId(roomId, userId)
.ifPresentOrElse(status -> {
status.updateLastReadAt(lastReadAt);
}, () -> {
GroupChatRoom room = groupChatRoomRepository.getById(roomId);
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateLastReadAt에서 상태가 없을 때 조회 후 INSERT를 수행해 동시 요청 시 PK(room_id,user_id) 중복으로 저장이 실패할 수 있습니다. DB upsert 사용 또는 DataIntegrityViolation 처리 후 재조회하는 방식으로 경쟁 조건을 안전하게 처리해 주세요.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

기능 새로운 기능을 개발합니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant