Skip to content

Conversation

@DwKwCs
Copy link
Collaborator

@DwKwCs DwKwCs commented Aug 21, 2025

#️⃣ 요약 설명

📝 작업 내용

user 계정을 삭제하는 로직 리펙터링

@Override
    public Long deleteUserAccount(Long userId) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> UserHandler.NOT_FOUND);

        adviceRepository.deleteByUserId(userId);
        consultationRequestRepository.deleteByUserId(userId);
        userNotificationRepository.deleteByUserId(userId);
        proposalRepository.deleteByUserId(userId);

        reportResultRepository.deleteAllByUserId(userId);
        reportRepository.deleteAllByUserId(userId);

        scheduleRepository.deleteAllByUserId(userId);

        userRepository.delete(user); // TODO : Hard delete이나 추후 soft로 변경예정
        return user.getId();
    }

동작 확인

💬 리뷰 요구사항(선택)

Summary by CodeRabbit

  • 버그 수정

    • 회원 탈퇴 시 사용자와 연관된 데이터(조언, 상담 요청, 알림, 제안, 보고서 및 결과, 일정 등)가 누락되어 남는 문제를 해결했습니다. 이제 탈퇴 시 관련 항목을 모두 정리해 개인정보 보호와 데이터 일관성이 향상됩니다.
  • 잡무(개발환경)

    • 로컬 실행 환경의 DB 초기화 동작을 변경해 개발 시 스키마 재생성 동작이 적용됩니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 21, 2025

Walkthrough

사용자 계정 삭제 전에 연관 엔티티들을 userId 기준으로 일괄 삭제하도록 리포지토리 메서드를 추가하고, UserCommandServiceImpl.deleteUserAccount에서 해당 삭제 호출을 순차적으로 수행하도록 변경했다.

Changes

Cohort / File(s) Summary of changes
User 삭제 선행 정리 통합
src/main/java/com/my_medi/domain/user/service/UserCommandServiceImpl.java
deleteUserAccount에 교차 엔티티 정리 로직 추가: advice, consultationRequest, userNotification, proposal, reportResult, report, schedule 순으로 userId 기반 삭제 호출을 수행하도록 Repository 의존성 주입 및 호출 추가.
userId 기반 일괄 삭제 추가 (단순 파생 쿼리)
src/main/java/com/my_medi/domain/advice/repository/AdviceRepository.java, src/main/java/com/my_medi/domain/consultationRequest/repository/ConsultationRequestRepository.java, src/main/java/com/my_medi/domain/notification/repository/UserNotificationRepository.java, src/main/java/com/my_medi/domain/proposal/repository/ProposalRepository.java, src/main/java/com/my_medi/domain/schedule/repository/ScheduleRepository.java
각 Repository에 void deleteByUserId(Long userId) 또는 void deleteAllByUserId(Long userId) 같은 Spring Data 파생 삭제 메서드 추가. 기존 메서드 변경 없음.
Report / ReportResult 저장소 변경
src/main/java/com/my_medi/domain/report/repository/ReportRepository.java, src/main/java/com/my_medi/domain/reportResult/repository/ReportResultRepository.java
ReportRepository에 List<Long> findIdsByUserId(Long userId)void deleteAllByUserId(Long userId) 추가. ReportResultRepository에 @Modifying/@query로 서브쿼리 기반 void deleteAllByUserId(Long userId) JPQL 삭제 메서드 추가(Param, Modifying, Query import 추가).
사소한 포맷/설정 변경
src/main/java/com/my_medi/domain/report/entity/Report.java, src/main/resources/application-local.yml
Report 엔티티의 공백 라인 제거(형식 변경). application-local.yml의 hibernate.ddl-auto를 update에서 create로 변경.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller as Caller
  participant UCS as UserCommandServiceImpl
  participant Adv as AdviceRepository
  participant CR as ConsultationRequestRepository
  participant Noti as UserNotificationRepository
  participant Prop as ProposalRepository
  participant RR as ReportResultRepository
  participant R as ReportRepository
  participant Sch as ScheduleRepository
  participant U as UserRepository

  Caller->>UCS: deleteUserAccount(userId)
  rect rgba(220,235,255,0.4)
    note right of UCS: 선행 교차 엔티티 일괄 삭제
    UCS->>Adv: deleteByUserId(userId)
    UCS->>CR: deleteByUserId(userId)
    UCS->>Noti: deleteByUserId(userId)
    UCS->>Prop: deleteByUserId(userId)
    UCS->>RR: deleteAllByUserId(userId)
    UCS->>R: deleteAllByUserId(userId)
    UCS->>Sch: deleteAllByUserId(userId)
  end
  UCS->>U: deleteById(userId)
  U-->>Caller: 완료
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • H4nnhoi
  • Seona12
  • Sohyunnnn

