Skip to content

Conversation

@yuripbong
Copy link
Contributor

@yuripbong yuripbong commented Dec 5, 2025

요약



작업 내용



참고 사항



관련 이슈

BACKEND-191



Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • Zoom 웹훅 기능 개선 및 검증 기능 추가
    • Zoom 참석자 입장/퇴장 추적 기능 강화
  • 버그 수정

    • Zoom 참석자 추적 시 날짜 경계 문제 해결
    • 프로젝트팀 모집 현황 관리 개선
  • 보안 개선

    • 프로젝트팀 및 스택 관리 엔드포인트에 역할 기반 접근 제어 추가
    • 웹훅 인증 보안 강화
  • 테스트

    • 프로젝트팀, 스택, Zoom 관련 단위 테스트 추가
  • 기타

    • 데이터베이스 스키마 업데이트
    • 에러 코드 및 설정 개선

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

종합 요약

Walkthrough

이 PR은 ProjectTeamController 및 StackController의 엔드포인트에 메서드 수준의 보안 어노테이션을 추가하고, 권한 평가자를 확장하며, 광범위한 테스트 스위트를 도입하고, Zoom 웹훅 처리를 재구성하고, 스키마를 마이그레이션합니다.

Changes

응집군 / 파일 변경 요약
보안 어노테이션
techeerzip/src/main/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamController.java, techeerzip/src/main/java/backend/techeerzip/domain/stack/controller/StackController.java
6개 엔드포인트에 @PreAuthorize 어노테이션 추가 (역할 및 권한 기반 접근 제어): createProjectTeam, updateProjectTeam, deleteProjectTeam, getApplicants, acceptApplicant, rejectApplicant; StackController.createStack에 ADMIN 역할 제약 추가
권한 평가자 확장
techeerzip/src/main/java/backend/techeerzip/global/permission/ProjectTeamPermissionEvaluator.java
isSupportedPermission에서 인식하는 권한 목록을 "CLOSE"에서 "CLOSE", "UPDATE", "DELETE", "READ"로 확장
ProjectTeam 엔티티 및 서비스 테스트
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectTeamTest.java, ProjectMainImageTest.java, ProjectResultImageTest.java, TeamStackTest.java, TeamUnionViewTest.java, techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamServiceTest.java, ProjectTeamFacadeServiceImplTest.java, TeamStackServiceTest.java
9개의 새로운 테스트 클래스 추가: 엔티티 생명주기, 상태 전환, 집계 로직, 서비스 계층 동작 검증 (성공/실패 시나리오 포함)
ProjectTeam 컨트롤러 및 저장소 테스트
techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/controller/ProjectTeamControllerTest.java, ProjectTeamDslRepositoryImplTest.java, TeamUnionViewDslRepositoryImplTest.java
ProjectTeamControllerTest 추가 (13개 시나리오 포함); 2개 저장소 관련 테스트 추가 (쿼리 빌더 검증); 3개 기존 저장소 테스트 삭제
Stack 도메인 테스트
techeerzip/src/test/java/backend/techeerzip/domain/stack/entity/StackTest.java, StackControllerTest.java, StackRepositoryTest.java, StackServiceTest.java
4개의 새로운 테스트 클래스: 엔티티 CRUD, 컨트롤러 엔드포인트 (생성/조회/오류), 저장소 필터링, 서비스 로직
Zoom 웹훅 구조 재구성
techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookController.java, ZoomWebhookService.java, ZoomWebhookHandler.java(삭제), ZoomWebhookAspect.java, ZoomTokenProvider.java
기존 ZoomWebhookHandler를 ZoomWebhookController, ZoomWebhookService, AOP 기반 @ZoomAuth 검증으로 재구성; 인증 토큰 생성 로직 분리
Zoom 설정 및 클라이언트 변경
techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomApiConfig.java, ZoomApiClient.java(삭제), ZoomAuth.java, ZoomAuth.java
ZoomApiClient 삭제 (OAuth 토큰 캐싱 제거); ZoomApiConfig에서 5개 필드 및 RestTemplate 빈 제거; 새로운 @ZoomAuth 어노테이션 추가
Zoom 서비스 및 저장소
techeerzip/src/main/java/backend/techeerzip/domain/zoom/service/ZoomAttendanceService.java, ZoomAttendanceRepository.java
웹훅 데이터 타입 ZoomWebhookEvent → WebhookParticipant로 변경; 활성 세션 기반 로직으로 리팩토링; findActiveSessionByParticipantUuid 메서드 추가
Zoom DTO 및 타입
techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookEvent.java, ZoomOAuthTokenResponse.java(삭제), ZoomWebhookValidationResponse.java, techeerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomLeaveReason.java, ZoomWebhookEventType.java
ZoomWebhookEvent에 plainToken, @JsonIgnoreProperties, 필드명 변경(event→eventName) 추가; ZoomOAuthTokenResponse 삭제; 새로운 Validation DTO, LeaveReason enum, WebhookEventType enum 추가
Zoom 예외 처리
techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomException.java, ZoomWebhookInvalidAuthenticationException.java, ZoomWebhookPlainTokenException.java, ZoomWebhookProcessingException.java
4개의 새로운 예외 클래스 추가: 기본 ZoomException, 인증/토큰/처리 실패 관련 전문화된 예외
전역 예외 및 설정
techeerzip/src/main/java/backend/techeerzip/global/exception/ErrorCode.java, GlobalExceptionHandler.java, SecurityConfig.java, WebConfig.java
6개의 새로운 Zoom 에러코드 추가 (ZOOM001~ZOOM006); GlobalExceptionHandler에서 lambda 포매팅 정리; SecurityConfig에서 csrf disable 스타일 변경; WebConfig에서 final 키워드 추가
부트캠프 DTO 및 매퍼
techeerzip/src/main/java/backend/techeerzip/domain/bootcamp/dto/response/BootcampListResponse.java, BootcampMapper.java
BootcampListItem에 name, projectExplain 필드 추가; 매퍼에서 해당 필드 매핑 로직 추가
스케줄러 및 마이그레이션
techeerzip/src/main/java/backend/techeerzip/domain/todayCs/scheduler/TodayCsScheduler.java, techeerzip/src/main/resources/db/migration/V17__add_is_main_to_team_stack.sql
Slack 알림 호출 포매팅 정리; TeamStack 테이블에 isMain 열 추가 (기본값: false)
테스트 포매팅
techeerzip/src/test/java/backend/techeerzip/domain/projectMember/service/ProjectMemberServiceTest.java
given/verify 호출에서 줄 바꿈 포매팅만 변경 (기능 변화 없음)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25분

Possibly related PRs

Suggested reviewers

  • printSANO
  • dlwhsk0
  • dhdid
  • HwangMinSeon
  • kimzini
  • dongwooooooo
  • jungeunyooon

Poem

🐰 보안 주인공 어노테이션이 파수꾼처럼 서 있고,
웹훅은 새롭게 단장하여 춤을 추네!
테스트는 무럭무럭 자라나고,
권한 평가는 지혜롭게 말하고,
부트캠프 정보는 더욱 풍성해졌으니 — 모두 함께 축하 인사! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.61% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용을 명확하게 반영하고 있습니다. Stack과 ProjectTeam의 테스트 코드 추가 및 @PreAuthorize 적용이라는 두 가지 핵심 변경 사항을 간결하게 요약했습니다.
✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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: 멘토 피드백 조회 기본 기간/관리자 권한 처리 로직 재검토 제안

  • startDateTimenull인 경우 LocalDateTime.now().minusMonths(1)로 기본 시작 시점을 한 달 전으로 두고, endDateTimenull이면 상한 없이 조회하는 구조라 기본 동작이 “최근 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() 포맷이 변경될 경우 깨질 수 있습니다.

더 견고한 테스트를 위해 다음 방법들을 고려해보세요:

  1. 실제 QueryDSL 표현식 구조를 직접 검증 (예: expression의 타입, 연산자 확인)
  2. 통합 테스트로 실제 쿼리 결과를 검증
  3. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1f66bc1 and 5b09311.

📒 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.java
  • techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamFacadeServiceImplTest.java
  • techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectResultImageTest.java
  • techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/entity/ProjectTeamTest.java
  • techeerzip/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: applyToProjectcancelApplication의 인가 처리 확인

이 두 엔드포인트는 @PreAuthorize 없이 @CurrentUser만 사용하고 있습니다. 서비스 레이어에서 본인 확인 로직이 있다면 문제없으나, 명시적인 권한 체크가 필요하다면 추가를 고려해보세요.

techeerzip/src/test/java/backend/techeerzip/domain/projectTeam/service/ProjectTeamServiceTest.java (2)

