Conversation
- chat_room.last_message_*가 null로 남아 채팅방 목록과 실제 메시지 상태가 어긋나는 문제를 막는다 - 메시지 저장 직후 chat_room 메타데이터를 명시적으로 업데이트해 영속성 컨텍스트 clear 경로에 의존하지 않도록 정리한다 - 회원가입 환영 메시지처럼 같은 패턴을 쓰는 경로도 함께 맞춰 신규 방의 초기 메타데이터 누락을 방지한다 - 기존 운영 데이터는 Flyway 백필 마이그레이션과 회귀 테스트로 함께 보강해 재발을 막는다
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 11 minutes and 26 seconds. ⌛ 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 (5)
📝 Walkthrough워크스루채팅 메시지 송신 시 마지막 메시지 메타데이터( 변경 사항
시퀀스 다이어그램sequenceDiagram
participant ChatSvc as ChatService
participant Entity as ChatRoom Entity
participant Repo as ChatRoomRepository
participant DB as Database
rect rgba(100, 200, 150, 0.5)
Note over ChatSvc,DB: 메시지 송신 시 마지막 메시지 동기화
ChatSvc->>Entity: room.updateLastMessage(content, sentAt)
activate Entity
Entity->>Entity: 메모리 필드 업데이트
deactivate Entity
ChatSvc->>Repo: updateLastMessage(roomId, content, sentAt)
activate Repo
Repo->>DB: UPDATE chat_room SET<br/>lastMessageContent = ?,<br/>lastMessageSentAt = ?
DB-->>Repo: 업데이트된 행 수
deactivate Repo
Repo-->>ChatSvc: 결과 반환
end
예상 코드 리뷰 노력🎯 3 (Moderate) | ⏱️ ~25분 관련 PR
추천 라벨
시
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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/repository/ChatRoomRepository.java`:
- Line 23: The `@Modifying` annotation currently uses clearAutomatically=true
which clears the persistence context and detaches the members entity loaded
earlier, so after syncLastMessage() the subsequent
restoreDirectRoomFromIncomingMessage() changes (leftAt, visibleMessageFrom,
lastReadAt) on the detached members entity are not persisted; remove the
clearAutomatically=true (or set it to false) on the `@Modifying` annotation in
ChatRoomRepository (the method annotated there that triggers
sendDirectMessage()/syncLastMessage()/restoreDirectRoomFromIncomingMessage()) so
the persistence context is not cleared and the later mutations to members remain
managed and are flushed at commit, leaving flushAutomatically as-is if you still
need immediate flushing.
In `@src/main/java/gg/agit/konect/domain/chat/service/ChatService.java`:
- Around line 1213-1217: syncLastMessage currently unconditionally writes
message metadata and can be overwritten by concurrent commits; change it to
perform a conditional DB update that only sets chat_room.last_message_* when the
stored latest is older than this message (use message.getCreatedAt() plus
message.getId() for tie-breaker) e.g. add/replace
chatRoomRepository.updateLastMessage(...) with a new repository method
(updateLastMessageIfLatest) that updates only when NOT EXISTS a message with
(createdAt > :sentAt OR (createdAt = :sentAt AND id > :messageId));
alternatively, acquire a write/row lock on the ChatRoom record before calling
room.updateLastMessage(...) and only call room.updateLastMessage(...) after the
DB update succeeds so in-memory state stays consistent with the persisted
conditional update.
🪄 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: 04889cdb-fae7-4f1a-88a6-21077cd77b6b
📒 Files selected for processing (5)
src/main/java/gg/agit/konect/domain/chat/repository/ChatRoomRepository.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/user/service/UserService.javasrc/main/resources/db/migration/V70__backfill_chat_room_last_message_metadata.sqlsrc/test/java/gg/agit/konect/integration/domain/chat/ChatApiTest.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). (2)
- GitHub Check: coverage
- GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (3)
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/user/service/UserService.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/repository/ChatRoomRepository.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/domain/user/service/UserService.javasrc/main/resources/db/migration/V70__backfill_chat_room_last_message_metadata.sqlsrc/test/java/gg/agit/konect/integration/domain/chat/ChatApiTest.javasrc/main/java/gg/agit/konect/domain/chat/service/ChatService.javasrc/main/java/gg/agit/konect/domain/chat/repository/ChatRoomRepository.java
src/main/resources/db/migration/**/*.sql
⚙️ CodeRabbit configuration file
src/main/resources/db/migration/**/*.sql: Flyway 마이그레이션 리뷰 규칙:
- 버전 파일명 규칙(V{number}__{description}.sql) 위반 여부를 우선 확인한다.
- 이미 배포된 마이그레이션 수정/재번호 부여 위험이 있으면 반드시 차단 코멘트를 남긴다.
- 파괴적 변경(drop, rename 등)은 롤백 가능성과 운영 영향 관점에서 검토한다.
Files:
src/main/resources/db/migration/V70__backfill_chat_room_last_message_metadata.sql
🧠 Learnings (1)
📚 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/repository/ChatRoomRepository.java
- sendMessage의 명시적 readOnly=false 표기를 기본 @transactional로 단순화해 클래스 레벨 설정과의 관계만 남긴다 - ChatRoomRepository updateLastMessage 시그니처 타입 표기를 정리해 읽는 부담을 줄인다 - UserService의 환영 메시지 경로도 syncLastMessage 헬퍼를 사용하도록 맞춰 채팅 메타데이터 갱신 책임을 일관되게 유지한다
- last_message 메타데이터 갱신 쿼리에서 clearAutomatically를 제거해 direct room 복구 흐름의 엔티티 변경이 detach로 유실되지 않게 한다 - 최신 메시지보다 오래된 트랜잭션이 chat_room 마지막 메시지 요약을 덮어쓰지 못하도록 messageId tie-breaker 조건을 추가한다 - direct room 재노출과 조건부 메타데이터 갱신을 테스트로 고정해 리뷰에서 지적된 회귀를 막는다
- 마지막 메시지 갱신 조건에서 현재 저장한 메시지 자신은 비교 대상에서 제외해 DB timestamp 정밀도 차이로 인한 오판을 막는다 - direct 채팅방 목록과 재입장 시나리오가 chat_room.last_message_* 컬럼에 의존하므로 간헐적으로 이전 메시지가 남는 상태를 방지한다 - CI에서 흔들리던 채팅방 나가기/마지막 메시지 메타데이터 테스트와 jacoco 경로를 로컬에서 다시 검증했다
🔍 개요
chat_message에 저장되지만chat_room.last_message_content,last_message_sent_at가 null로 남아 채팅방 목록 요약 정보가 비는 문제를 수정했습니다.🚀 주요 변경 내용
ChatRoomRepository에 마지막 메시지 메타데이터를 명시적으로 동기화하는 update 쿼리를 추가했습니다.syncLastMessage를 사용하도록 바꿨습니다.chat_room의 null 메타데이터를 최신chat_message기준으로 백필했습니다.chat_room메타데이터가 실제 DB에 반영되는지 검증하는 통합 테스트를 추가했습니다.💬 참고 사항
./gradlew test --tests "gg.agit.konect.integration.domain.chat.ChatApiTest$SendMessage" --tests "gg.agit.konect.integration.domain.user.UserSignupApiTest$Signup.signupSuccess"chore: 코드 포맷팅커밋 1개가 추가되었습니다.✅ Checklist (완료 조건)