Skip to content
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

feat: 우수 스터디원 처리시 중복된 요청에 대한 검증 추가 #825

Merged
merged 5 commits into from
Dec 22, 2024

Conversation

Sangwook02
Copy link
Member

@Sangwook02 Sangwook02 commented Dec 18, 2024

🌱 관련 이슈

📌 작업 내용 및 특이사항

  • 우수 스터디원으로 지정하면 StudyAchievement 테이블에 하나의 레코드가 추가됩니다.
    따닥 방지용으로 Unique Constraint 걸어뒀는데, 이게 따닥을 막는 과정에서 500 에러를 던지고 있습니다.
  • 우수 스터디원 처리시 처리하고 싶은 멤버들의 Id 리스트를 받는데, 이미 지정된게 하나라도 있으면 예외를 발생하도록 검증을 추가했습니다.

📝 참고사항

📚 기타

Summary by CodeRabbit

  • 새로운 기능

    • 학생의 우수성을 지정하기 위한 검증 로직 추가.
    • 새로운 메서드가 추가되어 특정 조건에 따른 성취 기록 수를 계산.
    • 새로운 클래스 StudyAchievementValidator가 도입되어 우수 학생 지정을 위한 검증 기능 제공.
    • 새로운 오류 코드 STUDY_ACHIEVEMENT_ALREADY_EXISTS 추가.
  • 버그 수정

    • 성취 기록 삭제 메서드의 쿼리 조건 개선.

@Sangwook02 Sangwook02 added the ✨ feature 새로운 기능 추가 및 수정 label Dec 18, 2024
@Sangwook02 Sangwook02 self-assigned this Dec 18, 2024
@Sangwook02 Sangwook02 requested a review from a team as a code owner December 18, 2024 11:24
Copy link

coderabbitai bot commented Dec 18, 2024

워크스루

이 풀 리퀘스트는 스터디 성과 관리 시스템에 중복된 우수 스터디원 지정을 방지하는 검증 로직을 추가합니다. 새로운 StudyAchievementValidator 클래스를 도입하여 이미 우수 스터디원으로 지정된 학생이 있는지 확인하는 기능을 구현했습니다. 이를 통해 중복 지정을 방지하고 데이터 무결성을 보장합니다.

변경 사항

파일 변경 요약
...study/application/MentorStudyAchievementService.java StudyAchievementValidator 의존성 추가 및 우수 스터디원 지정 로직 검증
...study/dao/StudyAchievementCustomRepository.java 스터디 성과 카운트를 위한 새로운 메서드 추가
...study/dao/StudyAchievementCustomRepositoryImpl.java 쿼리 조건 개선 및 카운트 메서드 구현
...study/domain/StudyAchievementValidator.java 우수 스터디원 중복 지정 검증 로직 추가
...global/exception/ErrorCode.java 중복 지정 시 사용할 새로운 에러 코드 추가

연결된 이슈 평가

