Skip to content

Conversation

@SeongHo5356
Copy link
Contributor

@SeongHo5356 SeongHo5356 commented Jan 1, 2026

🚀 Why - 해결하려는 문제가 무엇인가요?

  • SRLT-111 전문가 상세페이지에서 사용자가 작성한 사업계획서 중 AI 리포트 단계에 도달한 사업계획서 리스트를 조회해야 했습니다.
    • 각 사업계획서별로 해당 전문가(expertId)에게 요청된 피드백 건수를 함께 내려줘야 했습니다.
    • 각 사업계획서별로 AI 리포트 점수 기준(>=70) 여부(isOver70)도 함께 내려줘야 했습니다.
  • 전문가 피드백 요청이 전문가 상세페이지로 이동하면서, 기존 “피드백 요청한 전문가 목록 조회” API는 더 이상 사용하지 않게 되어 제거했습니다.
  • 컨트롤러/스웨거 등에서 @AuthenticationPrincipal AuthenticatedMember 파라미터 변수명이 제각각이었습니다.
    → (예: auth) 변수명을 authenticatedMember로 통일하였습니다.

✅ What - 무엇이 변경됐나요?

  • Expert
    • GET /v1/experts/{expertId}/business-plans/ai-reports API 추가
      • 응답: businessPlanId, businessPlanTitle, requestCount, isOver70
    • Swagger 문서(ExpertApiDoc)에 성공/에러 예시 및 설명 추가
  • AI Report / BusinessPlan / ExpertApplication 연동
    • 로그인 사용자(memberId)의 사업계획서 전체 조회 → 해당 planIds 기준 AI 리포트 점수 조회 → expertId 기준 요청 건수 집계 → 결과 조합
    • 조회를 위한 Port/Adapter 확장 (lookup/count 조회 지원)
  • ExpertApplication 정리
    • 사용하지 않는 “피드백 요청한 전문가 목록 조회” API 및 관련 Query/Port/Repository 메서드 제거
  • Auth 변수명 통일
    • AuthenticatedMember authAuthenticatedMember authenticatedMember 로 통일 (여러 컨트롤러/ApiDoc)

🛠️ How - 어떻게 해결했나요?

  • 사용자 사업계획서 조회흐름
    1. memberId로 사용자 사업계획서 목록 조회(최근 저장 기준 정렬)
    2. 해당 businessPlanIds로 AI 리포트 총점(totalScore)을 조회/파싱
    3. expertId + businessPlanIds 조합으로 사업계획서별 요청 건수 집계
    4. isOver70 = (totalScore >= 70) 로 계산 후 응답 DTO로 반환

💬 기타 코멘트

  • 요청 건수 집계는 expertId + IN (businessPlanIds) 로 묶어서 조회해 N+1을 피했습니다.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 전문가가 비즈니스 플랜별 AI 리포트 정보(요청 건수, 점수 등)를 조회할 수 있는 새로운 API 추가
  • 제거된 기능

    • 비즈니스 플랜별 요청한 전문가 목록 조회 엔드포인트 제거
  • 리팩토링

    • 쿼리 로직 최적화 및 내부 코드 일관성 개선
    • 비즈니스 플랜 및 AI 리포트 조회 기능 강화

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 1, 2026

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

전문가용 AI 리포트 기반 사업계획서 조회 기능을 추가하고, 관련 포트/레포지토리 및 API를 도입·재구성했으며 인증 파라미터명을 authenticatedMember로 일관되게 리네이밍했습니다.

Changes

응집도 / 파일(들) 변경 요약
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 파라미터명 authDetailsauthenticatedMember로 통일(호출 시 동작 영향 없음)
테스트 보강
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>>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

✨ FEAT, 🧵 REFACTOR, ✅ TEST

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning 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.

❤️ Share

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

@github-actions
Copy link

github-actions bot commented Jan 1, 2026

Test Results

249 tests   249 ✅  10s ⏱️
 46 suites    0 💤
 46 files      0 ❌

Results for commit 4f5254b.

♻️ This comment has been updated with latest results.

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: 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: 비즈니스 로직이 명확하게 구현되었습니다.

몇 가지 개선 포인트가 있습니다:

  1. Line 49: filter(plan -> totalScoreMap.containsKey(plan.getId()))로 이미 필터링했으므로 getOrDefault(plan.getId(), 0)는 항상 값이 존재합니다. get()으로 충분합니다.

  2. 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)가 파싱에 실패할 경우 예외가 발생할 수 있습니다. 현재 구조에서는 단일 리포트 파싱 실패 시 전체 조회가 실패하게 됩니다.

비즈니스 요구사항에 따라 다음 중 하나를 고려해 주세요:

  1. Fail-fast (현재 방식): 하나라도 실패하면 전체 실패 - 데이터 정합성 우선
  2. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 565a21f and b1b8c24.

📒 Files selected for processing (25)
  • src/main/java/starlight/adapter/aireport/persistence/AiReportJpa.java
  • src/main/java/starlight/adapter/aireport/persistence/AiReportRepository.java
  • src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanJpa.java
  • src/main/java/starlight/adapter/businessplan/persistence/BusinessPlanRepository.java
  • src/main/java/starlight/adapter/expert/webapi/ExpertController.java
  • src/main/java/starlight/adapter/expert/webapi/dto/ExpertAiReportBusinessPlanResponse.java
  • src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java
  • src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java
  • src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationRepository.java
  • src/main/java/starlight/adapter/expertApplication/webapi/ExpertApplicationController.java
  • src/main/java/starlight/adapter/expertApplication/webapi/swagger/ExpertApplicationApiDoc.java
  • src/main/java/starlight/adapter/member/webapi/MemberController.java
  • src/main/java/starlight/adapter/member/webapi/swagger/MemberApiDoc.java
  • src/main/java/starlight/adapter/order/webapi/OrderController.java
  • src/main/java/starlight/adapter/order/webapi/swagger/OrderApiDoc.java
  • src/main/java/starlight/application/expert/ExpertAiReportQueryService.java
  • src/main/java/starlight/application/expert/provided/ExpertAiReportQueryUseCase.java
  • src/main/java/starlight/application/expert/provided/dto/ExpertAiReportBusinessPlanResult.java
  • src/main/java/starlight/application/expert/required/AiReportSummaryLookupPort.java
  • src/main/java/starlight/application/expert/required/BusinessPlanLookupPort.java
  • src/main/java/starlight/application/expert/required/ExpertApplicationCountLookupPort.java
  • src/main/java/starlight/application/expertApplication/ExpertApplicationQueryService.java
  • src/main/java/starlight/application/expertApplication/provided/ExpertApplicationQueryUseCase.java
  • src/main/java/starlight/application/expertApplication/required/ExpertApplicationQueryPort.java
  • src/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 쿼리가 올바르게 작성되었으며, expertIdbusinessPlanIds 목록으로 필터링한 후 사업계획서별로 카운트를 집계하는 로직이 명확합니다. 파라미터 바인딩도 적절하게 처리되어 있습니다. 현재 상태의 코드에는 특별한 개선사항이 필요하지 않습니다.

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)에 의존하는 것은 같은 레이어 내 의존이므로 괜찮습니다.

다만, AiReportSummaryLookupPortapplication.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: 테스트 설정이 깔끔하게 구성되었습니다.

TestAuthenticatedMember record와 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을 반환 타입으로 사용하여 도메인 중심 설계를 유지하고 있습니다.

@SeongHo5356 SeongHo5356 changed the title Srlt 111 전문가 상세페이지 api [SRLT-111] AI 리포트를 생성한 사업계획서 목록을 조회한다 Jan 1, 2026
@SeongHo5356 SeongHo5356 requested a review from 2ghrms January 1, 2026 07:37
@SeongHo5356 SeongHo5356 self-assigned this Jan 1, 2026
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

🧹 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

📥 Commits

Reviewing files that changed from the base of the PR and between b1b8c24 and 4f5254b.

📒 Files selected for processing (4)
  • src/main/java/starlight/adapter/aireport/webapi/ImageController.java
  • src/main/java/starlight/adapter/aireport/webapi/swagger/ImageApiDoc.java
  • src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java
  • src/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 메서드와 일관된 패턴을 따르고 있으며, 에러 처리 및 로깅도 적절합니다.

  • expertId null 체크 (lines 61-63)
  • businessPlanIds null/empty 체크 (lines 64-66)
  • ✅ 일관된 에러 처리 및 로깅
  • ✅ Projection을 Map으로 변환하는 스트림 로직 정상

헥사고날 아키텍처 관점에서도 Port 구현체로서 적절하게 여러 application 레이어 인터페이스를 구현하고 있습니다.

src/main/java/starlight/adapter/expert/webapi/swagger/ExpertApiDoc.java (1)

187-190: 이전 리뷰 피드백이 반영되어 API 설명이 명확해졌습니다.

expertId의 역할과 조회 대상이 명확하게 표현되어 있습니다. "지정된 전문가의 전문가 상세 페이지에서"라는 컨텍스트와 "로그인한 사용자의 사업계획서"라는 조회 대상이 잘 드러나 있어, 이전 리뷰에서 지적되었던 모호함이 해소되었습니다.

@SeongHo5356 SeongHo5356 added ✅ TEST test 관련 ✨ FEAT 새로운 기능 구현 labels Jan 1, 2026
Copy link
Member

@2ghrms 2ghrms left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!! 좋아보여요

@SeongHo5356 SeongHo5356 merged commit 7913f52 into develop Jan 1, 2026
4 checks passed
@SeongHo5356 SeongHo5356 added the ☁️ DEPLOY 배포 관련 label Jan 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

☁️ DEPLOY 배포 관련 ✨ FEAT 새로운 기능 구현 ✅ TEST test 관련

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants