-
Notifications
You must be signed in to change notification settings - Fork 3
fix : 테스트케이스 삭제 시 쿼리 미발생 문제 해결 (JPA 양방향 관계 동기화) #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Problem(부모) 엔티티가 영속성 컨텍스트에 로딩된 상태에서 Testcase(자식)만 삭제 시도 시, 트랜잭션 커밋 시점에 부모의 컬렉션에 남아있는 자식 객체가 Cascade 설정으로 인해 다시 저장되는 현상 수정 - 삭제 로직 수행 전, 부모 객체의 컬렉션에서도 해당 자식 객체를 제거하는 로직 수행하도록 함
둘러보기세 개의 파일이 수정되었습니다. TestcaseDomainService에서 테스트 케이스 제거 시 검증 로직을 제거하고 단순 컬렉션 제거로 변경했습니다. TestcaseRepositoryImpl에서는 delete 메서드를 deleteById 호출로 교체했고, SwaggerConfig에서는 프로덕션 서버 URL을 https://ezcode.my에서 https://api.ezcode.my로 업데이트했습니다. 변경 사항
예상 코드 리뷰 소요 시간🎯 2 (단순) | ⏱️ ~10분
관련된 가능성 있는 PR들
제안하는 라벨
제안하는 리뷰어
시
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/org/ezcode/codetest/domain/problem/service/TestcaseDomainService.java (1)
45-56: Add ownership validation to prevent cross-problem testcase deletion.The current
removeTestcase()method lacks verification that the testcase actually belongs to the provided problem. This allowsremoveTestcase(problemA, testcaseIdOfProblemB)to delete problemB's testcase without raising an exception.Since Testcase already has a
problemIdMatched()method, use it to validate ownership before deletion:Testcase findTestcase = testcaseRepository.findByTestcase(testcaseId); +// Verify testcase belongs to the problem +if (!findTestcase.problemIdMatched(problem.getId())) { + throw new TestcaseException(TestcaseExceptionCode.TESTCASE_NOT_FOUND); +} -if (problem.getTestcases() != null) { - problem.getTestcases().remove(findTestcase); -} +problem.getTestcases().remove(findTestcase); testcaseRepository.delete(findTestcase);The null-check for
problem.getTestcases()can be removed since it's initialized as an empty ArrayList in the Problem entity. With@OneToMany(orphanRemoval=true), the removal from the collection is sufficient for cleanup.
🧹 Nitpick comments (1)
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/problem/TestcaseRepositoryImpl.java (1)
45-45:deleteById사용 시 중복 조회·예외 동작 한 번만 점검해 주세요.현재
removeTestcase흐름에서는 이미findByTestcase로 엔티티를 로딩한 뒤 여기로 내려오기 때문에, Spring Data JPA 기본 구현 기준으로는deleteById내부에서 다시 한 번findById를 호출해 동일 엔티티를 두 번 조회하게 됩니다.
특별히 ID 기반 삭제가 꼭 필요한 다른 사용처가 없다면, 기존처럼delete(testcase)를 유지하는 편이 더 단순하고 약간 더 효율적일 수 있습니다. 반대로 “detached 엔티티도 안전하게 삭제하고 싶다”는 의도라면, 그 의도를 주석으로 남겨 두시면 이후 코드를 읽는 사람이 이해하기 쉬울 것 같습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/java/org/ezcode/codetest/domain/problem/service/TestcaseDomainService.java(1 hunks)src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/problem/TestcaseRepositoryImpl.java(1 hunks)src/main/java/org/ezcode/codetest/infrastructure/swagger/config/SwaggerConfig.java(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-06-15T04:37:29.231Z
Learnt from: chat26666
Repo: ezcode-my/backend PR: 64
File: src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/game/EncounterChoiceRepositoryImpl.java:0-0
Timestamp: 2025-06-15T04:37:29.231Z
Learning: EncounterChoiceRepositoryImpl in src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/game/EncounterChoiceRepositoryImpl.java is intentionally a skeleton implementation that is work-in-progress and will be completed later.
Applied to files:
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/problem/TestcaseRepositoryImpl.java
📚 Learning: 2025-07-02T12:05:54.917Z
Learnt from: Kimminu7
Repo: ezcode-my/backend PR: 133
File: src/test/java/org/ezcode/codetest/domain/problem/service/ProblemDomainServiceTest.java:92-99
Timestamp: 2025-07-02T12:05:54.917Z
Learning: ProblemDomainService의 removeProblem 메서드는 DB에서 Problem을 삭제한 후 Elasticsearch에서도 해당 ProblemSearchDocument를 찾아서 삭제합니다. 만약 Elasticsearch에서 문서를 찾지 못하면 ProblemException(PROBLEM_NOT_FOUND)을 던지므로, 테스트에서는 problem.getId()와 searchRepository.findById() 모두 적절하게 mock해야 합니다.
Applied to files:
src/main/java/org/ezcode/codetest/domain/problem/service/TestcaseDomainService.java
📚 Learning: 2025-06-06T07:59:41.806Z
Learnt from: thezz9
Repo: ezcode-my/backend PR: 36
File: src/main/java/org/ezcode/codetest/domain/submission/dto/SubmissionData.java:47-58
Timestamp: 2025-06-06T07:59:41.806Z
Learning: SubmissionData.withAggregatedStats() 메서드에서 testCaseList는 상위 프로세스(base() 메서드의 problemInfo.testcaseList())에서 이미 null 체크가 처리되어 오므로 추가적인 null 체크가 불필요함
Applied to files:
src/main/java/org/ezcode/codetest/domain/problem/service/TestcaseDomainService.java
📚 Learning: 2025-06-05T00:52:54.460Z
Learnt from: chat26666
Repo: ezcode-my/backend PR: 31
File: src/main/java/org/ezcode/codetest/domain/problem/model/entity/ProblemSearchDocument.java:49-56
Timestamp: 2025-06-05T00:52:54.460Z
Learning: ProblemSearchDocument.from() 메서드는 트랜잭션 내에서 DB에서 실제로 조회한 Problem 엔티티만 사용하므로 null 체크가 불필요함. 이 컨텍스트에서는 null Problem이 있을 경우 이미 이전 단계에서 예외가 발생함.
Applied to files:
src/main/java/org/ezcode/codetest/domain/problem/service/TestcaseDomainService.java
⏰ 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). (1)
- GitHub Check: build
🔇 Additional comments (1)
src/main/java/org/ezcode/codetest/infrastructure/swagger/config/SwaggerConfig.java (1)
17-17: 운영 서버 URL 변경 의도만 한 번 더 확인해주세요.
https://api.ezcode.my로 변경 자체는 문제 없어 보입니다. 실제 운영 환경에서 사용하는 도메인/게이트웨이 주소와 완전히 일치하는지만 배포 전에 한 번만 더 확인해 주세요.
📌 이슈 상황 (Problem)
테스트케이스 삭제 API(DELETE /testcases/{id})를 호출하면 응답은 204 No Content로 정상 반환되나, 실제 DB에서는 데이터가 삭제되지 않음.
Hibernate 로그 확인 결과, DELETE 쿼리 자체가 생성되지 않음.
@transactional 및 deleteById 등을 점검했으나 정상이었음.
🔍 원인 분석 (Cause)
JPA 영속성 컨텍스트의 특성: getProblem()을 통해 부모 엔티티(Problem)가 이미 영속 상태(Managed)로 로딩되어 있음.
이때 Problem 엔티티 내부의 List 컬렉션은 여전히 삭제 대상인 Testcase 객체를 참조하고 있음.
testcaseRepository.delete()를 호출했더라도, 트랜잭션 Commit(Flush) 시점에 JPA가 부모 엔티티의 상태를 감지함.
부모 리스트에 자식이 남아있으므로 Cascade 옵션(또는 Dirty Checking)에 의해 삭제하려던 객체가 다시 저장되거나 삭제가 취소됨.
✅ 해결 방법 (Solution)
DB 삭제 요청뿐만 아니라, 객체 수준에서도 부모-자식 관계를 끊어줌.
problem.getTestcases().remove(testcase) 로직을 추가하여 영속성 컨텍스트 내의 데이터 일관성 확보.
📸 테스트 결과
수정 후 DELETE 쿼리 정상 발생 및 DB 데이터 삭제 확인 완료.
Summary by CodeRabbit
릴리스 노트
Bug Fixes
Chores
✏️ Tip: You can customize this high-level summary in your review settings.