목표 해결 여부 설명
우수 스터디원 중복 요청 검증 [#821]

관련 가능성 있는 PR

제안된 라벨

🐛 bug/error

제안된 리뷰어

  • kckc0608
  • uwoobeat

토끼의 시

🐰 우수한 스터디의 길
중복은 막고 검증하니
코드의 품격 높아지네
토끼의 눈빛 반짝이며
깨끗한 로직 노래하네! 🌟


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between bc33871 and 89e76a7.

📒 Files selected for processing (2)
  • src/main/java/com/gdschongik/gdsc/domain/study/application/MentorStudyAchievementService.java (3 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepositoryImpl.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/gdschongik/gdsc/domain/study/application/MentorStudyAchievementService.java
  • src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepositoryImpl.java

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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

Job Summary for Gradle

Check Style and Test to Develop :: build-test
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
gdsc check 8.5 Build Scan published

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 (6)
src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepository.java (1)

13-14: 매개변수 유효성 검사 추가 제안

구현 클래스에서 다음 사항들에 대한 유효성 검사를 추가하는 것이 좋을 것 같습니다:

  • studyId가 null이 아닌지 확인
  • achievementType이 null이 아닌지 확인
  • studentIds가 null이 아니거나 비어있지 않은지 확인
src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyAchievementValidator.java (1)

11-16: 검증 로직이 명확하고 잘 구현되어 있습니다.

검증 로직이 단순하고 목적에 맞게 잘 구현되어 있습니다. 다만 다음과 같은 개선사항을 제안드립니다:

  1. 메서드 파라미터 이름을 더 명확하게 표현하면 좋을 것 같습니다.
  2. 예외 메시지에 구체적인 정보를 포함하면 디버깅에 도움이 될 것 같습니다.
-    public void validateDesignateOutstandingStudent(long countStudyAchievementsAlreadyExist) {
+    public void validateDesignateOutstandingStudent(long existingAchievementsCount) {
         // 이미 우수 스터디원으로 지정된 스터디원이 있는 경우
-        if (countStudyAchievementsAlreadyExist > 0) {
-            throw new CustomException(STUDY_ACHIEVEMENT_ALREADY_EXISTS);
+        if (existingAchievementsCount > 0) {
+            throw new CustomException(STUDY_ACHIEVEMENT_ALREADY_EXISTS, 
+                String.format("이미 %d개의 우수 스터디원 지정이 존재합니다.", existingAchievementsCount));
         }
     }
src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepositoryImpl.java (2)

34-42: 새로운 카운트 메서드가 잘 구현되어 있습니다.

카운트 쿼리가 명확하게 구현되어 있으며, 기존 코드와 일관성이 있습니다. 다만 다음과 같은 개선사항을 제안드립니다:

  1. 메서드에 Javadoc을 추가하여 사용 목적과 파라미터를 문서화하면 좋을 것 같습니다.
  2. 반환값이 null일 수 있는 경우에 대한 처리를 추가하면 좋을 것 같습니다.
+    /**
+     * 특정 스터디의 우수 스터디원 지정 여부를 확인합니다.
+     *
+     * @param studyId 스터디 ID
+     * @param achievementType 업적 유형
+     * @param studentIds 학생 ID 목록
+     * @return 지정된 우수 스터디원 수
+     */
     @Override
     public long countByStudyIdAndAchievementTypeAndStudentIds(
             Long studyId, AchievementType achievementType, List<Long> studentIds) {
-        return (long) queryFactory
+        Long count = queryFactory
                 .select(studyAchievement.count())
                 .from(studyAchievement)
                 .where(eqStudyId(studyId), eqAchievementType(achievementType), containsStudentId(studentIds))
                 .fetchOne();
+        return count != null ? count : 0L;
     }

48-54: 조건 메서드들이 잘 분리되어 있습니다.

쿼리 조건들이 재사용 가능한 메서드로 잘 분리되어 있습니다. 다만 다음과 같은 개선사항을 제안드립니다:

각 메서드에 간단한 Javadoc을 추가하여 사용 목적을 문서화하면 좋을 것 같습니다.

+    /**
+     * 업적 유형 일치 여부를 확인하는 조건을 생성합니다.
+     */
     private BooleanExpression eqAchievementType(AchievementType achievementType) {
         return achievementType != null ? studyAchievement.achievementType.eq(achievementType) : null;
     }

+    /**
+     * 학생 ID 목록에 포함되는지 확인하는 조건을 생성합니다.
+     */
     private BooleanExpression containsStudentId(List<Long> memberIds) {
         return memberIds != null ? studyAchievement.student.id.in(memberIds) : null;
     }
src/main/java/com/gdschongik/gdsc/domain/study/application/MentorStudyAchievementService.java (1)

42-51: 중복 검증 로직이 잘 통합되어 있습니다.

검증 로직이 서비스에 잘 통합되어 있습니다. 다만 다음과 같은 개선사항을 제안드립니다:

  1. 검증 실패 시의 로그 처리를 추가하면 좋을 것 같습니다.
  2. 검증에 실패한 학생 ID들을 로그에 포함하면 디버깅에 도움이 될 것 같습니다.
         long countByStudyIdAndStudentIds =
                 studyHistoryRepository.countByStudyIdAndStudentIds(studyId, request.studentIds());
         long countStudyAchievementsAlreadyExist =
                 studyAchievementRepository.countByStudyIdAndAchievementTypeAndStudentIds(
                         studyId, request.achievementType(), request.studentIds());

+        log.debug(
+                "[MentorStudyAchievementService] 우수 스터디원 지정 검증: studyId={}, studentIds={}, existingCount={}",
+                studyId, request.studentIds(), countStudyAchievementsAlreadyExist);
+
         studyValidator.validateStudyMentor(currentMember, study);
         studyHistoryValidator.validateAppliedToStudy(
                 countByStudyIdAndStudentIds, request.studentIds().size());
         studyAchievementValidator.validateDesignateOutstandingStudent(countStudyAchievementsAlreadyExist);
src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java (1)

131-133: 코드 변경이 적절하며 문서화 추가 제안

에러 코드의 추가가 적절하게 이루어졌습니다. HttpStatus.CONFLICT 상태 코드의 사용이 중복 지정 상황을 잘 표현하고 있습니다.

다만, 이 에러 코드가 언제 발생하는지 다른 개발자들이 쉽게 이해할 수 있도록 주석을 추가하면 좋을 것 같습니다:

    // StudyAchievement
+   /**
+    * 이미 우수 스터디원으로 지정된 회원을 다시 지정하려고 할 때 발생하는 에러
+    */
    STUDY_ACHIEVEMENT_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 우수 스터디원으로 지정된 스터디원이 존재합니다."),
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 08ea2a9 and bc33871.

📒 Files selected for processing (5)
  • src/main/java/com/gdschongik/gdsc/domain/study/application/MentorStudyAchievementService.java (3 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepository.java (1 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepositoryImpl.java (1 hunks)
  • src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyAchievementValidator.java (1 hunks)
  • src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java (1 hunks)
🔇 Additional comments (2)
src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepository.java (1)

13-14: 메서드 구현이 잘 되었습니다!

중복 지정을 방지하기 위한 카운트 메서드가 적절하게 추가되었습니다. 메서드 시그니처가 명확하고 기존 코드 스타일과 일관성이 있습니다.

아래 스크립트를 실행하여 구현 클래스의 존재 여부를 확인하겠습니다:

✅ Verification successful

구현 클래스가 정상적으로 존재하며 메서드가 올바르게 구현되었습니다!

StudyAchievementCustomRepositoryImpl 클래스에서 QueryDSL을 사용하여 적절하게 구현되어 있습니다. 메서드의 구현이 명확하고 기존 코드 스타일과 일관성이 있으며, 요구사항에 맞게 잘 작성되었습니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation class exists
# Expected: Find the implementation class that contains the new method

rg -l "implements.*StudyAchievementCustomRepository" 

Length of output: 147


Script:

#!/bin/bash
# Check if the implementation class contains the method
rg -A 5 "countByStudyIdAndAchievementTypeAndStudentIds" src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepositoryImpl.java

Length of output: 530

src/main/java/com/gdschongik/gdsc/domain/study/application/MentorStudyAchievementService.java (1)

32-32: 의존성 주입이 적절합니다.

새로운 Validator가 final 필드로 적절하게 주입되어 있습니다.

Copy link
Member

@kckc0608 kckc0608 left a comment

Choose a reason for hiding this comment

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

lgtm

studyHistoryRepository.countByStudyIdAndStudentIds(studyId, request.studentIds());
long countStudyAchievementsAlreadyExist =
Copy link
Member

Choose a reason for hiding this comment

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

변수명이 조금 어색한 것 같네요. xxxCount 와 같은 구조가 되어야 할 것 같습니다.

@Override
public long countByStudyIdAndAchievementTypeAndStudentIds(
Long studyId, AchievementType achievementType, List<Long> studentIds) {
return (long) queryFactory
Copy link
Member

Choose a reason for hiding this comment

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

Long -> long으로 auto unboxing이 수행되기 때문에 명시적으로 캐스팅해주지 않아도 됩니다.
그리고 언박싱 과정에서 IDE에서 NPE 경고가 발생하는데, Objects.requireNonnull 을 사용하면 더 적절히 쓸 수 있을듯 합니다 (실제로는 null일 가능성이 없기 때문에 Optional.ofNullable을 사용하지 않는 점을 참고해주세요)

Copy link
Member Author

Choose a reason for hiding this comment

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

좋아요

@DomainService
public class StudyAchievementValidator {

public void validateDesignateOutstandingStudent(long countStudyAchievementsAlreadyExist) {
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 놓고 보니 designate라는 워딩이 좀 맘에 안드네요 (별로 안 직관적임). nominated나... candidate 워딩 써도 될 것 같은데... 투두로 파놓고 나중에 이슈로 일괄 변경해보면 어떨까 싶습니다

Copy link
Member Author

Choose a reason for hiding this comment

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

nominate는 후보로 올려두는 느낌이 있어서 designate로 했는데 candidate도 마찬가지로 후보의 느낌이 있는 것 같아요.

Copy link

Job Summary for Gradle

Check Style and Test to Develop :: build-test
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
gdsc check 8.5 Build Scan published

Copy link
Member

@uwoobeat uwoobeat left a comment

Choose a reason for hiding this comment

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

lgtm

@Sangwook02 Sangwook02 merged commit 775c1d4 into develop Dec 22, 2024
1 check passed
@Sangwook02 Sangwook02 deleted the fix/821-duplicate-request branch December 22, 2024 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ feature 새로운 기능 추가 및 수정
Projects
None yet
Development

Successfully merging this pull request may close these issues.

🐛 우수 스터디원 처리시 중복된 요청에 대한 검증 추가
3 participants