Poem

깡충깡충, 토끼가 쓸었네 🐇
userId 따라 흔적을 모아두고
리포트와 알림, 일정도 안녕이라 말해요
한 번의 호출로 정리 끝—가벼운 DB 밭
마지막엔 사용자도 조용히 퇴장 ✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch MEDI-121-refactor-user-account-delete

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (9)
src/main/java/com/my_medi/domain/schedule/repository/ScheduleRepository.java (1)

49-49: 일관된 메서드 네이밍 및 삭제 건수 반환 제안

  • 현재 레포지토리 메서드 네이밍이 혼용되어 있습니다

    • ProposalRepository : deleteByUserId(Long userId)
    • ReportResultRepository : deleteAllByUserId(Long userId)
    • ReportRepository : deleteAllByUserId(Long userId)
    • ScheduleRepository : deleteAllByUserId(Long userId)
      팀 컨벤션에 맞춰 하나의 네이밍(deleteByUserId 또는 deleteAllByUserId)을 선택하고, 모든 레포지토리에 일관되게 적용해주세요.
  • 벌크 삭제 시 삭제된 엔티티 수를 반환하도록 시그니처를 long delete…ByUserId(Long userId)로 변경할 것을 권장드립니다.
    반환값을 무시할 수도 있어 호출부 호환성에 미치는 영향이 낮습니다.

  • UserCommandServiceImpl 클래스는 클래스 레벨 @Transactional으로 선언되어 있어, 해당 메서드 내의 모든 벌크 삭제 호출이 단일 트랜잭션으로 안전하게 실행됩니다.
    엔티티 라이프사이클 콜백과 영속성 컨텍스트 동기화 이슈는 없으니 참고하시기 바랍니다.

src/main/java/com/my_medi/domain/notification/repository/UserNotificationRepository.java (1)

10-10: 삭제 건수 반환(long)으로 변경하여 운영 가시성 향상

대량 정리 작업의 신뢰도를 높이기 위해 삭제된 행 수를 반환하도록 두는 편이 실무적으로 유용합니다(로그/알림/사후 검증에 활용).

-    void deleteByUserId(Long userId);
+    long deleteByUserId(Long userId);

참고: 알림 데이터에 보존 의무(감사/분쟁 대응)가 없다면 전면 삭제가 맞지만, 보존 요구가 있을 경우에는 PII 비식별화+메타만 보관하는 전략도 검토 대상입니다.

src/main/java/com/my_medi/domain/report/repository/ReportRepository.java (1)

43-43: findIdsByUserId 메서드 제거 제안

현재 코드베이스 전체에서 findIdsByUserId 메서드를 호출하는 곳이 없으므로, API 표면을 줄여 혼동을 방지하고 유지보수 비용을 낮추기 위해 해당 메서드 제거를 권장드립니다.

– 제거 대상
• 파일: src/main/java/com/my_medi/domain/report/repository/ReportRepository.java
• 43행:

-    List<Long> findIdsByUserId(Long userId);
src/main/java/com/my_medi/domain/reportResult/repository/ReportResultRepository.java (2)

18-25: 서브쿼리 대신 경로 탐색으로 단순화하고 실행계획 유리하게 바꾸세요

현재 JPQL 벌크 삭제가 서브쿼리를 사용하고 있어 불필요하게 복잡합니다. 연관 경로 탐색으로 동일 효과를 더 간결하게 표현할 수 있고, 다수의 JPA 제공자/DB에서 최적화가 수월합니다.

아래처럼 쿼리를 단순화하는 것을 추천합니다.

-    @Query("""
-      delete from ReportResult rr
-      where rr.report.id in (
-        select r.id from Report r where r.user.id = :userId
-      )
-    """)
+    @Query("""
+      delete from ReportResult rr
+      where rr.report.user.id = :userId
+    """)

추가로, 대량 삭제 성능을 위해 다음 인덱스(또는 FK + 인덱스) 존재 여부를 확인해 주세요.

  • report(user_id)
  • report_result(report_id)

18-18: clearAutomatically=true가 EM 전체 clear를 유발합니다 — 서비스 레이어의 user 엔티티가 detach됩니다

이 메서드 실행 직후 영속성 컨텍스트가 비워져, 상위 서비스에서 직전에 조회한 User 엔티티가 detach 상태가 됩니다. 현재 구현은 이후 userRepository.delete(user) 호출 시 merge가 추가로 발생합니다. 성능/일관성 관점에서 두 가지 중 하나를 선택하는 것을 권장합니다.

옵션 A) 리포지토리에서 clear를 제거하고 flush만 유지
(현 시나리오에서는 ReportResult를 미리 로드하지 않으므로 안전)

-    @Modifying(clearAutomatically = true, flushAutomatically = true)
+    @Modifying(flushAutomatically = true)

옵션 B) 현 상태를 유지하되 서비스에서 최종 삭제를 deleteById(userId)로 변경
(merge 오버헤드 제거, 관련 제안은 UserCommandServiceImpl 코멘트 참조)

서비스 레이어에서 어떤 옵션이 팀 표준/성능 목표에 맞는지 확인 부탁드립니다.

src/main/java/com/my_medi/domain/advice/repository/AdviceRepository.java (1)

14-14: 삭제 건수 반환 및 flush 전략 통일을 권장합니다

운영/감사 로깅이나 삭제 검증을 위해 삭제 건수 반환이 유용합니다. 또한 파생 삭제 쿼리는 기본적으로 EM clear가 자동 수행되지 않기 때문에, 다중 삭제 체인에서 일관성을 위해 최소한 flush는 보장하는 편이 좋습니다.

권장 변경:

-    void deleteByUserId(Long userId);
+    @org.springframework.data.jpa.repository.Modifying(flushAutomatically = true)
+    int deleteByUserId(Long userId);

추가로 파일 상단에 import가 필요합니다:

import org.springframework.data.jpa.repository.Modifying;
src/main/java/com/my_medi/domain/user/service/UserCommandServiceImpl.java (3)

66-83: 동시성 안전성: 삭제 도중 신규 자식 레코드가 생성될 수 있는 경쟁 조건 가능성

현재는 사용자와 자식들을 순차 삭제합니다. 그러나 트랜잭션 내에서 외부 쓰기가 허용된다면(예: 다른 서비스가 같은 userId로 스케줄/알림을 생성), 자식 삭제 이후 사용자 삭제 직전에 새 FK가 생겨 사용자 삭제가 실패할 수 있습니다. 사용자 행에 대한 비관적 락 또는 “삭제 진행 중” 플래그로의 게이트가 필요할 수 있습니다.

권장안 A) 사용자 행 비관적 락

  • UserRepository에 락 메서드 추가:
// UserRepository.java
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select u from User u where u.id = :id")
Optional<User> findByIdForUpdate(@Param("id") Long id);
  • 서비스에서 조회 시 사용:
-        User user = userRepository.findById(userId)
+        User user = userRepository.findByIdForUpdate(userId)
                 .orElseThrow(() -> UserHandler.NOT_FOUND);

권장안 B) 삭제 요청 시 먼저 “삭제 중” 상태로 마킹하고, 쓰기 경로에서 이를 차단
(Soft-delete 전환 계획이 있다면 동일 플래그를 재활용 가능)

검증 요청:

  • 이 서비스 메서드 실행 중 해당 userId로 레코드를 생성할 수 있는 경로가 있는지 확인
  • DB FK가 ON DELETE CASCADE로 설정되어 있다면 삭제 체인을 단순화할 수도 있습니다(현 PR은 애플리케이션 레벨 벌크 삭제 전략이므로 현상 유지도 타당).

81-83: detach된 엔티티 merge 오버헤드 제거를 위해 deleteById 사용을 권장합니다

앞서 ReportResult 벌크 삭제에서 clearAutomatically=true를 유지한다면, user는 detach 상태가 되어 delete(user) 시 merge가 추가로 발생합니다. 단순/명확성을 위해 deleteById 사용을 제안합니다.

-        userRepository.delete(user); // TODO : Hard delete이나 추후 soft로 변경예정
-        return user.getId();
+        userRepository.deleteById(userId); // TODO : Hard delete이나 추후 soft로 변경예정
+        return userId;

66-83: 삭제 결과 가시성(로깅/메트릭) 확보 제안

운영/감사 대응을 위해 각 삭제 메서드가 삭제 건수를 반환하도록 통일하고(예: int delete…), 합계를 로깅하면 유용합니다. 이는 장애 시 재시도/보정 작업 판단에도 도움이 됩니다.

원하시면 리포지토리 시그니처/어노테이션 통일(diff)과 함께 간단한 로깅/메트릭 코드 패치를 제안드리겠습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8b1b36e and 3494224.

📒 Files selected for processing (8)
  • src/main/java/com/my_medi/domain/advice/repository/AdviceRepository.java (1 hunks)
  • src/main/java/com/my_medi/domain/consultationRequest/repository/ConsultationRequestRepository.java (1 hunks)
  • src/main/java/com/my_medi/domain/notification/repository/UserNotificationRepository.java (1 hunks)
  • src/main/java/com/my_medi/domain/proposal/repository/ProposalRepository.java (1 hunks)
  • src/main/java/com/my_medi/domain/report/repository/ReportRepository.java (1 hunks)
  • src/main/java/com/my_medi/domain/reportResult/repository/ReportResultRepository.java (2 hunks)
  • src/main/java/com/my_medi/domain/schedule/repository/ScheduleRepository.java (1 hunks)
  • src/main/java/com/my_medi/domain/user/service/UserCommandServiceImpl.java (4 hunks)
🔇 Additional comments (3)
src/main/java/com/my_medi/domain/consultationRequest/repository/ConsultationRequestRepository.java (1)

87-87: deleteByUserId 반환형 변경 및 인덱스 정책 검토

deleteByUserId(Long userId) 반환형을 void → long으로 변경하여 삭제 건수를 반환하도록 수정해주세요.

  • 메소드 시그니처 수정
-    void deleteByUserId(Long userId);
+    long deleteByUserId(Long userId);
  • 일부 상태(정산/분쟁 처리 완료 이전) 데이터 보존 요건이 있는지 비즈니스 정책을 재확인하고, 필요 시 예외 처리 또는 비식별화 로직을 추가해주세요.

  • 기존 DDL(yml/xml/sql) 및 JPA @Index 어노테이션에서 consultation_request.user_id에 대한 인덱스 정의가 확인되지 않았습니다. 대량 삭제 성능을 위해 아래 예시처럼 인덱스를 추가하는 것을 권장합니다.
    • DDL 예시 (마이그레이션 파일 등):

    CREATE INDEX idx_consultation_request_user_id ON consultation_request(user_id);

    • JPA 엔티티 예시:

    @Table(
      name = "consultation_request",
      indexes = @Index(
        name = "idx_consultation_request_user_id",
        columnList = "user_id"
      )
    )
    public class ConsultationRequest { ... }
src/main/java/com/my_medi/domain/user/service/UserCommandServiceImpl.java (2)

31-37: 필요 리포지토리 의존성 주입 구성 적절합니다

삭제 체인에 필요한 리포지토리들이 누락 없이 주입되어 있어 구성은 타당합니다.


71-79: 자식 → 부모 순서의 삭제 체인 구성은 합리적입니다

ReportResult → Report 순서로의 정리는 FK 제약 충돌을 예방합니다. 다른 도메인(Advice/ConsultationRequest/Notification/Proposal/Schedule)도 선삭제 후 사용자 삭제 흐름이라 일관적입니다.

이 단계에서 각 리포지토리의 메서드가 진짜 “userId 기준의 물리 삭제”를 수행하는지(soft-delete 아님)를 재확인해 주세요. 필요 시 삭제 건수를 반환하도록 변경해 로그/모니터링에 활용하는 것을 제안합니다.

Page<Advice> findAllByUserId(Long userId, Pageable pageable);
Page<Advice> findAllByExpertIdAndUserId(Long expertId, Long userId, Pageable pageable);
Optional<Advice> findTop1ByUserIdOrderByCreatedDateDescIdDesc(Long userId);
void deleteByUserId(Long userId);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

파생 삭제 쿼리의 프로퍼티 경로 확인 필요 (userId vs user.id)

deleteByUserId(Long userId)가 올바로 동작하려면 Advice 엔티티에 실제 필드명이 userId여야 합니다. 연관매핑으로 User user만 있고 PK를 통해 접근하는 구조라면 deleteByUser_Id(Long userId)가 맞습니다. 아래 스크립트로 엔티티 정의를 확인해 주세요.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Advice 엔티티 위치/구조 확인
fd -t f -a 'Advice.java'

# 클래스 선언 주변 출력
rg -nP -C3 'class\s+Advice\b'

# userId 필드 또는 User 연관 필드 존재 여부 확인
rg -nP -C2 '\b(Long|long)\s+userId\b|\bUser\s+user\b'

# (선택) user FK 컬럼 어노테이션 힌트
rg -nP -C2 '@ManyToOne|@JoinColumn\([^)]*user_?id'

Length of output: 110324


AdviceRepository의 삭제 메소드명 수정 필요

Advice 엔티티에 실제로는 userId 필드가 없고, @ManyToOne으로 매핑된 User user 속성만 존재합니다. Spring Data JPA의 파생 쿼리는 엔티티 프로퍼티명을 기준으로 이름을 해석하므로, 현재 정의된

void deleteByUserId(Long userId);

는 올바르게 동작하지 않습니다. 대신 user.id 경로를 지정하려면 메소드명을 deleteByUser_Id로 변경해야 합니다.

– 대상 파일
• src/main/java/com/my_medi/domain/advice/repository/AdviceRepository.java line 14
– 변경 전

- void deleteByUserId(Long userId);

– 변경 후

+ void deleteByUser_Id(Long userId);

이렇게 수정하면 Advice 엔티티의 user.id 값을 기준으로 모든 관련 레코드를 삭제하도록 파생 쿼리가 올바르게 생성됩니다.

🤖 Prompt for AI Agents
In src/main/java/com/my_medi/domain/advice/repository/AdviceRepository.java
around line 14, the repository method void deleteByUserId(Long userId) is
incorrect because the Advice entity has a User user field (mapped via
@ManyToOne) rather than a userId property; rename the method to void
deleteByUser_Id(Long userId) so Spring Data JPA will generate a derived delete
query that navigates the association and deletes by Advice.user.id.

""")
List<Proposal> findAllByUserIdInWithUser(@Param("userIds") Set<Long> userIds);

void deleteByUserId(Long userId);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

유저당 Proposal 단건 가정 확인 및 반환형(long)으로 변경 권장

위쪽에 Optional<Proposal> findByUserId(Long userId)가 있는 것으로 보아 유저당 Proposal이 1개라는 도메인 가정이 있는 듯합니다. 그렇다면 삭제도 정확히 1건이 삭제되는지 모니터링하는 편이 좋습니다. 삭제 건수를 반환하도록 바꾸면 데이터 이상(중복 레코드 등)을 조기에 탐지할 수 있습니다.

-    void deleteByUserId(Long userId);
+    long deleteByUserId(Long userId);

또한 유니크 제약(예: proposal.user_id unique)이 실제로 잡혀 있는지 확인 부탁드립니다. 없다면 findByUserId가 Optional임에도 중복이 허용될 수 있습니다.

유니크 제약 및 매핑을 점검하려면:


🏁 Script executed:

#!/bin/bash
# Proposal ↔ User 매핑과 unique 제약 흔적 검색
rg -nP -C3 '(class\s+Proposal\b|@OneToOne|@ManyToOne|@JoinColumn|unique\s*=\s*true)' --type=java
# 마이그레이션/DDL에서 proposal.user_id 유니크/인덱스 확인
rg -nP -i -C2 '(create\s+unique\s+index|unique\s*\(|add\s+constraint).*proposal.*user_id' -g 'src/**/*.{sql,yml,xml}'

Length of output: 14213


Proposal 삭제 메서드 반환 타입 변경 및 유니크 제약 확인/추가 필요

현재 Proposal 엔티티에 @OneToOne(fetch = FetchType.LAZY) 매핑이 존재하나, DDL/마이그레이션 파일에서는 proposal.user_id에 대한 명시적 유니크 제약이나 인덱스가 검색되지 않았습니다.
또한 deleteByUserId 메서드는 반환 타입이 void라 삭제 건수를 알 수 없어, 중복 레코드 등 데이터 이상을 조기에 감지하기 어렵습니다.

아래 조치를 권장드립니다:

  • 메서드 반환 타입을 long으로 변경하고 삭제 건수를 반환받아 모니터링
-    void deleteByUserId(Long userId);
+    long deleteByUserId(Long userId);
  • Proposal ↔ User 1:1 매핑 확인
    • src/main/java/com/my_medi/domain/proposal/entity/Proposal.java (23~25행)
      @OneToOne(fetch = FetchType.LAZY)
      @JoinColumn(name = "user_id", nullable = false)
      private User user;
  • proposal.user_id 유니크 제약 확인
    • 마이그레이션/DDL 자동 생성 설정을 검토하거나
    • migration 파일에 다음과 같은 unique index/constraint를 추가
      ALTER TABLE proposal
        ADD CONSTRAINT uq_proposal_user_id UNIQUE (user_id);

위 개선 사항은 데이터 무결성 강화를 위해 필수적으로 반영하는 것을 권장드립니다.

🤖 Prompt for AI Agents
In src/main/java/com/my_medi/domain/proposal/repository/ProposalRepository.java
around line 23, change the deleteByUserId method to return the number of deleted
rows (use long/Long) instead of void so callers can detect unexpected multiple
deletions; update any calling code/tests accordingly. Also verify the Proposal
entity mapping at src/main/java/com/my_medi/domain/proposal/entity/Proposal.java
(lines ~23-25) keeps the @OneToOne and @JoinColumn(name="user_id",
nullable=false) and ensure the database enforces the 1:1 relationship by adding
a unique constraint/index on proposal.user_id in your migration or DDL (or
enable schema generation to produce it), and run/adjust migrations to apply the
unique constraint.


List<Long> findIdsByUserId(Long userId);

void deleteAllByUserId(Long userId);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

벌크 삭제 주의사항 + 삭제 건수 반환(long)으로 변경 권장

  • 파생 삭제 쿼리는 벌크 연산이라 엔티티 콜백이 동작하지 않고 1차 캐시와 불일치가 생길 수 있습니다. 같은 트랜잭션에서 Report 엔티티를 로드하지 않도록 주의해 주세요.
  • 운영 가시성을 위해 삭제 행 수 반환을 권장합니다.
-    void deleteAllByUserId(Long userId);
+    long deleteAllByUserId(Long userId);

성능 관점에서 report.user_id에 인덱스가 없다면 대량 삭제 시 테이블 스캔이 발생할 수 있습니다. 마이그레이션에 인덱스 존재 여부를 확인/추가해 주세요. 또한 DB FK에 ON DELETE CASCADE를 설정하면 결과(ReportResult) 선삭제 없이도 Report 삭제만으로 정리가 가능해져 애플리케이션 레벨 삭제 순서를 단순화할 수 있습니다(정책에 따라 선택).

인덱스/제약을 점검하려면:


🏁 Script executed:

#!/bin/bash
# 마이그레이션 파일에서 report.user_id 인덱스/외래키/온딜리트 검색
rg -nPi -C2 '(create\s+index|add\s+constraint|foreign\s+key|on\s+delete\s+cascade).*report.*user_id' -g 'src/**/**/*.{sql,yml,xml}'

Length of output: 133


다음 스크립트로 저장소 전체의 SQL/YAML/XML 마이그레이션 파일을 다시 검색하여 report.user_id 관련 인덱스·제약·ON DELETE CASCADE 설정 여부를 확인해 주세요. 결과가 없다면 수동으로 마이그레이션을 검토해 인덱스 및 FK 설정을 추가해야 합니다.


🏁 Script executed:

#!/bin/bash
# 전역 검색: report.user_id 인덱스/제약/ON DELETE CASCADE 확인
rg -nPi -C2 '(create\s+index|add\s+constraint|foreign\s+key|on\s+delete\s+cascade).*report.*user_id' \
  -g '*.sql' -g '*.xml' -g '*.yml' -g '*.yaml'

Length of output: 147


리턴 타입 변경 및 인덱스·제약 검토 필요

  • deleteAllByUserId 메서드 시그니처를 voidlong으로 변경해 삭제 건수 반환
    -    void deleteAllByUserId(Long userId);
    +    long deleteAllByUserId(Long userId);
  • 벌크 삭제는 엔티티 콜백이 동작하지 않고 1차 캐시와 불일치가 발생할 수 있으므로, 같은 트랜잭션에서 Report 엔티티를 로드하지 않도록 주의
  • 마이그레이션 파일 전역 검색 결과 report.user_id 인덱스가 발견되지 않음 → 대량 삭제 시 테이블 스캔 방지를 위해 인덱스 추가 필수
  • 연관 테이블(ReportResult) FK에 ON DELETE CASCADE 설정 검토 (정책에 따라 선택)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void deleteAllByUserId(Long userId);
long deleteAllByUserId(Long userId);
🤖 Prompt for AI Agents
In src/main/java/com/my_medi/domain/report/repository/ReportRepository.java
around line 45, change the method signature from void deleteAllByUserId(Long
userId) to long deleteAllByUserId(Long userId) so the repository returns the
number of rows deleted; ensure callers handle the returned long. When performing
bulk deletes, avoid loading Report entities in the same transaction or clear the
persistence context (EntityManager.clear()) after the delete to prevent
1st-level cache inconsistency. Add a DB migration to create an index on
report.user_id to prevent full table scans during large deletes, and review/add
ON DELETE CASCADE on the ReportResult FK if your deletion policy requires
cascading cleanup.

Copy link
Contributor

@H4nnhoi H4nnhoi left a comment

Choose a reason for hiding this comment

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

수고했어!

테스트 많이 해본거지? delete는 다양한 케이스 많이 테스트해봐야해 일단 승인할테니까 형이 주석에 달아둔것 처럼 다음에 soft delete 추가해봐봐(delete 두개로 만들자 하나는 개발자 용 계정 삭제, 하나는 직접 클라이언트가 사용할 소프트 딜리트)

userNotificationRepository.deleteByUserId(userId);
proposalRepository.deleteByUserId(userId);

reportResultRepository.deleteAllByUserId(userId);
Copy link
Contributor

Choose a reason for hiding this comment

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

이 method 제대로 실행되는지 테스트 부탁드립니다. reportResult는 리포트와 oneToOne 매핑이라서 이 메서드가 정확한지 불분명하다고 판단됩니다

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/main/resources/application-local.yml (3)

15-15: 로컬 프로필에서 ddl-auto: create 전환은 데이터 초기화 위험이 큼 — 환경변수 기반/프로필 분리 권장

create는 애플리케이션 기동 시 스키마를 드롭 후 재생성하여 로컬 DB 데이터가 매번 사라질 수 있습니다. PR 목적(사용자 계정 삭제 로직 리팩터링)과는 무관한 설정 변경으로 보이며, 의도된 것인지 확인 부탁드립니다. 로컬 DB를 팀원이 공유하거나 실수로 원격 DB를 local 프로필로 붙였을 때 데이터 손실 위험이 있습니다. 또한 로컬과 다른 스키마(제약/인덱스 미적용 등)에서 기능을 검증하게 되어 삭제 플로우의 제약 조건 검증이 왜곡될 수 있습니다.

의도가 단순히 “가끔 초기화”라면, 기본값은 update로 두고 필요 시 환경변수로 오버라이드하도록 하는 방식을 권장합니다.

-      ddl-auto: create
+      ddl-auto: ${SPRING_JPA_HIBERNATE_DDL_AUTO:update}

또는 초기화 전용 별도 프로필(local-reset)을 만들어 거기서만 create-drop을 사용하도록 분리하는 것도 방법입니다.


17-21: sql.init.mode: never인데 schema-locations가 지정되어 있음 — 죽은 설정 정리/의도 주석 권장

현재 설정에서는 SQL 초기화가 수행되지 않으므로 schema-locations가 효과가 없습니다. 혼선을 줄이기 위해 제거하거나, 향후 사용 계획이라면 주석으로 의도를 명시해 주세요.

   sql:
     init:
-      mode: never
-      schema-locations: classpath:db/schema-batch.sql
+      mode: never
+      # schema-locations 는 mode != never 일 때만 동작. 필요 시 활성화하세요.
+      # schema-locations: classpath:db/schema-batch.sql

36-39: Hikari TRACE/DEBUG 로깅은 과도한 로그/잠재 정보 노출 유발 — 필요 시점에만 일시 활성화

로컬이라도 TRACE는 로그 볼륨이 매우 크고, 연결 정보 등 민감한 메타데이터 노출 가능성이 있습니다. 문제 분석 시 일시적으로 올리고 기본값은 INFO 수준 유지하는 것을 권장합니다.

 logging:
   level:
-    com.zaxxer.hikari: TRACE
-    com.zaxxer.hikari.HikariConfig: DEBUG
+    com.zaxxer.hikari: INFO
+    com.zaxxer.hikari.HikariConfig: INFO
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3494224 and e112f54.

📒 Files selected for processing (4)
  • src/main/java/com/my_medi/domain/report/entity/Report.java (0 hunks)
  • src/main/java/com/my_medi/domain/reportResult/repository/ReportResultRepository.java (2 hunks)
  • src/main/java/com/my_medi/domain/user/service/UserCommandServiceImpl.java (4 hunks)
  • src/main/resources/application-local.yml (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/com/my_medi/domain/report/entity/Report.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/my_medi/domain/reportResult/repository/ReportResultRepository.java
  • src/main/java/com/my_medi/domain/user/service/UserCommandServiceImpl.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants