-
Notifications
You must be signed in to change notification settings - Fork 1
[SRLT-111] AI 리포트를 생성한 사업계획서 목록을 조회한다 #71
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
The head ref may contain hidden characters: "SRLT-111-\uC804\uBB38\uAC00-\uC0C1\uC138\uD398\uC774\uC9C0-api"
Conversation
|
Note
|
| 응집도 / 파일(들) | 변경 요약 |
|---|---|
AI 리포트 JPA 및 레포지토리 src/main/java/starlight/adapter/aireport/persistence/AiReportJpa.java, src/main/java/starlight/adapter/aireport/persistence/AiReportRepository.java |
AiReportSummaryLookupPort 구현 추가, AiReportResponseParser 주입, findTotalScoresByBusinessPlanIds(List<Long>) 추가 및 findAllByBusinessPlanIdIn(Collection<Long>) 레포지토리 메서드 추가 |
비즈니스플랜 JPA 및 레포지토리 src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanJpa.java, .../BusinessPlanRepository.java |
BusinessPlanLookupPort 구현 추가, findAllByMemberId(Long)와 findAllByMemberIdOrderByLastSavedAt 레포지토리 메서드 추가 |
전문가 AI 리포트 API / DTO / 스웨거 src/main/java/starlight/adapter/expert/webapi/ExpertController.java, .../dto/ExpertAiReportBusinessPlanResponse.java, .../swagger/ExpertApiDoc.java |
새로운 엔드포인트 aiReportBusinessPlans(GET /{expertId}/business-plans/ai-reports) 추가, 응답 DTO 및 스웨거 문서 추가, 컨트롤러가 ExpertAiReportQueryUseCase 사용 |
전문가용 유스케이스/서비스 src/main/java/starlight/application/expert/ExpertAiReportQueryService.java, .../provided/ExpertAiReportQueryUseCase.java, .../provided/dto/ExpertAiReportBusinessPlanResult.java |
ExpertAiReportQueryUseCase 인터페이스와 구현 서비스 추가: 회원의 사업계획서 조회 → AI 총점 집계 → 전문가별 신청건수 집계 → 결과 조립 로직 구현 |
필수 포트 추가 src/main/java/starlight/application/expert/required/AiReportSummaryLookupPort.java, .../BusinessPlanLookupPort.java, .../ExpertApplicationCountLookupPort.java |
AI 리포트 요약 조회, 비즈니스플랜 조회, 전문가 신청 카운트 조회용 포트 인터페이스 신규 정의 |
전문가 신청 집계 변경(레포/포트/JPA 포트) src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java, .../ExpertApplicationRepository.java |
기존 findRequestedExpertIds 제거, 프로젝션 기반 countByExpertIdAndBusinessPlanIds 추가 및 포트/리포지토리 API 변경 |
전문가 신청 조회 유스케이스 삭제 src/main/java/starlight/application/expertApplication/ExpertApplicationQueryService.java, .../provided/ExpertApplicationQueryUseCase.java, .../required/ExpertApplicationQueryPort.java |
관련 서비스 및 인터페이스(및 메서드) 삭제로 기존 요청자 조회 유스케이스 제거 |
컨트롤러 파라미터명 일관화(인증) src/main/java/starlight/adapter/*/webapi/*Controller.java, .../swagger/*ApiDoc.java |
여러 컨트롤러·API 문서에서 @AuthenticationPrincipal 파라미터명 authDetails → authenticatedMember로 통일(호출 시 동작 영향 없음) |
테스트 보강 src/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.java |
보안 테스트 설정 추가(SecurityTestConfig), TestAuthenticatedMember 레코드 및 aiReportBusinessPlans 테스트 추가, 보안 컨텍스트 정리 훅 추가 |
Sequence Diagram(s)
sequenceDiagram
participant Client as HTTP Client
participant Controller as ExpertController
participant UseCase as ExpertAiReportQueryService
participant BPLookup as BusinessPlanLookupPort
participant AILookup as AiReportSummaryLookupPort
participant AppLookup as ExpertApplicationCountLookupPort
Client->>Controller: GET /{expertId}/business-plans/ai-reports (with AuthenticatedMember)
rect rgb(220,240,250)
Note over Controller,UseCase: 컨트롤러 → 유스케이스 호출
Controller->>UseCase: findAiReportBusinessPlans(expertId, memberId)
end
rect rgb(220,250,220)
Note over UseCase,BPLookup: 1) 회원의 사업계획서 조회
UseCase->>BPLookup: findAllByMemberId(memberId)
BPLookup-->>UseCase: List<BusinessPlan>
end
rect rgb(250,240,220)
Note over UseCase,AILookup: 2) AI 리포트 총점 조회 (여러 ID)
UseCase->>AILookup: findTotalScoresByBusinessPlanIds(planIds)
AILookup-->>UseCase: Map<planId, totalScore>
end
rect rgb(240,220,250)
Note over UseCase,AppLookup: 3) 전문가별 신청건수 집계
UseCase->>AppLookup: countByExpertIdAndBusinessPlanIds(expertId, planIds)
AppLookup-->>UseCase: Map<planId, count>
end
rect rgb(235,235,235)
Note over UseCase: 4) 결과 조립 (isOver70 판정) → 반환
UseCase-->>Controller: List<ExpertAiReportBusinessPlanResult>
end
Controller-->>Client: ApiResponse<List<ExpertAiReportBusinessPlanResponse>>
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
- [SRLT-11] 사업 계획서 AI 리포트 생성 및 조회 개발 #31 — AiReportJpa 및 AiReportResponseParser 관련 변경과 중복/연관된 코드영향(다중 ID 점수 집계)
- [SRLT-81] 사업계획서 목록 조회 페이지네이션 구현 및 기타 수정 #50 — 비즈니스플랜 회원별 조회(레포/서비스) 변경 사항과 영역 중복 가능성
- [SRLT-33] 전문가를 고르고 피드백을 요청한다 #26 — ExpertApplicationRepository/포트의 신청 집계 관련 쿼리·프로젝션 변경과 높은 연관성
Suggested labels
✨ FEAT, 🧵 REFACTOR, ✅ TEST
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 1.69% 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 제목이 변경 사항의 핵심을 명확히 반영하고 있습니다. '전문가 상세페이지에서 AI 리포트를 생성한 사업계획서 목록 조회' 기능 구현을 정확하게 설명하고 있습니다. |
✨ 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.
Comment @coderabbitai help to get the list of available commands and usage tips.
Test Results249 tests 249 ✅ 10s ⏱️ Results for commit 4f5254b. ♻️ This comment has been updated with latest results. |
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: 4
🧹 Nitpick comments (5)
src/main/java/starlight/adapter/expertApplication/webapi/ExpertApplicationController.java (1)
27-29: 파라미터 네이밍 개선이 좋습니다만, 예외 처리를 구체화해주세요.
auth에서authenticatedMember로의 리네이밍은 코드 가독성과 일관성을 높여줍니다. 다만 메서드 시그니처의throws Exception은 너무 광범위합니다.🔎 예외 처리 구체화 제안
@PostMapping(value = "/{expertId}/request", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ApiResponse<String> requestFeedback( @PathVariable Long expertId, @RequestParam Long businessPlanId, @RequestParam("file") MultipartFile file, @AuthenticationPrincipal AuthenticatedMember authenticatedMember -) throws Exception { +) { applicationServiceUseCase.requestFeedback(expertId, businessPlanId, file, authenticatedMember.getMemberName()); return ApiResponse.success("피드백 요청이 전달되었습니다."); }비즈니스 로직에서 발생하는 구체적인 예외들은
@ControllerAdvice를 통해 전역으로 처리하는 것이 Spring Boot 모범 사례입니다.src/main/java/starlight/application/expert/provided/dto/ExpertAiReportBusinessPlanResult.java (1)
3-9: Record DTO 구조는 적절합니다.불변 DTO로 record를 사용한 것은 좋은 선택입니다.
isOver70필드명은 매직 넘버(70)를 포함하고 있어, 나중에 기준이 변경될 경우 혼란을 줄 수 있습니다.isHighScore또는isEligibleForExpert같은 의미 기반 네이밍을 고려해 보세요.src/main/java/starlight/application/expert/ExpertAiReportQueryService.java (1)
46-59: 비즈니스 로직이 명확하게 구현되었습니다.몇 가지 개선 포인트가 있습니다:
Line 49:
filter(plan -> totalScoreMap.containsKey(plan.getId()))로 이미 필터링했으므로getOrDefault(plan.getId(), 0)는 항상 값이 존재합니다.get()으로 충분합니다.Line 59:
Collectors.toList()대신.toList()를 사용하면 다른 부분(line 36, 43)과 일관성을 유지할 수 있습니다.🔎 제안된 수정
return plans.stream() .filter(plan -> totalScoreMap.containsKey(plan.getId())) .map(plan -> { - Integer totalScore = totalScoreMap.getOrDefault(plan.getId(), 0); + Integer totalScore = totalScoreMap.get(plan.getId()); boolean isOver70 = totalScore >= 70; Long requestCount = requestCountMap.getOrDefault(plan.getId(), 0L); return new ExpertAiReportBusinessPlanResult( plan.getId(), plan.getTitle(), requestCount, isOver70 ); }) - .collect(Collectors.toList()); + .toList();src/main/java/starlight/adapter/aireport/persistence/AiReportJpa.java (1)
33-48: 파싱 실패 시 예외 처리 검토 필요
responseParser.toResponse(report)가 파싱에 실패할 경우 예외가 발생할 수 있습니다. 현재 구조에서는 단일 리포트 파싱 실패 시 전체 조회가 실패하게 됩니다.비즈니스 요구사항에 따라 다음 중 하나를 고려해 주세요:
- Fail-fast (현재 방식): 하나라도 실패하면 전체 실패 - 데이터 정합성 우선
- Resilient: 파싱 실패한 리포트는 건너뛰고 나머지 반환 - 가용성 우선
🔎 Resilient 방식 예시
for (AiReport report : reports) { - Integer totalScore = responseParser.toResponse(report).totalScore(); - totalScoreMap.put(report.getBusinessPlanId(), totalScore != null ? totalScore : 0); + try { + Integer totalScore = responseParser.toResponse(report).totalScore(); + totalScoreMap.put(report.getBusinessPlanId(), totalScore != null ? totalScore : 0); + } catch (Exception e) { + log.warn("AI 리포트 파싱 실패 - businessPlanId: {}", report.getBusinessPlanId(), e); + totalScoreMap.put(report.getBusinessPlanId(), 0); + } }src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanRepository.java (1)
27-33: 메서드 이름 불일치 확인 필요기존 메서드는
findAllByMemberIdOrderedByLastSavedAt(Ordered)이고, 새 메서드는findAllByMemberIdOrderByLastSavedAt(Order)입니다. 일관성을 위해 동일한 네이밍 규칙을 적용하는 것이 좋습니다.🔎 네이밍 일관성을 위한 수정 제안
- List<BusinessPlan> findAllByMemberIdOrderByLastSavedAt(@Param("memberId") Long memberId); + List<BusinessPlan> findAllByMemberIdOrderedByLastSavedAt(@Param("memberId") Long memberId);
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
src/main/java/starlight/adapter/aireport/persistence/AiReportJpa.javasrc/main/java/starlight/adapter/aireport/persistence/AiReportRepository.javasrc/main/java/starlight/adapter/businessplan/persistence/BusinessPlanJpa.javasrc/main/java/starlight/adapter/businessplan/persistence/BusinessPlanRepository.javasrc/main/java/starlight/adapter/expert/webapi/ExpertController.javasrc/main/java/starlight/adapter/expert/webapi/dto/ExpertAiReportBusinessPlanResponse.javasrc/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.javasrc/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.javasrc/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationRepository.javasrc/main/java/starlight/adapter/expertApplication/webapi/ExpertApplicationController.javasrc/main/java/starlight/adapter/expertApplication/webapi/swagger/ExpertApplicationApiDoc.javasrc/main/java/starlight/adapter/member/webapi/MemberController.javasrc/main/java/starlight/adapter/member/webapi/swagger/MemberApiDoc.javasrc/main/java/starlight/adapter/order/webapi/OrderController.javasrc/main/java/starlight/adapter/order/webapi/swagger/OrderApiDoc.javasrc/main/java/starlight/application/expert/ExpertAiReportQueryService.javasrc/main/java/starlight/application/expert/provided/ExpertAiReportQueryUseCase.javasrc/main/java/starlight/application/expert/provided/dto/ExpertAiReportBusinessPlanResult.javasrc/main/java/starlight/application/expert/required/AiReportSummaryLookupPort.javasrc/main/java/starlight/application/expert/required/BusinessPlanLookupPort.javasrc/main/java/starlight/application/expert/required/ExpertApplicationCountLookupPort.javasrc/main/java/starlight/application/expertApplication/ExpertApplicationQueryService.javasrc/main/java/starlight/application/expertApplication/provided/ExpertApplicationQueryUseCase.javasrc/main/java/starlight/application/expertApplication/required/ExpertApplicationQueryPort.javasrc/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.java
💤 Files with no reviewable changes (3)
- src/main/java/starlight/application/expertApplication/required/ExpertApplicationQueryPort.java
- src/main/java/starlight/application/expertApplication/provided/ExpertApplicationQueryUseCase.java
- src/main/java/starlight/application/expertApplication/ExpertApplicationQueryService.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-26T15:38:55.886Z
Learnt from: SeongHo5356
Repo: StartUpLight/STARLIGHT_BE PR: 67
File: src/main/java/starlight/adapter/expert/persistence/ExpertJpa.java:36-51
Timestamp: 2025-12-26T15:38:55.886Z
Learning: StarLight의 Expert 조회에서 `findByIdWithCareersAndTags()` 메서드는 단건 상세 조회용으로, ExpertDetailResponse가 categories 필드를 포함하지 않기 때문에 의도적으로 categories 페치를 제외합니다. ExpertListResponse만 categories를 포함합니다.
Applied to files:
src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java
🧬 Code graph analysis (1)
src/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.java (2)
src/test/java/starlight/adapter/member/auth/webapi/AuthControllerSliceTest.java (1)
TestConfiguration(71-96)src/test/java/starlight/adapter/member/auth/security/oauth2/OAuth2LoginFlowTest.java (1)
TestConfiguration(61-80)
⏰ 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). (2)
- GitHub Check: test
- GitHub Check: build-and-push
🔇 Additional comments (25)
src/main/java/starlight/adapter/expertApplication/webapi/ExpertApplicationController.java (1)
20-20: ExpertApplicationQueryUseCase 제거가 완전히 처리되었습니다. ✓코드베이스 전체를 확인한 결과,
ExpertApplicationQueryUseCase에 대한 참조가 완전히 제거되었습니다. 해당 클래스 정의, import 구문, 필드 선언 어디에도 남아있는 것이 없습니다.ExpertApplicationController는 현재ExpertApplicationCommandUseCase만을 사용하여 피드백 요청 기능을 처리하고 있으며, 쿼리 관련 엔드포인트도 모두 정리되어 있습니다. 클린업이 잘 진행되었습니다.src/main/java/starlight/adapter/member/webapi/swagger/MemberApiDoc.java (1)
68-68: 파라미터 이름 개선이 잘 되었습니다.
authDetails보다authenticatedMember가 의미적으로 더 명확하고, 도메인 언어와도 잘 일치합니다. 일관된 네이밍 개선 작업 좋습니다!src/main/java/starlight/adapter/member/webapi/MemberController.java (1)
25-29: 일관성 있는 리팩토링이 잘 적용되었습니다.인터페이스 변경사항에 맞춰 구현체도 정확하게 업데이트되었고,
authenticatedMember.getMemberId()호출도 올바르게 수정되었습니다.src/main/java/starlight/adapter/order/webapi/swagger/OrderApiDoc.java (1)
120-120: 결제 관련 API 전체에 일관되게 적용되었습니다.결제 준비, 승인, 내역 조회 세 개의 엔드포인트 모두에서 파라미터명이 통일되어 코드베이스 전체의 일관성이 향상되었습니다. 체계적인 리팩토링 작업입니다.
Also applies to: 218-218, 331-331
src/main/java/starlight/adapter/order/webapi/OrderController.java (3)
33-37: 결제 준비 로직이 올바르게 업데이트되었습니다.파라미터명 변경과 함께
getMemberId()호출도 정확하게 수정되어 있습니다.
49-54: 결제 승인 로직도 일관되게 수정되었습니다.인터페이스 변경사항이 구현체에 정확히 반영되었습니다.
78-80: 결제 내역 조회 로직이 정확하게 업데이트되었습니다.파라미터명 변경과
getMemberId()호출이 올바르게 적용되었습니다.src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationRepository.java (2)
27-30: 새로운 프로젝션 인터페이스 구조가 적절합니다.기존
ExpertIdCountProjection과 동일한 패턴을 따르고 있어 코드 일관성이 유지됩니다. 명명도 명확하고 의도를 잘 전달합니다.
32-42: 쿼리 로직이 정확하게 구현되었습니다.JPQL 쿼리가 올바르게 작성되었으며,
expertId와businessPlanIds목록으로 필터링한 후 사업계획서별로 카운트를 집계하는 로직이 명확합니다. 파라미터 바인딩도 적절하게 처리되어 있습니다. 현재 상태의 코드에는 특별한 개선사항이 필요하지 않습니다.src/main/java/starlight/application/expert/required/ExpertApplicationCountLookupPort.java (1)
10-10: 인터페이스 메서드 추가가 깔끔합니다.기존
countByExpertIds메서드와 일관된 네이밍 패턴을 따르며, 반환 타입인Map<Long, Long>도 사업계획서 ID별 카운트를 표현하기에 적합합니다. 헥사고날 아키텍처의 포트 계층에 잘 부합하는 추가입니다.src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java (2)
58-74: 전체적인 구현 패턴이 우수합니다.
countByExpertIds메서드와 동일한 패턴을 따라 구현되어 있어 코드 일관성이 뛰어납니다. 방어적 코딩(empty list 처리), 에러 핸들링, 로깅이 모두 적절하게 구현되었으며, 프로젝션을 Map으로 변환하는 로직도 명확합니다.
58-74:findRequestedExpertIds메서드 제거는 안전합니다.제거된
findRequestedExpertIds(Long businessPlanId)메서드의 사용처를 확인한 결과, 코드베이스 어디에서도 호출되는 부분이 없습니다. 따라서 이 메서드의 제거로 인한 영향 범위는 없으며, 리팩토링이 안전하게 처리되었습니다.src/main/java/starlight/adapter/expertApplication/webapi/swagger/ExpertApplicationApiDoc.java (1)
1-254: 리뷰 대상 파일에 제거된search()메서드가 없습니다.코드 검토 결과,
ExpertApplicationApiDoc에는search()메서드가 존재하지 않으며, 현재 인터페이스는requestFeedback()메서드만 정의하고 있습니다.리뷰 의견에서 언급한 "피드백 요청한 전문가 목록 조회" 기능이 제거되었다는 내용은 다른 모듈의
ExpertApiDoc(전문가 조회 API)과의 혼동으로 보입니다.ExpertApiDoc.search()는 전체 전문가 목록을 반환하는 별도의 엔드포인트이며,ExpertApplicationApiDoc과는 다른 모듈입니다.현재 변경 사항에서
ExpertApplicationApiDoc은 피드백 요청 기능만 관리하고 있으며, 새로운 AI 리포트 기능은 별도의ExpertReportApiDoc모듈에서 처리됩니다.Likely an incorrect or invalid review comment.
src/main/java/starlight/adapter/aireport/persistence/AiReportRepository.java (1)
14-15: LGTM!Spring Data JPA 네이밍 컨벤션을 잘 따르고 있습니다.
Collection<Long>파라미터 타입은 유연성을 제공하여List,Set등 다양한 컬렉션 타입을 받을 수 있습니다.src/main/java/starlight/application/expert/required/AiReportSummaryLookupPort.java (1)
6-9: LGTM!헥사고날 아키텍처의 required port로 잘 정의되어 있습니다. 인터페이스가 간결하고 명확합니다.
src/main/java/starlight/application/expert/provided/ExpertAiReportQueryUseCase.java (1)
7-10: LGTM!헥사고날 아키텍처의 provided port(use case)로 적절하게 정의되어 있습니다.
src/main/java/starlight/adapter/expert/webapi/dto/ExpertAiReportBusinessPlanResponse.java (1)
13-20: 정적 팩토리 메서드 패턴이 잘 적용되었습니다.
from()메서드에서result가 null인 경우 NPE가 발생할 수 있습니다. 호출부에서 null을 전달하지 않도록 보장되어 있다면 괜찮지만, 방어적으로 null 체크를 추가하는 것도 고려해 볼 수 있습니다.src/main/java/starlight/adapter/expert/webapi/ExpertController.java (1)
40-48: 새로운 엔드포인트가 잘 구현되었습니다.인증된 사용자의
memberId를 사용하여 해당 사용자의 사업계획서만 조회하는 것으로 보입니다.다만,
expertId에 대한 권한 검증이 없는 것으로 보이는데, 모든 인증된 사용자가 어떤 전문가 ID로든 조회할 수 있는 것이 의도된 동작인지 확인해 주세요. 예를 들어, 해당 전문가에게 컨설팅을 요청한 사용자만 조회할 수 있어야 하는 등의 제약이 필요한지 검토가 필요합니다.src/main/java/starlight/application/expert/ExpertAiReportQueryService.java (1)
38-41: 빈 AI 리포트 처리 로직 확인 필요
totalScoreMap.isEmpty()시 빈 리스트를 반환하는 것은 AI 리포트가 하나도 없는 경우를 처리합니다.그러나 사용자가 사업계획서는 있지만 AI 리포트를 아직 생성하지 않은 경우, 이 API가 빈 리스트를 반환하는 것이 의도된 UX인지 확인해 주세요. 만약 "AI 리포트가 없는 사업계획서"도 목록에 표시해야 한다면 로직 수정이 필요합니다.
src/main/java/starlight/adapter/aireport/persistence/AiReportJpa.java (1)
18-21: 헥사고날 아키텍처 관점에서 의존성 방향 확인
AiReportJpa(Adapter 레이어)가AiReportResponseParser(Adapter 레이어의 util)에 의존하는 것은 같은 레이어 내 의존이므로 괜찮습니다.다만,
AiReportSummaryLookupPort가application.expert.required패키지에 있다면, 이 포트 인터페이스가 Expert 도메인 전용인지 확인해 주세요. AI 리포트 점수 조회가 여러 도메인에서 필요할 경우,application.aireport.required패키지로 이동하는 것이 더 적절할 수 있습니다.src/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.java (3)
95-113: 테스트 케이스가 잘 작성되었습니다.새로운
aiReportBusinessPlans엔드포인트에 대한 테스트가 인증된 사용자 컨텍스트와 함께 올바르게 구성되어 있습니다. Mock 설정과 응답 검증이 적절합니다.추가 테스트 케이스를 고려해 보세요:
- 인증되지 않은 사용자 요청 시 동작 (401 또는 403 응답)
- AI 리포트가 없는 경우 빈 리스트 반환
159-177: 테스트 설정이 깔끔하게 구성되었습니다.
TestAuthenticatedMemberrecord와SecurityTestConfig가 기존 프로젝트의 테스트 패턴(AuthControllerSliceTest)과 일관성 있게 작성되었습니다.
AuthenticationPrincipalArgumentResolver를 사용한 것은 Spring Security의 표준 방식이며,@WebMvcTest환경에서@AuthenticationPrincipal어노테이션이 올바르게 동작하도록 보장합니다.
115-118: 테스트 격리를 위한 SecurityContext 정리가 적절합니다.
@AfterEach에서SecurityContextHolder.clearContext()를 호출하여 테스트 간 보안 컨텍스트가 공유되지 않도록 합니다. 이는 테스트 격리를 보장하는 좋은 관행입니다.src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanJpa.java (1)
17-17: LGTM! 헥사고날 아키텍처 패턴에 맞게 잘 구현되었습니다.
BusinessPlanLookupPort인터페이스를 구현하여 application 레이어와 adapter 레이어 간의 의존성 방향을 올바르게 유지하고 있습니다. Repository에 위임하는 구현도 깔끔합니다.Also applies to: 49-53
src/main/java/starlight/application/expert/required/BusinessPlanLookupPort.java (1)
1-10: LGTM! Port 인터페이스가 헥사고날 아키텍처 원칙에 맞게 잘 정의되었습니다.
application.expert.required패키지에 위치하여 driven port로서의 역할이 명확합니다. 도메인 엔티티BusinessPlan을 반환 타입으로 사용하여 도메인 중심 설계를 유지하고 있습니다.
src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanRepository.java
Show resolved
Hide resolved
src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java
Show resolved
Hide resolved
src/main/java/starlight/adapter/expertApplication/webapi/swagger/ExpertApplicationApiDoc.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
🧹 Nitpick comments (1)
src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java (1)
191-258: 인증 실패 시 응답 문서화를 추가하는 것을 고려해보세요.현재 200(성공)과 500(서버 오류) 응답은 잘 문서화되어 있습니다. 다만
@AuthenticationPrincipal을 사용하는 인증이 필요한 엔드포인트이므로, 401(인증 실패) 응답도 명시적으로 문서화하면 API 사용자에게 더 명확한 가이드를 제공할 수 있습니다.물론 전역 예외 처리로 커버되고 있다면 생략 가능하지만, Swagger 문서 완성도 측면에서는 추가하는 것이 좋습니다.
🔎 401 응답 추가 예시
@ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse( responseCode = "200", description = "성공", content = @Content( mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = ExpertAiReportBusinessPlanResponse.class)), examples = @ExampleObject( name = "성공 예시", value = """ { "result": "SUCCESS", "data": [ { "businessPlanId": 10, "businessPlanTitle": "테스트 사업계획서", "requestCount": 2, "isOver70": true }, { "businessPlanId": 11, "businessPlanTitle": "신규 사업계획서", "requestCount": 0, "isOver70": false } ], "error": null } """ ) ) ), + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "401", + description = "인증 실패", + content = @Content( + mediaType = "application/json", + examples = @ExampleObject( + value = """ + { + "result": "ERROR", + "data": null, + "error": { + "code": "AUTHENTICATION_FAILED", + "message": "인증에 실패했습니다." + } + } + """ + ) + ) + ), @io.swagger.v3.oas.annotations.responses.ApiResponse( responseCode = "500", description = "조회 오류",
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/starlight/adapter/aireport/webapi/ImageController.javasrc/main/java/starlight/adapter/aireport/webapi/swagger/ImageApiDoc.javasrc/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.javasrc/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: SeongHo5356
Repo: StartUpLight/STARLIGHT_BE PR: 71
File: src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanRepository.java:27-33
Timestamp: 2026-01-01T07:33:57.732Z
Learning: `ExpertAiReportQueryService`에서 사용하는 `findAllByMemberIdOrderByLastSavedAt` 메서드는 AI 리포트를 생성한 사업계획서만 조회 대상이므로 회원당 20~30개 수준으로 제한되어, 전체 조회 방식(페이징 없음)이 적절합니다. 향후 50개 이상으로 증가 시 페이징 추가 예정입니다.
📚 Learning: 2026-01-01T07:33:57.732Z
Learnt from: SeongHo5356
Repo: StartUpLight/STARLIGHT_BE PR: 71
File: src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanRepository.java:27-33
Timestamp: 2026-01-01T07:33:57.732Z
Learning: `ExpertAiReportQueryService`에서 사용하는 `findAllByMemberIdOrderByLastSavedAt` 메서드는 AI 리포트를 생성한 사업계획서만 조회 대상이므로 회원당 20~30개 수준으로 제한되어, 전체 조회 방식(페이징 없음)이 적절합니다. 향후 50개 이상으로 증가 시 페이징 추가 예정입니다.
Applied to files:
src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java
📚 Learning: 2025-12-26T15:38:55.886Z
Learnt from: SeongHo5356
Repo: StartUpLight/STARLIGHT_BE PR: 67
File: src/main/java/starlight/adapter/expert/persistence/ExpertJpa.java:36-51
Timestamp: 2025-12-26T15:38:55.886Z
Learning: StarLight의 Expert 조회에서 `findByIdWithCareersAndTags()` 메서드는 단건 상세 조회용으로, ExpertDetailResponse가 categories 필드를 포함하지 않기 때문에 의도적으로 categories 페치를 제외합니다. ExpertListResponse만 categories를 포함합니다.
Applied to files:
src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java
⏰ 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). (2)
- GitHub Check: build-and-push
- GitHub Check: test
🔇 Additional comments (4)
src/main/java/starlight/adapter/aireport/webapi/ImageController.java (1)
22-25: 인증 파라미터명 통일 작업이 일관되게 적용되었습니다.
authDetails에서authenticatedMember로의 리네이밍이 파라미터 선언부와 사용처 모두에 올바르게 적용되었습니다. 전체 코드베이스에서 인증 관련 파라미터명을 통일하는 것은 코드 가독성과 유지보수성을 향상시키는 좋은 접근입니다.src/main/java/starlight/adapter/aireport/webapi/swagger/ImageApiDoc.java (1)
49-49: 일관된 파라미터 명명 규칙 적용 확인전체 PR에서 인증된 회원 파라미터명을
authenticatedMember로 통일하는 변경사항의 일부로, Swagger 문서 인터페이스에도 일관되게 적용되었습니다. ImageController 구현체도 함께 업데이트되어 일관성이 유지됩니다.src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java (1)
58-77: 이전 리뷰 피드백이 완벽하게 반영되었습니다! 👍
expertId에 대한 null 검증이 추가되어 이전 리뷰에서 지적된 사항이 해결되었습니다. 또한 구현이 기존countByExpertIds메서드와 일관된 패턴을 따르고 있으며, 에러 처리 및 로깅도 적절합니다.
- ✅
expertIdnull 체크 (lines 61-63)- ✅
businessPlanIdsnull/empty 체크 (lines 64-66)- ✅ 일관된 에러 처리 및 로깅
- ✅ Projection을 Map으로 변환하는 스트림 로직 정상
헥사고날 아키텍처 관점에서도 Port 구현체로서 적절하게 여러 application 레이어 인터페이스를 구현하고 있습니다.
src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java (1)
187-190: 이전 리뷰 피드백이 반영되어 API 설명이 명확해졌습니다.
expertId의 역할과 조회 대상이 명확하게 표현되어 있습니다. "지정된 전문가의 전문가 상세 페이지에서"라는 컨텍스트와 "로그인한 사용자의 사업계획서"라는 조회 대상이 잘 드러나 있어, 이전 리뷰에서 지적되었던 모호함이 해소되었습니다.
2ghrms
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.
고생하셨습니다!! 좋아보여요
🚀 Why - 해결하려는 문제가 무엇인가요?
isOver70)도 함께 내려줘야 했습니다.@AuthenticationPrincipal AuthenticatedMember파라미터 변수명이 제각각이었습니다.→ (예:
auth) 변수명을authenticatedMember로 통일하였습니다.✅ What - 무엇이 변경됐나요?
GET /v1/experts/{expertId}/business-plans/ai-reportsAPI 추가businessPlanId,businessPlanTitle,requestCount,isOver70ExpertApiDoc)에 성공/에러 예시 및 설명 추가AuthenticatedMember auth→AuthenticatedMember authenticatedMember로 통일 (여러 컨트롤러/ApiDoc)🛠️ How - 어떻게 해결했나요?
memberId로 사용자 사업계획서 목록 조회(최근 저장 기준 정렬)businessPlanIds로 AI 리포트 총점(totalScore)을 조회/파싱expertId+businessPlanIds조합으로 사업계획서별 요청 건수 집계isOver70 = (totalScore >= 70)로 계산 후 응답 DTO로 반환💬 기타 코멘트
expertId+IN (businessPlanIds)로 묶어서 조회해 N+1을 피했습니다.Summary by CodeRabbit
릴리스 노트
새로운 기능
제거된 기능
리팩토링
✏️ Tip: You can customize this high-level summary in your review settings.