-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor/#22 학생회 회원가입 시 당선 정보가 담긴 사진을 첨부하고 이를 관리자가 승인해야만 회원가입이 되도록 구현 #32
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
Walkthrough학생회 엔티티에 영구 필드( Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Manager
participant Controller as ManagerController
participant Service as ManagerService
participant Repo as StudentCouncilRepository
participant Mail as JavaMailSender
Manager->>Controller: PATCH /managers/approve/council/{id} (certifyResult)
Controller->>Service: approveOrDenyCouncil(id, request)
Service->>Repo: findByIdAndManagerApprovedIsFalseAndDeletedAtIsNull(id)
alt found
Repo-->>Service: StudentCouncil
alt certifyResult == true
Service->>Service: studentCouncil.managerApprove()
Service->>Repo: save(studentCouncil)
Service->>Mail: sendApprovalEmail(studentCouncil)
Mail-->>Service: sent
else certifyResult == false
Service->>Mail: sendDenialEmail(studentCouncil)
Mail-->>Service: sent
end
Service-->>Controller: CouncilApproveOrDenyResponse
Controller-->>Manager: 200 OK
else not found
Repo-->>Service: empty
Service-->>Controller: StudentCouncilNotFoundException
Controller-->>Manager: 404 Not Found
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45분 Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
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: 13
Fix all issues with AI Agents 🤖
In
@src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.java:
- Around line 40-41: The StudentCouncilSignUpRequest DTO is missing a required
validation annotation on the electionImageUrl field; add @NotBlank to the
electionImageUrl field in the StudentCouncilSignUpRequest class (and import
javax.validation.constraints.NotBlank) so it is validated consistently with
loginId/password/email/councilType/schoolId; ensure any constructors/builders or
tests that instantiate StudentCouncilSignUpRequest still compile and that
validation is triggered where request binding occurs.
In
@src/main/java/com/campus/campus/domain/council/application/util/CouncilNameGenerator.java:
- Around line 10-18: Add a NotNull validation to the councilType field on the
StudentCouncil entity to enforce non-null at the object level: annotate the
councilType field (used by buildCouncilName in CouncilNameGenerator) with
@NotNull (or @jakarta.validation.constraints.NotNull), import the appropriate
annotation, and run/adjust any tests that construct StudentCouncil instances to
provide a councilType to satisfy the new constraint.
In
@src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java:
- Around line 93-95: The method name has a typo: rename the method
mangerApprove() to managerApprove() in StudentCouncil (update the method
declaration and its javadoc if any) and update all call sites and references
(e.g., any service, controller, test, or mapper that calls
StudentCouncil.mangerApprove()) to use managerApprove(); ensure any
serialization frameworks/reflection-based usages (e.g., Spring, Jackson,
MapStruct) are updated or tested, run compilation/tests to catch remaining
references, and keep the method behavior (setting managerApproved = true)
unchanged.
In
@src/main/java/com/campus/campus/domain/councilnotice/application/service/StudentCouncilNoticeService.java:
- Around line 52-54: The update() and delete() methods currently only verify the
post's author and thus allow unapproved councils to modify notices; change them
to load the StudentCouncil using the same query used in create —
findByIdWithDetailsAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) — and
throw StudentCouncilNotFoundException if not present, then proceed with the
existing author check/operation; apply the identical fix to
StudentCouncilPostService's update/delete equivalents so only
managerApproved=true councils can call council-only APIs.
In
@src/main/java/com/campus/campus/domain/manager/application/dto/request/CouncilApproveOrDenyRequest.java:
- Around line 6-10: The @NotNull annotation on the primitive field in record
CouncilApproveOrDenyRequest has no effect because primitive boolean cannot be
null; change the field type certifyResult from primitive boolean to the wrapper
Boolean so validation will detect missing/null JSON values and avoid silently
defaulting to false. Update the record signature to use Boolean for
certifyResult and keep the @NotNull and @Schema annotations so the bean
validation and OpenAPI docs continue to work as intended.
In
@src/main/java/com/campus/campus/domain/manager/application/exception/ErrorCode.java:
- Around line 13-14: manager.ErrorCode enum values conflict with
place.ErrorCode; change the numeric range for the manager domain (e.g., move
from 2601/2602 to a dedicated 2700s range) by updating MANAGER_NOT_FOUND and
PASSWORD_NOT_CORRECT to new unique codes (for example 2701 and 2702) in the
ErrorCode enum, then search for and update any usages/references of
ErrorCode.MANAGER_NOT_FOUND and ErrorCode.PASSWORD_NOT_CORRECT (including tests,
mappers, and API responses) so they reflect the new numeric values and keep
error codes globally unique.
In
@src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java:
- Around line 1-10: PasswordNotCorrectException currently imports the council
ErrorCode causing the wrong status (2304); update the import to the manager
ErrorCode used by the manager domain so the exception uses the correct code
(2602). Edit the import in PasswordNotCorrectException to reference
com.campus.campus.domain.manager.application.exception.ErrorCode and keep the
constructor calling super(ErrorCode.PASSWORD_NOT_CORRECT) unchanged.
In
@src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java:
- Line 72: There's a typo in the method call: replace
studentCouncil.mangerApprove() with the correctly spelled
studentCouncil.managerApprove(); update any other occurrences of "mangerApprove"
to "managerApprove" (or vice versa if the method declaration is misspelled) so
the call matches the method name in the StudentCouncil class and the code
compiles.
- Around line 90-95: The getCertifyRequestCouncil method currently fetches any
StudentCouncil by ID; change the repository call to only return pending,
non-deleted councils by using
StudentCouncilRepository.findByIdAndManagerApprovedIsFalseAndDeletedAtIsNull(councilId)
and keep throwing StudentCouncilNotFoundException when absent, then pass the
result to managerMapper.toCertifyRequestCouncilResponse as before.
In @src/main/java/com/campus/campus/domain/manager/domain/entity/Manager.java:
- Around line 27-34: Update the Manager entity field definitions to enforce DB
constraints and indicate password handling: mark loginId with nullable=false and
unique=true and a sensible length (e.g., length=100), mark password with
nullable=false and a reasonable max length (e.g., length=255), and add
validation annotations if used (e.g., @NotNull/@Size) on the corresponding
fields in class Manager; additionally, ensure the codebase treats
Manager.password as a hashed value by documenting/commenting that the service
layer must store only encrypted passwords (BCrypt) and consider renaming or
annotating the field to reflect it contains a hashed password.
In @src/main/java/com/campus/campus/global/config/PermitUrlConfig.java:
- Around line 27-29: The list of permitted URLs in PermitUrlConfig contains a
duplicate entry "/jwt/token/reissue"; remove the redundant instance so each path
appears only once. Locate the array or collection in PermitUrlConfig (e.g., the
list/array of permitted URLs used by the security configuration) and delete the
duplicated "/jwt/token/reissue" entry while preserving the remaining order and
formatting.
In @src/main/java/com/campus/campus/global/util/jwt/ManagerPrincipal.java:
- Line 17: The field comment on ManagerPrincipal.managerName is inconsistent
with its name; either rename the field to reflect it stores the login ID (e.g.,
loginId or username) or update the comment to indicate it is the manager's
display/name. Locate the ManagerPrincipal class and adjust the field and any
usages: if you change the name, update constructors/getters/setters and
references; if you update the comment, make it match other principals (see
StudentCouncilPrincipal.username) for consistency.
🧹 Nitpick comments (14)
src/main/java/com/campus/campus/domain/manager/domain/entity/Manager.java (1)
15-21: BaseEntity 확장 및 @builder 사용 검토 필요다른 엔티티들(User, StudentCouncil 등)과 달리 Manager 엔티티는 BaseEntity를 확장하지 않아 생성/수정 시각 추적이 불가능합니다. 또한 @SuperBuilder를 사용하고 있지만 실제로 상속 관계가 없으므로 일반 @builder로 충분합니다.
감사 로그가 필요하지 않다면 현재 구조를 유지하되, 일관성을 위해 @builder 사용을 권장합니다.
🔎 BaseEntity 확장 제안
감사 추적이 필요한 경우:
-@SuperBuilder -public class Manager { +@SuperBuilder +public class Manager extends BaseEntity {감사 추적이 불필요한 경우:
-@SuperBuilder +@Builder public class Manager {src/main/java/com/campus/campus/global/annotation/CurrentManagerIdArgumentResolver.java (1)
15-64: 구현이 올바르며 기존 패턴을 잘 따르고 있습니다CurrentUserIdArgumentResolver 및 CurrentCouncilIdArgumentResolver와 일관된 구조로 구현되어 있어 코드 가독성과 유지보수성이 좋습니다. 인증 처리 로직도 적절합니다.
선택적으로, 세 개의 ArgumentResolver(User, Council, Manager) 간 중복 코드를 줄이기 위해 공통 추상 클래스나 헬퍼 메서드를 고려할 수 있지만, 현재 구현도 충분히 명확합니다.
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java (1)
57-59: 게시글 작성 시 승인 상태 검증이 추가되어 적절합니다학생회 게시글 생성 시 managerApproved 검증을 추가하여, 승인된 학생회만 게시글을 작성할 수 있도록 제한한 것은 PR 목표와 일치합니다.
고려 사항:
- 메서드명이 다소 길어 가독성이 떨어질 수 있습니다. 필요시 repository 메서드명을
findApprovedCouncilWithDetails(id)등으로 간소화하는 것을 고려해보세요.- 이미 승인된 학생회가 게시글을 작성한 후 승인이 취소되는 경우, 기존 게시글의 조회/수정/삭제 권한 처리가 일관되게 적용되는지 확인이 필요합니다.
src/main/java/com/campus/campus/domain/manager/domain/repository/ManagerRepository.java (1)
1-11: 리포지토리 구현이 깔끔합니다.Spring Data JPA 컨벤션을 잘 따르고 있으며,
Optional반환 타입을 사용하여 null 안정성을 확보했습니다.로그인 시
loginId로 조회가 빈번하게 발생할 것으로 예상되므로,Manager엔티티의loginId필드에 데이터베이스 인덱스가 설정되어 있는지 확인하시면 좋습니다.🔎 loginId 인덱스 확인 스크립트
#!/bin/bash # Description: Manager 엔티티에서 loginId 필드의 인덱스 설정 확인 # Manager 엔티티 파일에서 loginId 필드와 인덱스 관련 어노테이션 검색 ast-grep --pattern $'class Manager { $$$ @$_ $_ loginId; $$$ }'src/main/java/com/campus/campus/domain/manager/application/exception/ErrorCode.java (1)
14-14: PASSWORD_NOT_CORRECT 중복 고려
PASSWORD_NOT_CORRECT가council.ErrorCode(2304)와 동일한 개념으로 중복됩니다. 도메인별로 분리하는 것도 합리적이지만, 공통 인증 에러를 위한 글로벌 에러 코드로 통합하는 것도 고려해볼 수 있습니다.src/main/java/com/campus/campus/domain/manager/application/dto/response/CouncilApproveOrDenyResponse.java (1)
9-10: 필드명 명확성 개선 제안
certifyResult보다는approved또는isApproved가 승인/거부 결과를 더 명확하게 표현합니다. 현재 "인증 결과"라는 description도 승인 여부를 나타내기에 다소 모호합니다.🔎 필드명 개선 제안
public record CouncilApproveOrDenyResponse( @Schema(description = "인증한 학생회 id", example = "1") Long councilId, - @Schema(description = "인증 결과", example = "true") - boolean certifyResult + @Schema(description = "승인 여부", example = "true") + boolean approved ) { }src/main/java/com/campus/campus/domain/council/application/service/CouncilLoginService.java (4)
70-99: 회원가입 흐름 변경 확인회원가입 시 토큰을 즉시 발급하지 않도록 변경된 것이 PR 목표와 일치합니다.
councilName생성 및 저장 로직도 올바르게 구현되었습니다.다만 사용자 경험 측면에서, 회원가입 후 관리자 승인이 필요하다는 안내가 없으면 혼란을 줄 수 있습니다. 컨트롤러 레이어의 응답 메시지나 이메일 알림을 통해 "관리자 승인 대기 중" 상태를 명확히 전달하는 것을 권장합니다.
103-104: 로그인 시 승인 여부 검증 개선 제안
ManagerApprovedIsTrue필터 추가로 승인된 학생회만 로그인할 수 있도록 제한한 것은 올바른 보안 조치입니다.다만 승인 대기 중인 학생회가 로그인 시도 시
StudentCouncilNotFoundException이 발생하여 "존재하지 않는 학생회"라는 오해를 줄 수 있습니다. 승인 대기 중임을 명확히 알리는 별도 예외(예:CouncilApprovalPendingException)를 고려해보세요.🔎 개선 제안 예시
// 1. 먼저 loginId로 학생회 존재 여부 확인 StudentCouncil studentCouncil = studentCouncilRepository .findByLoginIdAndDeletedAtIsNull(studentCouncilLoginRequest.loginId()) .orElseThrow(StudentCouncilNotFoundException::new); // 2. 승인 여부 확인 if (!studentCouncil.getManagerApproved()) { throw new CouncilApprovalPendingException(); }
127-129: 아이디 찾기에도 동일한 승인 여부 처리 필요로그인과 마찬가지로, 승인 대기 중인 학생회가 아이디 찾기 시
StudentCouncilNotFoundException을 받게 됩니다. 사용자 경험을 위해 승인 대기 상태를 명확히 알리는 것을 권장합니다.
139-140: 비밀번호 찾기에도 동일한 승인 여부 처리 필요비밀번호 찾기에도 승인 필터가 적용되어 일관성이 있습니다. 다만 승인 대기 중인 사용자를 위한 명확한 피드백 제공을 권장합니다.
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java (1)
71-72:Boolean대신 primitiveboolean사용 권장
Boolean타입은null이 될 수 있어 비교 시NullPointerException이 발생할 수 있습니다. 초기값이 항상false여야 하는 승인 플래그에는 primitiveboolean이 더 적합합니다.🔎 수정 제안
@Column(name = "manager_approved") - private Boolean managerApproved; + private boolean managerApproved = false;src/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.java (1)
14-16: 불필요한@RequiredArgsConstructor어노테이션이 클래스에는 생성자 주입이 필요한 필드가 없으므로
@RequiredArgsConstructor가 불필요합니다.🔎 수정 제안
@Component -@RequiredArgsConstructor public class ManagerMapper {src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java (1)
34-34: 메서드 명명 규칙: PascalCase → camelCaseJava 메서드 명명 규칙에 따라
Signup은signup으로 변경하는 것이 좋습니다.Login메서드도 동일하게login으로 변경을 권장드립니다.🔎 수정 제안
- public CommonResponse<Void> Signup(@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) { + public CommonResponse<Void> signup(@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) {src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (1)
73-73: 명시적 save() 호출이 불필요할 수 있습니다.
@Transactional내에서 JPA의 더티 체킹(dirty checking)이 자동으로 변경사항을 감지하고 저장하므로, 명시적인save()호출은 선택적입니다. 하지만 의도를 명확히 하기 위해 유지하는 것도 나쁘지 않습니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (37)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.javasrc/main/java/com/campus/campus/domain/council/application/dto/response/StudentCouncilLoginResponse.javasrc/main/java/com/campus/campus/domain/council/application/mapper/StudentCouncilLoginMapper.javasrc/main/java/com/campus/campus/domain/council/application/service/CouncilLoginService.javasrc/main/java/com/campus/campus/domain/council/application/service/CouncilService.javasrc/main/java/com/campus/campus/domain/council/application/util/CouncilNameGenerator.javasrc/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.javasrc/main/java/com/campus/campus/domain/council/domain/repository/StudentCouncilRepository.javasrc/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.javasrc/main/java/com/campus/campus/domain/council/presentation/StudentCouncilResponseCode.javasrc/main/java/com/campus/campus/domain/councilnotice/application/mapper/StudentCouncilNoticeMapper.javasrc/main/java/com/campus/campus/domain/councilnotice/application/service/StudentCouncilNoticeService.javasrc/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.javasrc/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.javasrc/main/java/com/campus/campus/domain/manager/application/dto/request/CouncilApproveOrDenyRequest.javasrc/main/java/com/campus/campus/domain/manager/application/dto/request/ManagerLoginRequest.javasrc/main/java/com/campus/campus/domain/manager/application/dto/response/CertifyRequestCouncilListResponse.javasrc/main/java/com/campus/campus/domain/manager/application/dto/response/CertifyRequestCouncilResponse.javasrc/main/java/com/campus/campus/domain/manager/application/dto/response/CouncilApproveOrDenyResponse.javasrc/main/java/com/campus/campus/domain/manager/application/dto/response/ManagerLoginResponse.javasrc/main/java/com/campus/campus/domain/manager/application/exception/ErrorCode.javasrc/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.javasrc/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.javasrc/main/java/com/campus/campus/domain/manager/application/mapper/ManagerMapper.javasrc/main/java/com/campus/campus/domain/manager/application/service/ManagerService.javasrc/main/java/com/campus/campus/domain/manager/domain/entity/Manager.javasrc/main/java/com/campus/campus/domain/manager/domain/repository/ManagerRepository.javasrc/main/java/com/campus/campus/domain/manager/presentation/ManagerController.javasrc/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.javasrc/main/java/com/campus/campus/global/annotation/CurrentManagerId.javasrc/main/java/com/campus/campus/global/annotation/CurrentManagerIdArgumentResolver.javasrc/main/java/com/campus/campus/global/config/PermitUrlConfig.javasrc/main/java/com/campus/campus/global/config/WebMvcConfig.javasrc/main/java/com/campus/campus/global/util/jwt/JwtAuthenticationFilter.javasrc/main/java/com/campus/campus/global/util/jwt/JwtProvider.javasrc/main/java/com/campus/campus/global/util/jwt/ManagerPrincipal.javasrc/main/java/com/campus/campus/global/util/jwt/application/service/TokenReissueService.java
🧰 Additional context used
🧬 Code graph analysis (22)
src/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.java (9)
src/main/java/com/campus/campus/domain/user/application/exception/UserNotFoundException.java (1)
UserNotFoundException(5-9)src/main/java/com/campus/campus/domain/school/application/exception/CollegeNotFoundException.java (2)
CollegeNotFoundException(5-9)CollegeNotFoundException(6-8)src/main/java/com/campus/campus/domain/school/application/exception/MajorNotFoundException.java (1)
MajorNotFoundException(5-9)src/main/java/com/campus/campus/domain/councilnotice/application/exception/NoticeNotFoundException.java (1)
NoticeNotFoundException(5-9)src/main/java/com/campus/campus/domain/school/application/exception/SchoolNotFoundException.java (1)
SchoolNotFoundException(5-9)src/main/java/com/campus/campus/domain/council/application/exception/StudentCouncilNotFoundException.java (1)
StudentCouncilNotFoundException(5-9)src/main/java/com/campus/campus/domain/mail/application/exception/EmailVerificationNotFoundException.java (1)
EmailVerificationNotFoundException(5-9)src/main/java/com/campus/campus/domain/place/application/exception/PlaceCreationException.java (1)
PlaceCreationException(5-9)src/main/java/com/campus/campus/domain/councilpost/application/exception/PostNotFoundException.java (1)
PostNotFoundException(5-9)
src/main/java/com/campus/campus/global/annotation/CurrentManagerId.java (4)
src/main/java/com/campus/campus/global/annotation/CurrentCouncilId.java (1)
Target(10-20)src/main/java/com/campus/campus/global/annotation/CurrentUserId.java (1)
Target(10-21)src/main/java/com/campus/campus/global/annotation/CurrentCouncilIdArgumentResolver.java (3)
Component(15-63)Override(27-62)Override(17-25)src/main/java/com/campus/campus/global/annotation/CurrentUserIdArgumentResolver.java (3)
Component(15-65)Override(28-64)Override(18-26)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.java (5)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangePasswordRequest.java (1)
StudentCouncilChangePasswordRequest(9-24)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilWithdrawRequest.java (1)
StudentCouncilWithdrawRequest(6-14)src/main/java/com/campus/campus/domain/user/application/dto/request/UserProfileRequest.java (1)
UserProfileRequest(5-12)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginRequest.java (1)
StudentCouncilLoginRequest(7-16)src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilController.java (1)
PatchMapping(26-33)
src/main/java/com/campus/campus/domain/council/application/service/CouncilLoginService.java (1)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilController.java (1)
RestController(19-43)
src/main/java/com/campus/campus/global/util/jwt/ManagerPrincipal.java (2)
src/main/java/com/campus/campus/global/util/jwt/UserPrincipal.java (5)
Getter(14-77)UserPrincipal(22-29)from(31-41)Override(58-61)Override(63-66)src/main/java/com/campus/campus/global/util/jwt/StudentCouncilPrincipal.java (5)
Getter(15-78)Override(59-62)Override(44-47)StudentCouncilPrincipal(22-32)from(34-42)
src/main/java/com/campus/campus/global/util/jwt/application/service/TokenReissueService.java (1)
src/main/java/com/campus/campus/global/util/jwt/StudentCouncilPrincipal.java (2)
Override(69-72)Override(74-77)
src/main/java/com/campus/campus/domain/council/application/dto/response/StudentCouncilLoginResponse.java (3)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangeEmailRequest.java (1)
StudentCouncilChangeEmailRequest(6-11)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangePasswordRequest.java (1)
StudentCouncilChangePasswordRequest(9-24)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginRequest.java (1)
StudentCouncilLoginRequest(7-16)
src/main/java/com/campus/campus/global/util/jwt/JwtAuthenticationFilter.java (3)
src/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.java (1)
ManagerNotFoundException(5-9)src/main/java/com/campus/campus/domain/council/application/exception/StudentCouncilNotFoundException.java (1)
StudentCouncilNotFoundException(5-9)src/main/java/com/campus/campus/global/util/jwt/StudentCouncilPrincipal.java (6)
Override(69-72)Override(74-77)Getter(15-78)Override(59-62)Override(44-47)StudentCouncilPrincipal(22-32)
src/main/java/com/campus/campus/domain/council/application/service/CouncilService.java (2)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilController.java (3)
PatchMapping(26-33)RestController(19-43)PatchMapping(35-42)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangePasswordRequest.java (1)
StudentCouncilChangePasswordRequest(9-24)
src/main/java/com/campus/campus/global/annotation/CurrentManagerIdArgumentResolver.java (5)
src/main/java/com/campus/campus/global/annotation/CurrentUserIdArgumentResolver.java (3)
Component(15-65)Override(28-64)Override(18-26)src/main/java/com/campus/campus/global/util/jwt/exception/UnAuthorizedException.java (1)
UnAuthorizedException(5-9)src/main/java/com/campus/campus/global/annotation/CurrentCouncilIdArgumentResolver.java (3)
Component(15-63)Override(27-62)Override(17-25)src/main/java/com/campus/campus/global/annotation/CurrentUserId.java (1)
Target(10-21)src/main/java/com/campus/campus/global/annotation/CurrentCouncilId.java (1)
Target(10-20)
src/main/java/com/campus/campus/global/config/WebMvcConfig.java (3)
src/main/java/com/campus/campus/global/annotation/CurrentCouncilIdArgumentResolver.java (3)
Component(15-63)Override(27-62)Override(17-25)src/main/java/com/campus/campus/global/annotation/CurrentUserIdArgumentResolver.java (2)
Component(15-65)Override(28-64)src/main/java/com/campus/campus/global/annotation/CurrentCouncilId.java (1)
Target(10-20)
src/main/java/com/campus/campus/domain/manager/application/dto/request/ManagerLoginRequest.java (1)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginRequest.java (1)
StudentCouncilLoginRequest(7-16)
src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java (2)
src/main/java/com/campus/campus/domain/council/application/exception/PasswordNotCorrectException.java (2)
PasswordNotCorrectException(6-8)PasswordNotCorrectException(5-9)src/main/java/com/campus/campus/domain/user/application/exception/NicknameNotMatchException.java (1)
NicknameNotMatchException(5-9)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (3)
src/main/java/com/campus/campus/domain/council/application/exception/StudentCouncilNotFoundException.java (1)
StudentCouncilNotFoundException(5-9)src/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.java (1)
ManagerNotFoundException(5-9)src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java (1)
PasswordNotCorrectException(6-10)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java (1)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilController.java (3)
RestController(19-43)PatchMapping(35-42)PatchMapping(26-33)
src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (2)
src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostResponse.java (1)
Builder(13-35)src/main/java/com/campus/campus/domain/councilpost/domain/entity/StudentCouncilPost.java (1)
Entity(25-98)
src/main/java/com/campus/campus/domain/manager/application/dto/request/CouncilApproveOrDenyRequest.java (8)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangePasswordRequest.java (1)
StudentCouncilChangePasswordRequest(9-24)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilWithdrawRequest.java (1)
StudentCouncilWithdrawRequest(6-14)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangeEmailRequest.java (1)
StudentCouncilChangeEmailRequest(6-11)src/main/java/com/campus/campus/domain/councilnotice/application/dto/request/NoticeRequest.java (1)
NoticeRequest(5-10)src/main/java/com/campus/campus/domain/councilpost/application/dto/request/PostRequest.java (1)
PostRequest(14-42)src/main/java/com/campus/campus/domain/mail/application/dto/request/EmailVerificationRequest.java (1)
EmailVerificationRequest(6-11)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginRequest.java (1)
StudentCouncilLoginRequest(7-16)src/main/java/com/campus/campus/global/util/jwt/application/dto/request/TokenReissueRequest.java (1)
TokenReissueRequest(6-11)
src/main/java/com/campus/campus/domain/councilnotice/application/mapper/StudentCouncilNoticeMapper.java (2)
src/main/java/com/campus/campus/domain/councilnotice/domain/entity/StudentCouncilNotice.java (1)
Entity(21-54)src/main/java/com/campus/campus/domain/councilnotice/application/dto/response/NoticeResponse.java (1)
Builder(8-20)
src/main/java/com/campus/campus/domain/manager/application/dto/response/CouncilApproveOrDenyResponse.java (6)
src/main/java/com/campus/campus/domain/school/application/dto/response/CollegeFindResponse.java (1)
CollegeFindResponse(5-12)src/main/java/com/campus/campus/domain/school/application/dto/response/MajorFindResponse.java (1)
MajorFindResponse(5-12)src/main/java/com/campus/campus/domain/councilpost/application/dto/response/PostListItemResponse.java (1)
PostListItemResponse(8-18)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilWithdrawRequest.java (1)
StudentCouncilWithdrawRequest(6-14)src/main/java/com/campus/campus/domain/user/application/dto/response/UserFirstProfileResponse.java (1)
Builder(6-17)src/main/java/com/campus/campus/domain/place/application/dto/response/SavedPlaceInfo.java (1)
SavedPlaceInfo(9-36)
src/main/java/com/campus/campus/domain/manager/domain/entity/Manager.java (7)
src/main/java/com/campus/campus/domain/school/domain/entity/Major.java (1)
Entity(18-40)src/main/java/com/campus/campus/domain/user/domain/entity/User.java (1)
Entity(25-77)src/main/java/com/campus/campus/domain/school/domain/entity/College.java (1)
Entity(18-36)src/main/java/com/campus/campus/global/entity/BaseEntity.java (1)
Getter(16-29)src/main/java/com/campus/campus/domain/place/domain/entity/LikedPlace.java (1)
Entity(21-45)src/main/java/com/campus/campus/domain/school/domain/entity/School.java (1)
Entity(15-29)src/main/java/com/campus/campus/domain/place/domain/entity/PlaceImages.java (1)
Entity(18-40)
src/main/java/com/campus/campus/domain/council/domain/repository/StudentCouncilRepository.java (3)
src/main/java/com/campus/campus/domain/councilnotice/domain/repository/StudentCouncilNoticeRepository.java (2)
StudentCouncilNoticeRepository(13-27)Query(23-26)src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java (1)
StudentCouncilPostRepository(15-40)src/main/java/com/campus/campus/domain/councilnotice/domain/entity/StudentCouncilNotice.java (1)
Entity(21-54)
src/main/java/com/campus/campus/domain/manager/application/exception/ErrorCode.java (9)
src/main/java/com/campus/campus/domain/school/application/exception/ErrorCode.java (1)
Getter(10-22)src/main/java/com/campus/campus/domain/user/application/exception/ErrorCode.java (1)
Getter(10-21)src/main/java/com/campus/campus/domain/council/application/exception/ErrorCode.java (1)
Getter(10-28)src/main/java/com/campus/campus/domain/councilnotice/application/exception/ErrorCode.java (1)
Getter(10-23)src/main/java/com/campus/campus/domain/place/application/exception/ErrorCode.java (1)
Getter(10-23)src/main/java/com/campus/campus/domain/mail/application/exception/ErrorCode.java (1)
Getter(10-23)src/main/java/com/campus/campus/domain/councilpost/application/exception/ErrorCode.java (1)
Getter(8-29)src/main/java/com/campus/campus/global/common/exception/ErrorCodeInterface.java (2)
ErrorCodeInterface(5-11)getStatus(8-8)src/main/java/com/campus/campus/global/common/exception/ApplicationException.java (1)
Getter(5-18)
⏰ 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 (34)
src/main/java/com/campus/campus/domain/councilnotice/application/mapper/StudentCouncilNoticeMapper.java (1)
39-39: 리팩토링 변경사항이 올바릅니다.
getFullCouncilName()에서getCouncilName()으로의 변경은 PR의 전반적인 councilName 리팩토링과 일치하며, 다른 매퍼 클래스들(예: StudentCouncilPostMapper)과도 일관성을 유지합니다.src/main/java/com/campus/campus/domain/manager/application/dto/response/ManagerLoginResponse.java (1)
1-20: 구현이 올바릅니다.관리자 로그인 응답 DTO가 프로젝트의 다른 로그인 응답 패턴과 일관되게 구현되었으며, 필요한 모든 필드를 포함하고 있습니다.
src/main/java/com/campus/campus/global/util/jwt/ManagerPrincipal.java (1)
14-37: 구조가 올바릅니다.
ManagerPrincipal클래스가 기존UserPrincipal및StudentCouncilPrincipal패턴을 일관되게 따르고 있으며,ROLE_MANAGER권한 설정도 적절합니다.src/main/java/com/campus/campus/global/util/jwt/application/service/TokenReissueService.java (1)
77-79: 관리자 승인 검증 추가로 보안이 강화되었습니다토큰 재발급 시 학생회의 managerApproved 상태를 확인하도록 변경되어, 승인되지 않은 학생회나 승인이 취소된 학생회의 토큰 재발급을 방지합니다. 이는 PR 목표와 일치하며 적절한 보안 조치입니다.
참고: 이미 로그인한 학생회의 승인이 취소되는 경우, 리프레시 토큰을 사용한 재발급이 실패하여 재로그인이 필요하게 됩니다. 이는 의도된 동작으로 보이나, 필요시 사용자에게 명확한 에러 메시지 제공을 고려하세요.
src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java (1)
42-42: 변경사항이 완전하고 올바르게 구현됨다음 사항들이 모두 확인되었습니다:
- StudentCouncil 엔티티에 councilName 필드가 정상적으로 정의되어 있고 (@Getter로 getCouncilName() 자동 생성)
- CouncilLoginService에서 CouncilNameGenerator를 통해 councilName이 로그인 시점에 생성되어 setCouncilName()으로 설정됨
- getFullCouncilName() 메서드는 완전히 제거되었으며, 모든 사용처(StudentCouncilNoticeMapper, ManagerMapper, StudentCouncilLoginMapper, StudentCouncilPostMapper)가 getCouncilName()으로 일관되게 변경됨
mapper에서의 메서드 호출 변경은 안전하며 null 값 위험이 없습니다.
src/main/java/com/campus/campus/domain/council/application/service/CouncilService.java (2)
34-35: 관리자 승인 검증이 올바르게 적용되었습니다.이메일 변경 시
managerApproved상태를 확인하도록 변경된 점이 적절합니다. 승인되지 않은 학생회 계정이 이메일을 변경할 수 없도록 보호하는 로직이 정확합니다.
54-55: 관리자 승인 검증이 일관되게 적용되었습니다.비밀번호 변경 시에도 동일하게
managerApproved검증이 적용되어 일관성 있는 보안 정책을 유지하고 있습니다.src/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.java (1)
1-9: 예외 클래스가 일관된 패턴을 따릅니다.기존 프로젝트의 다른
NotFoundException클래스들(예:UserNotFoundException,StudentCouncilNotFoundException)과 동일한 구조로 구현되어 코드베이스의 일관성을 유지하고 있습니다.src/main/java/com/campus/campus/domain/council/application/dto/response/StudentCouncilLoginResponse.java (1)
35-38: 응답 DTO에 학생회 이름 필드가 적절히 추가되었습니다.
councilName필드가 기존 필드들과 동일한 패턴으로 구현되어 일관성을 유지하고 있습니다. 다만, 응답 DTO에 새로운 필드를 추가하는 것은 API를 사용하는 클라이언트에 영향을 줄 수 있는 변경사항입니다.API 클라이언트(모바일 앱, 프론트엔드 등)가 이 변경사항을 처리할 수 있는지 확인하시기 바랍니다. 특히 역직렬화 시 알 수 없는 필드를 무시하도록 설정되어 있는지 검토가 필요합니다.
src/main/java/com/campus/campus/domain/manager/application/dto/response/CertifyRequestCouncilResponse.java (1)
1-15: 인증 요청 응답 DTO가 명확하게 구현되었습니다.학생회 인증 요청 정보를 담는 DTO의 구조가 간결하고 명확합니다. 모든 필드에 적절한 설명과 예시가 포함된
@Schema어노테이션이 추가되어 API 문서화가 잘 되어 있습니다.src/main/java/com/campus/campus/global/util/jwt/JwtAuthenticationFilter.java (2)
93-98: 승인된 학생회만 인증 허용 - 좋은 변경입니다
managerApproved가true인 학생회만 JWT 인증을 통과하도록 변경된 것은 승인 흐름의 핵심 보안 요구사항을 잘 반영하고 있습니다. 미승인 학생회의 토큰은 자동으로 무효화됩니다.
99-103: 관리자 조회 시 soft delete 확인 불필요검토 결과,
Manager엔티티는deletedAt필드를 갖지 않습니다.USER와COUNCIL은 soft delete를 지원하지만,Manager는 별도의 삭제 전략을 적용하고 있으므로findById사용이 올바릅니다. 현재 구현에 문제가 없습니다.Likely an incorrect or invalid review comment.
src/main/java/com/campus/campus/global/annotation/CurrentManagerId.java (1)
1-21: LGTM!기존
CurrentUserId,CurrentCouncilId어노테이션과 동일한 패턴을 따르고 있으며, Javadoc도 적절하게 작성되어 있습니다.CurrentManagerIdArgumentResolver와 함께 잘 동작할 것입니다.src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilResponseCode.java (1)
13-13: LGTM!회원가입 후 관리자 승인이 필요한 새로운 흐름을 반영하여
SIGNUP_SUCCESS를SIGNUP_REQUEST_SUCCESS로 변경한 것은 적절합니다. 메시지도 "요청에 성공했습니다"로 명확하게 표현하고 있습니다.src/main/java/com/campus/campus/domain/manager/application/dto/request/ManagerLoginRequest.java (1)
6-15: LGTM!
StudentCouncilLoginRequest와 동일한 패턴을 따르고 있으며,@NotBlank검증과@Schema문서화가 적절합니다.src/main/java/com/campus/campus/domain/manager/application/dto/response/CertifyRequestCouncilListResponse.java (1)
7-17: LGTM!인증 요청 목록 조회를 위한 응답 DTO로 적절하게 설계되었습니다.
@Schema어노테이션으로 API 문서화도 잘 되어 있습니다.src/main/java/com/campus/campus/global/config/WebMvcConfig.java (1)
10-10: LGTM!관리자 전용 인증 처리를 위한
CurrentManagerIdArgumentResolver등록이 기존 패턴(CurrentUserIdArgumentResolver,CurrentCouncilIdArgumentResolver)과 일관되게 구현되었습니다.Also applies to: 21-21, 27-27
src/main/java/com/campus/campus/domain/council/application/mapper/StudentCouncilLoginMapper.java (2)
33-34: LGTM!회원가입 시
electionImageUrl을 저장하고managerApproved를false로 초기화하는 것이 PR 목표와 일치합니다. 관리자 승인이 필요한 회원가입 플로우가 올바르게 구현되었습니다.
49-50: LGTM!로그인 응답에
councilName추가가 적절합니다. 학생회 이름이 응답에 포함되어 클라이언트에서 활용할 수 있습니다.src/main/java/com/campus/campus/domain/council/application/service/CouncilLoginService.java (2)
26-26: LGTM!
CouncilNameGenerator의존성 추가가 적절합니다. 학생회 이름 생성 로직이 별도 컴포넌트로 분리되어 단일 책임 원칙을 따릅니다.Also applies to: 64-64
158-160: LGTM!회원 탈퇴 시에도
ManagerApprovedIsTrue필터를 적용하여 승인된 학생회만 탈퇴할 수 있도록 한 것이 올바릅니다. 승인되지 않은 계정은 탈퇴 대신 관리자가 거부 처리하는 것이 적절합니다.src/main/java/com/campus/campus/global/util/jwt/JwtProvider.java (1)
104-134: LGTM!Manager 토큰 관련 메서드가 기존 USER/COUNCIL 패턴과 일관되게 구현되었습니다. 역할 기반 검증이 적절히 적용되어 있습니다.
src/main/java/com/campus/campus/domain/manager/presentation/ManagerResponseCode.java (1)
10-21: LGTM!
ManagerResponseCodeenum이 프로젝트 컨벤션에 맞게 잘 구현되었습니다. 응답 코드와 메시지가 적절합니다.src/main/java/com/campus/campus/domain/manager/presentation/ManagerController.java (1)
27-71: LGTM!컨트롤러가 적절히 구현되었습니다. 로그인 엔드포인트는 인증 없이 접근 가능하고, 관리자 전용 엔드포인트는
@PreAuthorize("hasRole('MANAGER')")로 적절히 보호되어 있습니다.src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java (1)
32-38: 회원가입 플로우 변경 승인관리자 승인 전까지 토큰을 발급하지 않도록 반환 타입이
Void로 변경된 것이 PR 목표와 일치합니다.src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (5)
31-44: 서비스 구조 및 의존성 설정이 적절합니다.클래스 수준의 읽기 전용 트랜잭션 설정과 필요한 의존성 주입이 올바르게 구성되어 있습니다.
46-61: 관리자 로그인 로직이 안전하게 구현되어 있습니다.비밀번호 검증, JWT 토큰 생성, Redis 저장 흐름이 올바르게 처리되고 있습니다.
83-88: 대기 중인 인증 요청 목록 조회 로직이 올바릅니다.리포지토리 쿼리와 DTO 매핑이 적절하게 구현되어 있습니다.
97-114: 승인 이메일 전송 로직이 적절합니다.이메일 내용과 전송 로직이 올바르게 구현되어 있습니다. 에러 처리는 이전 코멘트에서 다루었습니다.
116-131: 거부 이메일 전송 로직이 적절합니다.이메일 내용과 전송 로직이 올바르게 구현되어 있습니다. 에러 처리는 이전 코멘트에서 다루었습니다.
src/main/java/com/campus/campus/domain/council/domain/repository/StudentCouncilRepository.java (4)
17-23: 연관 엔티티를 효율적으로 조회하는 쿼리가 잘 구성되었습니다.
LEFT JOIN FETCH를 사용하여 연관된 학교, 대학, 학과 정보를 한 번에 가져오면서 N+1 문제를 방지하고 있으며, 승인 상태와 삭제 여부를 함께 확인하는 것이 적절합니다.
25-30: 승인 대기 중인 학생회 목록 조회 쿼리가 올바르게 구현되었습니다.관리자가 승인 대기 중인 학생회를 상세 정보와 함께 조회할 수 있도록 적절히 구성되어 있습니다.
38-40: ID 기반 조회 메서드에 승인 상태 확인이 추가되었습니다.승인된 학생회만 조회하도록 일관성 있게 구현되어 있습니다. 참고로
existsByLoginId,existsByEmail등의 메서드는managerApproved체크를 하지 않는데, 이는 회원가입 시 중복 확인 용도로 의도된 것으로 보입니다.
13-15: 메서드 시그니처 변경이 모든 호출부에 반영되었습니다.
managerApproved조건이 추가된 새로운 메서드 시그니처로 변경되었으며, 코드베이스 내 모든 호출부가 이미 업데이트되었습니다. CouncilLoginService의 login(), findPassword() 메서드 등에서 새로운 시그니처를 올바르게 사용하고 있으며, 기존 메서드 시그니처에 대한 호출부가 없으므로 호환성 문제는 없습니다.
...va/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.java
Show resolved
Hide resolved
| public String buildCouncilName(StudentCouncil studentCouncil) { | ||
| String schoolName = studentCouncil.getSchool() != null ? studentCouncil.getSchool().getSchoolName() : ""; | ||
| CouncilType councilType = studentCouncil.getCouncilType(); | ||
|
|
||
| return switch (councilType) { | ||
| case SCHOOL_COUNCIL -> String.format("%s 총학생회", schoolName).trim(); | ||
| case COLLEGE_COUNCIL -> String.format("%s %s 학생회", schoolName, getCollegeName(studentCouncil)).trim(); | ||
| case MAJOR_COUNCIL -> String.format("%s %s 학생회", schoolName, getMajorName(studentCouncil)).trim(); | ||
| }; |
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.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "StudentCouncil.java" | head -5Repository: our-campUS/our-campUS-BE
Length of output: 150
🏁 Script executed:
cat -n ./src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.javaRepository: our-campUS/our-campUS-BE
Length of output: 3225
councilType에 @NotNull 검증 추가 권장
councilType 필드는 데이터베이스 레벨에서 nullable = false 제약이 있어 NULL 값이 저장될 수 없습니다. 다만 엔티티 수준의 유효성 검증을 강화하기 위해 @NotNull 또는 @jakarta.validation.constraints.NotNull 애너테이션을 추가하는 것이 좋습니다. 이는 메모리 상태에서의 객체 생성 시에도 안전성을 보장합니다.
🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/council/application/util/CouncilNameGenerator.java
around lines 10-18, Add a NotNull validation to the councilType field on the
StudentCouncil entity to enforce non-null at the object level: annotate the
councilType field (used by buildCouncilName in CouncilNameGenerator) with
@NotNull (or @jakarta.validation.constraints.NotNull), import the appropriate
annotation, and run/adjust any tests that construct StudentCouncil instances to
provide a councilType to satisfy the new constraint.
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java
Outdated
Show resolved
Hide resolved
.../com/campus/campus/domain/councilnotice/application/service/StudentCouncilNoticeService.java
Show resolved
Hide resolved
| public record CouncilApproveOrDenyRequest( | ||
| @NotNull | ||
| @Schema(description = "인증 결과", example = "true") | ||
| boolean certifyResult | ||
| ) { |
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.
@NotNull이 primitive boolean에는 효과가 없습니다
primitive boolean은 null이 될 수 없으므로 @NotNull 검증이 작동하지 않습니다. JSON 요청에서 certifyResult 필드가 누락되면 기본값 false로 설정되어 의도치 않은 거부 처리가 될 수 있습니다.
Boolean wrapper 타입을 사용해야 null 검증이 정상 작동합니다.
🔎 제안하는 수정
public record CouncilApproveOrDenyRequest(
@NotNull
@Schema(description = "인증 결과", example = "true")
- boolean certifyResult
+ Boolean certifyResult
) {
}🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/manager/application/dto/request/CouncilApproveOrDenyRequest.java
around lines 6-10, The @NotNull annotation on the primitive field in record
CouncilApproveOrDenyRequest has no effect because primitive boolean cannot be
null; change the field type certifyResult from primitive boolean to the wrapper
Boolean so validation will detect missing/null JSON values and avoid silently
defaulting to false. Update the record signature to use Boolean for
certifyResult and keep the @NotNull and @Schema annotations so the bean
validation and OpenAPI docs continue to work as intended.
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java
Outdated
Show resolved
Hide resolved
| sendCouncilApprovedMail(studentCouncil.getEmail()); | ||
| } else { | ||
| sendCouncilDeniedMail(studentCouncil.getEmail()); | ||
| } |
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.
이메일 전송 실패에 대한 에러 처리가 필요합니다.
이메일 전송 중 예외가 발생하면 트랜잭션이 롤백될 수 있으며, 이 경우 승인은 완료되지 않고 사용자에게도 통지되지 않습니다. 이메일 전송 실패가 승인 프로세스 자체를 실패시켜야 하는지, 아니면 로깅만 하고 승인은 완료해야 하는지 비즈니스 요구사항을 고려하여 예외 처리 전략을 구현해야 합니다.
옵션:
- 이메일 전송을 별도 트랜잭션으로 분리 (
@Async또는 이벤트 기반) - try-catch로 예외를 잡아 로깅하고 승인은 계속 진행
- 이메일 전송 실패 시 전체 트랜잭션 롤백 (현재 동작이지만 명시적 처리 필요)
🔎 옵션 2 예시 (이메일 실패해도 승인 진행)
if (certifyResult) {
studentCouncil.managerApprove();
studentCouncilRepository.save(studentCouncil);
- sendCouncilApprovedMail(studentCouncil.getEmail());
+ try {
+ sendCouncilApprovedMail(studentCouncil.getEmail());
+ } catch (Exception e) {
+ // 로깅 후 계속 진행
+ log.error("Failed to send approval email to: {}", studentCouncil.getEmail(), e);
+ }
} else {
- sendCouncilDeniedMail(studentCouncil.getEmail());
+ try {
+ sendCouncilDeniedMail(studentCouncil.getEmail());
+ } catch (Exception e) {
+ log.error("Failed to send denial email to: {}", studentCouncil.getEmail(), e);
+ }
}📝 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.
| sendCouncilApprovedMail(studentCouncil.getEmail()); | |
| } else { | |
| sendCouncilDeniedMail(studentCouncil.getEmail()); | |
| } | |
| if (certifyResult) { | |
| studentCouncil.managerApprove(); | |
| studentCouncilRepository.save(studentCouncil); | |
| try { | |
| sendCouncilApprovedMail(studentCouncil.getEmail()); | |
| } catch (Exception e) { | |
| // 로깅 후 계속 진행 | |
| log.error("Failed to send approval email to: {}", studentCouncil.getEmail(), e); | |
| } | |
| } else { | |
| try { | |
| sendCouncilDeniedMail(studentCouncil.getEmail()); | |
| } catch (Exception e) { | |
| log.error("Failed to send denial email to: {}", studentCouncil.getEmail(), e); | |
| } | |
| } |
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java
Show resolved
Hide resolved
| @Column(name = "login_id") | ||
| private String loginId; | ||
|
|
||
| @Column(name = "password") | ||
| private String password; | ||
|
|
||
| @Column(name = "manager_name") | ||
| private String managerName; |
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 | 🟠 Major
필수 제약 조건 및 보안 관련 컬럼 정의 추가 필요
현재 loginId와 password 필드에 제약 조건이 없어 다음 문제가 발생할 수 있습니다:
- loginId: unique 및 nullable=false 제약 조건 누락
- password: nullable=false 제약 조건 누락
- 두 필드 모두 길이 제한 없음
추가로, password가 일반 컬럼으로 정의되어 있는데, 서비스 계층에서 암호화(BCrypt 등)가 반드시 적용되어야 합니다.
🔎 제약 조건 추가 제안
- @Column(name = "login_id")
+ @Column(name = "login_id", nullable = false, unique = true, length = 50)
private String loginId;
- @Column(name = "password")
+ @Column(name = "password", nullable = false)
private String password;
- @Column(name = "manager_name")
+ @Column(name = "manager_name", nullable = false, length = 100)
private String managerName;🤖 Prompt for AI Agents
In @src/main/java/com/campus/campus/domain/manager/domain/entity/Manager.java
around lines 27-34, Update the Manager entity field definitions to enforce DB
constraints and indicate password handling: mark loginId with nullable=false and
unique=true and a sensible length (e.g., length=100), mark password with
nullable=false and a reasonable max length (e.g., length=255), and add
validation annotations if used (e.g., @NotNull/@Size) on the corresponding
fields in class Manager; additionally, ensure the codebase treats
Manager.password as a hashed value by documenting/commenting that the service
layer must store only encrypted passwords (BCrypt) and consider renaming or
annotating the field to reflect it contains a hashed password.
src/main/java/com/campus/campus/global/config/PermitUrlConfig.java
Outdated
Show resolved
Hide resolved
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: 2
Fix all issues with AI Agents 🤖
In
@src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java:
- Around line 71-72: The managerApproved field in the StudentCouncil entity is
declared as a nullable wrapper Boolean which can lead to NPEs; change it to a
primitive boolean (private boolean managerApproved) or explicitly initialize it
to false (e.g., private Boolean managerApproved = false) and update the
constructor/any builder to set false by default; also adjust any getters/setters
or logic that checks managerApproved (e.g.,
isManagerApproved/getManagerApproved) to use the primitive or handle the
non-null default so approval checks are safe.
In
@src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java:
- Around line 63-81: The method approveOrDenyCouncil currently loads a
StudentCouncil by ID using studentCouncilRepository.findById(...) which allows
already-approved or deleted councils to be processed; change the lookup to use
studentCouncilRepository.findByIdAndManagerApprovedIsFalseAndDeletedAtIsNull(councilId)
(same pattern as getCertifyRequestCouncil) and throw
StudentCouncilNotFoundException if not present so only pending, non-deleted
councils can be approved or denied; keep the rest of approveOrDenyCouncil
(certifyResult check, managerApprove/save, and mail sending) unchanged.
♻️ Duplicate comments (1)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (1)
75-78: 이메일 전송 실패 시 예외 처리 전략이 필요합니다.이메일 전송 중 예외가 발생하면 트랜잭션이 롤백되어 승인 처리가 완료되지 않습니다. 비즈니스 요구사항에 따라 다음 중 하나를 선택해야 합니다:
- 이메일 전송 실패 시에도 승인은 완료 (try-catch로 로깅)
- 이메일 전송을 비동기로 분리 (
@Async또는 이벤트)- 이메일 전송 실패 시 전체 롤백 (현재 동작이지만 명시적 처리 필요)
🔎 옵션 1: 이메일 실패해도 승인 진행
if (certifyResult) { studentCouncil.managerApprove(); studentCouncilRepository.save(studentCouncil); - sendCouncilApprovedMail(studentCouncil.getEmail()); + try { + sendCouncilApprovedMail(studentCouncil.getEmail()); + } catch (Exception e) { + log.error("Failed to send approval email to: {}", studentCouncil.getEmail(), e); + } } else { - sendCouncilDeniedMail(studentCouncil.getEmail()); + try { + sendCouncilDeniedMail(studentCouncil.getEmail()); + } catch (Exception e) { + log.error("Failed to send denial email to: {}", studentCouncil.getEmail(), e); + } }
🧹 Nitpick comments (1)
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java (1)
89-91:setCouncilName이 일반 setter로 구현되어 도메인 로직이 명확하지 않습니다.일반 setter는 언제 어떤 조건에서 호출되는지 명확하지 않아 엔티티의 캡슐화를 약화시킵니다.
CouncilNameGenerator에서 이름을 생성한 후 설정하는 용도라면, 메서드명을 더 구체적으로 변경하거나 생성자/빌더에서 처리하는 것이 좋습니다.🔎 대안 제안
대안 1: 메서드명을 더 명확하게 변경
- public void setCouncilName(String councilName) { + public void assignCouncilName(String councilName) { this.councilName = councilName; }대안 2: 빌더에서 처리하도록 변경 (CouncilNameGenerator를 회원가입 서비스에서 사용)
이 경우
setCouncilName메서드를 제거하고 회원가입 시councilName을 미리 계산하여 빌더에 전달합니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.javasrc/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.javasrc/main/java/com/campus/campus/domain/council/domain/repository/StudentCouncilRepository.javasrc/main/java/com/campus/campus/domain/councilnotice/application/service/StudentCouncilNoticeService.javasrc/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.javasrc/main/java/com/campus/campus/domain/manager/application/exception/ErrorCode.javasrc/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.javasrc/main/java/com/campus/campus/domain/manager/application/service/ManagerService.javasrc/main/java/com/campus/campus/global/config/PermitUrlConfig.javasrc/main/java/com/campus/campus/global/util/jwt/ManagerPrincipal.java
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.java
- src/main/java/com/campus/campus/global/util/jwt/ManagerPrincipal.java
🧰 Additional context used
🧬 Code graph analysis (3)
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java (2)
src/main/java/com/campus/campus/domain/council/application/exception/StudentCouncilNotFoundException.java (1)
StudentCouncilNotFoundException(5-9)src/main/java/com/campus/campus/domain/council/application/service/CouncilService.java (1)
Service(24-89)
src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java (3)
src/main/java/com/campus/campus/domain/council/application/exception/PasswordNotCorrectException.java (2)
PasswordNotCorrectException(6-8)PasswordNotCorrectException(5-9)src/main/java/com/campus/campus/domain/user/application/exception/UserNotFirstLoginException.java (1)
UserNotFirstLoginException(5-9)src/main/java/com/campus/campus/domain/user/application/exception/NicknameNotMatchException.java (1)
NicknameNotMatchException(5-9)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (3)
src/main/java/com/campus/campus/domain/council/application/exception/StudentCouncilNotFoundException.java (1)
StudentCouncilNotFoundException(5-9)src/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.java (1)
ManagerNotFoundException(5-9)src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java (1)
PasswordNotCorrectException(5-9)
⏰ 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 (11)
src/main/java/com/campus/campus/global/config/PermitUrlConfig.java (1)
28-28: 관리자 로그인 엔드포인트 경로가 올바르게 구성되었습니다.PermitUrlConfig의 "/managers/login" 경로가 ManagerController의 실제 엔드포인트 매핑과 일치합니다 (클래스 레벨 @RequestMapping("/managers") + 메서드 레벨 @PostMapping("/login")). 또한 이전 리뷰에서 지적된 "/jwt/token/reissue" 중복 문제도 해결되었습니다.
src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java (1)
1-9: 과거 리뷰에서 지적된 ErrorCode import 문제가 해결되었습니다.ErrorCode가 동일 패키지(
com.campus.campus.domain.manager.application.exception)에 위치하므로 별도의 import 문 없이 올바르게 참조됩니다. 구조가 다른 도메인(council, user)의 예외 클래스들과 일관성 있게 작성되었습니다.src/main/java/com/campus/campus/domain/manager/application/exception/ErrorCode.java (2)
1-19: 전체 구현이 잘 되어 있습니다.ErrorCodeInterface를 구현하고 Lombok 어노테이션을 사용한 enum 구조가 다른 도메인들과 일관성 있게 작성되었습니다.
13-14: 에러 코드 충돌 해결이 완벽하게 검증되었습니다.관리자 도메인의 에러 코드가 2701, 2702로 변경되어 장소 도메인의 2600-2605 범위와의 충돌이 완전히 제거되었으며, HTTP 상태 코드도 적절하게 설정되어 있습니다. 전체 도메인 및 글로벌 예외 처리기를 대상으로 한 검색 결과 2700-2799 범위는 관리자 도메인에서만 독점적으로 사용되고 있으므로 다른 도메인과의 추가 충돌 가능성은 없습니다.
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (2)
46-61: LGTM!관리자 로그인 로직이 올바르게 구현되었습니다. 인증 흐름, 토큰 생성, Redis 저장이 모두 정상적으로 처리됩니다.
83-96: LGTM!미승인 학생회 목록 조회와 개별 조회 로직이 올바르게 구현되었습니다.
managerApproved = false필터가 일관되게 적용되어 대기 중인 학생회만 조회됩니다.src/main/java/com/campus/campus/domain/councilnotice/application/service/StudentCouncilNoticeService.java (2)
52-54: LGTM!승인된 학생회만 공지사항을 생성할 수 있도록 제약이 올바르게 적용되었습니다.
109-110: LGTM!
update와delete메서드에 승인 상태 확인이 추가되어, 승인되지 않은 학생회가 기존 공지사항을 수정/삭제할 수 없도록 보호되었습니다. PR 목표인 "managerApproved가 true인 경우에만 학생회 전용 API 사용 가능"이 올바르게 구현되었습니다.Also applies to: 151-152
src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java (2)
57-59: LGTM!승인된 학생회만 게시글을 생성할 수 있도록 제약이 올바르게 적용되었습니다.
135-137: LGTM!
delete와update메서드에 승인 상태 확인이 추가되어, 승인되지 않은 학생회가 기존 게시글을 수정/삭제할 수 없도록 보호되었습니다.StudentCouncilNoticeService와 일관된 패턴으로 구현되었습니다.Also applies to: 171-173
src/main/java/com/campus/campus/domain/council/domain/repository/StudentCouncilRepository.java (1)
13-43: LGTM!리포지토리 메서드들이
managerApproved제약을 일관되게 적용하고 있습니다. 승인된 학생회와 미승인 학생회를 명확하게 구분하여 조회할 수 있도록 설계되었으며, fetch join을 통해 N+1 문제도 방지하고 있습니다.
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java
Outdated
Show resolved
Hide resolved
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java
Show resolved
Hide resolved
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
♻️ Duplicate comments (1)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (1)
76-79: 이메일 전송 실패에 대한 에러 처리가 여전히 필요합니다.이전 리뷰에서 지적된 사항이지만, 이메일 전송 중 예외 발생 시 트랜잭션이 롤백되어 승인 처리가 완료되지 않는 문제가 여전히 존재합니다. 비즈니스 요구사항에 따라 다음 중 하나를 선택하여 구현해야 합니다:
- 이메일 전송을 별도 트랜잭션으로 분리 (
@Async또는 이벤트 기반)- try-catch로 예외를 잡아 로깅하고 승인은 계속 진행
- 이메일 전송 실패 시 전체 트랜잭션 롤백 (현재 동작이지만 명시적 문서화 필요)
🔎 옵션 2 예시 (이메일 실패해도 승인 진행)
if (certifyResult) { studentCouncil.managerApprove(); studentCouncilRepository.save(studentCouncil); - sendCouncilApprovedMail(studentCouncil.getEmail()); + try { + sendCouncilApprovedMail(studentCouncil.getEmail()); + } catch (Exception e) { + log.error("Failed to send approval email to: {}", studentCouncil.getEmail(), e); + } } else { - sendCouncilDeniedMail(studentCouncil.getEmail()); + try { + sendCouncilDeniedMail(studentCouncil.getEmail()); + } catch (Exception e) { + log.error("Failed to send denial email to: {}", studentCouncil.getEmail(), e); + } }
🧹 Nitpick comments (2)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (1)
73-74: 명시적save()호출을 생략할 수 있습니다.
@Transactional컨텍스트에서 JPA/Hibernate는 관리되는(managed) 엔티티의 변경사항을 트랜잭션 커밋 시 자동으로 저장합니다. Line 74의save()호출은 불필요할 수 있으나, 명시적으로 작성하는 것도 가독성 측면에서 문제는 없습니다.🔎 save() 호출 제거 (선택사항)
if (certifyResult) { studentCouncil.managerApprove(); - studentCouncilRepository.save(studentCouncil); sendCouncilApprovedMail(studentCouncil.getEmail()); } else {src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java (1)
89-91: 도메인 메서드 네이밍 고려 (선택 사항)현재 구현은 동작하지만, DDD 관점에서
setCouncilName대신assignCouncilName과 같은 도메인 의도를 드러내는 메서드명을 고려해 볼 수 있습니다. 다른 상태 변경 메서드(delete,changePassword,changeEmail,managerApprove)와 네이밍 스타일을 일관되게 유지하면 가독성이 향상됩니다.🔎 제안하는 수정
- public void setCouncilName(String councilName) { + public void assignCouncilName(String councilName) { this.councilName = councilName; }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.javasrc/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (3)
src/main/java/com/campus/campus/domain/council/application/exception/StudentCouncilNotFoundException.java (1)
StudentCouncilNotFoundException(5-9)src/main/java/com/campus/campus/domain/manager/application/exception/ManagerNotFoundException.java (1)
ManagerNotFoundException(5-9)src/main/java/com/campus/campus/domain/manager/application/exception/PasswordNotCorrectException.java (1)
PasswordNotCorrectException(5-9)
⏰ 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 (9)
src/main/java/com/campus/campus/domain/manager/application/service/ManagerService.java (6)
31-44: 클래스 구조와 의존성 주입이 적절합니다.클래스 레벨의
@Transactional(readOnly = true)와 필요한 메서드에만 명시적으로@Transactional을 적용하는 구조가 좋습니다. 모든 필요한 의존성이 생성자 주입으로 올바르게 구성되어 있습니다.
46-61: 로그인 로직이 올바르게 구현되었습니다.관리자 조회, 비밀번호 검증, 토큰 생성 및 Redis 저장의 흐름이 정확합니다. 적절한 예외 처리도 포함되어 있습니다.
77-79: 거부된 학생회의 처리 방식을 확인해주세요.현재 거부 처리 시 학생회 엔티티는
managerApproved = false상태를 유지하며 별도의 상태 변경이나 삭제가 이루어지지 않습니다. 이것이 의도된 동작인지 확인이 필요합니다:
- 의도: 거부된 학생회가 재신청 가능하도록 유지
- 또는: 거부 상태를 명시적으로 표시(예: 별도
denied필드)하거나 소프트 삭제 필요비즈니스 요구사항에 따라 현재 구현이 적절한지 검토해주세요.
84-89: 미승인 학생회 목록 조회 로직이 올바릅니다.Repository 쿼리와 매핑 로직이 정확하게 구현되어 있습니다.
91-97: 특정 학생회 조회 로직이 올바르게 수정되었습니다.이전 리뷰에서 지적된 대로 미승인 학생회만 조회하도록 수정되어 일관성이 확보되었습니다.
99-133: 이메일 메시지 구조와 내용이 적절합니다.승인/거부 이메일의 내용이 명확하고 사용자 친화적입니다.
SimpleMailMessage를 사용한 구현도 적절합니다. 에러 처리는 앞서 언급한approveOrDenyCouncil메서드 리뷰를 참고해주세요.src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java (3)
71-72: 이전 리뷰 피드백이 반영되었습니다.
Boolean래퍼 타입을boolean기본 타입으로 변경하고nullable = false제약을 추가하여 NPE 위험을 제거했습니다. 기본 타입은 자동으로false로 초기화되므로 회원가입 시 미승인 상태가 올바르게 설정됩니다.
93-95: LGTM!메서드명 오타(
mangerApprove→managerApprove)가 수정되었고, 승인 상태를true로 변경하는 단방향 상태 전이가 명확하게 구현되었습니다.
65-69:electionImageUrl의 필수 필드 검증은 이미 적용되어 있습니다DTO 레벨의
@NotBlank검증으로electionImageUrl은 필수 필드로 강제되고 있습니다. 현재 설계는councilName,
jjaeroong
left a 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.
고생하셨습니다.
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java
Show resolved
Hide resolved
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
🤖 Fix all issues with AI Agents
In
@src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java:
- Around line 41-48: The @PostMapping path in StudentCouncilLoginController on
the validateLoginId endpoint is missing a leading slash which can break routing;
update the mapping on the validateLoginId method (annotated with
@PostMapping("signup/validate")) to include the leading slash (i.e.,
@PostMapping("/signup/validate")) so it correctly composes with the controller
base path and routes requests to validateLoginId properly.
🧹 Nitpick comments (3)
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java (1)
89-91: councilName 파라미터에 대한 null 검증 추가를 고려하세요.
generateCouncilName메서드는 도메인 로직을 잘 표현하고 있으나,councilName파라미터가 null일 경우에 대한 방어 로직이 없습니다. 도메인 무결성을 보장하기 위해 null 검증을 추가하는 것이 좋습니다.🔎 제안하는 수정
public void generateCouncilName(String councilName) { + if (councilName == null || councilName.isBlank()) { + throw new IllegalArgumentException("Council name cannot be null or blank"); + } this.councilName = councilName; }src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java (2)
33-39: 회원가입 엔드포인트가 새로운 워크플로우를 올바르게 반영하고 있습니다.반환 타입을
Void로 변경하고SIGNUP_REQUEST_SUCCESS응답 코드를 사용하여 승인 대기 상태를 명확히 표현하고 있습니다.다만, 메서드명
Signup은 Java 네이밍 컨벤션에 따라signup으로 변경하는 것이 좋습니다.🔎 제안하는 수정
- public CommonResponse<Void> Signup(@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) { + public CommonResponse<Void> signup(@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) {
50-57: 메서드명은 camelCase를 따르는 것이 좋습니다.메서드명
Login을 Java 네이밍 컨벤션에 따라login으로 변경하는 것을 권장합니다.🔎 제안하는 수정
- public CommonResponse<StudentCouncilLoginResponse> Login( + public CommonResponse<StudentCouncilLoginResponse> login(
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginIdValidateRequest.javasrc/main/java/com/campus/campus/domain/council/application/service/CouncilLoginService.javasrc/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.javasrc/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.javasrc/main/java/com/campus/campus/domain/council/presentation/StudentCouncilResponseCode.javasrc/main/java/com/campus/campus/global/config/PermitUrlConfig.java
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/com/campus/campus/global/config/PermitUrlConfig.java
🧰 Additional context used
🧬 Code graph analysis (3)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginIdValidateRequest.java (6)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.java (1)
StudentCouncilSignUpRequest(11-40)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilChangePasswordRequest.java (1)
StudentCouncilChangePasswordRequest(9-24)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginRequest.java (1)
StudentCouncilLoginRequest(7-16)src/main/java/com/campus/campus/domain/mail/application/dto/request/EmailVerificationRequest.java (1)
EmailVerificationRequest(6-11)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilFindPasswordRequest.java (1)
StudentCouncilFindPasswordRequest(9-24)src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java (1)
UserWithdrawRequest(6-11)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java (3)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilController.java (3)
RestController(19-43)PatchMapping(35-42)PatchMapping(26-33)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.java (1)
StudentCouncilSignUpRequest(11-40)src/main/java/com/campus/campus/domain/council/application/mapper/StudentCouncilLoginMapper.java (1)
Component(16-56)
src/main/java/com/campus/campus/domain/council/application/service/CouncilLoginService.java (3)
src/main/java/com/campus/campus/domain/council/application/service/CouncilService.java (2)
Service(24-89)Transactional(50-75)src/main/java/com/campus/campus/domain/council/application/mapper/StudentCouncilLoginMapper.java (2)
Component(16-56)createStudentCouncil(21-34)src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilSignUpRequest.java (1)
StudentCouncilSignUpRequest(11-40)
🔇 Additional comments (11)
src/main/java/com/campus/campus/domain/council/application/dto/request/StudentCouncilLoginIdValidateRequest.java (1)
6-11: LGTM! DTO 구조가 올바릅니다.로그인 ID 검증을 위한 DTO가 프로젝트의 다른 요청 DTO들과 일관된 패턴을 따르고 있으며, 적절한 검증 어노테이션이 적용되어 있습니다.
src/main/java/com/campus/campus/domain/council/domain/entity/StudentCouncil.java (2)
93-95: 승인 상태 전환 메서드가 잘 구현되었습니다.
managerApprove메서드는 관리자 승인 상태를 명확하게 표현하는 도메인 행위 메서드입니다. 메서드명과 구현이 의도를 잘 드러내고 있습니다.
65-72: 회원가입 시 councilName과 electionImageUrl이 안전하게 초기화됩니다.
electionImageUrl은StudentCouncilSignUpRequest에서@NotBlank로 검증되며 매퍼에서 직접 매핑됩니다.councilName은 매퍼에서 초기화되지 않지만,CouncilLoginService.signUp()메서드에서councilNameGenerator.buildCouncilName(studentCouncil)호출 후studentCouncil.generateCouncilName(councilName)메서드로 엔티티에 설정되고 저장됩니다. 따라서 두 필드 모두 회원가입 시점에 안전하게 초기화됩니다.Likely an incorrect or invalid review comment.
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilResponseCode.java (1)
13-14: 응답 코드가 새로운 승인 워크플로우를 잘 반영하고 있습니다.
SIGNUP_REQUEST_SUCCESS로의 이름 변경은 즉시 회원가입 완료가 아닌 승인 대기 상태를 명확히 표현하며, 새로운VALIDATE_LOGIN_ID_SUCCESS코드도 적절하게 추가되었습니다.src/main/java/com/campus/campus/domain/council/application/service/CouncilLoginService.java (7)
71-84: 회원가입 메서드가 승인 기반 워크플로우를 올바르게 구현했습니다.반환 타입을
void로 변경하여 즉시 토큰을 발급하지 않는 새로운 승인 기반 워크플로우를 잘 반영하고 있습니다. 중복 검증 로직도 적절하게 구성되어 있으며,@Transactional어노테이션으로 인해 동시성 이슈로부터 보호됩니다.
94-100: councilName 생성 로직이 잘 구현되었습니다.
CouncilNameGenerator를 활용하여 학생회 이름을 생성하고, 도메인 메서드generateCouncilName을 통해 설정하는 방식이 DDD 패턴을 잘 따르고 있습니다. 이메일 검증도 사용 처리되어 올바른 흐름을 보장합니다.
102-106: 로그인 ID 검증 메서드가 명확하게 구현되었습니다.중복 검증 로직이 간단하고 명확하며, 회원가입 플로우의 사전 검증 단계로 적절히 기능합니다.
108-124: 로그인 메서드에 관리자 승인 검증이 적절히 추가되었습니다.
findByLoginIdAndManagerApprovedIsTrueAndDeletedAtIsNull메서드를 사용하여 승인된 학생회만 로그인할 수 있도록 제한하는 것이 PR의 주요 목적과 일치합니다.
126-141: 아이디 찾기 메서드에 관리자 승인 검증이 적절히 추가되었습니다.이메일 기반 조회 시에도 승인된 학생회만 처리할 수 있도록
findByEmailAndManagerApprovedIsTrueAndDeletedAtIsNull메서드를 사용하고 있습니다.
143-161: 비밀번호 찾기 메서드에 관리자 승인 검증이 적절히 추가되었습니다.미승인 학생회는 비밀번호 재설정을 할 수 없도록 적절히 제한되어 있습니다.
163-180: 회원탈퇴 메서드에 관리자 승인 검증이 적절히 추가되었습니다.승인된 학생회만 탈퇴할 수 있도록 보호하는 로직이 일관되게 적용되었습니다.
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java
Outdated
Show resolved
Hide resolved
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/campus/campus/domain/council/presentation/StudentCouncilLoginController.java (1)
35-35: 메서드 이름이 Java 명명 규칙을 위반합니다.메서드 이름
Signup은 대문자로 시작하고 있습니다. Java 명명 규칙에 따라 메서드 이름은 소문자로 시작해야 합니다.🔎 제안하는 수정
- public CommonResponse<Void> Signup(@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) { + public CommonResponse<Void> signup(@Valid @RequestBody StudentCouncilSignUpRequest studentCouncilSignUpRequest) {
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java
🔇 Additional comments (3)
src/main/java/com/campus/campus/domain/council/presentation/StudentCouncilLoginController.java (3)
13-13: LGTM!새로운 로그인 ID 중복 검증 엔드포인트에 필요한 import가 올바르게 추가되었습니다.
36-39: LGTM!회원가입 시 즉시 토큰을 발급하지 않도록 변경되었으며, 응답 코드가
SIGNUP_REQUEST_SUCCESS로 적절하게 업데이트되었습니다. 관리자 승인 워크플로우에 맞게 올바르게 구현되었습니다.
41-48: 엔드포인트 구현이 올바르게 되었습니다.로그인 ID 중복 검증 엔드포인트가 적절하게 구현되었습니다. 입력 유효성 검사, 서비스 호출, 응답 처리가 모두 올바르게 처리되었습니다.
참고: Line 41의
@PostMapping경로에서 선행 슬래시가 누락된 문제는 이전 리뷰 코멘트에서 이미 지적되었습니다.
🔀 변경 내용
✅ 작업 항목
++ 현재 디자인 상으로 회원가입 id의 중복여부를 검증하는 api가 별도로 필요하여 추가 구현했습니다.
📸 스크린샷 (선택)
학생회 이름이 생성되어 로그인이 학생회 이름도 반환 (ex: 가천대학교 IT융합대학 학생회)
관리자 로그인 기능
관리자의 학생회 인증 요청 목록 조회
관리자의 특정 학생회 계정 인증 요청 당선 사진 조회
관리자의 학생회 계정 승인
📎 참고 이슈
관련 이슈 번호 #22
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.