43-83: 테스트 구조가 잘 작성되었습니다.

Given/When/Then 패턴을 명확하게 따르고 있고, 핵심 시나리오를 잘 검증하고 있습니다. Mock 설정과 검증 로직이 적절합니다.


85-105: 중복 이름 예외 테스트 LGTM

중복 팀 이름에 대한 예외 처리를 올바르게 검증하고 있습니다.

Comment on lines +41 to +42
@WebMvcTest(ProjectTeamController.class)
@AutoConfigureMockMvc(addFilters = false)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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 java

Repository: 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.java

Repository: 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.java

Repository: 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.java

Repository: Techeer-Hogwarts/backend

Length of output: 4344


🏁 Script executed:

# Search for @EnableMethodSecurity or @EnableGlobalMethodSecurity in the codebase
rg -n "@EnableMethodSecurity|@EnableGlobalMethodSecurity" --type java

Repository: 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.java

Repository: 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.java

Repository: 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.java

Repository: 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.java

Repository: 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/test

Repository: 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.java

Repository: 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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.classIllegalArgumentException.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

📥 Commits

Reviewing files that changed from the base of the PR and between 5b09311 and bfd8dfe.

📒 Files selected for processing (30)
  • techeerzip/src/main/java/backend/techeerzip/domain/bootcamp/dto/response/BootcampListResponse.java
  • techeerzip/src/main/java/backend/techeerzip/domain/bootcamp/mapper/BootcampMapper.java
  • techeerzip/src/main/java/backend/techeerzip/domain/zoom/repository/ZoomAttendanceRepository.java
  • techeerzip/src/main/java/backend/techeerzip/domain/zoom/service/ZoomAttendanceService.java
  • techeerzip/src/main/java/backend/techeerzip/global/config/SecurityConfig.java
  • techeerzip/src/main/java/backend/techeerzip/global/config/WebConfig.java
  • techeerzip/src/main/java/backend/techeerzip/global/exception/ErrorCode.java
  • techeerzip/src/main/java/backend/techeerzip/global/exception/GlobalExceptionHandler.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/client/ZoomApiClient.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomApiConfig.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomAuth.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomTokenProvider.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/config/ZoomWebhookAspect.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomOAuthTokenResponse.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookEvent.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/dto/ZoomWebhookValidationResponse.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomException.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookInvalidAuthenticationException.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookPlainTokenException.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/exception/ZoomWebhookProcessingException.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomLeaveReason.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/type/ZoomWebhookEventType.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookController.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookHandler.java
  • techeerzip/src/main/java/backend/techeerzip/infra/zoom/webhook/ZoomWebhookService.java
  • techeerzip/src/main/resources/db/migration/V17__add_is_main_to_team_stack.sql
  • techeerzip/src/test/java/backend/techeerzip/domain/projectMember/service/ProjectMemberServiceTest.java
  • techeerzip/src/test/java/backend/techeerzip/domain/stack/controller/StackControllerTest.java
  • techeerzip/src/test/java/backend/techeerzip/domain/stack/repository/StackRepositoryTest.java
  • techeerzip/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의 새 필드들과 일치하도록 nameprojectExplain을 매핑하고 있습니다. Bootcamp 엔티티에서 해당 값들을 올바르게 가져오고 있습니다.

techeerzip/src/main/java/backend/techeerzip/domain/bootcamp/dto/response/BootcampListResponse.java (1)

23-24: 새 필드 추가는 올바르게 구현되었습니다.

BootcampListItem에 추가된 nameprojectExplain 필드는 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 적용 확인

eventNamepayload@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가 생성되기 때문입니다.

날짜 조건 제거는 실제 문제(자정을 기준으로 입장 → 다음 날 퇴장하는 경우)를 해결하기 위한 의도적인 설계이며, 이는 두 곳의 사용 사례(handleParticipantJoinedhandleParticipantLeft)에서 올바르게 적용되고 있습니다. 서비스 코드의 주석도 이러한 의도를 명확히 문서화하고 있으므로 추가 개선은 필요하지 않습니다.

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 관련 인프라 예외를 위한 기본 클래스가 적절하게 구현되었습니다. ErrorCodeThrowable 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에 위임하고 있습니다. participantUuid null 체크도 적절히 처리되어 있습니다.

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()를 활용하여 완전 퇴장 케이스만 처리하고, 활성 세션의 퇴장 시간을 업데이트하는 로직이 명확합니다.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 6, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants