-
Notifications
You must be signed in to change notification settings - Fork 0
test: stack, projectTeam 테스트 코드 / PreAuthorize 적용 #133
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
base: develop
Are you sure you want to change the base?
Conversation
종합 요약Walkthrough이 PR은 ProjectTeamController 및 StackController의 엔드포인트에 메서드 수준의 보안 어노테이션을 추가하고, 권한 평가자를 확장하며, 광범위한 테스트 스위트를 도입하고, Zoom 웹훅 처리를 재구성하고, 스키마를 마이그레이션합니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25분 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: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
techeerzip/src/main/java/backend/techeerzip/domain/feedback/service/FeedbackService.java (1)
159-243: 멘토 피드백 조회 기본 기간/관리자 권한 처리 로직 재검토 제안
startDateTime이null인 경우LocalDateTime.now().minusMonths(1)로 기본 시작 시점을 한 달 전으로 두고,endDateTime이null이면 상한 없이 조회하는 구조라 기본 동작이 “최근 1개월 ~ 현재”가 됩니다. 실제 요구사항이 이 정책(최근 1개월을 기본)인지 한 번 더 확인해 두면 좋겠습니다.- 두 메서드 모두
validateMentorRole(currentUser)호출 후에if (!currentUser.getUserId().equals(mentorId) && !currentUser.isAdmin())체크를 하는데, ADMIN이RoleType.MENTOR가 아니라면 앞단에서 이미 예외가 발생해 뒤의isAdmin()분기는 실행되지 않을 가능성이 큽니다. ADMIN도 멘토 피드백을 조회할 수 있어야 한다면, 관리자에 대한 예외 케이스를validateMentorRole안에서 처리하거나, ADMIN일 때는 mentorRole 검증을 건너뛰도록 분기 순서를 조정하는 쪽을 고려해 볼 수 있습니다. (동일 패턴이getAllReceivedFeedbacks에도 존재합니다.)
🧹 Nitpick comments (9)
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamDslRepositoryImplTest.java (2)
19-87: 문자열 기반 assertion의 취약성을 개선하는 것을 권장합니다.현재 테스트는
expression.toString().contains()를 사용하여 QueryDSL 표현식을 검증하고 있습니다. 이 방식은 QueryDSL의 내부 구현이나 toString() 포맷이 변경될 경우 깨질 수 있습니다.더 견고한 테스트를 위해 다음 방법들을 고려해보세요:
- 실제 QueryDSL 표현식 구조를 직접 검증 (예: expression의 타입, 연산자 확인)
- 통합 테스트로 실제 쿼리 결과를 검증
- QueryDSL의
accept()메서드를 활용한 visitor 패턴으로 표현식 구조 검증다만, 현재 정적 유틸리티 메서드를 테스트하는 수준에서는 문자열 검증도 합리적인 트레이드오프일 수 있습니다.
109-128: OR 조건 검증 로직을 더 명확하게 개선할 수 있습니다.Line 127에서
exprString.toLowerCase().contains("||")로 OR 조건을 검증하고 있는데, 이는 다음과 같은 한계가 있습니다:
- "||"가 다른 컨텍스트(주석, 문자열 리터럴 등)에서 나타날 수 있음
- QueryDSL의 내부 표현 방식이 변경되면 실패할 수 있음
더 구체적인 검증을 위해 다음을 고려해보세요:
- 각 포지션별로 개별 조건이 존재하는지 확인 (예: "BACKEND", "FRONTEND" 문자열 포함 여부)
- 표현식의 타입이나 구조를 직접 검증
- 실제 데이터로 통합 테스트를 수행하여 필터링이 정상 작동하는지 확인
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/TeamUnionViewTest.java (1)
15-33: Getter 테스트의 가치를 재검토해보세요.ReflectionTestUtils를 사용한 getter 테스트는 기술적으로는 정확하지만, 실제로는 제한적인 가치를 제공합니다. 데이터베이스 뷰 매핑이나 ORM 설정 오류를 잡기 위한 목적이라면 통합 테스트에서 더 효과적으로 검증할 수 있습니다. 단순 getter만 테스트하는 것보다 실제 사용 시나리오를 반영하는 테스트를 우선하는 것을 고려해보세요.
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/TeamStackServiceTest.java (1)
51-69: Mock Stack의 name 설정을 고려해보세요.Line 61의 mock Stack이
getName()을 반환하도록 설정하지 않았습니다. 현재는 요청된 스택 수와 DB에서 조회된 스택 수의 불일치로 예외가 발생하지만, 실제 서비스 로직이 이름으로 매칭을 시도한다면 더 명확한 테스트를 위해when(stack.getName()).thenReturn("Java")를 추가하는 것이 좋습니다.Stack stack = mock(Stack.class); + when(stack.getName()).thenReturn("Java"); // DB에는 "Java" 하나만 있다고 가정 given(stackRepository.findAllByNameIn(anyCollection())).willReturn(List.of(stack));techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.java (1)
178-178: 개발 과정의 주석을 정리하세요.
"nextInfo로 수정됨 (Good!)"주석은 개발 중 변경사항을 기록한 것으로 보입니다. 테스트 코드의 가독성을 위해 제거하는 것이 좋습니다.- .andExpect(jsonPath("$.nextInfo.hasNext").value(false)); // nextInfo로 수정됨 (Good!) + .andExpect(jsonPath("$.nextInfo.hasNext").value(false));techeerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.java (1)
76-116: 에러 응답 코드를 재검토해보세요.
IllegalArgumentException에 대해 500 Internal Server Error를 반환하도록 테스트하고 있습니다. 일반적으로 잘못된 입력에 대해서는 400 Bad Request가 더 적절할 수 있습니다. 전역 예외 핸들러의 설계 의도에 따라 다르지만, RESTful API 관례상 클라이언트 오류와 서버 오류를 명확히 구분하는 것이 좋습니다.techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java (1)
72-109: 예외 검증 스타일을 통일하는 것을 고려하세요.실패 시나리오를 잘 다루고 있지만,
org.junit.jupiter.api.Assertions.assertThrows와 AssertJ의assertThatThrownBy를 혼용하고 있습니다. 파일 전체에서 AssertJ를 사용하고 있으므로, 일관성을 위해 예외 검증도 AssertJ로 통일하는 것이 좋습니다.예시:
- org.junit.jupiter.api.Assertions.assertThrows( - IllegalArgumentException.class, () -> stackService.create(request)); + assertThatThrownBy(() -> stackService.create(request)) + .isInstanceOf(IllegalArgumentException.class);techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamServiceTest.java (2)
107-124:teamData.getName()mock 누락서비스에서 모집 인원 유효성 검사 전에
teamData.getName()을 호출하면NullPointerException이 발생하거나 테스트가 불안정해질 수 있습니다. 다른 테스트와 일관성을 위해 추가하는 것이 좋습니다.// 음수 인원 설정 when(recruitCounts.getBackendNum()).thenReturn(-1); + when(teamData.getName()).thenReturn("Test Team"); // When & Then
6-8: Mockito 스타일 일관성
when().thenReturn()과given().willReturn()두 스타일이 혼용되고 있습니다. BDD 스타일로 통일하면 Given/When/Then 구조와 더 잘 어울립니다.-import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.BDDMockito.*;그리고 테스트 내에서
when()대신given()을 사용:- when(request.getTeamData()).thenReturn(teamData); + given(request.getTeamData()).willReturn(teamData);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (28)
techeerzip/build.gradle(1 hunks)techeerzip/src/main/java/backend/techeerzip/domain/feedback/controller/FeedbackController.java(1 hunks)techeerzip/src/main/java/backend/techeerzip/domain/feedback/repository/FeedbackRepositoryCustom.java(1 hunks)techeerzip/src/main/java/backend/techeerzip/domain/feedback/repository/FeedbackRepositoryImpl.java(3 hunks)techeerzip/src/main/java/backend/techeerzip/domain/feedback/service/FeedbackService.java(3 hunks)techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamController.java(6 hunks)techeerzip/src/main/java/backend/techeerzip/domain/stack/controller/StackController.java(2 hunks)techeerzip/src/main/java/backend/techeerzip/domain/todayCs/scheduler/TodayCsScheduler.java(1 hunks)techeerzip/src/main/java/backend/techeerzip/global/permission/ProjectTeamPermissionEvaluator.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectMainImageTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectResultImageTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectTeamTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/TeamStackTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/TeamUnionViewTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectResultImageRepositoryTest.java(0 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamDslRepositoryImplTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamRepositoryTest.java(0 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamStackRepositoryTest.java(0 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/TeamUnionViewDslRepositoryImplTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/querydsl/ProjectTeamDslRepositoryImplTest.java(0 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImplTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamServiceTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/TeamStackServiceTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/stack/entity/StackTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java(1 hunks)techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java(1 hunks)
💤 Files with no reviewable changes (4)
- techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamRepositoryTest.java
- techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/querydsl/ProjectTeamDslRepositoryImplTest.java
- techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectResultImageRepositoryTest.java
- techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamStackRepositoryTest.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-24T09:09:44.221Z
Learnt from: dongwooooooo
Repo: Techeer-Hogwarts/backend PR: 7
File: techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImpl.java:50-58
Timestamp: 2025-05-24T09:09:44.221Z
Learning: ProjectTeam의 메인 이미지는 데이터베이스에서 NOT NULL 제약조건을 가지므로, 항상 메인 이미지가 존재해야 하며 삭제 시에는 반드시 새로운 이미지로 교체해야 합니다.
Applied to files:
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectMainImageTest.javatecheerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImplTest.javatecheerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectResultImageTest.javatecheerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectTeamTest.javatecheerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/TeamStackTest.java
📚 Learning: 2025-05-23T11:55:51.352Z
Learnt from: dongwooooooo
Repo: Techeer-Hogwarts/backend PR: 7
File: techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeService.java:19-50
Timestamp: 2025-05-23T11:55:51.352Z
Learning: ProjectTeamFacadeService에서 MultipartFile을 직접 노출하는 것은 의도적인 설계 결정으로, 서비스 계층은 순수한 도메인 로직을 관리하고 파사드 계층에서 S3, 인덱스, Slack 등의 외부 시스템 처리와 롤백을 담당하기 위함이다.
Applied to files:
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImplTest.java
🧬 Code graph analysis (8)
techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java (3)
techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java (1)
ExtendWith(17-44)techeerzip/src/main/java/backend/techeerzip/domain/stack/dto/StackDto.java (1)
StackDto(9-27)techeerzip/src/main/java/backend/techeerzip/infra/index/IndexEvent.java (1)
IndexEvent(8-23)
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.java (1)
techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/exception/ProjectTeamNotFoundException.java (1)
ProjectTeamNotFoundException(6-11)
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImplTest.java (1)
techeerzip/src/main/java/backend/techeerzip/infra/index/IndexEvent.java (1)
IndexEvent(8-23)
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/TeamStackServiceTest.java (2)
techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/dto/request/TeamStackInfo.java (1)
TeamStackInfo(10-35)techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/exception/ProjectInvalidTeamStackException.java (1)
ProjectInvalidTeamStackException(6-11)
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamServiceTest.java (2)
techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/exception/ProjectDuplicateTeamName.java (1)
ProjectDuplicateTeamName(6-15)techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/exception/TeamInvalidRecruitNumException.java (1)
TeamInvalidRecruitNumException(6-10)
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamDslRepositoryImplTest.java (1)
techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/exception/ProjectTeamInvalidSortType.java (1)
ProjectTeamInvalidSortType(6-10)
techeerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.java (1)
techeerzip/src/main/java/backend/techeerzip/domain/stack/dto/StackDto.java (1)
StackDto(9-27)
techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java (1)
techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java (1)
ExtendWith(23-110)
🔇 Additional comments (31)
techeerzip/src/main/java/backend/techeerzip/domain/todayCs/scheduler/TodayCsScheduler.java (1)
37-37: 코드 포맷팅 개선이 적절합니다.Slack 이벤트 발행 코드를 한 줄로 정리하여 가독성이 향상되었습니다. 기능적 변경 없이 코드 일관성을 개선하는 좋은 변경입니다.
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/ProjectTeamDslRepositoryImplTest.java (1)
1-128: 테스트 구조와 커버리지가 잘 구성되어 있습니다.이 테스트 파일은 다음과 같은 강점이 있습니다:
- Happy path, edge case (null, empty), 예외 케이스를 모두 커버
- Given/When/Then 패턴을 일관되게 사용하여 가독성이 좋음
- DisplayName을 통한 명확한 테스트 의도 표현
- 정적 메서드 테스트에 적합한 간결한 구조
문자열 기반 assertion의 취약성에도 불구하고, QueryDSL 표현식 빌더의 기본 동작을 검증하는 목적으로는 충분히 효과적입니다.
techeerzip/src/main/java/backend/techeerzip/domain/feedback/repository/FeedbackRepositoryCustom.java (1)
27-33: findReceivedFeedbacks 시그니처 포맷팅 변경 확인파라미터 순서와 타입이 구현체/서비스 계층과 일관되게 유지되고 있어, 이번 변경은 포맷팅 정리 수준으로 보이며 기능상 문제는 없어 보입니다.
techeerzip/src/main/java/backend/techeerzip/domain/feedback/controller/FeedbackController.java (1)
210-258: 멘토용 조회 API와 서비스 호출 시그니처 정합성 양호컨트롤러에서 받은
status,startDateTime,endDateTime,cursor,limit,currentUser를 서비스 레이어로 그대로 전달하고 있고, 두 서비스 메서드 시그니처와 파라미터 순서도 일관되게 맞춰져 있어 매핑 오류나 누락은 없어 보입니다.techeerzip/src/main/java/backend/techeerzip/domain/feedback/repository/FeedbackRepositoryImpl.java (1)
171-209: findReceivedFeedbacks 조회 조건/페이징 로직 일관성 확인특정 멘토(recipientId)의 삭제되지 않은 피드백을 대상으로,
status,startDateTime,endDateTime,cursor가 있을 때만 순차적으로 AND 조건을 추가하고 마지막에limit + 1로 페이지네이션하는 패턴이 상단 메서드들과 일관적입니다. 인터페이스·서비스 시그니처와도 정합성이 유지되고 있어 기능상 문제는 없어 보입니다.techeerzip/build.gradle (1)
80-80: LGTM!Spring Security 테스트를 위한 적절한 의존성 추가입니다. PR에서 추가된
@PreAuthorize어노테이션들의 테스트를 지원합니다.techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectResultImageTest.java (2)
11-26: LGTM!빌더 패턴을 통한 엔티티 생성과 기본값 검증이 적절합니다.
isDeleted의 초기값이 false임을 확인하는 것은 중요한 검증입니다.
28-44: LGTM!소프트 삭제 로직이 올바르게 검증되었습니다.
isDeleted플래그와updatedAt타임스탬프 업데이트를 모두 확인합니다.techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectMainImageTest.java (2)
11-26: LGTM!메인 이미지 엔티티의 생성 로직이 올바르게 검증되었습니다.
28-44: LGTM!소프트 삭제 메커니즘이 적절하게 테스트되었습니다. 메인 이미지의 NOT NULL 제약조건은 애플리케이션 레벨에서 처리되며, 이 테스트는 엔티티 메서드 자체의 동작을 검증합니다.
Based on learnings, 메인 이미지는 데이터베이스에서 NOT NULL 제약조건을 가지므로 실제 삭제 시에는 새로운 이미지로 교체해야 하지만, 이 테스트는
delete()메서드의 동작 자체를 검증하는 것으로 적절합니다.techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectTeamTest.java (4)
19-35: LGTM!테스트 픽스처를 위한 헬퍼 메서드가 잘 구성되어 있습니다. 재사용 가능하고 명확합니다.
37-101: LGTM!기본값 초기화, 소프트 삭제, 상태 전환, 조회수 증가 로직이 모두 올바르게 검증되었습니다. Given-When-Then 패턴이 일관되게 적용되었습니다.
103-142: LGTM!팀 역할 카운트 관리 로직이 포괄적으로 테스트되었습니다. 경계값 테스트(0 이하로 내려가지 않는지)를 포함한 것이 좋습니다.
144-213: LGTM!리더 필터링, 메인 이미지 교체, 결과 이미지 삭제 로직이 모두 올바르게 검증되었습니다. Mock을 활용한 테스트 전략이 적절합니다.
techeerzip/src/test/java/backend/techeerzip/domain/stack/entity/StackTest.java (1)
10-46: LGTM!Stack 엔티티의 핵심 동작(생성, 수정, 삭제)이 모두 적절하게 검증되었습니다. 간결하면서도 필요한 커버리지를 제공합니다.
techeerzip/src/main/java/backend/techeerzip/global/permission/ProjectTeamPermissionEvaluator.java (1)
63-68: LGTM!지원하는 권한을
UPDATE,DELETE,READ로 확장하여 더 세밀한 권한 제어가 가능해졌습니다. 이는 컨트롤러에 추가된@PreAuthorize어노테이션들과 일치합니다.techeerzip/src/main/java/backend/techeerzip/domain/stack/controller/StackController.java (1)
6-6: LGTM!스택 생성 API에 ADMIN 역할 제한을 추가하여 보안이 강화되었습니다. 이는 스택이 시스템 전체에 영향을 미치는 리소스이므로 적절한 제약입니다.
ADMIN역할이 시스템에서 올바르게 설정되어 있는지 확인하세요. 필요하다면 관련 설정과 사용자 역할 매핑을 검토하세요.Also applies to: 19-24
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/TeamStackTest.java (2)
13-29: 테스트 구조가 명확하고 적절합니다.Given-When-Then 패턴을 잘 따르고 있으며, 빌더 패턴을 통한 객체 생성과 필드 검증이 올바르게 수행되고 있습니다.
31-48: updatedAt 필드 검증이 포함되어 우수합니다.메인 스택 여부 변경과 함께 감사 필드(updatedAt)가 업데이트되는지 확인하여 완전성을 보장합니다.
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/repository/TeamUnionViewDslRepositoryImplTest.java (3)
15-30: 인스턴스 동일성 검증이 포함되어 좋습니다.
isSameAs검증을 통해 불필요한 복사가 발생하지 않음을 확인하는 것은 성능 최적화 측면에서 의미 있는 테스트입니다.
32-46: subList 동작에 대한 이해가 반영되어 있습니다.코멘트를 통해 subList가 원본의 뷰라는 점을 인지하고 있으며, 적절한 검증 범위를 설정했습니다.
48-60: 엣지 케이스를 적절히 다루고 있습니다.limit이 0인 경우를 명시적으로 테스트하여 경계 조건 처리를 검증합니다.
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/TeamStackServiceTest.java (1)
30-49: 성공 케이스를 잘 검증하고 있습니다.스택 조회 및 변환 로직이 올바르게 작동하는지 확인하며, 결과 객체의 필드들을 적절히 검증합니다.
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.java (1)
64-121: 컨트롤러 테스트 구조는 우수합니다.각 엔드포인트에 대한 테스트가 체계적으로 작성되어 있으며, CSRF 토큰 처리, Multipart 요청 처리, JSON 응답 검증 등이 올바르게 수행되고 있습니다. 다만 앞서 언급한 보안 필터 비활성화 문제를 해결하면 더욱 완전한 테스트가 될 것입니다.
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImplTest.java (2)
38-62: 파사드 계층의 오케스트레이션을 잘 검증하고 있습니다.S3 업로드, DB 저장, 이벤트 발행의 순서와 상호작용을 체계적으로 확인하고 있으며, 이는 파사드 서비스의 핵심 책임을 정확히 테스트합니다. Based on learnings, 파사드가 외부 시스템 처리를 담당한다는 설계 의도와도 잘 부합합니다.
64-87: 실패 시 롤백 로직을 검증하는 중요한 테스트입니다.DB 저장 실패 시 S3에 업로드된 리소스를 정리하는 것을 확인하여, 데이터 일관성과 리소스 누수 방지를 보장합니다. 이는 분산 트랜잭션 환경에서 매우 중요한 테스트 케이스입니다.
techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java (1)
32-49: 저장 및 이벤트 발행을 적절히 검증하고 있습니다.리포지토리 저장과 인덱스 이벤트 발행이 모두 정확히 한 번씩 호출되는지 확인하여, 서비스 계층의 책임을 명확히 테스트합니다.
techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamController.java (2)
55-55: 보안 어노테이션 적용이 잘 되었습니다.역할 기반 접근 제어(
hasAnyRole)와 권한 기반 접근 제어(hasPermission)가 적절히 적용되어 있습니다. SpEL 표현식에서#projectTeamId파라미터 참조도 올바르게 사용되고 있습니다.Also applies to: 67-67, 104-104, 116-116
129-148:applyToProject와cancelApplication의 인가 처리 확인이 두 엔드포인트는
@PreAuthorize없이@CurrentUser만 사용하고 있습니다. 서비스 레이어에서 본인 확인 로직이 있다면 문제없으나, 명시적인 권한 체크가 필요하다면 추가를 고려해보세요.techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamServiceTest.java (2)
43-83: 테스트 구조가 잘 작성되었습니다.Given/When/Then 패턴을 명확하게 따르고 있고, 핵심 시나리오를 잘 검증하고 있습니다. Mock 설정과 검증 로직이 적절합니다.
85-105: 중복 이름 예외 테스트 LGTM중복 팀 이름에 대한 예외 처리를 올바르게 검증하고 있습니다.
| @WebMvcTest(ProjectTeamController.class) | ||
| @AutoConfigureMockMvc(addFilters = false) |
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 the ProjectTeamController file
fd -t f "ProjectTeamController.java" --exclude="Test.java"Repository: Techeer-Hogwarts/backend
Length of output: 168
🏁 Script executed:
# Check the test file and controller for @PreAuthorize annotations
rg -n "@PreAuthorize" --type javaRepository: Techeer-Hogwarts/backend
Length of output: 3802
🏁 Script executed:
# Get the full context of the test file
wc -l techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.javaRepository: Techeer-Hogwarts/backend
Length of output: 176
🏁 Script executed:
# Check the ProjectTeamController for @PreAuthorize and method signatures
sed -n '50,170p' techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamController.javaRepository: Techeer-Hogwarts/backend
Length of output: 5909
🏁 Script executed:
# Check the test file for the full setup and test methods
head -100 techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.javaRepository: Techeer-Hogwarts/backend
Length of output: 4344
🏁 Script executed:
# Search for @EnableMethodSecurity or @EnableGlobalMethodSecurity in the codebase
rg -n "@EnableMethodSecurity|@EnableGlobalMethodSecurity" --type javaRepository: Techeer-Hogwarts/backend
Length of output: 176
🏁 Script executed:
# Check for @WithMockUser or other security test annotations
rg -n "@WithMockUser|@WithMockCustomUser|@SecurityMockMvcRequestPostProcessors\|with" techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.javaRepository: Techeer-Hogwarts/backend
Length of output: 50
🏁 Script executed:
# Check the createProjectTeam test method
sed -n '75,120p' techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.javaRepository: Techeer-Hogwarts/backend
Length of output: 2054
🏁 Script executed:
# Check for any post processors or security configuration in test methods
sed -n '100,200p' techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.javaRepository: Techeer-Hogwarts/backend
Length of output: 4104
🏁 Script executed:
# Check the MethodSecurityConfig to understand the setup
cat techeerzip/src/main/java/backend/techeerzip/global/config/MethodSecurityConfig.javaRepository: Techeer-Hogwarts/backend
Length of output: 1084
🏁 Script executed:
# Look for any test configuration that might affect security
fd -t f "SecurityConfig\|TestSecurityConfig\|SecurityConfiguration" techeerzip/src/testRepository: Techeer-Hogwarts/backend
Length of output: 50
🏁 Script executed:
# Check if there are any authorization exception tests or expected 403/401 responses
rg -n "status\(\).isForbidden|status\(\).isUnauthorized|403|401|AccessDeniedException" techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.javaRepository: Techeer-Hogwarts/backend
Length of output: 50
@PreAuthorize 어노테이션이 테스트되지 않습니다.
현재 테스트 설정에서 mock principal이 authorities를 갖지 않으면서(Collections.emptyList()) 동시에 @EnableMethodSecurity가 구성되어 있지만, 실제로는 @PreAuthorize 검증이 작동하지 않는 것으로 보입니다. 예를 들어, createProjectTeam()은 @PreAuthorize("hasAnyRole('ADMIN', 'MENTOR', 'TECHEER'")을 요구하지만 권한 없는 mock principal로도 테스트가 통과합니다.
보안 접근 제어를 검증하려면 mock principal에 적절한 authorities를 설정하거나, 권한 검증 실패 케이스를 별도로 테스트해야 합니다.
🤖 Prompt for AI Agents
In
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.java
around lines 41-42, the test uses @WebMvcTest and a mock principal with
Collections.emptyList() so @PreAuthorize checks are effectively bypassed; update
the tests to supply a mock principal with appropriate authorities (e.g., roles
ADMIN, MENTOR, or TECHEER) for positive authorization tests and add separate
negative tests that assert forbidden responses when the principal lacks required
roles, or alternatively enable method security in the test context and configure
MockMvc to run security so @PreAuthorize is actually evaluated.
techeerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.java
Show resolved
Hide resolved
techeerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.java
Outdated
Show resolved
Hide resolved
techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java
Outdated
Show resolved
Hide resolved
techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java
Show resolved
Hide resolved
fix: zoom header debugging
feat: 부트캠프 목록 조회 응답값 수정
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
techeerzip/src/main/java/backend/techeerzip/domain/zoom/service/ZoomAttendanceService.java (1)
73-77:participantUuid.substring(0, 8)호출 시 길이 검증이 필요합니다.
participantUuid가 8자 미만인 경우StringIndexOutOfBoundsException이 발생할 수 있습니다. 현재 catch 블록에서 잡히긴 하지만, 실제 처리는 정상적으로 완료된 상태에서 로깅만 실패하게 됩니다.🔎 안전한 substring 처리 제안
log.info( "새로운 출석 기록 저장: {} (uuid: {}, 시간: {})", userName, - participantUuid.substring(0, 8) + "...", + participantUuid.length() > 8 + ? participantUuid.substring(0, 8) + "..." + : participantUuid, joinTime);Line 129에도 동일한 패턴이 적용되어야 합니다.
🧹 Nitpick comments (5)
techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java (1)
84-93: 예외 타입을 더 구체적으로 지정하는 것을 권장합니다.현재
Exception.class를 사용하고 있어 너무 광범위합니다.NullPointerException.class나IllegalArgumentException.class처럼 더 구체적인 예외 타입을 사용하면 테스트의 의도가 명확해집니다.🔎 더 구체적인 예외 타입 사용 제안
@Test @DisplayName("실패: null 카테고리 값으로 스택 생성 시 예외가 발생한다.") void createStackWithNullCategory() { // Given StackDto.Create request = new StackDto.Create("Java", null); // When & Then org.junit.jupiter.api.Assertions.assertThrows( - Exception.class, () -> stackService.create(request)); + NullPointerException.class, () -> stackService.create(request)); }techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomTokenProvider.java (1)
22-55: 토큰 암호화 로직은 정상 동작하나 hex 변환 코드 간소화 권장HMAC-SHA256 암호화 및 에러 처리 로직은 올바르게 구현되었습니다. 다만 수동 hex 변환 코드(40-47줄)를 표준 라이브러리로 대체하면 가독성과 유지보수성이 향상됩니다.
🔎 Hex 변환 코드 간소화 제안
옵션 1: Java 17+ HexFormat 사용 (권장)
- // 16진수 문자열로 변환 - StringBuilder hexString = new StringBuilder(); - for (byte b : encryptedBytes) { - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) { - hexString.append('0'); - } - hexString.append(hex); - } - - return hexString.toString(); + // 16진수 문자열로 변환 + return HexFormat.of().formatHex(encryptedBytes);옵션 2: Apache Commons Codec 사용 (이미 의존성이 있다면)
- // 16진수 문자열로 변환 - StringBuilder hexString = new StringBuilder(); - for (byte b : encryptedBytes) { - String hex = Integer.toHexString(0xff & b); - if (hex.length() == 1) { - hexString.append('0'); - } - hexString.append(hex); - } - - return hexString.toString(); + // 16진수 문자열로 변환 + return Hex.encodeHexString(encryptedBytes);techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookPlainTokenException.java (1)
6-11: Javadoc 주석을 더 구체적으로 수정하세요.현재 주석 "Zoom Webhook 처리 중 발생하는 예외"가
ZoomWebhookProcessingException과 동일합니다. 이 예외의 목적(plain token 누락)을 명확하게 구분해 주세요.🔎 제안된 수정
-/** Zoom Webhook 처리 중 발생하는 예외 */ +/** Zoom Webhook URL 검증 시 plainToken이 누락된 경우 발생하는 예외 */ public class ZoomWebhookPlainTokenException extends InfraException {techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomWebhookAspect.java (1)
47-50: 중복된 헤더 대소문자 처리
HttpServletRequest.getHeader()는 Servlet 스펙에 따라 이미 대소문자를 구분하지 않습니다. 따라서"authorization"과"Authorization"을 별도로 확인하는 것은 불필요합니다.🔎 제안된 수정
- String authHeader = - request.getHeader("authorization") != null - ? request.getHeader("authorization") - : request.getHeader("Authorization"); + String authHeader = request.getHeader("Authorization");techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookService.java (1)
30-30:plainToken로깅 관련 보안 검토가 필요합니다.Zoom URL 검증용 토큰이 로그에 남게 됩니다. 검증용 토큰이므로 민감도가 낮지만, 프로덕션 환경에서는 로그 레벨을
debug로 변경하거나 토큰 일부만 마스킹하는 것을 고려해 주세요.🔎 토큰 마스킹 제안
- log.info("Zoom Webhook URL 검증 요청 - plainToken: {}", plainToken); + log.debug("Zoom Webhook URL 검증 요청 - plainToken: {}...", + plainToken.substring(0, Math.min(8, plainToken.length())));
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (30)
techeerzip/src/main/java/backend/techeerzip/domain/bootcamp/dto/response/BootcampListResponse.javatecheerzip/src/main/java/backend/techeerzip/domain/bootcamp/mapper/BootcampMapper.javatecheerzip/src/main/java/backend/techeerzip/domain/zoom/repository/ZoomAttendanceRepository.javatecheerzip/src/main/java/backend/techeerzip/domain/zoom/service/ZoomAttendanceService.javatecheerzip/src/main/java/backend/techeerzip/global/config/SecurityConfig.javatecheerzip/src/main/java/backend/techeerzip/global/config/WebConfig.javatecheerzip/src/main/java/backend/techeerzip/global/exception/ErrorCode.javatecheerzip/src/main/java/backend/techeerzip/global/exception/GlobalExceptionHandler.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/client/ZoomApiClient.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomApiConfig.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomAuth.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomTokenProvider.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomWebhookAspect.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomOAuthTokenResponse.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookEvent.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookValidationResponse.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomException.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookInvalidAuthenticationException.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookPlainTokenException.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookProcessingException.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomLeaveReason.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomWebhookEventType.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookController.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookHandler.javatecheerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookService.javatecheerzip/src/main/resources/db/migration/V17__add_is_main_to_team_stack.sqltecheerzip/src/test/java/backend/techeerzip/domain/projectMember/service/ProjectMemberServiceTest.javatecheerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.javatecheerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.javatecheerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java
💤 Files with no reviewable changes (4)
- techeerzip/src/main/java/backend/techeerzip/infra/zoom/client/ZoomApiClient.java
- techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookHandler.java
- techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomOAuthTokenResponse.java
- techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomApiConfig.java
✅ Files skipped from review due to trivial changes (2)
- techeerzip/src/test/java/backend/techeerzip/domain/projectMember/service/ProjectMemberServiceTest.java
- techeerzip/src/main/resources/db/migration/V17__add_is_main_to_team_stack.sql
🚧 Files skipped from review as they are similar to previous changes (1)
- techeerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-24T09:05:10.807Z
Learnt from: dongwooooooo
Repo: Techeer-Hogwarts/backend PR: 7
File: techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImpl.java:76-78
Timestamp: 2025-05-24T09:05:10.807Z
Learning: 이 프로젝트(Techeer-Hogwarts/backend)는 Java 21을 사용하므로 `List#getFirst()` 같은 Java 21의 SequencedCollection 기능들을 사용할 수 있다.
Applied to files:
techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java
🧬 Code graph analysis (6)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomTokenProvider.java (3)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomException.java (1)
ZoomException(7-16)techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookController.java (1)
Slf4j(15-36)techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookService.java (1)
Slf4j(14-79)
techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java (2)
techeerzip/src/main/java/backend/techeerzip/domain/stack/dto/StackDto.java (1)
StackDto(9-27)techeerzip/src/main/java/backend/techeerzip/infra/index/IndexEvent.java (1)
IndexEvent(8-23)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomLeaveReason.java (1)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookEvent.java (4)
Getter(11-111)Getter(28-41)Getter(43-74)Getter(76-110)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookService.java (4)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookPlainTokenException.java (1)
ZoomWebhookPlainTokenException(7-12)techeerzip/src/main/java/backend/techeerzip/domain/zoom/service/ZoomAttendanceService.java (1)
Slf4j(18-151)techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookController.java (1)
Slf4j(15-36)techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomTokenProvider.java (1)
Slf4j(15-56)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomWebhookAspect.java (2)
techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookInvalidAuthenticationException.java (1)
ZoomWebhookInvalidAuthenticationException(10-21)techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookController.java (1)
Slf4j(15-36)
techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java (1)
techeerzip/src/test/java/backend/techeerzip/config/RepositoryTestSupport.java (1)
ActiveProfiles(7-17)
⏰ 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 (31)
techeerzip/src/main/java/backend/techeerzip/global/config/WebConfig.java (1)
18-18: 좋은 변경사항입니다!생성자 주입되는 필드에
final키워드를 추가하여 불변성을 명시적으로 표현했습니다. 이는 실수로 재할당되는 것을 방지하고 스레드 안전성을 보장하는 Spring 모범 사례입니다.techeerzip/src/main/java/backend/techeerzip/global/config/SecurityConfig.java (1)
10-10: LGTM! 코드 스타일 개선람다 표현식에서 메서드 참조로 CSRF 비활성화 방식을 리팩토링한 것이 좋습니다.
AbstractHttpConfigurer::disable방식은 더 간결하고 Spring Security의 현대적인 관례를 따릅니다. 두 필터 체인에서 일관되게 적용되었으며, 기능적으로는 동일하게 동작합니다.Also applies to: 40-40, 114-114
techeerzip/src/main/java/backend/techeerzip/domain/bootcamp/mapper/BootcampMapper.java (1)
98-99: 매퍼 로직이 올바르게 구현되었습니다.DTO의 새 필드들과 일치하도록
name과projectExplain을 매핑하고 있습니다. Bootcamp 엔티티에서 해당 값들을 올바르게 가져오고 있습니다.techeerzip/src/main/java/backend/techeerzip/domain/bootcamp/dto/response/BootcampListResponse.java (1)
23-24: 새 필드 추가는 올바르게 구현되었습니다.BootcampListItem에 추가된
name과projectExplain필드는 Bootcamp 엔티티에서 모두@Column(nullable = false)제약조건으로 보호되고 있으며, BootcampMapper의toListItem()메서드에서 정상적으로 매핑되고 있습니다. Null 값 반환에 대한 우려는 불필요합니다.Likely an incorrect or invalid review comment.
techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java (4)
25-38: LGTM! 레포지토리 테스트 설정이 올바릅니다.
@DataJpaTest를 사용하여 실제 PostgreSQL 컨테이너로 테스트하고 있으며,@BeforeEach에서 데이터를 정리하는 구조가 적절합니다. 이전 리뷰에서 지적된 mocking 이슈가 완전히 해결되었습니다.
44-72: LGTM! 소프트 삭제 필터링을 정확하게 테스트합니다.active와 deleted 스택을 모두 저장한 후, 삭제되지 않은 스택만 조회되는지 검증하는 로직이 정확합니다.
clearPersistenceContext()를 적절히 활용하여 캐시 영향을 배제하고 있습니다.
74-129: LGTM! 엣지 케이스를 잘 커버하고 있습니다.모든 스택이 삭제된 경우와 여러 활성 스택이 있는 경우를 모두 테스트하여
findAllByIsDeletedFalse()메서드의 동작을 포괄적으로 검증합니다.
132-135: LGTM! 유용한 헬퍼 메서드입니다.영속성 컨텍스트를 비워 테스트 간 격리를 보장하고 실제 데이터베이스에서 조회하도록 강제하는 좋은 패턴입니다.
techeerzip/src/test/java/backend/techeerzip/domain/stack/service/StackServiceTest.java (4)
23-30: LGTM! 서비스 단위 테스트 설정이 적절합니다.Mockito를 사용하여 의존성을 mocking하고 실제 서비스 로직만 테스트하는 올바른 구조입니다.
32-49: LGTM! 스택 생성 로직을 정확하게 검증합니다.레포지토리 저장과 인덱스 이벤트 발행을 모두 검증하여 서비스의 핵심 동작을 확인하고 있습니다.
51-70: LGTM! 정렬 로직을 올바르게 검증합니다.레포지토리에서 순서 없이 반환된 데이터를 서비스 레이어에서 이름순으로 정렬하는 로직을 정확히 테스트하고 있습니다.
95-108: LGTM! 레포지토리 예외 처리를 적절히 검증합니다.레포지토리에서 예외가 발생했을 때 서비스가 이를 올바르게 전파하는지 확인하는 중요한 테스트입니다.
techeerzip/src/main/java/backend/techeerzip/global/exception/GlobalExceptionHandler.java (1)
30-30: 람다 파라미터 간소화 완료불필요한 괄호를 제거하여 코드가 더 간결해졌습니다.
techeerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomWebhookEventType.java (1)
17-27: 이벤트 타입 비교 로직 확인 완료정적 헬퍼 메서드들이 null-safe 패턴(non-null 객체에서 equals 호출)을 올바르게 사용하고 있습니다. 전달받은
eventName이 null인 경우 안전하게 false를 반환합니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookEvent.java (3)
3-3: 웹훅 DTO 안정성 개선 완료모든 DTO 클래스에
@JsonIgnoreProperties(ignoreUnknown = true)를 추가하여 향후 Zoom 웹훅 페이로드 변경에 대한 하위 호환성을 확보했습니다. 이는 알 수 없는 필드가 추가되어도 역직렬화가 실패하지 않도록 보장합니다.Also applies to: 8-8, 14-14, 30-30, 45-45, 78-78
17-19: 필수 필드에 @nonnull 적용 확인
eventName과payload에@NonNull어노테이션을 적용하여 null 안전성을 강화했습니다. 웹훅 처리 로직에서 NPE 발생 위험을 줄여줍니다.Also applies to: 24-24
39-40: 웹훅 페이로드 확장 필드 추가 확인URL 검증을 위한
plainToken필드와 참가자 정보 확장을 위한publicIp,participantUserId필드가 추가되었습니다. 관련 서비스 로직(ZoomWebhookService, ZoomTokenProvider)에서 이 필드들을 올바르게 사용하고 있습니다.Also applies to: 105-109
techeerzip/src/main/java/backend/techeerzip/domain/zoom/repository/ZoomAttendanceRepository.java (1)
40-44: 코드 설계가 적절하며 날짜 조건 제거는 의도적이고 필요한 변경입니다.Zoom API의
participantUuid동작 방식상 동일한 UUID로 여러 활성 세션을 가질 수 없습니다. 사용자가 완전히 퇴장했다가 재접속할 때 새로운 UUID가 생성되기 때문입니다.날짜 조건 제거는 실제 문제(자정을 기준으로 입장 → 다음 날 퇴장하는 경우)를 해결하기 위한 의도적인 설계이며, 이는 두 곳의 사용 사례(
handleParticipantJoined와handleParticipantLeft)에서 올바르게 적용되고 있습니다. 서비스 코드의 주석도 이러한 의도를 명확히 문서화하고 있으므로 추가 개선은 필요하지 않습니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomAuth.java (1)
1-10: LGTM!Zoom 웹훅 인증을 위한 마커 어노테이션이 올바르게 정의되었습니다.
@Retention(RetentionPolicy.RUNTIME)과@Target(ElementType.METHOD)가 AOP 인터셉션에 적합합니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomException.java (1)
1-16: LGTM!Zoom 관련 인프라 예외를 위한 기본 클래스가 적절하게 구현되었습니다.
ErrorCode와Throwable cause를 모두 지원하는 생성자 구조가 좋습니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookProcessingException.java (1)
1-12: LGTM!웹훅 처리 실패를 위한 예외 클래스가 명확하게 구현되었습니다.
techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookInvalidAuthenticationException.java (1)
1-21: LGTM!Spring Security의
AuthenticationException을 확장하여 인증 실패 처리 흐름에 자연스럽게 통합되도록 잘 설계되었습니다.getErrorCode()메서드를 통해 에러 코드에 접근할 수 있어 에러 핸들링에 유용합니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookValidationResponse.java (1)
1-20: LGTM!Zoom 웹훅 URL 검증 응답을 위한 DTO가 깔끔하게 구현되었습니다. Lombok 어노테이션을 적절히 활용하고 있습니다.
techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomWebhookAspect.java (2)
41-44: URL 검증 요청 시 인증 우회에 대한 확인URL 검증 이벤트(
endpoint.url_validation)에서 인증을 건너뛰는 것은 Zoom 웹훅 플로우에서 의도된 동작입니다. Zoom은 URL 검증 시 인증 헤더 없이 요청을 보내고, 서버는 암호화된 토큰을 반환해야 합니다.다만,
handleUrlValidationRequest에서 반환하는 데이터가 민감하지 않은지 확인해 주세요.
28-39: LGTM - AOP 기반 인증 로직
@Before어드바이스를 사용하여@ZoomAuth어노테이션이 붙은 메서드의 인증을 처리하는 구조가 적절합니다. 메서드 인자에서ZoomWebhookEvent를 스트림으로 추출하는 방식도 깔끔합니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomLeaveReason.java (1)
45-78: LGTM! 완전 퇴장 여부 판별 로직이 잘 구현되어 있습니다.제외 케이스(소회의실, 대기실, 호스트 미참여)를 먼저 확인한 후 정의된 케이스만 매칭하고, 알 수 없는 경우 안전하게
false를 반환하는 방어적 구현입니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookController.java (1)
15-35: LGTM! 컨트롤러가 깔끔하게 구현되어 있습니다.서비스 계층에 적절히 위임하고 있으며,
@ZoomAuth를 통한 인증이 적용되어 있습니다.techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookService.java (1)
38-78: LGTM! 웹훅 이벤트 처리 로직이 적절합니다.참가자 입장/퇴장 이벤트를 구분하여
ZoomAttendanceService에 위임하고 있습니다.participantUuidnull 체크도 적절히 처리되어 있습니다.techeerzip/src/main/java/backend/techeerzip/global/exception/ErrorCode.java (1)
230-242: LGTM! Zoom 관련 에러 코드가 적절하게 추가되었습니다.HTTP 상태 코드(500, 401, 400)가 각 에러 상황에 맞게 설정되어 있으며, 기존 에러 코드 패턴과 일관성을 유지하고 있습니다.
techeerzip/src/main/java/backend/techeerzip/domain/zoom/service/ZoomAttendanceService.java (2)
27-82: LGTM! 참가자 입장 이벤트 처리 로직이 잘 구현되어 있습니다.활성 세션 조회를 통해 소회의실 복귀 케이스를 적절히 필터링하고 있으며, 새로운 세션에 대해서만 출석 기록을 생성합니다. 메서드 가시성을
public으로 변경하여ZoomWebhookService에서 호출할 수 있도록 한 점도 적절합니다.
85-136: LGTM! 참가자 퇴장 이벤트 처리가ZoomLeaveReason과 잘 통합되어 있습니다.
ZoomLeaveReason.isCompleteExit()를 활용하여 완전 퇴장 케이스만 처리하고, 활성 세션의 퇴장 시간을 업데이트하는 로직이 명확합니다.
|



요약
작업 내용
참고 사항
관련 이슈
BACKEND-191
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정
보안 개선
테스트
기타
✏️ Tip: You can customize this high-level summary in your review settings.