Conversation
- ChatService에 남아 있던 messageId 접근 검증과 페이지 계산을 별도 서비스로 분리 - 권한 없음과 메시지 미존재를 같은 404로 처리하는 조회 정책을 한곳에 모음 - 기존 ChatService 단위 테스트가 실제 resolver를 사용하도록 구성해 기존 정책 검증을 유지
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughChatService에서 메시지 접근·가시성·페이지 계산 책임을 분리하여 신규 서비스들로 위임했습니다: 메시지 페이지 계산은 Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant ChatService
participant ChatMessagePageResolver as Resolver
participant ChatRoomSystemAdminService as AdminSvc
participant MessageRepo
participant MessageCountRepo
Client->>ChatService: getMessages(roomId, messageId, ...)
ChatService->>Resolver: resolvePageForMessage(roomId, messageId, room, user, limit)
Resolver->>AdminSvc: isSystemAdminRoom(roomId)?
alt club/group room
Resolver->>ChatRoomMembershipRepo: validate club membership
else direct/normal room
Resolver->>ChatRoomMembershipRepo: validate membership (non-left)
end
Resolver->>MessageRepo: findByIdWithChatRoom(messageId)
Resolver->>AdminSvc: findSystemAdminMember(members) (if direct)
Resolver->>MessageCountRepo: count newer messages with visibleMessageFrom cutoff
MessageCountRepo-->>Resolver: newerCount
Resolver-->>ChatService: pageIndex
ChatService-->>Client: return messages page
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested labels
개요 (업데이트된 기존 섹션)채팅 메시지 페이지 해석 및 접근 제어 로직을 변경 사항 (업데이트된 기존 표)
예상 검토 소요 시간 (업데이트된 기존 값)🎯 4 (Complex) | ⏱️ ~45 minutes
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧪 JaCoCo Coverage Report (Changed Files)Summary
Coverage by File
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java`:
- Around line 63-105: ensureMessageLookupAccess and resolveVisibleMessageFrom
perform duplicate DB lookups for the same (roomId, userId) and repeat
findRoomMemberIdsByChatRoomIds for admin/system-room checks; modify
resolveVisibleMessageFrom to accept the already-fetched ChatRoomMember Optional
(or the List<ChatRoomMember> for admin path) instead of querying inside it, and
have ensureMessageLookupAccess return or forward that Optional/List to
resolveVisibleMessageFrom so you reuse the result from
chatRoomMemberRepository.findByChatRoomIdAndUserId(...) (and
chatRoomMemberRepository.findByChatRoomId(...) in the admin branch) rather than
calling those repository methods again. Ensure method signatures updated (e.g.,
resolveVisibleMessageFrom(ChatRoom, User, Optional<ChatRoomMember>) or
resolveVisibleMessageFrom(ChatRoom, User, List<ChatRoomMember>)) and update
callers accordingly to remove the duplicated DB calls.
- Around line 107-121: The two private helpers isSystemAdminRoom and
findRoomMember in ChatMessagePageResolver duplicate logic already present in
ChatService; refactor by removing these methods from ChatMessagePageResolver and
delegating their behavior to the single authoritative implementation in
ChatService (or extract a new shared component like ChatRoomMembershipService
and call that from both places). Locate references to isSystemAdminRoom and
findRoomMember in ChatMessagePageResolver, replace direct calls to the removed
methods with calls to ChatService's corresponding methods (or the new shared
service), and update imports/constructor injection to obtain ChatService or the
new shared service so the resolver uses the single source of truth.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5c732b5b-87d7-4f0a-9b1f-98976a46a4f9
📒 Files selected for processing (3)
src/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: coverage
- GitHub Check: Code Style Check
- GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.java
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.java: Java 코드에서 import로 해결할 수 있는 경우 FQCN(Full Qualified Class Name)을 사용하지 않도록 지적한다
JPA/QueryDSL 조회 변경 시 N+1, 잘못된 fetch join, count 쿼리 왜곡, pagination 깨짐, distinct 누락을 확인한다
권한 로직은 관리자 우회, 요청자와 대상자 관계, 클럽/채팅방/공지/일정의 소속 검증이 빠지지 않았는지 확인한다
soft delete, 탈퇴 사용자, 차단/제외 조건, 중복 제거가 필요한 조회에서는 응답에 노출되면 안 되는 데이터가 포함되는지 확인한다
DTO 응답 변경은 기존 클라이언트가 기대하는 필드명, nullability, enum/string 값, 정렬 순서를 깨지 않는지 확인한다
조건이 2개 이상 결합된 비즈니스 규칙, 권한 조건, soft delete 제외, 중복 제거, fallback 우선순위, 대표값 선택, DTO 변환, count 쿼리 분리, fetch join 선택 이유처럼 코드만으로 의도가 숨겨지는 지점에는 주석을 권장한다
단순 생성자 호출, 필드 매핑, 컬렉션 반환, 이름만으로 명확한 분기에는 주석을 요구하지 않는다
Files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
**/*.{sql,java}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
데이터베이스 변경에서는 마이그레이션 순서, 기존 데이터 호환성, nullable/default 처리, 롤백 난이도, 인덱스 필요성을 확인한다
Files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
src/main/java/**/*.java
⚙️ CodeRabbit configuration file
src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.
- 코멘트는 반드시 한국어로 작성한다.
- 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
- 각 코멘트 첫 줄에 심각도를
[LEVEL: high|medium|low]형식으로 반드시 표기한다.- 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
- 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
- 가능하면 재현 조건 및 실패 시나리오도 포함한다.
- 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
- 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
- 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
- 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
- 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.
Files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
🧠 Learnings (38)
📓 Common learnings
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,page,jump}/**/*.{ts,tsx} : MessageId page calculation must validate room access permission and message visibility range before calculating pagination position to prevent information disclosure
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,service}/**/chat.service.{ts,tsx} : Chat service must be the central point where message sending, list summaries, access handling, and membership changes interact, coordinating all state updates
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,page,jump}/**/*.{ts,tsx} : MessageId page calculation must validate room access permission and message visibility range before calculating pagination position to prevent information disclosure
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message}/**/*.{ts,tsx} : When modifying message storage logic, verify that last message metadata, direct room restoration conditions, sender's read status updates, list summary sorting, inquiry room special handling, and notification/event side effects remain consistent
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,search}/**/{find,search,jump,page}*.{ts,tsx} : Permission denied (no access) must not be exposed as message not found in messageId lookup and search paths to prevent information disclosure about hidden messages
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,message,search}/**/{list,summary,search}*.{ts,tsx} : Direct chat rooms must not expose invisible messages (based on visibleMessageFrom) in list summaries or search results
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-25T06:58:54.393Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/notification/AGENTS.md:0-0
Timestamp: 2026-04-25T06:58:54.393Z
Learning: Applies to src/main/java/gg/agit/konect/domain/notification/**/*ChatPresenceService*.java : Chat presence service tracks which users are currently viewing specific chat rooms. During chat push notification sending, check presence state to exclude users already present in the target chatroom from receiving push notifications.
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-25T06:58:54.393Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/notification/AGENTS.md:0-0
Timestamp: 2026-04-25T06:58:54.393Z
Learning: Applies to src/main/java/gg/agit/konect/domain/notification/**/*.java : When modifying inbox functionality, verify: chat-related type exclusion set (`CHAT_MESSAGE`, `GROUP_CHAT_MESSAGE`, `UNREAD_CHAT_COUNT`), page size default of 20, sort order `createdAt DESC, id DESC`, userId ownership check for single read, and chat-type exclusion in bulk read operations.
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,repository}/**/*.{ts,tsx} : Repository layer must implement optimized queries for latest message retrieval, unreadCount calculation, list optimization, and distinct handling of direct vs group list logic
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,message}/**/{repository,query,list,search}*.{ts,tsx} : When modifying list/search queries, verify custom room name priority, mute status composition, search target room type scope, direct room visible message criteria, messageId page calculation sort consistency, and inquiry room list optimization query conditions remain correct
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,direct}/**/*.{ts,tsx} : Direct chat room message visibility must be determined by visibleMessageFrom combined with leftAt status, not by simple member existence
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,member,service}/**/chat-room-membership*.{ts,tsx} : ChatRoomMembershipService must handle club_group member guarantee, direct/group membership updates, inquiry room read status exceptions, and concurrent creation/duplicate membership absorption
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,member,message}/**/{unread,count}*.{ts,tsx} : UnreadCount calculation for direct chat rooms must consider both lastReadAt and actual visible message range (visibleMessageFrom), not just lastReadAt alone
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,direct}/**/{send,receive,create}*.{ts,tsx} : Direct room visibility may be restored when a new message arrives even if the receiving user has leftAt set
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,direct}/**/{send,create}*.{ts,tsx} : Sending a message in a direct room must update the sender's lastReadAt to the new message timestamp and may restore the room visibility if the sender was in leftAt state
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.java
📚 Learning: 2026-04-24T12:50:59.744Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/user/AGENTS.md:0-0
Timestamp: 2026-04-24T12:50:59.744Z
Learning: Applies to src/main/java/gg/agit/konect/domain/user/**/*user*/**/*.{ts,tsx,js,jsx} : Reuse existing direct chat room if already present; create new one only if none exists for welcome message flow
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-24T12:50:59.744Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/user/AGENTS.md:0-0
Timestamp: 2026-04-24T12:50:59.744Z
Learning: Applies to src/main/java/gg/agit/konect/domain/user/**/*user*/**/*.{ts,tsx,js,jsx} : Ensure direct chat membership before saving operator welcome message; sync saved message to `chat_room.last_message_*` using latest message conditions
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,member,message}/**/*{inquiry,admin,system}*.{ts,tsx} : Inquiry room (SYSTEM_ADMIN type) unreadCount and read status must be aggregated/updated by SYSTEM_ADMIN user record, not by individual admin users
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-13T00:26:23.225Z
Learnt from: dh2906
Repo: BCSDLab/KONECT_BACK_END PR: 533
File: src/main/java/gg/agit/konect/domain/chat/service/ChatService.java:1511-1516
Timestamp: 2026-04-13T00:26:23.225Z
Learning: In ChatService.java (Spring Boot + JPA, MySQL InnoDB), within a `Transactional(readOnly = true)` method, retrying a repository count query (e.g., `countNewerMessagesByChatRoomId`) to handle concurrent inserts is ineffective under REPEATABLE READ isolation: the same DB snapshot is used throughout the transaction, so the retry always returns the same result. A new transaction (`Propagation.REQUIRES_NEW`) would be required for a true retry, but accepting a 1-page offset as a UX tradeoff is preferred for search navigation in this codebase.
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,inquiry,admin,system}/**/{send,create}*.{ts,tsx} : Inquiry room (SYSTEM_ADMIN) message send by general admin must not add the admin as a room member and must use special handling
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/chat/**/*{inquiry,admin,system}*.{ts,tsx} : SYSTEM_ADMIN inquiry room reuse policy (SYSTEM_ADMIN + regular user pair) must not be broken by adding general admins as room members
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/chat/**/*direct*member*.{ts,tsx} : When modifying direct membership policy, verify that leftAt, visibleMessageFrom, lastReadAt states, room re-exposure conditions, past message visibility range, search/message jump visibility, and list re-exposure conditions remain consistent
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatService.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,list}/**/{list,summary}*.{ts,tsx} : Chat room list summaries must combine last message, last send time, unreadCount, room name, and mute status consistently without treating direct and group room construction separately
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,club}/**/{access,validate,query}*.{ts,tsx} : Club group message queries must verify both chat member existence AND current club membership status to determine access, not member table alone
Applied to files:
src/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.javasrc/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-25T06:58:54.393Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/notification/AGENTS.md:0-0
Timestamp: 2026-04-25T06:58:54.393Z
Learning: Applies to src/main/java/gg/agit/konect/domain/notification/**/*IntegrationTest*.java : Add regression tests for: club application event rollback resulting in zero notifications created (AFTER_COMMIT integration test), inbox list/unread count/bulk read excluding chat-related types (repository integration test), Expo push partial ticket failures logged without whole-request exception propagation, group chat token vs target user count mismatch in current policy.
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-25T06:58:54.393Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/notification/AGENTS.md:0-0
Timestamp: 2026-04-25T06:58:54.393Z
Learning: Applies to src/main/java/gg/agit/konect/domain/notification/**/*.java : When modifying chat notification logic, verify: ChatPresenceService presence filtering, NotificationMuteSetting CHAT_ROOM mute filtering, sender exclusion for group chat, Unicode code point basis (not Java string length) for 30-character preview limit, payload path format `chats/{roomId}`, and that exceptions are caught in async boundary.
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,club,member}/**/*.{ts,tsx} : When club membership changes (add/remove), chat room membership must be synchronized to maintain consistent access guarantees for club_group rooms
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T09:44:01.804Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/club/AGENTS.md:0-0
Timestamp: 2026-04-22T09:44:01.804Z
Learning: Applies to src/main/java/gg/agit/konect/domain/club/**/*ClubMemberManagement*|**/*ClubApplication*|**/*SheetImport* : When members are created through multiple paths (direct approval, pre-member conversion, sheet import), always add them to club group chat membership. When members are removed or demoted from leadership, remove their chat membership. Maintain consistency between `ClubMember` state and chat group membership.
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,message,club}/**/*.{ts,tsx} : Club group room access must validate both chat member records AND current club membership status, not just chat member table alone
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,club}/**/{send,create}*.{ts,tsx} : Club group message send must verify sender is currently a club member and may need to ensure chat room membership is guaranteed
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,group,club}/**/{name,display}*.{ts,tsx} : Club_group room default name is the club name, and general group default name is 'group chat' - these must not be confused or swapped
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/chat/**/{group,club}*.{ts,tsx} : When modifying group or club_group membership policy, ensure access permissions, member removal and kick policies, list visibility, and club membership synchronization remain consistent
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,club}/**/{create,find}*.{ts,tsx} : Concurrent room creation attempts for club_group must not produce duplicates - instead converge to the same room via re-query
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,direct,inquiry}/**/{create,find,reuse}*.{ts,tsx} : Direct room reuse must match the same two-user combination; admin direct chats must follow SYSTEM_ADMIN inquiry room reuse policy
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,message,group}/**/{send,create,validate}*.{ts,tsx} : Group room message send must reject messages from users with leftAt set (left members)
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T09:44:01.804Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/club/AGENTS.md:0-0
Timestamp: 2026-04-22T09:44:01.804Z
Learning: Applies to src/main/java/gg/agit/konect/domain/club/**/*ClubService* : Club creation is admin-exclusive. At creation, atomically: (1) save club entity, (2) create club group chat room, (3) register specified president as `PRESIDENT` member, (4) add president to chat membership, (5) create 2 default application questions: 'Phone number (required)' and 'Motivation (optional)'
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room,member}/**/*.{ts,tsx} : The direct chat room leave->restore->reopen policy must not be generalized to group or club_group room types
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
📚 Learning: 2026-04-22T08:07:59.395Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/chat/AGENTS.md:0-0
Timestamp: 2026-04-22T08:07:59.395Z
Learning: Applies to src/main/java/gg/agit/konect/domain/chat/**/{chat,room}/**/*group*.{ts,tsx} : Group chat room reuse must be based on requester-excluding non-duplicate user sets as the key, not including the requester themselves
Applied to files:
src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java
There was a problem hiding this comment.
Pull request overview
messageId 기반 메시지 조회에서 접근 검증/visibleMessageFrom 검증/페이지 계산 책임을 ChatService에서 분리해 읽기 흐름을 명확히 하는 리팩터링입니다(#592 1차 작업). 기존 조회 정책을 유지하면서, 관련 로직을 ChatMessagePageResolver로 위임해 ChatService의 복잡도를 낮춥니다.
Changes:
ChatMessagePageResolver를 추가하여 messageId 기반 조회 시 접근 검증, 가시성 검증, 페이지 계산을 담당ChatService.getMessages()에서 messageId 케이스의 페이지 계산을 resolver로 위임ChatServiceTest가 resolver 실구현을 사용하도록 테스트 구성 변경(수동 생성)
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/main/java/gg/agit/konect/domain/chat/service/ChatService.java | messageId 기반 페이지 계산/접근 검증 로직을 ChatMessagePageResolver로 위임하도록 정리 |
| src/main/java/gg/agit/konect/domain/chat/service/ChatMessagePageResolver.java | messageId 기반 조회의 접근 검증 + visibleMessageFrom 검증 + 페이지 계산 로직을 신규 서비스로 분리 |
| src/test/java/gg/agit/konect/unit/domain/chat/service/ChatServiceTest.java | 단위 테스트에서 ChatService를 수동 생성하고 실제 resolver를 주입하도록 변경 |
- ChatMessagePageResolver의 방 타입별 접근 검증과 페이지 계산 분기를 단위 테스트로 보강 - direct visibleMessageFrom, system admin 문의방, club membership 예외 변환 정책을 직접 검증 - messageId 조회 전에 권한 없는 요청을 차단하는 오라클 방지 흐름을 유지
- 리뷰에서 지적된 ChatMessagePageResolver의 중복 멤버십 조회를 제거하기 위해 접근 검증 결과를 재사용 - SYSTEM_ADMIN 방 판별과 시스템 관리자 멤버 선택을 공용 서비스로 분리해 ChatService와 MembershipService의 중복 기준을 제거 - 관련 단위 테스트를 갱신하고 공용 서비스 테스트를 추가해 문의방 조회 정책을 유지
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/main/java/gg/agit/konect/domain/chat/service/ChatService.java:1158
- getOrCreateDirectRoomMember()의 orElseGet 블록에서 if (admin && system-admin 방) 분기가 있지만, if/else 모두 동일하게 FORBIDDEN_CHAT_ROOM_ACCESS를 던지고 있어 조건문이 의미가 없습니다. 조건을 제거해 단순화하거나(항상 동일 예외라면), 분기를 유지할 의도가 있다면 각 케이스의 예외/처리 차이를 코드로 드러내는 편이 유지보수에 안전합니다.
return chatRoomMemberRepository.findByChatRoomIdAndUserId(chatRoom.getId(), user.getId())
.orElseGet(() -> {
// 어드민은 SYSTEM_ADMIN 방에 멤버로 추가되지 않음
if (user.isAdmin() && chatRoomSystemAdminService.isSystemAdminRoom(chatRoom.getId())) {
throw CustomException.of(FORBIDDEN_CHAT_ROOM_ACCESS);
}
throw CustomException.of(FORBIDDEN_CHAT_ROOM_ACCESS);
});
- SYSTEM_ADMIN 방 판별은 멤버 목록 조회 대신 존재 여부 쿼리로 처리해 불필요한 로딩을 줄임 - direct room 멤버십이 이미 확인된 경우 SYSTEM_ADMIN 방 판별을 건너뛰어 일반 admin 조회 경로의 추가 쿼리를 방지 - 동일 예외만 던지던 직접방 멤버 조회 분기를 단순화해 조건 드리프트 여지를 제거
Addressed: 동일 예외만 던지는 분기를 제거하고 orElseThrow(() -> CustomException.of(FORBIDDEN_CHAT_ROOM_ACCESS))로 단순화했습니다. |
- direct room 멤버가 없는 admin 문의방 경로에서 빈 멤버십 상태를 명시해 조건 추적을 줄임 - 이미 검증된 SYSTEM_ADMIN 방 접근 여부만 컨텍스트에 담아 기존 조회 정책은 그대로 유지
🔍 개요
🚀 주요 변경 내용
💬 참고 사항
./gradlew checkstyleMainCI=true ./gradlew test --tests 'gg.agit.konect.unit.domain.chat.service.ChatServiceTest' --rerun-tasksCI=true ./gradlew test --tests 'gg.agit.konect.integration.domain.chat.ChatApiTest' --rerun-tasks✅ Checklist (완료 조건)