-
Notifications
You must be signed in to change notification settings - Fork 1
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: 스터디 공지 관련 API 구현 #933
Conversation
📝 WalkthroughWalkthrough이번 PR은 기존 MentorStudyServiceV2 클래스에서 변수 선언 순서를 조정하고 로깅 메시지의 오타를 수정하는 변경과 함께, 멘토 스터디 공지사항 기능을 위한 새로운 Controller, Service, Repository, DTO가 추가되었습니다. 새로 추가된 코드는 공지사항의 생성, 수정, 삭제 기능을 지원하며, 멘토 권한 검증 및 관련 데이터 처리를 수행합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant C as MentorStudyAnnouncementControllerV2
participant S as MentorStudyAnnouncementServiceV2
participant M as MemberUtil
participant R as Repository
C->>S: createStudyAnnouncement(request)
S->>M: getCurrentMember()
S->>R: StudyV2 조회 및 StudyAnnouncementV2 생성
S-->>C: HTTP 200 OK 응답
Possibly related PRs
Poem
Tip CodeRabbit's docstrings feature is now available as part of our Pro Plan! Simply use the command ✨ Finishing Touches
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Job Summary for GradleCheck Style and Test to Develop :: build-test
|
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
🧹 Nitpick comments (8)
src/main/java/com/gdschongik/gdsc/domain/studyv2/api/MentorStudyControllerV2.java (3)
44-50
: 생성 엔드포인트는 201 응답 코드를 반환하는 것이 더 적절합니다.리소스 생성 시 HTTP 규약에 따르면 201 Created 상태 코드를 반환하는 것이 좋습니다.
다음과 같이 수정하는 것을 고려해보세요:
@PostMapping("/{studyId}/announcements") public ResponseEntity<Void> createStudyAnnouncement( @PathVariable Long studyId, @Valid @RequestBody StudyAnnouncementCreateUpdateRequest request) { mentorStudyServiceV2.createStudyAnnouncement(studyId, request); - return ResponseEntity.ok().build(); + return ResponseEntity.status(201).build(); }
52-58
: API 패턴이 일관되지 않습니다.생성 API는
/{studyId}/announcements
형태인데 비해, 수정 API는/announcements/{studyAnnouncementId}
형태입니다. 일관된 패턴을 사용하는 것이 좋습니다.리소스 식별자를 좀 더 일관되게 관리하는 방식으로 변경하는 것을 고려해보세요. 예를 들어:
-@PutMapping("/announcements/{studyAnnouncementId}") +@PutMapping("/{studyId}/announcements/{studyAnnouncementId}") public ResponseEntity<Void> updateStudyAnnouncement( - @PathVariable Long studyAnnouncementId, @Valid @RequestBody StudyAnnouncementCreateUpdateRequest request) { + @PathVariable Long studyId, @PathVariable Long studyAnnouncementId, + @Valid @RequestBody StudyAnnouncementCreateUpdateRequest request) { mentorStudyServiceV2.updateStudyAnnouncement(studyAnnouncementId, request); return ResponseEntity.ok().build(); }서비스 메서드 서명도 함께 수정해야 합니다.
60-65
: API 패턴이 일관되지 않습니다.삭제 API도 생성 API와 다른 패턴을 사용하고 있습니다. 일관된 패턴을 사용하는 것이 좋습니다.
-@DeleteMapping("/announcements/{studyAnnouncementId}") +@DeleteMapping("/{studyId}/announcements/{studyAnnouncementId}") public ResponseEntity<Void> deleteStudyAnnouncement( - @PathVariable Long studyAnnouncementId) { + @PathVariable Long studyId, @PathVariable Long studyAnnouncementId) { mentorStudyServiceV2.deleteStudyAnnouncement(studyAnnouncementId); return ResponseEntity.ok().build(); }서비스 메서드 서명도 함께 수정해야 합니다.
src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java (5)
59-60
: findFetchById 대신 findById를 사용하고 있습니다.
updateStudy
메서드에서는findFetchById
를 사용하고 있는데, 여기서는findById
를 사용하고 있습니다. 일관성을 위해 동일한 메서드를 사용하는 것이 좋습니다.-StudyV2 study = studyV2Repository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND)); +StudyV2 study = studyV2Repository.findFetchById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
66-67
: 로그 메시지에 오타가 있습니다.로그 메시지에 "Mentory"라는 오타가 있습니다. 클래스 이름과 일치하도록 "Mentor"로 수정해야 합니다.
-log.info("[MentoryStudyServiceV2] 스터디 공지 생성: studyAnnouncementId={}", studyAnnouncement.getId()); +log.info("[MentorStudyServiceV2] 스터디 공지 생성: studyAnnouncementId={}", studyAnnouncement.getId());
82-83
: 로그 메시지에 오타가 있습니다.로그 메시지에 "Mentory"라는 오타가 있습니다. 클래스 이름과 일치하도록 "Mentor"로 수정해야 합니다.
-log.info("[MentoryStudyServiceV2] 스터디 공지 수정 완료: studyAnnouncementId={}", studyAnnouncement.getId()); +log.info("[MentorStudyServiceV2] 스터디 공지 수정 완료: studyAnnouncementId={}", studyAnnouncement.getId());
97-98
: 로그 메시지에 오타가 있습니다.로그 메시지에 "Mentory"라는 오타가 있습니다. 클래스 이름과 일치하도록 "Mentor"로 수정해야 합니다.
-log.info("[MentoryStudyServiceV2] 스터디 공지 삭제 완료: studyAnnouncementId={}", studyAnnouncement.getId()); +log.info("[MentorStudyServiceV2] 스터디 공지 삭제 완료: studyAnnouncementId={}", studyAnnouncement.getId());
31-31
: DDD 원칙에 따른 설계 고려 필요이전 리뷰에서 배운 내용을 참고하면, StudyV2는 애그리게이트 루트이고 StudySession은 자식 엔티티로서 StudyV2Repository를 통해서만 접근해야 한다고 명시되어 있습니다. 비슷한 맥락에서 StudyAnnouncementV2도 같은 원칙을 적용해야 할 수 있습니다.
애그리게이트 패턴에 따라 StudyAnnouncementV2Repository를 별도로 생성하는 대신, StudyV2Repository를 통해 StudyAnnouncementV2에 접근하는 방식을 고려해보세요. 이는 DDD 원칙을 더 잘 따르는 방식일 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/java/com/gdschongik/gdsc/domain/studyv2/api/MentorStudyControllerV2.java
(2 hunks)src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java
(3 hunks)src/main/java/com/gdschongik/gdsc/domain/studyv2/dao/StudyAnnouncementV2Repository.java
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main/java/com/gdschongik/gdsc/domain/studyv2/dao/StudyAnnouncementV2Repository.java
🧰 Additional context used
🧠 Learnings (1)
src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java (1)
Learnt from: uwoobeat
PR: GDSC-Hongik/gdsc-server#889
File: src/main/java/com/gdschongik/gdsc/domain/studyv2/api/MentorStudyControllerV2.java:35-39
Timestamp: 2025-02-12T11:11:19.196Z
Learning: In the GDSC server project's V2 domain, following DDD principles, StudyV2 is an aggregate root and StudySession is its child entity. Therefore, StudySession should only be accessed through StudyV2 aggregate root using StudyV2Repository, not directly via a separate StudySessionRepository.
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.
lgtm
Job Summary for GradleCheck Style and Test to Develop :: build-test
|
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
🧹 Nitpick comments (1)
src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java (1)
56-97
: 코드 중복 감소를 위한 리팩토링 고려세 개의 메서드(createStudyAnnouncement, updateStudyAnnouncement, deleteStudyAnnouncement)가 유사한 패턴(현재 멤버 조회, 유효성 검증, 작업 수행, 로깅)을 따르고 있습니다. 이러한 공통 로직을 추출하여 코드 중복을 줄이는 것을 고려해보세요.
예를 들어, 공지사항 ID로 공지사항과 관련 스터디를 조회하고 멘토 권한을 검증하는 헬퍼 메서드를 만들 수 있습니다:
private StudyAnnouncementV2 validateAndGetAnnouncement(Long announcementId) { Member currentMember = memberUtil.getCurrentMember(); StudyAnnouncementV2 announcement = studyAnnouncementV2Repository .findById(announcementId) .orElseThrow(() -> new CustomException(STUDY_ANNOUNCEMENT_NOT_FOUND)); StudyV2 study = announcement.getStudy(); studyValidatorV2.validateStudyMentor(currentMember, study); return announcement; }이를 사용하면 업데이트와 삭제 메서드의 코드를 간소화할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java
(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java (1)
Learnt from: uwoobeat
PR: GDSC-Hongik/gdsc-server#889
File: src/main/java/com/gdschongik/gdsc/domain/studyv2/api/MentorStudyControllerV2.java:35-39
Timestamp: 2025-02-12T11:11:19.196Z
Learning: In the GDSC server project's V2 domain, following DDD principles, StudyV2 is an aggregate root and StudySession is its child entity. Therefore, StudySession should only be accessed through StudyV2 aggregate root using StudyV2Repository, not directly via a separate StudySessionRepository.
🔇 Additional comments (4)
src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java (4)
6-7
: 의존성 주입 및 임포트 추가가 적절합니다.StudyAnnouncementV2 관련 기능을 구현하기 위한 필요한 클래스들이 적절하게 임포트되었으며, repository 의존성이 주입되었습니다.
Also applies to: 9-9, 31-31
56-67
: 스터디 공지 생성 기능이 잘 구현되었습니다.스터디 공지 생성 메서드가 다음과 같은 적절한 로직을 포함하고 있습니다:
- 현재 인증된 멤버 확인
- 스터디 존재 여부 확인
- 멘토 권한 검증
- 공지사항 생성 및 저장
- 로그 기록
한 가지 고려사항으로는 59번 줄에서
findById
를 사용하고 있는데, 44번 줄의updateStudy
메서드에서는findFetchById
를 사용합니다. 연관 엔티티의 즉시 로딩이 필요하지 않다면 현재 구현도 충분하지만, 일관성을 위해 검토해보세요.
69-83
: 스터디 공지 수정 기능이 적절하게 구현되었습니다.스터디 공지 수정 메서드는 다음과 같은 필수 로직을 포함합니다:
- 현재 인증된 멤버 확인
- 공지사항 존재 여부 확인
- 관련 스터디 조회
- 멘토 권한 검증
- 공지사항 업데이트 및 저장
- 로그 기록
Domain-Driven Design 원칙에 따라 StudyV2 애그리게이트 루트를 통해 StudyAnnouncementV2 엔티티에 접근하는 방식이 잘 구현되어 있습니다.
85-97
: 스터디 공지 삭제 기능이 잘 구현되었습니다.스터디 공지 삭제 메서드도 다른 메서드들과 일관된 패턴으로 구현되었습니다:
- 현재 인증된 멤버 확인
- 공지사항 존재 여부 확인
- 관련 스터디 조회
- 멘토 권한 검증
- 공지사항 삭제
- 로그 기록
트랜잭션 관리가 잘 되어 있고, 적절한 예외 처리와 로깅이 포함되어 있습니다.
Job Summary for GradleCheck Style and Test to Develop :: build-test
|
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: 1
🧹 Nitpick comments (4)
src/main/java/com/gdschongik/gdsc/domain/studyv2/dto/request/StudyAnnouncementCreateRequest.java (1)
8-11
: 유효성 검증 일관성 개선이 필요합니다.link 필드에는 URL 유효성 검증이 없습니다. 링크 URL이 유효한지 확인하는 @url 어노테이션을 추가하는 것이 좋겠습니다.
또한 일관성을 위해 studyId에도 에러 메시지를 추가하고, 모든 필드에 @Schema 어노테이션을 사용하는 것이 좋겠습니다:
public record StudyAnnouncementCreateRequest( - @NotNull @Positive Long studyId, + @NotNull(message = "스터디 ID가 필요합니다.") @Positive(message = "스터디 ID는 양수여야 합니다.") @Schema(description = "스터디 ID") Long studyId, @NotBlank(message = "공지제목이 비었습니다.") @Schema(description = "공지제목") String title, - @Schema(description = "공지링크") String link) {} + @URL(message = "유효한 URL 형식이어야 합니다.") @Schema(description = "공지링크") String link) {}src/main/java/com/gdschongik/gdsc/domain/studyv2/api/MentorStudyAnnouncementControllerV2.java (1)
29-32
: 생성 API의 응답 코드 개선이 필요합니다.리소스 생성 시 HTTP 상태 코드로 200(OK) 대신 201(Created)을 반환하는 것이 REST API 설계 관행에 더 적합합니다. 또한 생성된 리소스의 위치를 location 헤더에 포함시키는 것이 좋습니다.
@PostMapping public ResponseEntity<Void> createStudyAnnouncement(@Valid @RequestBody StudyAnnouncementCreateRequest request) { - mentorStudyAnnouncementServiceV2.createStudyAnnouncement(request); - return ResponseEntity.ok().build(); + Long announcementId = mentorStudyAnnouncementServiceV2.createStudyAnnouncement(request); + return ResponseEntity.created(URI.create("/v2/mentor/study-announcements/" + announcementId)).build(); }이를 위해 service 메서드도 생성된 ID를 반환하도록 수정해야 합니다.
src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyAnnouncementServiceV2.java (2)
30-42
: createStudyAnnouncement 메서드 반환 값 개선이 필요합니다.생성된 공지사항의 ID를 반환하면 컨트롤러에서 location 헤더 설정 등에 활용할 수 있습니다.
@Transactional -public void createStudyAnnouncement(StudyAnnouncementCreateRequest request) { +public Long createStudyAnnouncement(StudyAnnouncementCreateRequest request) { Member currentMember = memberUtil.getCurrentMember(); StudyV2 study = studyV2Repository.findById(request.studyId()).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND)); studyValidatorV2.validateStudyMentor(currentMember, study); StudyAnnouncementV2 studyAnnouncement = StudyAnnouncementV2.create(request.title(), request.link(), study); studyAnnouncementV2Repository.save(studyAnnouncement); log.info("[MentorStudyAnnouncementServiceV2] 스터디 공지 생성: studyAnnouncementId={}", studyAnnouncement.getId()); + return studyAnnouncement.getId(); }
55-55
: 불필요한 저장소 호출이 있습니다.JPA의 영속성 컨텍스트 내에서 엔티티 변경은 자동으로 감지되어 트랜잭션 종료 시 DB에 반영됩니다.
@Transactional
애노테이션이 있으므로 별도의 save 호출은 불필요합니다.studyAnnouncement.update(request.title(), request.link()); -studyAnnouncementV2Repository.save(studyAnnouncement);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/com/gdschongik/gdsc/domain/studyv2/api/MentorStudyAnnouncementControllerV2.java
(1 hunks)src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyAnnouncementServiceV2.java
(1 hunks)src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java
(2 hunks)src/main/java/com/gdschongik/gdsc/domain/studyv2/dto/request/StudyAnnouncementCreateRequest.java
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main/java/com/gdschongik/gdsc/domain/studyv2/application/MentorStudyServiceV2.java
import com.gdschongik.gdsc.domain.study.dto.request.StudyAnnouncementCreateUpdateRequest; | ||
import com.gdschongik.gdsc.domain.studyv2.application.MentorStudyAnnouncementServiceV2; | ||
import com.gdschongik.gdsc.domain.studyv2.dto.request.StudyAnnouncementCreateRequest; |
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.
🛠️ Refactor suggestion
패키지 일관성 문제가 있습니다.
StudyAnnouncementCreateUpdateRequest
는 study 패키지에서 가져오고, StudyAnnouncementCreateRequest
는 studyv2 패키지에서 가져오고 있습니다. 일관성을 위해 모두 studyv2 패키지로 통일하는 것이 좋겠습니다.
🌱 관련 이슈
📌 작업 내용 및 특이사항
📝 참고사항
📚 기타
Summary by CodeRabbit