Conversation
Walkthrough이 PR은 채팅 전송·동기화, 읽음·퇴장 처리, 사진 요청(요청/수락/거절), 만남 위치 인증, 신고 기능(엔티티·서비스·컨트롤러) 및 관련 리포지토리와 DB 마이그레이션을 추가·수정합니다. Changes통합 기능 변경
Estimated code review effort:
🚥 Pre-merge checks | ✅ 4✅ 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 |
There was a problem hiding this comment.
Actionable comments posted: 7
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
manabom/src/main/java/mannabom_server/manabom/presentation/notification/controller/NotificationController.java (1)
28-41:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win테스트 엔드포인트에 인가 검증이 없어 보안 위험이 있습니다.
이 엔드포인트는 요청자가
targetUserId에 알림을 보낼 권한이 있는지 검증하지 않습니다. 악의적인 사용자가 임의의 사용자에게 알림을 스팸으로 보낼 수 있습니다. 프로덕션에 배포되면 심각한 보안 취약점이 됩니다.권장 조치:
- 이 엔드포인트를 개발 환경으로만 제한 (
@Profile("dev")추가)- 또는 관리자 권한 검증 추가
- 또는 배포 전 완전히 제거
🛡️ 프로파일 기반 제한 적용 예시
+ `@Profile`("dev") /** * 배포 전 삭제 * **/ `@PostMapping`("/test/send") public String sendTestNotification(`@RequestBody` TestNotificationRequest request) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/notification/controller/NotificationController.java` around lines 28 - 41, The sendTestNotification test endpoint lacks authorization checks allowing anyone to trigger notifications; restrict it by either annotating the controller/method with `@Profile`("dev") to limit it to development, OR add an authorization guard such as `@PreAuthorize`("hasRole('ADMIN')") (or an explicit SecurityContextHolder check) inside sendTestNotification using TestNotificationRequest.targetUserId() before calling notificationService.sendNotification, and remove or disable the endpoint prior to production deploy; target symbols: sendTestNotification, TestNotificationRequest, notificationService, and the `@PostMapping`("/test/send") method.
🟠 Major comments (20)
manabom/src/main/java/mannabom_server/manabom/application/report/service/ReportService.java-20-26 (1)
20-26:⚠️ Potential issue | 🟠 Major | ⚡ Quick win입력/인증값 null 방어가 없어 500 오류로 이어질 수 있습니다.
userId또는request.getTargetId()가 null인 경우 현재 흐름은 NPE/서버 오류로 떨어질 수 있습니다. 서비스 시작 지점에서 필수값을 명시적으로 검증해 주세요.🔧 제안 수정안
`@Transactional` public void createReport(Long userId, CreateReportRequest request, ReportType type) { + if (userId == null) { + throw new IllegalArgumentException("인증 정보가 없습니다."); + } + if (request == null || request.getTargetId() == null || request.getReason() == null) { + throw new IllegalArgumentException("신고 필수 값이 누락되었습니다."); + } if (userId.equals(request.getTargetId())) throw new IllegalArgumentException("자기 자신을 신고할 수 없습니다.");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/report/service/ReportService.java` around lines 20 - 26, Add explicit null checks at the start of createReport: validate that request, userId, and request.getTargetId() are not null (e.g., using Objects.requireNonNull or explicit ifs) and throw a clear IllegalArgumentException when any are null; then proceed with the existing logic (including the self-report check and repository lookups using userRepository.findById) so you avoid NPEs and return a 400-level error instead of a 500.manabom/src/main/resources/db/migration/V16__create_tables_report_meetingVerification_profileRequest.sql-22-23 (1)
22-23:⚠️ Potential issue | 🟠 Major | ⚡ Quick win검증 플래그 컬럼을
NOT NULL로 고정하세요.Line 22, Line 31의
is_verified가 nullable이라NULL상태가 유입될 수 있습니다. 검증 상태는 이진값으로 고정하는 편이 안전합니다.수정 제안
- is_verified BOOLEAN DEFAULT FALSE, + is_verified BOOLEAN NOT NULL DEFAULT FALSE,- is_verified BOOLEAN DEFAULT FALSE, + is_verified BOOLEAN NOT NULL DEFAULT FALSE,Also applies to: 31-31
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/resources/db/migration/V16__create_tables_report_meetingVerification_profileRequest.sql` around lines 22 - 23, The is_verified boolean column definitions (currently "is_verified BOOLEAN DEFAULT FALSE") should be made NOT NULL to prevent NULL states; update both occurrences (the is_verified column in the table created near the top of this diff and the second is_verified occurrence referenced at line 31) to "is_verified BOOLEAN NOT NULL DEFAULT FALSE" so the column is a strict binary flag and cannot store NULL values.manabom/src/main/resources/db/migration/V14__add_column.sql-10-11 (1)
10-11:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
meeting.deleted_at컬럼 타입을TIMESTAMPTZ(6)로 통일하세요.
V14__add_column.sql은meeting.deleted_at을TIMESTAMP(6)으로 추가하지만,Meeting.deletedAt는Instant이고V17__add_deleted_at_to_chat_rooms.sql의chat_rooms.deleted_at은timestamptz(6)입니다. (타임존 해석/비교/직렬화에서 차이 가능)수정 제안
-ALTER TABLE meeting ADD COLUMN deleted_at TIMESTAMP(6) NULL; +ALTER TABLE meeting ADD COLUMN deleted_at TIMESTAMPTZ(6) NULL;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/resources/db/migration/V14__add_column.sql` around lines 10 - 11, V14__add_column.sql currently adds meeting.deleted_at as TIMESTAMP(6) but the domain uses Meeting.deletedAt (Instant) and other migrations (e.g., chat_rooms.deleted_at in V17) use timestamptz(6); update the ALTER TABLE statement that creates meeting.deleted_at to use TIMESTAMPTZ(6) NULL instead of TIMESTAMP(6) to ensure consistent timezone-aware storage for Meeting.deletedAt and compatibility with existing chat_rooms.deleted_at semantics.manabom/src/main/resources/db/migration/V17__add_deleted_at_to_chat_rooms.sql-1-2 (1)
1-2:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFlyway 버전드 마이그레이션에서
ADD COLUMN IF NOT EXISTS제거 권장
V17__add_deleted_at_to_chat_rooms.sql의deleted_at추가가ADD COLUMN IF NOT EXISTS라서, 컬럼이 이미 존재하더라도(타입/정의가 다른 경우) Flyway가 불일치를 조용히 통과할 수 있습니다.수정 제안
ALTER TABLE public.chat_rooms -ADD COLUMN IF NOT EXISTS deleted_at timestamptz(6) NULL; +ADD COLUMN deleted_at timestamptz(6) NULL;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/resources/db/migration/V17__add_deleted_at_to_chat_rooms.sql` around lines 1 - 2, The migration V17__add_deleted_at_to_chat_rooms.sql should not use "IF NOT EXISTS" because Flyway must fail on schema drift; replace the current ALTER TABLE statement with a plain ALTER TABLE public.chat_rooms ADD COLUMN deleted_at timestamptz(6) NULL; so Flyway will error if the column already exists with a different definition, or alternatively add an explicit pre-check (e.g., a DO block / SELECT from information_schema.columns) that verifies if "deleted_at" exists and has the exact type/nullability and explicitly RAISE EXCEPTION on mismatch so the migration fails rather than silently skipping.manabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/Meeting.java-121-124 (1)
121-124:⚠️ Potential issue | 🟠 Major | ⚡ Quick win마지막 멤버 퇴장 분기에서 점유율 점수가 갱신되지 않습니다.
Line 121~123 분기에서는
currentMembers와avgAge만 0으로 바꾸고occupancyScore를 갱신하지 않아, 점유율 데이터가 이전 값으로 남습니다.수정 제안
} else { this.currentMembers = 0; this.avgAge = 0.0; + updateOccupancyScore(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/Meeting.java` around lines 121 - 124, In Meeting.java's branch that handles the last member leaving (where it sets this.currentMembers = 0 and this.avgAge = 0.0) also reset/update the occupancy score so stale occupancy data isn't left behind; either assign this.occupancyScore = 0.0 (or the appropriate zero value/type) in that else block or invoke the existing occupancy recalculation method (e.g., updateOccupancyScore()/recalculateOccupancy()) so occupancyScore is consistent with currentMembers and avgAge.manabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingMatch.java-31-36 (1)
31-36:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
@NotFound(IGNORE)로 인해 연관 엔티티 누락 시 NPE 위험
MeetingMatch에서meeting1/meeting2에@NotFound(action = NotFoundAction.IGNORE)가 적용돼 FK 대상 행이 누락되면 해당 필드가null로 로딩될 수 있습니다. 그런데reject/accept/autoAccept및 매칭 성공 경로에서meeting1.getId(),meeting2.getId(),meeting1.changeToMatchedStatus(),meeting2.changeToMatchedStatus(),determineAutoDecision(meeting1/meeting2)등을 null 가드 없이 호출하고 있어 런타임 NPE로 이어질 수 있습니다.수정 제안 (무결성 기반)
-import org.hibernate.annotations.NotFound; -import org.hibernate.annotations.NotFoundAction; @@ `@ManyToOne`(fetch = FetchType.LAZY) - `@NotFound`(action = NotFoundAction.IGNORE) `@JoinColumn`(name = "meeting1_id", nullable = false) private Meeting meeting1; `@ManyToOne`(fetch = FetchType.LAZY) - `@NotFound`(action = NotFoundAction.IGNORE) `@JoinColumn`(name = "meeting2_id", nullable = false) private Meeting meeting2;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingMatch.java` around lines 31 - 36, MeetingMatch currently uses `@NotFound`(action = NotFoundAction.IGNORE) on meeting1/meeting2 but methods reject, accept, autoAccept and the matching-success path call meeting1.getId(), meeting2.getId(), meeting1.changeToMatchedStatus(), meeting2.changeToMatchedStatus(), and determineAutoDecision(...) without null checks, risking NPEs; fix by adding explicit null-guards early (e.g., a private validateMeetingsPresent() called from reject, accept, autoAccept and the matching result flow) that checks meeting1 != null && meeting2 != null and throws a clear IllegalStateException (or a custom DomainInvariantException) including the MeetingMatch id and which meeting is missing, or alternatively remove the `@NotFound` IGNORE if you prefer DB integrity enforcement so the fields are guaranteed non-null—ensure all references to meeting1/meeting2 use the guard before calling getId(), changeToMatchedStatus(), or determineAutoDecision.manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java-97-103 (1)
97-103:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
nearbyIds를ChatMemberPK로 조회하고 있어 성별 판정이 틀어질 수 있습니다.Line 97의
findAllById(nearbyIds)는userId가 아니라ChatMember.id기준 조회입니다. 현재nearbyIds는 Line 90에서 userId를 넣고 있어,hasMale/hasFemale계산이 잘못될 가능성이 높습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java` around lines 97 - 103, The code incorrectly calls chatMemberRepository.findAllById(nearbyIds) (where nearbyIds holds userIds) causing wrong gender checks; change the lookup to use the user id field instead (e.g. use profileRepository.findAllByUserIdIn(nearbyIds) or chatMemberRepository.findAllByUserIdIn(nearbyIds) if available) and then compute hasMale/hasFemale from those Profile objects (or from the ChatMember->User->Profile mapping) instead of calling profileRepository.findByUser(...) inside the stream; ensure you reference nearbyIds, replace findAllById(nearbyIds) with the repository method that queries by user id, and update the stream to inspect profile.getGender() (or m.getUser().getProfile().getGender()) accordingly.manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingMatchingService.java-391-393 (1)
391-393:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift핵심 환불 분기가 미구현 상태입니다.
Line 391 조건을 만족해도 Line 392-393이 비어 있어 실제 환불이 발생하지 않습니다. 이 분기는 사용자 금전 처리와 직결되므로 TODO 상태로 두면 안 됩니다.
원하시면 이 분기의 환불 트랜잭션(중복 환불 방지 포함)까지 반영한 구현안을 같이 정리해드릴게요. 이건 별도 이슈로 분리해도 좋습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingMatchingService.java` around lines 391 - 393, Condition branch in MeetingMatchingService for full refund is empty; implement the refund flow when (myMeeting.getCurrentMembers()+opponent.getCurrentMembers())*2 < myMeeting.getMaxMembers()+opponent.getMaxMembers(). Inside MeetingMatchingService implement a transactional, idempotent full-refund path: 1) start a DB transaction (e.g., `@Transactional` on the service method), 2) check and set a persistent "refunded" marker for each involved meeting or matching record to prevent duplicate refunds, 3) call the payment/refund API (use your existing PaymentService.refund(...) or add a processFullRefund(myMeeting, opponent) helper) for each participant or the original transaction owner, 4) update account/wallet balances and meeting state and persist RefundRecord entries in RefundRepository, and 5) handle and log failures so refunds are rolled back on exception. Ensure you reference and update the exact entities used in the condition (myMeeting.getCurrentMembers(), opponent.getCurrentMembers(), myMeeting.getMaxMembers(), opponent.getMaxMembers()) and perform locking/consistency checks before issuing refunds.manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java-96-96 (1)
96-96:⚠️ Potential issue | 🟠 Major | ⚡ Quick win절반 이상 판정식이 홀수 인원에서 완화됩니다.
Line 96의
nearbyCount >= (totalMembers/2)는 정수 나눗셈이라 총원 3명일 때 1명만으로 조건이 참이 됩니다. “절반 이상” 의도라면 올림 기준이 필요합니다.수정 제안
- if(nearbyCount>=(totalMembers/2)){ + int required = (totalMembers + 1) / 2; + if(nearbyCount >= required){🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java` at line 96, The comparison in MeetingVerificationService using nearbyCount >= (totalMembers/2) incorrectly uses integer division and relaxes the condition for odd totalMembers; change the threshold to a proper "half or more" ceiling (e.g., compute required = (totalMembers + 1) / 2 or use Math.ceil(totalMembers / 2.0)) and compare nearbyCount >= required so that totals like 3 require 2 nearby members; update the check where nearbyCount and totalMembers are used in the verification logic accordingly.manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java-109-113 (1)
109-113:⚠️ Potential issue | 🟠 Major | ⚡ Quick win최종 위치 키 만료가 설정되지 않아 레이트컴어 유효기간 정책과 불일치합니다.
Line 110에서 자정까지 시간을 계산하지만, Line 112 저장 시 TTL을 쓰지 않아
FINAL_LOC_KEY가 남습니다. 메시지(“당일 자정”)와 실제 동작이 다릅니다.수정 제안
- stringRedisTemplate.opsForValue().set(String.format(FINAL_LOC_KEY, chatRoomId), latitude + "," + longitude); + stringRedisTemplate.opsForValue().set( + String.format(FINAL_LOC_KEY, chatRoomId), + latitude + "," + longitude, + timeUntilMidnight + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java` around lines 109 - 113, The FINAL_LOC_KEY is stored without a TTL despite computing timeUntilMidnight; update the save to set an expiry so the key auto-expires at midnight. Replace the current stringRedisTemplate.opsForValue().set(...) call (for FINAL_LOC_KEY with chatRoomId, latitude, longitude) with a call that sets a TTL based on timeUntilMidnight (e.g., use opsForValue().set(key, value, timeUntilMidnight.getSeconds(), TimeUnit.SECONDS) or call stringRedisTemplate.expire(key, timeUntilMidnight) after set) so the stored location expires at the computed midnight boundary.manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.java-79-95 (1)
79-95:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift동시 요청 시 PENDING 요청이 중복 생성될 수 있습니다.
최신 요청 조회 → 저장이 원자적이지 않아 동시 트래픽에서 둘 다 통과할 수 있습니다. 운영 중 중복 상태 데이터가 생길 수 있습니다.🔧 제안 방향
- Optional<LoveViewPhotoRequest> latestOpt = photoRequestRepository.findTopByHistoryIdOrderByIdDesc(loveViewId); + // 1) PESSIMISTIC WRITE 잠금 조회 메서드 도입 또는 + // 2) DB 유니크 제약(활성 요청 1개 보장) + 예외 처리로 방어+ // 예: repository에 잠금 조회 추가 + // `@Lock`(LockModeType.PESSIMISTIC_WRITE) + // Optional<LoveViewPhotoRequest> findTopByHistoryIdOrderByIdDesc(Long historyId);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.java` around lines 79 - 95, The current non-atomic "find latest → save" flow using photoRequestRepository.findTopByHistoryIdOrderByIdDesc (latestOpt) can produce duplicate PENDING rows under concurrent requests; make the operation atomic by wrapping the request creation method in a transaction and preventing concurrent passes—either (1) obtain a DB lock on the LoveView/history row before checking (e.g. add a repository method like LoveViewRepository.findByIdForUpdate or annotate a finder with PESSIMISTIC_WRITE and call it inside the transactional method) then re-check latestOpt and save the LoveViewPhotoRequest builder only if safe, or (2) enforce uniqueness at the database level (add a unique constraint/partial index preventing multiple PENDING rows per history_id) and handle the unique-constraint violation when calling photoRequestRepository.save; reference photoRequestRepository.findTopByHistoryIdOrderByIdDesc, photoRequestRepository.save, LoveViewPhotoRequest.builder(), and the method that contains this logic to apply the fix.manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.java-79-84 (1)
79-84:⚠️ Potential issue | 🟠 Major | ⚡ Quick winREJECTED 이후 재요청 쿨다운 규칙이 생성 로직에서 누락되어 있습니다.
상태 조회는
REJECTED + 10메시지 미만을 제한하지만, 생성 로직은 REJECTED면 즉시 재요청을 허용합니다. 규칙 불일치로 우회가 가능합니다.🔧 제안 수정
Optional<LoveViewPhotoRequest> latestOpt = photoRequestRepository.findTopByHistoryIdOrderByIdDesc(loveViewId); if(latestOpt.isPresent()){ - if(latestOpt.get().getStatus()!=PhotoRequestStatus.REJECTED){ + LoveViewPhotoRequest latest = latestOpt.get(); + if(latest.getStatus()!=PhotoRequestStatus.REJECTED){ throw new IllegalStateException("프로필 요청이 중복되었거나 이미 수락된 상태입니다."); } + int messageCount = chatMessageRepository.countChatMessagesByRoom_IdAndCreatedAtAfter( + room.getId(), latest.getUpdatedAt() + ); + if (messageCount < 10) { + throw new IllegalStateException("거절 이후 10마디 이상 대화 후 다시 요청할 수 있습니다."); + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.java` around lines 79 - 84, The creation logic currently only blocks when the latest request is not REJECTED; add the same REJECTED+cooldown check used elsewhere: after retrieving latestOpt from photoRequestRepository.findTopByHistoryIdOrderByIdDesc(loveViewId), if latestOpt.isPresent() then if latest.get().getStatus() != PhotoRequestStatus.REJECTED throw the existing IllegalStateException, else if latest.get().getStatus() == PhotoRequestStatus.REJECTED and latest.get().getMessageCount() < 10 also throw the same IllegalStateException to enforce the 10-message cooldown before allowing a new LoveViewPhotoRequest.manabom/src/main/java/mannabom_server/manabom/application/chat/dto/request/ChatSendRequest.java-9-15 (1)
9-15:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSTOMP 페이로드 역직렬화 시
ChatSendRequest바인딩 실패 가능성
ChatWsController.send(...)에서ChatSendRequest request를 STOMP payload로 역직렬화하는데, DTO가@AllArgsConstructor만 있고@NoArgsConstructor/@JsonCreator·@JsonProperty가 없어 런타임에 생성자 파라미터 매핑이 실패할 수 있습니다.수정 제안
import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import mannabom_server.manabom.domain.chat.enums.ChatMessageType; `@Getter` `@AllArgsConstructor` +@NoArgsConstructor public class ChatSendRequest { private Long roomId; private ChatMessageType messageType; private String content; private String clientMessageId; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/chat/dto/request/ChatSendRequest.java` around lines 9 - 15, ChatSendRequest lacks a Jackson-friendly constructor/annotations so STOMP payloads bound in ChatWsController.send(...) can fail; fix by making the DTO deserializable by adding a Lombok `@NoArgsConstructor` (keep `@AllArgsConstructor`) and either annotate the all-args constructor with `@JsonCreator` and each parameter with `@JsonProperty`("roomId")/@JsonProperty("messageType")/@JsonProperty("content")/@JsonProperty("clientMessageId"), or annotate the fields with `@JsonProperty` so Jackson can map STOMP payload properties to the ChatSendRequest fields.manabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatRoomListResponse.java-17-17 (1)
17-17:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
lastMessageAt응답값이 항상 null이 되는 구현입니다.Line 17의 필드명이
lastmessageAt로 선언되어 있고, Line 20-26의 빌더에서 시간 필드를 세팅하지 않아 최신 메시지 시간이 누락됩니다. 방 목록 정렬/표시에 직접 영향이 납니다.수정 예시
@@ - private Instant lastmessageAt; + private Instant lastMessageAt; @@ public static ChatRoomListResponse of(ChatRoom room,String roomName, ChatMessage lastMsg, int unreadCount){ return ChatRoomListResponse.builder() .roomId(room.getId()) .roomName(roomName) .lastMessage(lastMsg!=null ? lastMsg.getType().getDisplayMessage(lastMsg.getContent()): null ) + .lastMessageAt(lastMsg != null ? lastMsg.getCreatedAt() : null) .unreadCount(unreadCount) .build(); }Also applies to: 20-26
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatRoomListResponse.java` at line 17, The DTO field is misspelled as lastmessageAt and the builder never sets the last message time, causing nulls; rename the field to lastMessageAt (or add a correctly-cased JSON/serialization alias) and update the builder in ChatRoomListResponse to set lastMessageAt from the source (e.g., use chatRoom.getLastMessageAt() or the appropriate lastMessage timestamp) so the built response includes the latest message time.manabom/src/main/java/mannabom_server/manabom/domain/chat/entity/ChatMember.java-54-56 (1)
54-56:⚠️ Potential issue | 🟠 Major | ⚡ Quick win읽음 포인터가 역행할 수 있습니다.
Line 54-56은
lastReadMessageId를 무조건 덮어써서, 늦게 도착한 이전 이벤트가 최신 읽음 위치를 되돌릴 수 있습니다. 읽음 포인터는 단조 증가로 갱신되어야 합니다.수정 예시
public void updateLastReadMessageId(Long messageId){ - this.lastReadMessageId = messageId; + if (messageId == null) return; + if (this.lastReadMessageId == null || messageId > this.lastReadMessageId) { + this.lastReadMessageId = messageId; + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/domain/chat/entity/ChatMember.java` around lines 54 - 56, updateLastReadMessageId가 전달된 messageId로 무조건 덮어써서 읽음 포인터가 역행할 수 있으므로, ChatMember.updateLastReadMessageId에서 전달된 messageId가 null이 아니고 현재 this.lastReadMessageId보다 큰 경우에만 갱신하도록 변경하세요; 즉, this.lastReadMessageId가 null이면 무조건 설정하고, 그렇지 않으면 messageId > this.lastReadMessageId 일 때만 this.lastReadMessageId = messageId 하도록 조건부 업데이트 로직을 추가해 역행을 방지합니다.manabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatRoomService.java-106-113 (1)
106-113:⚠️ Potential issue | 🟠 Major | ⚡ Quick win웰컴 SYSTEM 메시지가 동기화 응답에서 누락됩니다.
여기서는
user(null)로 저장하는데, 현재 sync/history 쿼리들은JOIN FETCH m.user를 사용합니다. 그래서 이 메시지는 최신 동기화/히스토리 응답에서 필터링됩니다. 쿼리를LEFT JOIN으로 바꾸거나 SYSTEM 발신자 모델을 분리해야 합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatRoomService.java` around lines 106 - 113, The SYSTEM welcome message is saved with user(null) in sendSystemWelcomeMessage (ChatMessage) which causes it to be omitted by sync/history queries that use JOIN FETCH m.user; fix by either (A) introducing/using a dedicated system sender User entity (e.g., a static/system User record) and save the ChatMessage with that User instead of null (update sendSystemWelcomeMessage and ensure the system User exists), or (B) change the repository queries used by sync/history from JOIN FETCH m.user to LEFT JOIN FETCH m.user so messages with null user are included; pick one approach and update sendSystemWelcomeMessage or the repository JPQL methods (and any tests) accordingly.manabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatService.java-219-225 (1)
219-225:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
hasNext계산값이 응답에 반영되지 않습니다.Line 225에서 항상
false를 넘기고 있어서, 더 오래된 메시지가 남아 있어도 클라이언트는 페이지 종료로 오인합니다.패치 예시
- return buildChatHistoryResponse(roomId, messages, false); + return buildChatHistoryResponse(roomId, messages, hasNext);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatService.java` around lines 219 - 225, The response always passes false for the pagination flag, so clients never see hasNext; use the previously computed boolean by changing the call buildChatHistoryResponse(roomId, messages, false) to buildChatHistoryResponse(roomId, messages, hasNext) (ensure hasNext is computed before you possibly trim messages with messages = messages.subList(0, PAGE_SIZE) and keep the existing Collections.reverse(messages) logic).manabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatMemberService.java-27-53 (1)
27-53:⚠️ Potential issue | 🟠 Major | ⚡ Quick win읽음 처리가 Redis만 줄이고 멤버 상태를 저장하지 않습니다.
lastReadMessageId를 갱신하지 않아서 이후 room list / initial sync가 계속 예전 기준으로 unread를 다시 계산합니다.패치 예시
for (ChatMessage m : unreadMessages) { String key = UNREAD_PREFIX + m.getId(); Long count = stringRedisTemplate.opsForValue().decrement(key); if (count != null && count < 0) { stringRedisTemplate.opsForValue().set(key, "0"); } } + + chatMember.updateLastReadMessageId(lastSeenMessageId); ChatReadEvent readEvent = ChatReadEvent.builder() .roomId(roomId)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatMemberService.java` around lines 27 - 53, The method updateReadStatus currently decrements Redis unread counts but never persists the member's lastReadMessageId; update the ChatMember entity's lastReadMessageId (use chatMember.setLastReadMessageId(lastSeenMessageId)) and persist it via chatMemberRepository (e.g., save or saveAndFlush) after the Redis decrement loop so room list/initial sync compute correct unread state; keep existing early-return (if lastSeenMessageId <= lastReadId) and use the existing chatMember variable to locate the record to update.manabom/src/main/java/mannabom_server/manabom/domain/chat/repository/ChatMessageRepository.java-27-31 (1)
27-31:⚠️ Potential issue | 🟠 Major | ⚡ Quick win동기화/히스토리 JPQL 경계 조건이 실제 코드와 맞지 않습니다.
findChatMessagesAfterJPQL이:lastMessageId를 참조하는데 메서드 파라미터는@Param("lastReadId")라 바인딩이 깨집니다.findChatMessagesBefore인데 비교 연산자가m.id > :firstMessageId로 되어 있어 “이전(과거)” 히스토리 대신 더 최신 메시지를 가져올 수 있습니다(경계는m.id < :firstMessageId로 정리 필요).패치 예시
- `@Query`("SELECT m FROM ChatMessage m JOIN FETCH m.user u JOIN Profile p on p.user.userId = u.userId WHERE m.room.id = :roomId AND m.id > :lastMessageId ORDER BY m.id desc ") + `@Query`("SELECT m FROM ChatMessage m JOIN FETCH m.user u JOIN Profile p ON p.user.userId = u.userId WHERE m.room.id = :roomId AND m.id > :lastReadId ORDER BY m.id DESC") List<ChatMessage> findChatMessagesAfter(`@Param`("roomId") Long roomId, `@Param`("lastReadId") Long lastReadId, Pageable pageable); - `@Query`("SELECT m FROM ChatMessage m JOIN FETCH m.user u JOIN Profile p on p.user.userId = u.userId WHERE m.room.id = :roomId AND m.id > :firstMessageId ORDER BY m.id desc ") + `@Query`("SELECT m FROM ChatMessage m JOIN FETCH m.user u JOIN Profile p ON p.user.userId = u.userId WHERE m.room.id = :roomId AND m.id < :firstMessageId ORDER BY m.id DESC") List<ChatMessage> findChatMessagesBefore(`@Param`("roomId") Long roomId, `@Param`("firstMessageId") Long firstMessageId, Pageable pageable);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/domain/chat/repository/ChatMessageRepository.java` around lines 27 - 31, The JPQL parameter name in findChatMessagesAfter doesn't match its `@Param` and the before-query uses the wrong comparison operator: update the query for findChatMessagesAfter to reference :lastReadId (or change the `@Param` to "lastMessageId" so the JPQL and `@Param` match) and update findChatMessagesBefore's JPQL comparison from m.id > :firstMessageId to m.id < :firstMessageId so it returns earlier messages; ensure the method signatures findChatMessagesAfter and findChatMessagesBefore keep their `@Param` names consistent with the JPQL placeholders.manabom/src/main/java/mannabom_server/manabom/presentation/chat/controller/ChatApiController.java-53-61 (1)
53-61:⚠️ Potential issue | 🟠 Major | ⚡ Quick win동기화 조회 API에서 GET 바디 제거 및 트레일링 슬래시 매핑 정규화 필요
@GetMapping에@RequestBody를 두면 HTTP GET의 본문 미사용(권장 위반)으로 인해 일부 클라이언트/프록시가 바디를 누락해 400 등 예측 불가 동작이 발생할 수 있습니다.@GetMapping("/sync/chat/{roomId}/")처럼 끝에/가 붙은 매핑은 Spring Boot 3의 기본 URL 매칭이 엄격해서, 슬래시 없는 호출은 404로 이어질 수 있습니다.(대상)
ChatApiController.java라인 53-61수정 예시 diff
- `@GetMapping`("/sync/chat/{roomId}/") - public ResponseEntity<ApiResponse<ChatSyncResponse>> getLatestChatMessagesList(`@PathVariable` Long roomId, `@AuthenticationPrincipal` Long userId, `@RequestBody` ChatMessageRequest request){ - ChatSyncResponse response = chatService.getLatestChatMessageListSync(roomId, userId, request.getLastReadMessageId()); + `@GetMapping`("/sync/chat/{roomId}") + public ResponseEntity<ApiResponse<ChatSyncResponse>> getLatestChatMessagesList( + `@PathVariable` Long roomId, + `@AuthenticationPrincipal` Long userId, + `@RequestParam`("lastReadMessageId") Long lastReadMessageId) { + ChatSyncResponse response = chatService.getLatestChatMessageListSync(roomId, userId, lastReadMessageId); return ResponseEntity.ok(ApiResponse.success(response,"최신 채팅 메시지 리스트 동기화 완료했습니다.")); } `@GetMapping`("/history/chat/{roomId}") - public ResponseEntity<ApiResponse<ChatHistoryResponse>> getChatMessagesListHistory(`@PathVariable` Long roomId, `@AuthenticationPrincipal` Long userId, `@RequestBody` ChatMessageRequest request){ - ChatHistoryResponse response = chatService.getChatHistory(roomId, userId, request.getLastReadMessageId()); + public ResponseEntity<ApiResponse<ChatHistoryResponse>> getChatMessagesListHistory( + `@PathVariable` Long roomId, + `@AuthenticationPrincipal` Long userId, + `@RequestParam`("lastReadMessageId") Long lastReadMessageId) { + ChatHistoryResponse response = chatService.getChatHistory(roomId, userId, lastReadMessageId); return ResponseEntity.ok(ApiResponse.success(response,"과거 채팅 메시지 조회 완료했습니다.")); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/chat/controller/ChatApiController.java` around lines 53 - 61, The GET handlers getLatestChatMessagesList and getChatMessagesListHistory must not accept a request body and their mappings should not end with a trailing slash; change the `@GetMapping` paths from "/sync/chat/{roomId}/" to "/sync/chat/{roomId}" (and ensure "/history/chat/{roomId}" is consistent), remove the `@RequestBody` ChatMessageRequest parameter, add a `@RequestParam` Long lastReadMessageId (e.g. `@RequestParam`(name="lastReadMessageId", required=false) Long lastReadMessageId) to each method signature, and pass that lastReadMessageId into chatService.getLatestChatMessageListSync(...) and chatService.getChatHistory(...). Ensure the method parameter list references `@AuthenticationPrincipal` Long userId and the `@PathVariable` Long roomId remain unchanged.
🟡 Minor comments (1)
manabom/src/main/java/mannabom_server/manabom/presentation/matching/controller/PhotoRequestController.java-42-42 (1)
42-42:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win거절 API 성공 메시지가 의미상 반대로 작성되어 있습니다.
거절 처리 후
"거절을 수락하였습니다"로 응답되어 사용자 혼동이 발생합니다.🔧 제안 수정
- return ResponseEntity.ok(ApiResponse.success(null, "프로필 사진 거절을 수락하였습니다.")); + return ResponseEntity.ok(ApiResponse.success(null, "프로필 사진 요청을 거절하였습니다."));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/matching/controller/PhotoRequestController.java` at line 42, The success message in PhotoRequestController is reversed: update the ApiResponse.success call that currently returns "프로필 사진 거절을 수락하였습니다." to a correct rejection confirmation such as "프로필 사진 거절을 완료하였습니다." (or "프로필 사진 거절을 처리하였습니다."). Locate the ResponseEntity.ok(ApiResponse.success(null, "...")) invocation in PhotoRequestController and replace the message string accordingly so the response accurately reflects the rejection outcome.
🧹 Nitpick comments (3)
manabom/src/main/java/mannabom_server/manabom/presentation/notification/controller/NotificationController.java (1)
25-27: ⚡ Quick win프로파일 기반 활성화로 테스트 엔드포인트를 보호하세요.
주석으로 삭제를 안내하는 것은 좋지만, 실수로 배포될 위험이 있습니다.
@Profile("!prod")또는@Profile("dev")를 사용하여 프로덕션 환경에서는 이 엔드포인트가 자동으로 비활성화되도록 하는 것이 더 안전합니다.🔒 프로파일 기반 보호 적용 예시
+ `@Profile`("!prod") // 또는 `@Profile`({"dev", "test"}) /** * 배포 전 삭제 * **/ `@PostMapping`("/test/send")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/notification/controller/NotificationController.java` around lines 25 - 27, Add a Spring profile restriction so the temporary test endpoint(s) in the NotificationController are disabled in production: annotate the NotificationController class (or the specific test handler methods) with `@Profile`("!prod") or `@Profile`("dev"), import org.springframework.context.annotation.Profile, and keep the existing comment but remove the risk of accidental deployment by ensuring the controller (or methods) are only active under non-prod profiles.manabom/src/main/resources/db/migration/V16__create_tables_report_meetingVerification_profileRequest.sql (1)
14-15: ⚡ Quick winFK 컬럼 인덱스를 추가해 조회/정합성 작업 비용을 줄이세요.
현재 FK 제약만 있고 참조 컬럼 인덱스가 없어, 부모 삭제/업데이트 검증 및 조인 성능이 급격히 떨어질 수 있습니다.
수정 제안
+CREATE INDEX idx_reports_reporter_id ON reports(reporter_id); +CREATE INDEX idx_reports_target_id ON reports(target_id); + +CREATE INDEX idx_meeting_verification_room_id ON meeting_verification(room_id); + +CREATE INDEX idx_meeting_participant_chat_member_id ON meeting_participant(chat_member_id); + +CREATE INDEX idx_photo_requests_history_id ON love_view_photo_requests(history_id); +CREATE INDEX idx_photo_requests_sender_id ON love_view_photo_requests(sender_id); +CREATE INDEX idx_photo_requests_receiver_id ON love_view_photo_requests(receiver_id);Also applies to: 24-24, 33-33, 45-47
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/resources/db/migration/V16__create_tables_report_meetingVerification_profileRequest.sql` around lines 14 - 15, The foreign key constraints (fk_report_reporter, fk_report_target) are defined without indexes on the referenced columns (reporter_id, target_id) which hurts delete/update validation and join performance; add explicit indexes for each FK column in this migration (e.g., CREATE INDEX idx_report_reporter_id ON report(reporter_id); CREATE INDEX idx_report_target_id ON report(target_id)) and likewise add indexes for every other FK column introduced later in this file (the FK columns mentioned around lines 24, 33, and 45-47) so that each FK has a supporting index.manabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingParticipant.java (1)
26-35: ⚡ Quick win필드명 오타(
rewaredAt)는 지금 정리하는 게 안전합니다.Line 26, 34의
rewaredAt는rewardedAt오타로 보이며, 지금 두면 엔티티/컬럼 네이밍이 계속 전파됩니다.수정 제안
- private Instant rewaredAt; + private Instant rewardedAt; @@ - this.rewaredAt = Instant.now(); + this.rewardedAt = Instant.now();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingParticipant.java` around lines 26 - 35, Rename the misspelled field rewaredAt to rewardedAt in the MeetingParticipant entity and update all usages (the field declaration, the verify() method which sets it, any getters/setters, Builder references and any places referencing MeetingParticipant.rewaredAt); also update JPA/serialization annotations if present (e.g., `@Column`(name=...), `@JsonProperty`) to keep the DB/JSON contract stable or add a migration/column rename if the DB column is already persisted; ensure tests/other classes that reference the old name are updated accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8f5a7a42-542b-4376-b750-7c41f73d3ed7
📒 Files selected for processing (59)
manabom/src/main/java/mannabom_server/manabom/application/chat/dto/event/ChatRoomLeaveEvent.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/request/ChatMessageRequest.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/request/ChatSendRequest.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatHistoryResponse.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatInitialSyncResponse.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatMemberInfo.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatMessageEvent.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatMessageResponse.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatReadEvent.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatRoomListResponse.javamanabom/src/main/java/mannabom_server/manabom/application/chat/dto/response/ChatSyncResponse.javamanabom/src/main/java/mannabom_server/manabom/application/chat/handler/MeetingChatEventHandler.javamanabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatMemberService.javamanabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatRoomService.javamanabom/src/main/java/mannabom_server/manabom/application/chat/service/ChatService.javamanabom/src/main/java/mannabom_server/manabom/application/matching/dto/response/LoveViewPhotoStatusResponse.javamanabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.javamanabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingMatchingService.javamanabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingMemberService.javamanabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingService.javamanabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.javamanabom/src/main/java/mannabom_server/manabom/application/report/dto/request/CreateReportRequest.javamanabom/src/main/java/mannabom_server/manabom/application/report/service/ReportService.javamanabom/src/main/java/mannabom_server/manabom/domain/chat/entity/ChatMember.javamanabom/src/main/java/mannabom_server/manabom/domain/chat/entity/ChatMessage.javamanabom/src/main/java/mannabom_server/manabom/domain/chat/entity/ChatRoom.javamanabom/src/main/java/mannabom_server/manabom/domain/chat/enums/ChatMemberStatus.javamanabom/src/main/java/mannabom_server/manabom/domain/chat/enums/ChatMessageType.javamanabom/src/main/java/mannabom_server/manabom/domain/chat/repository/ChatMemberRepository.javamanabom/src/main/java/mannabom_server/manabom/domain/chat/repository/ChatMessageRepository.javamanabom/src/main/java/mannabom_server/manabom/domain/matching/entity/LoveViewPhotoRequest.javamanabom/src/main/java/mannabom_server/manabom/domain/matching/enums/LoveViewPhotoStatus.javamanabom/src/main/java/mannabom_server/manabom/domain/matching/enums/PhotoRequestStatus.javamanabom/src/main/java/mannabom_server/manabom/domain/matching/repository/LoveViewPhotoRequestRepository.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/Meeting.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingMatch.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingMember.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingParticipant.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingVerification.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/enums/SseEventName.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/repository/MeetingMemberRepository.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/repository/MeetingParticipantRepository.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/repository/MeetingVerificationRepository.javamanabom/src/main/java/mannabom_server/manabom/domain/report/entity/Report.javamanabom/src/main/java/mannabom_server/manabom/domain/report/entity/ReportReason.javamanabom/src/main/java/mannabom_server/manabom/domain/report/entity/ReportStatus.javamanabom/src/main/java/mannabom_server/manabom/domain/report/entity/ReportType.javamanabom/src/main/java/mannabom_server/manabom/domain/report/repository/ReportRepository.javamanabom/src/main/java/mannabom_server/manabom/global/util/LocationUtils.javamanabom/src/main/java/mannabom_server/manabom/infrastructure/security/websocket/StompAuthChannelInterceptor.javamanabom/src/main/java/mannabom_server/manabom/presentation/chat/controller/ChatApiController.javamanabom/src/main/java/mannabom_server/manabom/presentation/chat/controller/ChatWsController.javamanabom/src/main/java/mannabom_server/manabom/presentation/matching/controller/PhotoRequestController.javamanabom/src/main/java/mannabom_server/manabom/presentation/notification/controller/NotificationController.javamanabom/src/main/java/mannabom_server/manabom/presentation/report/controller/ReportController.javamanabom/src/main/resources/db/migration/V14__add_column.sqlmanabom/src/main/resources/db/migration/V15__alter_chat_member_last_read_id.sqlmanabom/src/main/resources/db/migration/V16__create_tables_report_meetingVerification_profileRequest.sqlmanabom/src/main/resources/db/migration/V17__add_deleted_at_to_chat_rooms.sql
Summary by CodeRabbit
릴리스 노트
New Features
Improvements