-
Notifications
You must be signed in to change notification settings - Fork 1
[SRLT-113] AiReport, Expert, ExpertReport/Application, Wallet 도메인의 패키지 구조를 정리한다 #68
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-113-\uD074\uB798\uC2A4\uBA85-\uC815\uB9AC"
Conversation
|
Note
|
| Cohort / File(s) | 변경 내용 |
|---|---|
AIReport / OCR 패키지 이동 및 RestClient 리네이밍 src/main/java/starlight/adapter/aireport/infrastructure/clova/..., src/main/java/starlight/adapter/aireport/infrastructure/ocr/..., src/main/java/starlight/bootstrap/RestClientConfig.java |
패키지 경로 ncp → aireport.infrastructure로 이동, RestClient 빈명/Qualifier 변경(clovaClient→clovaStudioRestClient, downloadClient→pdfDownloadRestClient), ClovaStudioClient에 @Deprecated 추가 |
Presigned URL / Storage 포트 이동 src/main/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProvider.java, src/main/java/starlight/application/aireport/required/PresignedUrlProvider.java, src/main/java/starlight/adapter/aireport/webapi/ImageController.java |
PresignedUrlProvider 포트 패키지 이동(provided → aireport.required) 및 관련 import 업데이트, S3Presigner 빈명 변경(ncpS3Presigner) 반영 |
ExpertReport: 도메인/DTO 이름·필드 리팩토링 src/main/java/starlight/domain/expertReport/entity/ExpertReport.java, .../ExpertReportComment.java, src/main/java/starlight/adapter/expertReport/webapi/dto/*, src/main/java/starlight/adapter/expertReport/webapi/mapper/ExpertReportMapper.java |
엔티티/DTO명 Detail→Comment로 변경, 필드 details→comments로 전환, 팩토리/매퍼/컨트롤러/테스트 반영 |
ExpertReport 포트 분리 및 API 문서화 src/main/java/starlight/application/expertReport/required/*, src/main/java/starlight/adapter/expertReport/persistence/*, src/main/java/starlight/adapter/expertReport/webapi/swagger/ExpertReportApiDoc.java |
기존 Query 인터페이스 제거 및 ExpertReportQueryPort/ExpertReportCommandPort 추가, JPA @EntityGraph attributePaths details→comments, 컨트롤러가 새 ApiDoc 인터페이스 구현 |
전문가 카테고리 필터 제거 src/main/java/starlight/adapter/expert/persistence/ExpertJpa.java, src/main/java/starlight/adapter/expert/persistence/ExpertRepository.java, src/main/java/starlight/adapter/expert/webapi/ExpertController.java, src/main/java/starlight/application/expert/* |
Repository와 서비스의 findByAllCategories 삭제, 컨트롤러 및 API 문서에서 categories 파라미터 제거(search → searchAll), 관련 테스트 삭제/수정 |
ExpertApplication / ExpertApplication 포트·서비스 변경 src/main/java/starlight/adapter/expertApplication/*, src/main/java/starlight/application/expertApplication/* |
UseCase/Port 명칭 변경(예: ServiceUseCase → CommandUseCase, Query → QueryPort), 의존성·예외 처리·메서드 호출 경로 일부 변경 |
Order: Query/Command 분리 및 API 문서 추가 src/main/java/starlight/adapter/order/persistence/OrderRepositoryJpa.java, src/main/java/starlight/application/order/*, src/main/java/starlight/adapter/order/webapi/swagger/OrderApiDoc.java |
OrdersQuery → OrderQueryPort/OrderCommandPort 분리, 서비스명 변경(OrderPaymentServiceImpl → OrderPaymentService), OrderApiDoc 인터페이스 추가 |
Usage: Query/Command 분리 및 서비스명 변경 src/main/java/starlight/adapter/usage/persistence/*, src/main/java/starlight/application/usage/*, src/main/java/starlight/application/usage/required/* |
UsageWallet/History 포트 분리(QueryPort/CommandPort 추가), 서비스명 UsageCreditService → UsageCreditChargeService, 기존 provided 포트 삭제/대체 |
설정 변경(빈·메서드명) src/main/java/starlight/bootstrap/ObjectStorageConfig.java, src/main/java/starlight/bootstrap/RestClientConfig.java |
S3Presigner 빈명 메서드명 변경(s3Presigner → ncpS3Presigner), RestClient bean명 리네이밍 |
도메인 에러 타입/메시지 변경 src/main/java/starlight/domain/expertApplication/exception/ExpertApplicationErrorType.java, src/main/java/starlight/domain/expertReport/exception/ExpertReportErrorType.java |
일부 enum 메시지·HTTP 상태값 수정(EXPERT_APPLICATION_QUERY_ERROR 상태 → INTERNAL_SERVER_ERROR 등) |
테스트: 패키지/참조 업데이트 및 일부 테스트 삭제 src/test/java/starlight/adapter/aireport/..., src/test/java/starlight/adapter/expert/persistence/ExpertRepositoryTest.java, src/test/.../ExpertControllerTest.java, src/test/.../ExpertReportTest.java |
테스트 패키지 ncp→aireport로 변경, expert 관련 findByAllCategories 테스트 삭제, ExpertReport 테스트들 Detail→Comment로 수정 |
Sequence Diagram(s)
(조건 미충족 — 변경은 주로 패키지/명칭 리팩터링, 포트 분리 및 타입 교체로 제어 흐름의 신규 다중 컴포넌트 상호작용을 도식화할 필요 없음)
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45분
Possibly related PRs
#36: ExpertReport의 details→comments 리팩토링 및 포트/컨트롤러 변경과 코드영역이 직접 중첩됩니다.#25: 전문가 카테고리 기반 검색(findByAllCategories) 기능을 도입/변경했던 PR로, 이번 PR이 해당 기능을 제거하는 변경과 직접 연관됩니다.#31: AIReport/OCR 모듈 및 aireport 패키지 구조 관련 변경과 중복·연관 파일들이 있어 관련성이 높습니다.
Suggested labels
🧵 REFACTOR, ✂️ CHORE, ✅ TEST
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 5.63% 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 제목은 6개 도메인(AiReport, ExpertReport, Order, Usage, ExpertApplication, Expert)의 패키지 구조 정리라는 핵심 변경사항을 명확하게 전달하고 있습니다. |
✨ Finishing touches
- 📝 Generate docstrings
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
SRLT-113-클래스명-정리
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 Results248 tests 248 ✅ 9s ⏱️ Results for commit b634b5a. ♻️ 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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/main/java/starlight/application/order/OrderPaymentService.java (2)
119-137:cancel메서드에 주문 소유자 검증이 없습니다.
confirm메서드와 달리cancel에서는buyerId파라미터가 없어 주문 소유자 검증이 불가능합니다. 악의적인 사용자가 타인의 주문을 취소할 수 있는 보안 취약점입니다.🔎 제안된 수정
@Override -public TossClientResponse.Cancel cancel(OrderCancelRequest request) { +public TossClientResponse.Cancel cancel(OrderCancelRequest request, Long buyerId) { Orders order = orderQueryPort.getByOrderCodeOrThrow(request.orderCode()); + order.validateSameBuyer(buyerId); PaymentRecords payment = order.getLatestDoneOrThrow();
OrderCancelRequest와OrderPaymentServiceUseCase인터페이스도 함께 수정이 필요합니다.
139-168:getPaymentHistory메서드에@Override어노테이션을 추가해주세요.
OrderPaymentServiceUseCase인터페이스에서 선언된 메서드이고,prepare(),confirm(),cancel()메서드들은 모두@Override어노테이션을 가지고 있는데 이 메서드만 누락되어 있습니다. 일관성을 위해 추가하길 권장합니다.src/main/java/starlight/adapter/order/webapi/OrderController.java (1)
62-71:cancelPayment메서드에 인증 정보와 소유권 검증이 누락되어 있습니다.
prepareOrder와confirmPayment는@AuthenticationPrincipal AuthDetails를 통해 사용자를 전달받지만,cancelPayment는 인증 정보 없이 요청을 처리합니다. 더욱이OrderPaymentService.cancel()메서드 내에서도Orders.validateSameBuyer(buyerId)호출이 없어, 누구든 orderCode만 알면 다른 사용자의 주문을 취소할 수 있습니다.제안:
prepareOrder와 동일한 패턴으로@AuthenticationPrincipal AuthDetails authDetails파라미터를 추가하고,orderPaymentService.cancel(request, authDetails.getMemberId())로 호출하여 서비스 레이어에서 소유권을 검증하세요.
🧹 Nitpick comments (17)
src/main/java/starlight/domain/expertApplication/exception/ExpertApplicationErrorType.java (1)
10-26: ErrorType enum 네이밍 컨벤션 검토를 고려해보세요.학습된 컨벤션에 따르면, StarLight 프로젝트의 ErrorType enum은 "도메인명 + 4자리 숫자" 형식을 사용합니다 (예:
OCR4001,BUSINESSPLAN4002). 이는 HTTP 상태 코드만으로는 구분이 어려운 세부 에러를 명확히 식별하기 위한 규칙입니다.현재 enum 상수들(
EXPERT_APPLICATION_QUERY_ERROR,EXPERT_APPLICATION_NOT_FOUND등)은 설명적인 이름을 사용하고 있어, 프로젝트의 다른 ErrorType enum들과 일관성이 다를 수 있습니다.팀 내에서 이 컨벤션을 따를지 확인하고, 필요하다면 아래와 같은 형식으로 리팩토링을 고려해볼 수 있습니다:
EXPERT_APPLICATION_QUERY_ERROR→EXPERT_APPLICATION4001EXPERT_APPLICATION_NOT_FOUND→EXPERT_APPLICATION4002- 등...
Based on learnings from previous code reviews.
src/main/java/starlight/application/expertApplication/required/ExpertApplicationQueryPort.java (1)
7-12: QueryPort에save메서드가 포함되어 있습니다.헥사고날 아키텍처와 CQRS 원칙에 따르면,
QueryPort는 조회 연산만 포함하고CommandPort는 변경 연산을 담당하는 것이 일반적입니다. 현재ExpertApplicationQueryPort에save메서드가 포함되어 있어 네이밍과 책임이 불일치합니다.향후 리팩토링 시
ExpertApplicationCommandPort를 분리하여save메서드를 이동시키는 것을 고려해보세요.src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java (1)
56-60: Projection에서 Long 타입 직접 반환 고려
p.getCount()를(long)으로 캐스팅하는 대신,ExpertIdCountProjection인터페이스에서Long getCount()또는 별도Long getCountAsLong()을 정의하면 더 명확합니다.🔎 Projection 인터페이스 개선 예시
// ExpertApplicationRepository 내 Projection 인터페이스 interface ExpertIdCountProjection { Long getExpertId(); Long getCount(); // int 대신 Long 반환 }src/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.java (1)
19-22: 사용하지 않는 import가 남아있습니다.
TagCategory와Set은 더 이상 테스트 메서드에서 직접 사용되지 않습니다.expertResult헬퍼 메서드 시그니처에서만 사용되는데, 헬퍼 메서드도 리팩토링 대상이 될 수 있습니다.🔎 정리 제안
-import starlight.domain.expert.enumerate.TagCategory; - import java.util.List; -import java.util.Set; +import java.util.Set; // expertResult 헬퍼에서 사용 +import starlight.domain.expert.enumerate.TagCategory; // expertResult 헬퍼에서 사용또는 헬퍼 메서드에서 categories 파라미터를 제거하고 테스트 내에서 직접 생성하는 방식도 고려할 수 있습니다.
src/main/java/starlight/application/expert/ExpertDetailQueryService.java (1)
24-36: LGTM! 구현이 깔끔하고 효율적입니다.
findAllWithCareersTagsCategories()로 연관 데이터를 한 번에 페치하고,countByExpertIds()로 배치 조회하여 N+1 문제를 방지합니다.다만, 전문가 수가 증가할 경우 페이지네이션 도입을 고려해보세요. 현재는 전체 조회이므로 대규모 데이터에서 성능 이슈가 발생할 수 있습니다.
src/main/java/starlight/application/expertApplication/ExpertApplicationCommandService.java (1)
60-62: 도메인 예외를 명시적으로 catch & rethrow하는 목적을 재검토해보세요.현재 코드는 도메인 예외(
ExpertApplicationException,BusinessPlanException,ExpertException)를 catch한 후 변경 없이 그대로 throw하고 있습니다. 이는 실질적으로 catch하지 않는 것과 동일한 효과입니다.만약 의도가 "도메인 예외는 그대로 전파하고, 그 외 예외만 wrapping"이라면 더 간결한 방법이 있습니다:
🔎 더 간결한 예외 처리 방식
도메인 예외를 명시적으로 catch하지 않고 그대로 전파하도록 할 수 있습니다:
- } catch (ExpertApplicationException | BusinessPlanException | ExpertException e) { - throw e; - } catch (Exception e) { + } catch (ExpertApplicationException | BusinessPlanException | ExpertException e) { + throw e; // 도메인 예외는 그대로 전파 + } catch (Exception e) { log.error("Failed to request Feedback. planId={}, expertId={}", planId, expertId, e); throw new ExpertApplicationException(ExpertApplicationErrorType.EXPERT_FEEDBACK_REQUEST_FAILED); }또는 더 간결하게:
try { validateFile(file); BusinessPlan plan = planQuery.getOrThrow(planId); Expert expert = expertLookupPort.findById(expertId); plan.updateStatus(PlanStatus.EXPERT_MATCHED); registerApplicationRecord(expertId, planId); publishEmailEvent(expert, plan, file, menteeName); - } catch (ExpertApplicationException | BusinessPlanException | ExpertException e) { - throw e; } catch (Exception e) { + // 이미 도메인 예외면 그대로 전파됨 + if (e instanceof ExpertApplicationException || e instanceof BusinessPlanException || e instanceof ExpertException) { + throw e; + } log.error("Failed to request Feedback. planId={}, expertId={}", planId, expertId, e); throw new ExpertApplicationException(ExpertApplicationErrorType.EXPERT_FEEDBACK_REQUEST_FAILED); }다만, 현재 패턴이 명시적이어서 가독성이 더 좋다고 판단되면 그대로 유지하셔도 좋습니다.
src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractorUnitTest.java (1)
5-5: 불필요한 import 제거 권장Line 5의
OcrTextExtractorimport는 테스트 클래스와 같은 패키지(starlight.adapter.aireport.infrastructure.ocr.util)에 위치하므로 불필요합니다.🔎 제안하는 수정사항
package starlight.adapter.aireport.infrastructure.ocr.util; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import starlight.adapter.aireport.infrastructure.ocr.util.OcrTextExtractor; import starlight.shared.dto.infrastructure.OcrResponse;src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PrivateConstructorTests.java (1)
5-7: 불필요한 import 제거 권장Lines 5-7의
OcrResponseMerger,OcrTextExtractor,PdfUtilsimport는 테스트 클래스와 같은 패키지에 위치하므로 불필요합니다.🔎 제안하는 수정사항
package starlight.adapter.aireport.infrastructure.ocr.util; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import starlight.adapter.aireport.infrastructure.ocr.util.OcrResponseMerger; -import starlight.adapter.aireport.infrastructure.ocr.util.OcrTextExtractor; -import starlight.adapter.aireport.infrastructure.ocr.util.PdfUtils; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier;src/test/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProviderUnitTest.java (1)
17-17: 불필요한 import 제거 권장Line 17의
NcpPresignedUrlProviderimport는 테스트 클래스와 같은 패키지(starlight.adapter.aireport.infrastructure.storage)에 위치하므로 불필요합니다.🔎 제안하는 수정사항
import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; -import starlight.adapter.aireport.infrastructure.storage.NcpPresignedUrlProvider; import starlight.shared.dto.infrastructure.PreSignedUrlResponse;src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMergerUnitTest.java (1)
5-5: 불필요한 import 제거 권장Line 5의
OcrResponseMergerimport는 테스트 클래스와 같은 패키지(starlight.adapter.aireport.infrastructure.ocr.util)에 위치하므로 불필요합니다.🔎 제안하는 수정사항
package starlight.adapter.aireport.infrastructure.ocr.util; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import starlight.adapter.aireport.infrastructure.ocr.util.OcrResponseMerger; import starlight.shared.dto.infrastructure.OcrResponse;src/test/java/starlight/adapter/aireport/infrastructure/webapi/ImageControllerIntegrationTest.java (1)
59-84: 주석 처리된 테스트 메서드 확인 필요
getPresignedUrl_Success테스트 메서드가 주석 처리되어 있습니다. 이 테스트가 일시적으로 비활성화된 것인지, 아니면 제거 예정인지 명확하지 않습니다. 테스트 커버리지 유지를 위해 주석 처리 이유를 확인하고, 필요시 재활성화를 고려해주세요.주석 처리 이유가 있다면 TODO 주석을 추가하거나, 이슈 트래킹이 필요하다면 도와드릴 수 있습니다. 이 테스트를 복구하는 작업을 이슈로 만들어드릴까요?
src/main/java/starlight/adapter/expertReport/webapi/dto/UpsertExpertReportRequest.java (1)
6-6: 동일 패키지 내 import 제거 고려
CreateExpertReportCommentRequest는 현재 파일과 같은 패키지(starlight.adapter.expertReport.webapi.dto)에 위치하므로 import 문이 불필요합니다. Java는 동일 패키지 내의 클래스를 자동으로 인식합니다.🔎 제안하는 수정사항
-import starlight.adapter.expertReport.webapi.dto.CreateExpertReportCommentRequest;src/main/java/starlight/adapter/expertReport/webapi/swagger/ExpertReportApiDoc.java (1)
35-72: 동일한responseCode = "404"가 중복 정의되었습니다.
@ApiResponses내에서 동일한 응답 코드("404")가 두 번 정의되어 있습니다. OpenAPI 스펙에서는 동일한 응답 코드에 대해 하나의 정의만 허용하므로, Swagger UI에서 마지막 정의만 표시되거나 예기치 않은 동작이 발생할 수 있습니다.두 가지 해결 방안을 고려해 보세요:
examples배열을 사용하여 하나의 404 응답에 여러 예시를 포함- 가장 대표적인 에러 케이스 하나만 문서화
🔎 예시: 하나의 404 응답에 여러 에러 케이스 통합
- @io.swagger.v3.oas.annotations.responses.ApiResponse( - responseCode = "404", - description = "사업계획서 없음", - content = @Content( - mediaType = "application/json", - examples = @ExampleObject( - value = """ - { - "result": "ERROR", - "data": null, - "error": { - "code": "BUSINESS_PLAN_NOT_FOUND", - "message": "해당 사업계획서가 존재하지 않습니다." - } - } - """ - ) - ) - ), - @io.swagger.v3.oas.annotations.responses.ApiResponse( - responseCode = "404", - description = "전문가 없음", - content = @Content( - mediaType = "application/json", - examples = @ExampleObject( - value = """ - { - "result": "ERROR", - "data": null, - "error": { - "code": "EXPERT_NOT_FOUND", - "message": "해당 전문가를 찾을 수 없습니다." - } - } - """ - ) - ) - ) + @io.swagger.v3.oas.annotations.responses.ApiResponse( + responseCode = "404", + description = "리소스 없음", + content = @Content( + mediaType = "application/json", + examples = { + @ExampleObject( + name = "사업계획서 없음", + value = """ + { + "result": "ERROR", + "data": null, + "error": { + "code": "BUSINESS_PLAN_NOT_FOUND", + "message": "해당 사업계획서가 존재하지 않습니다." + } + } + """ + ), + @ExampleObject( + name = "전문가 없음", + value = """ + { + "result": "ERROR", + "data": null, + "error": { + "code": "EXPERT_NOT_FOUND", + "message": "해당 전문가를 찾을 수 없습니다." + } + } + """ + ) + } + ) + )src/main/java/starlight/application/expertReport/ExpertReportService.java (1)
110-112: 전문가 존재 여부 검증 로직 검토가 필요합니다.현재 로직
!expertIds.isEmpty() && expertsMap.size() != expertIds.size()는 일부 전문가가 누락된 경우를 잡아내지만, 어떤 전문가가 누락되었는지 식별하기 어렵습니다. 디버깅이나 로깅 관점에서 누락된 ID를 명시하면 문제 추적이 용이해집니다.🔎 누락된 전문가 ID 로깅 추가 제안
Map<Long, Expert> expertsMap = expertLookupPort.findByIds(expertIds); if (!expertIds.isEmpty() && expertsMap.size() != expertIds.size()) { + Set<Long> missingIds = expertIds.stream() + .filter(id -> !expertsMap.containsKey(id)) + .collect(Collectors.toSet()); + log.warn("전문가를 찾을 수 없음: {}", missingIds); throw new ExpertException(ExpertErrorType.EXPERT_NOT_FOUND); }src/main/java/starlight/adapter/order/persistence/OrderRepositoryJpa.java (1)
14-40: 클래스명 일관성 검토 필요
UsageWalletJpa는 "Repository" 접미사를 제거했지만,OrderRepositoryJpa는 여전히 "Repository"를 포함하고 있습니다. PR 제목이 "클래스명 정리"인 만큼, 네이밍 일관성을 위해OrderJpa로 변경하는 것을 고려해 주세요.🔎 제안된 수정
-public class OrderRepositoryJpa implements OrderQueryPort, OrderCommandPort { +public class OrderJpa implements OrderQueryPort, OrderCommandPort {src/main/java/starlight/application/usage/UsageCreditChargeService.java (2)
37-43: 지갑 저장이 두 번 호출되는 구조입니다.새 지갑 생성 시
usageWalletCommandPort.save()(line 39)와 충전 후usageWalletCommandPort.save()(line 43)가 연속으로 호출됩니다. 새 지갑의 경우 불필요한 저장이 발생합니다.🔎 제안된 수정
// 지갑 조회 or 생성 -UsageWallet wallet = usageWalletQueryPort.findByUserId(userId) - .orElseGet(() -> usageWalletCommandPort.save(UsageWallet.init(userId))); +UsageWallet wallet = usageWalletQueryPort.findByUserId(userId) + .orElseGet(() -> UsageWallet.init(userId)); // 사용권 충전 wallet.chargeAiReport(usageCount); usageWalletCommandPort.save(wallet);JPA의 경우
@Transactional내에서 새 엔티티도 최종 저장 시점에 한 번만 persist 됩니다.
33-35: 도메인 예외 타입 검토
UsageCreditChargeService는 Usage 도메인에 속하지만OrderException을 사용하고 있습니다. Usage 도메인 전용 예외 (UsageException등)를 사용하는 것이 도메인 경계를 더 명확하게 할 수 있습니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (84)
src/main/java/starlight/adapter/aireport/infrastructure/clova/infra/ClovaStudioClient.javasrc/main/java/starlight/adapter/aireport/infrastructure/clova/util/ClovaUtil.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProvider.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/dto/ClovaOcrRequest.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrErrorType.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClient.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClient.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMerger.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractor.javasrc/main/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtils.javasrc/main/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProvider.javasrc/main/java/starlight/adapter/aireport/webapi/ImageController.javasrc/main/java/starlight/adapter/aireport/webapi/swagger/ImageApiDoc.javasrc/main/java/starlight/adapter/expert/persistence/ExpertJpa.javasrc/main/java/starlight/adapter/expert/persistence/ExpertRepository.javasrc/main/java/starlight/adapter/expert/webapi/ExpertController.javasrc/main/java/starlight/adapter/expert/webapi/swagger/ExpertQueryApiDoc.javasrc/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.javasrc/main/java/starlight/adapter/expertApplication/webapi/ExpertApplicationController.javasrc/main/java/starlight/adapter/expertApplication/webapi/swagger/ExpertApplicationApiDoc.javasrc/main/java/starlight/adapter/expertReport/persistence/ExpertReportJpa.javasrc/main/java/starlight/adapter/expertReport/persistence/ExpertReportRepository.javasrc/main/java/starlight/adapter/expertReport/webapi/ExpertReportController.javasrc/main/java/starlight/adapter/expertReport/webapi/dto/CreateExpertReportCommentRequest.javasrc/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportCommentResponse.javasrc/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportDetailResponse.javasrc/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportResponse.javasrc/main/java/starlight/adapter/expertReport/webapi/dto/UpsertExpertReportRequest.javasrc/main/java/starlight/adapter/expertReport/webapi/mapper/ExpertReportMapper.javasrc/main/java/starlight/adapter/expertReport/webapi/swagger/ExpertReportApiDoc.javasrc/main/java/starlight/adapter/order/persistence/OrderRepositoryJpa.javasrc/main/java/starlight/adapter/order/toss/TossClient.javasrc/main/java/starlight/adapter/order/webapi/OrderController.javasrc/main/java/starlight/adapter/order/webapi/swagger/OrderApiDoc.javasrc/main/java/starlight/adapter/usage/persistence/UsageHistoryJpa.javasrc/main/java/starlight/adapter/usage/persistence/UsageWalletJpa.javasrc/main/java/starlight/application/aireport/AiReportServiceImpl.javasrc/main/java/starlight/application/aireport/required/OcrProvider.javasrc/main/java/starlight/application/aireport/required/PresignedUrlProvider.javasrc/main/java/starlight/application/expert/ExpertDetailQueryService.javasrc/main/java/starlight/application/expert/provided/ExpertDetailQueryUseCase.javasrc/main/java/starlight/application/expert/required/ExpertQueryPort.javasrc/main/java/starlight/application/expertApplication/ExpertApplicationCommandService.javasrc/main/java/starlight/application/expertApplication/ExpertApplicationQueryService.javasrc/main/java/starlight/application/expertApplication/provided/ExpertApplicationCommandUseCase.javasrc/main/java/starlight/application/expertApplication/required/ExpertApplicationQueryPort.javasrc/main/java/starlight/application/expertReport/ExpertReportService.javasrc/main/java/starlight/application/expertReport/provided/ExpertReportServiceUseCase.javasrc/main/java/starlight/application/expertReport/required/ExpertReportCommandPort.javasrc/main/java/starlight/application/expertReport/required/ExpertReportQuery.javasrc/main/java/starlight/application/expertReport/required/ExpertReportQueryPort.javasrc/main/java/starlight/application/order/OrderPaymentService.javasrc/main/java/starlight/application/order/provided/OrderPaymentServiceUseCase.javasrc/main/java/starlight/application/order/required/OrderCommandPort.javasrc/main/java/starlight/application/order/required/OrderQueryPort.javasrc/main/java/starlight/application/order/required/UsageCreditChargePort.javasrc/main/java/starlight/application/usage/UsageCreditChargeService.javasrc/main/java/starlight/application/usage/provided/UsageCreditPort.javasrc/main/java/starlight/application/usage/required/UsageHistoryCommandPort.javasrc/main/java/starlight/application/usage/required/UsageWalletCommandPort.javasrc/main/java/starlight/application/usage/required/UsageWalletQueryPort.javasrc/main/java/starlight/bootstrap/ObjectStorageConfig.javasrc/main/java/starlight/bootstrap/RestClientConfig.javasrc/main/java/starlight/domain/expertApplication/exception/ExpertApplicationErrorType.javasrc/main/java/starlight/domain/expertReport/entity/ExpertReport.javasrc/main/java/starlight/domain/expertReport/entity/ExpertReportComment.javasrc/main/java/starlight/domain/expertReport/exception/ExpertReportErrorType.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProviderTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClientTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClientIntegrationTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClientTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMergerUnitTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractorUnitTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtilsUnitTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PrivateConstructorTests.javasrc/test/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProviderUnitTest.javasrc/test/java/starlight/adapter/aireport/infrastructure/webapi/ImageControllerIntegrationTest.javasrc/test/java/starlight/adapter/expert/persistence/ExpertRepositoryTest.javasrc/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.javasrc/test/java/starlight/application/aireport/AiReportServiceImplIntegrationTest.javasrc/test/java/starlight/application/aireport/AiReportServiceImplUnitTest.javasrc/test/java/starlight/domain/expertReport/entity/ExpertReportTest.java개발가이드.md
💤 Files with no reviewable changes (7)
- src/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportDetailResponse.java
- src/main/java/starlight/application/usage/provided/UsageCreditPort.java
- src/main/java/starlight/application/expertReport/required/ExpertReportQuery.java
- src/test/java/starlight/adapter/expert/persistence/ExpertRepositoryTest.java
- src/main/java/starlight/adapter/expert/persistence/ExpertJpa.java
- src/main/java/starlight/application/expert/required/ExpertQueryPort.java
- src/main/java/starlight/adapter/expert/persistence/ExpertRepository.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-26T15:38:51.383Z
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:51.383Z
Learning: StarLight의 Expert 조회에서 `findByIdWithCareersAndTags()` 메서드는 단건 상세 조회용으로, ExpertDetailResponse가 categories 필드를 포함하지 않기 때문에 의도적으로 categories 페치를 제외합니다. ExpertListResponse만 categories를 포함합니다.
Applied to files:
src/main/java/starlight/application/expert/provided/ExpertDetailQueryUseCase.javasrc/main/java/starlight/application/expert/ExpertDetailQueryService.javasrc/main/java/starlight/adapter/expert/webapi/swagger/ExpertQueryApiDoc.javasrc/main/java/starlight/adapter/expert/webapi/ExpertController.javasrc/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.java
📚 Learning: 2025-10-26T16:17:33.090Z
Learnt from: 2ghrms
Repo: StartUpLight/STARLIGHT_BE PR: 21
File: src/main/java/starlight/adapter/ncp/ocr/exception/OcrErrorType.java:11-16
Timestamp: 2025-10-26T16:17:33.090Z
Learning: StarLight 프로젝트에서는 ErrorType enum의 이름을 "도메인명 + 4자리 숫자" 형식으로 작성합니다 (예: OCR4001, BUSINESSPLAN4002). 이는 HTTP 상태 코드만으로는 구분이 어려운 세부 에러를 명확히 식별하기 위한 컨벤션입니다.
Applied to files:
src/main/java/starlight/domain/expertApplication/exception/ExpertApplicationErrorType.javasrc/main/java/starlight/domain/expertReport/exception/ExpertReportErrorType.java
🧬 Code graph analysis (18)
src/main/java/starlight/application/order/required/UsageCreditChargePort.java (1)
src/main/java/starlight/application/usage/provided/UsageCreditPort.java (1)
UsageCreditPort(3-6)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClient.java (4)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProvider.java (1)
Slf4j(18-69)src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClient.java (1)
Slf4j(13-61)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtils.java (1)
Slf4j(13-59)src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
OcrException(6-10)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractorUnitTest.java (2)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PrivateConstructorTests.java (1)
DisplayName(14-91)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractor.java (1)
OcrTextExtractor(8-97)
src/main/java/starlight/adapter/usage/persistence/UsageHistoryJpa.java (1)
src/main/java/starlight/adapter/usage/persistence/UsageWalletJpa.java (1)
Repository(11-26)
src/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportCommentResponse.java (1)
src/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportDetailResponse.java (1)
ExpertReportDetailResponse(6-17)
src/main/java/starlight/adapter/aireport/infrastructure/clova/infra/ClovaStudioClient.java (1)
src/main/java/starlight/adapter/aireport/infrastructure/clova/util/ClovaUtil.java (1)
Deprecated(11-81)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClientIntegrationTest.java (1)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
OcrException(6-10)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProviderTest.java (3)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
OcrException(6-10)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMerger.java (1)
OcrResponseMerger(8-28)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractor.java (1)
OcrTextExtractor(8-97)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClientTest.java (1)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
OcrException(6-10)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClientTest.java (1)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
OcrException(6-10)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMergerUnitTest.java (2)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PrivateConstructorTests.java (1)
DisplayName(14-91)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMerger.java (1)
OcrResponseMerger(8-28)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtils.java (4)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProvider.java (1)
Slf4j(18-69)src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClient.java (1)
Slf4j(12-42)src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClient.java (1)
Slf4j(13-61)src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
OcrException(6-10)
src/main/java/starlight/adapter/expertReport/webapi/ExpertReportController.java (1)
src/main/java/starlight/adapter/expert/webapi/ExpertController.java (1)
RestController(16-34)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProvider.java (7)
src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClient.java (1)
Slf4j(12-42)src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClient.java (1)
Slf4j(13-61)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtils.java (1)
Slf4j(13-59)src/main/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProvider.java (1)
Slf4j(22-119)src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
OcrException(6-10)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMerger.java (1)
OcrResponseMerger(8-28)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractor.java (1)
OcrTextExtractor(8-97)
src/main/java/starlight/application/expertReport/ExpertReportService.java (1)
src/main/java/starlight/domain/expert/exception/ExpertException.java (1)
ExpertException(6-11)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PrivateConstructorTests.java (3)
src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtilsUnitTest.java (1)
DisplayName(18-249)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMerger.java (1)
OcrResponseMerger(8-28)src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractor.java (1)
OcrTextExtractor(8-97)
src/main/java/starlight/adapter/usage/persistence/UsageWalletJpa.java (1)
src/main/java/starlight/adapter/usage/persistence/UsageHistoryJpa.java (1)
Repository(8-18)
src/main/java/starlight/application/expertApplication/ExpertApplicationCommandService.java (2)
src/main/java/starlight/domain/businessplan/exception/BusinessPlanException.java (1)
BusinessPlanException(6-11)src/main/java/starlight/domain/expert/exception/ExpertException.java (1)
ExpertException(6-11)
⏰ 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-and-push
🔇 Additional comments (86)
src/main/java/starlight/domain/expertApplication/exception/ExpertApplicationErrorType.java (1)
13-14: 의미론적 개선이 잘 적용되었습니다!두 가지 변경사항 모두 도메인 컨텍스트를 명확히 하는 좋은 개선입니다:
Line 13 (EXPERT_APPLICATION_QUERY_ERROR):
- HTTP 상태 코드를
NOT_FOUND에서INTERNAL_SERVER_ERROR로 변경한 것은 적절합니다. 쿼리 중 발생하는 오류는 내부 서버 에러(예: DB 연결 문제, 쿼리 실행 오류 등)에 해당하며,NOT_FOUND는 특정 리소스가 존재하지 않을 때 사용되어야 합니다.- 메시지에 "신청"을 추가하여 도메인 명확성도 향상되었습니다.
Line 14 (EXPERT_APPLICATION_NOT_FOUND):
- "해당 전문가"에서 "해당 전문가 신청"으로 변경하여 전문가 엔티티가 아닌 전문가 신청서를 찾을 수 없다는 의미를 명확히 전달합니다.
src/main/java/starlight/adapter/aireport/infrastructure/clova/infra/ClovaStudioClient.java (1)
18-20: Bean qualifier 변경이 안전하게 완료되었습니다."clovaStudioRestClient" 이름의 RestClient 빈이
RestClientConfig.java의 61번 라인에 정의되어 있으며,ClovaStudioClient가 올바르게 참조하고 있습니다. 코드베이스 전체에서 이전의 "clovaClient" qualifier에 대한 참조는 발견되지 않아, qualifier 변경이 완벽하게 마무리된 상태입니다.다만
ClovaStudioClient가@Deprecated어노테이션을 가지고 있으므로, 추후 리팩토링 로드맵에서 대체 구현으로의 마이그레이션 계획을 수립하시기를 권장합니다.src/main/java/starlight/application/expertApplication/provided/ExpertApplicationCommandUseCase.java (1)
7-9: LGTM! 네이밍 변경이 헥사고날 아키텍처 패턴에 부합합니다.
ExpertApplicationCommandUseCase로의 이름 변경은 Command/Query 분리 원칙을 명확히 반영합니다.provided패키지에 위치하여 애플리케이션 레이어의 제공 포트(Provided Port)로서 적절합니다.단,
IOException을 인터페이스에서 직접 던지는 것은 구현 세부사항이 노출되는 측면이 있으나,MultipartFile처리 특성상 수용 가능한 수준입니다.src/main/java/starlight/adapter/expertApplication/persistence/ExpertApplicationJpaPort.java (1)
7-20: LGTM! 포트 인터페이스 구현이 적절합니다.
ExpertApplicationQueryPort와ExpertApplicationLookupPort인터페이스를 구현하여 헥사고날 아키텍처의 어댑터 역할을 올바르게 수행하고 있습니다. 에러 핸들링과 로깅도 적절하게 구현되어 있습니다.src/main/java/starlight/application/expertApplication/ExpertApplicationQueryService.java (1)
17-23: LGTM! 사업계획서 존재 검증 추가가 적절합니다.
businessPlanQuery.getOrThrow(businessPlanId)호출로 존재하지 않는 사업계획서에 대한 요청을 사전에 차단합니다. 이는 방어적 프로그래밍 패턴으로 적절한 개선입니다.
ExpertApplicationQueryPort로의 네이밍 변경도 헥사고날 아키텍처 컨벤션에 부합합니다.src/test/java/starlight/adapter/expert/webapi/ExpertControllerTest.java (1)
44-49: LGTM! 테스트가 구현 변경사항을 올바르게 반영합니다.
searchAll()메서드 모킹으로 변경되어 카테고리 필터링 제거와 일관성을 유지합니다. 테스트 시나리오가 새로운 API 동작을 정확히 검증하고 있습니다.src/main/java/starlight/application/expert/provided/ExpertDetailQueryUseCase.java (1)
9-9: 카테고리 필드가 상세 응답에서 의도적으로 제외되었습니다.
searchAll()로의 변경이 맞습니다.ExpertDetailResponse는 아키텍처상categories필드를 포함하지 않도록 설계되었고, 이는 테스트에서도 명시적으로 검증되어 있습니다(.andExpect(jsonPath("$.data.categories").doesNotExist())).categories는ExpertListResponse에서만 제공되는 필드로, 상세 조회 메서드에서 필터링 파라미터를 제거한 것은 올바른 결정입니다.src/main/java/starlight/adapter/expert/webapi/ExpertController.java (1)
23-26: LGTM! 컨트롤러가 깔끔하게 리팩토링되었습니다.카테고리 파라미터 제거로 API가 단순화되었고, Swagger 문서도 올바르게 업데이트되어 있습니다. 테스트도 새로운 API 계약에 맞춰 잘 작성되었습니다. 컨트롤러는 유즈케이스에 위임만 하고 있어 헥사고날 아키텍처의 어댑터 역할에 충실합니다.
src/main/java/starlight/adapter/expertApplication/webapi/ExpertApplicationController.java (1)
12-12: LGTM! Command UseCase 네이밍으로 CQRS 패턴이 명확해졌습니다.
ExpertApplicationServiceUseCase에서ExpertApplicationCommandUseCase로 변경하여 명령(Command) 작업의 의도가 더 명확하게 드러납니다. 헥사고날 아키텍처의 포트 네이밍 일관성 측면에서도 적절한 개선입니다.Also applies to: 24-24
src/main/java/starlight/application/expertApplication/ExpertApplicationCommandService.java (2)
12-12: LGTM! 헥사고날 아키텍처의 포트-어댑터 패턴이 강화되었습니다.
ExpertApplicationCommandService로 클래스명 변경: CQRS의 Command 책임이 명확해짐ExpertApplicationQueryPort로 포트 네이밍 일관성 확보ExpertReportUseCase로 간결화이러한 변경으로 도메인 레이어와 어댑터 레이어의 경계가 더 명확해졌습니다.
Also applies to: 14-14, 32-32, 36-36, 38-38
84-84: LGTM! 포트 메서드 호출이 일관되게 업데이트되었습니다.
applicationQueryPort와expertReportUseCase를 통한 메서드 호출이 올바르게 변경되었습니다.Also applies to: 89-89, 129-129
src/main/java/starlight/adapter/expert/webapi/swagger/ExpertQueryApiDoc.java (2)
22-23: LGTM! 카테고리 필터링 제거로 API가 단순화되었습니다.
search(Set<TagCategory> categories)→search()로 변경되어 API가 더 간결해졌습니다. "전체 전문가 목록을 반환합니다"라는 명확한 설명과 함께 전문가 목록 조회 API의 책임이 단순화되었습니다.Also applies to: 112-112
72-109: LGTM! Swagger 문서에 에러 케이스가 추가되어 API 명세가 명확해졌습니다.
search()엔드포인트: 404 (EXPERT_QUERY_ERROR), 500 (EXPERT_APPLICATION_QUERY_ERROR) 추가detail()엔드포인트: 404 (EXPERT_NOT_FOUND, EXPERT_QUERY_ERROR), 500 (EXPERT_APPLICATION_QUERY_ERROR) 추가API 사용자가 발생 가능한 에러를 미리 파악하고 적절히 처리할 수 있게 되었습니다.
Also applies to: 124-177
src/main/java/starlight/adapter/expertApplication/webapi/swagger/ExpertApplicationApiDoc.java (4)
17-17: LGTM! ApiResponse import 추가로 코드가 더 간결해졌습니다.
starlight.shared.apiPayload.response.ApiResponse를 import하여 메서드 시그니처에서 fully-qualified name을 사용하지 않아도 되어 가독성이 개선되었습니다.Also applies to: 84-84, 277-277
29-29: LGTM! Fully-qualified ApiResponse annotation으로 명확성이 향상되었습니다.
@io.swagger.v3.oas.annotations.responses.ApiResponse를 명시적으로 사용하여 Spring의@ApiResponse와 Swagger의@ApiResponse간 혼동을 방지했습니다.Also applies to: 45-45, 64-64, 105-105, 121-121, 141-141, 160-160, 179-179, 214-214, 233-233
123-140: LGTM! HTTP 상태 코드별 상세한 에러 응답이 추가되었습니다.
- 400: EMPTY_FILE - 빈 파일 업로드 시
- 413: FILE_SIZE_EXCEEDED - 20MB 초과 시
- 415: UNSUPPORTED_FILE_TYPE - PDF가 아닌 파일 업로드 시
- 500: FILE_READ_ERROR, EXPERT_FEEDBACK_REQUEST_FAILED - 다양한 서버 오류 케이스
각 HTTP 상태 코드가 RFC 표준과 잘 매칭되며(413 Payload Too Large, 415 Unsupported Media Type), API 사용자가 클라이언트 측에서 적절한 에러 핸들링을 구현할 수 있도록 명확한 가이드를 제공합니다.
Also applies to: 141-178, 235-266
47-82: LGTM! 조회 API의 에러 케이스가 명확하게 문서화되었습니다.
- 404: BUSINESS_PLAN_NOT_FOUND - "해당 사업계획서가 존재하지 않습니다."
- 500: EXPERT_APPLICATION_QUERY_ERROR - "전문가 신청 정보를 조회하는 중에 오류가 발생했습니다."
비즈니스 로직 에러(404)와 시스템 에러(500)가 명확히 구분되어 있습니다.
개발가이드.md (1)
34-35: 포트 배치 전략이 명확해졌습니다.헥사고날 아키텍처에서 포트의 위치를 명확히 정의한 좋은 가이드라인입니다. 도메인 의미가 있는 포트(예:
OcrProvider,PresignedUrlProvider)를 소비자 도메인의required패키지에 배치하고, 순수 인프라 포트를shared/infrastructure로 분리하는 전략이 이번 PR의 패키지 재구성과도 일관성 있게 적용되었습니다.src/main/java/starlight/domain/expertReport/exception/ExpertReportErrorType.java (1)
13-13: 에러 메시지가 도메인 용어와 일치하도록 개선되었습니다."전문가 신청"에서 "전문가 리포트"로의 메시지 변경이 도메인 모델의 명확성을 향상시킵니다. 이번 PR의 다른 변경사항들(details → comments, 포트 분리 등)과도 일관성 있는 용어 정리입니다.
src/main/java/starlight/application/aireport/required/OcrProvider.java (1)
1-10: OCR 포트가 도메인 패키지로 올바르게 이동되었습니다.
OcrProvider의 패키지 이동이 완벽하게 완료되었습니다.infrastructure.provided의 일반적인 위치에서 벗어나aireport.required의 도메인 특화 아웃바운드 포트로 재배치되어 헥사고날 아키텍처 원칙을 잘 따르고 있습니다.검증 결과:
- ✅ 이전 패키지(
infrastructure.provided) 참조가 모두 제거됨- ✅
ClovaOcrProvider어댑터가 새 위치의 포트를 올바르게 구현- ✅
AiReportServiceImpl과 모든 테스트 파일이 새 위치에서 포트를 importsrc/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrTextExtractor.java (1)
1-1: 패키지 마이그레이션이 완벽하게 완료되었습니다.
ncp어댑터에서aireport.infrastructure구조로의 패키지 이전이 헥사고날 아키텍처의 계층을 명확하게 표현합니다. 모든 참조(ClovaOcrProvider, 유닛 테스트 4건)가 새로운 패키지 경로로 정확하게 업데이트되었으며, 이전ncp.ocr패키지에 대한 남은 참조는 없습니다.src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrException.java (1)
1-10: OCR 예외 클래스 패키지 이동이 완벽하게 반영되었습니다.
starlight.adapter.ncp.ocr.exception에서starlight.adapter.aireport.infrastructure.ocr.exception으로의 패키지 이동이 코드베이스 전체에서 일관되게 적용되었습니다. PdfUtils, ClovaOcrClient, ClovaOcrProvider, PdfDownloadClient 등 모든 사용처에서 새 패키지 경로로 import가 정확하게 업데이트되었으며, throw 문, catch 블록, JavaDoc의 @throws 선언까지 모두 새 위치를 참조하고 있습니다. 헥사고날 아키텍처의 인프라 계층 내에서 일관된 예외 처리 방식이 유지되고 있으니 승인합니다.src/main/java/starlight/adapter/aireport/infrastructure/ocr/dto/ClovaOcrRequest.java (1)
1-1: Clova OCR DTO 패키지 이동이 완전히 완료되었습니다.패키지 마이그레이션이 문제없이 완료되었습니다. 구 패키지(
starlight.adapter.ncp.ocr.dto)에 대한 참조가 없으며,ClovaOcrClient와ClovaOcrClientTest모두 새 패키지 경로(starlight.adapter.aireport.infrastructure.ocr.dto)를 올바르게 사용하고 있습니다.aireport.infrastructure.ocr모듈로의 통합이 정상적으로 진행되었습니다.src/main/java/starlight/application/aireport/required/PresignedUrlProvider.java (1)
1-10: 포트 인터페이스 위치 이동이 헥사고날 아키텍처 원칙을 정확히 따르고 있습니다.
infrastructure.provided에서aireport.required로의 이동은 도메인별 포트 관리 가이드라인을 충실히 반영합니다. 검증 결과NcpPresignedUrlProvider어댑터와ImageController컨트롤러 모두 새 패키지 경로(starlight.application.aireport.required.PresignedUrlProvider)로 완벽하게 업데이트되었으며, 이전 패키지 참조는 전혀 남아있지 않습니다. 모든 테스트도 새 위치를 올바르게 참조하고 있습니다.src/main/java/starlight/application/expertReport/required/ExpertReportQueryPort.java (1)
7-16: 쿼리 포트 분리가 탄탄하게 구현되었습니다.
ExpertReportQueryPort의 4가지 조회 메서드가 모두ExpertReportJpa어댑터에서 올바르게 구현되었습니다:
- 명시적인 로딩 전략:
findByTokenWithComments와findAllByBusinessPlanIdOrderByCreatedAtDesc는 저장소의@EntityGraph(attributePaths = {"comments"})로 N+1 문제를 방지하고 있습니다.- 명확한 예외 처리:
findByIdOrThrow와findByTokenWithComments는 일관되게orElseThrow()로 구현되어 예외 발생 동작을 명확히 표현합니다.- 우수한 아키텍처 설계: 응용 계층의 포트 정의, 어댑터 계층의 구현으로 헥사고날 아키텍처를 잘 따르고 있으며, 조회와 명령 책임이 깔끔하게 분리되어 있습니다.
src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/OcrResponseMerger.java (1)
1-1: 패키지 이동 완료
OcrResponseMerger클래스의 패키지 경로가aireport.infrastructure.ocr.util로 올바르게 업데이트되었습니다. 헥사고날 아키텍처의 인프라 계층 구조화가 잘 반영되었습니다.src/main/java/starlight/adapter/aireport/webapi/swagger/ImageApiDoc.java (1)
1-1: 패키지 구조 개선Web API Swagger 문서 인터페이스의 패키지 경로가
aireport.webapi.swagger로 올바르게 업데이트되었습니다. AI 리포트 도메인의 아웃바운드 어댑터 구조가 명확해졌습니다.src/main/java/starlight/adapter/aireport/infrastructure/ocr/exception/OcrErrorType.java (1)
1-1: 예외 타입 패키지 이동 완료OCR 관련 예외 타입이
aireport.infrastructure.ocr.exception패키지로 올바르게 이동되었습니다. 인프라 계층의 예외 처리 구조가 명확해졌습니다.src/test/java/starlight/application/aireport/AiReportServiceImplIntegrationTest.java (1)
20-20: 헥사고날 아키텍처 포트 구조 개선
OcrProvider인터페이스가application.aireport.required패키지로 이동하여 헥사고날 아키텍처의 포트-어댑터 패턴이 더욱 명확해졌습니다. AI 리포트 도메인의 required port로서 역할이 분명히 정의되었습니다.src/test/java/starlight/application/aireport/AiReportServiceImplUnitTest.java (1)
10-10: 패키지 재구성이 올바르게 적용되었습니다.
OcrProvider가starlight.application.aireport.required패키지로 이동하여 헥사고날 아키텍처의 포트-어댑터 패턴이 명확해졌습니다. 테스트 코드가 새로운 경로를 올바르게 참조하고 있습니다.src/main/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtils.java (1)
1-6: 패키지 구조 개선이 잘 적용되었습니다.OCR 유틸리티가
starlight.adapter.aireport.infrastructure.ocr.util패키지로 이동하여 어댑터 계층의 인프라 관심사가 명확하게 분리되었습니다. 예외 타입 임포트도 일관되게 업데이트되었습니다.src/test/java/starlight/adapter/aireport/infrastructure/ocr/util/PdfUtilsUnitTest.java (1)
1-9: 테스트 코드의 패키지 재구성이 올바릅니다.테스트 패키지가 프로덕션 코드의 새로운 구조를 정확하게 반영하고 있으며, 모든 임포트가 일관되게 업데이트되었습니다.
src/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClientIntegrationTest.java (1)
1-14: 통합 테스트의 패키지 이동이 정확합니다.
PdfDownloadClient의 통합 테스트가 새로운 인프라 계층 패키지 구조를 올바르게 따르고 있습니다. 예외 처리 및 클라이언트 임포트가 일관되게 업데이트되었습니다.src/main/java/starlight/application/aireport/AiReportServiceImpl.java (1)
18-18: 헥사고날 아키텍처 원칙에 맞게 개선되었습니다.
OcrProvider가starlight.application.aireport.required패키지로 이동하면서 애플리케이션 계층이 요구하는 포트(required port)로 명확하게 정의되었습니다. 이는 의존성 역전 원칙(DIP)을 잘 따르고 있습니다.src/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClientTest.java (1)
1-10: 단위 테스트의 패키지 재구성이 올바릅니다.테스트가 새로운 인프라 계층 구조를 정확하게 반영하고 있으며, 모든 의존성 임포트가 일관되게 업데이트되었습니다.
src/test/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProviderTest.java (1)
1-18: OCR 제공자 테스트의 패키지 이동이 완벽합니다.
ClovaOcrProvider테스트가 새로운 어댑터 계층 구조를 올바르게 따르고 있습니다. 인프라(infra), 예외(exception), 유틸(util) 하위 패키지의 모든 임포트가 일관되게 업데이트되었습니다.src/main/java/starlight/adapter/expertReport/webapi/dto/CreateExpertReportCommentRequest.java (1)
7-13: 도메인 모델 개선이 적절하게 적용되었습니다.
Detail에서Comment로의 명명 변경은 전문가 리포트의 도메인 언어를 더욱 명확하게 만듭니다. 필드명을commentType에서type으로 단순화한 것도 레코드 타입 자체가CreateExpertReportCommentRequest로 명확하므로 중복을 제거한 좋은 선택입니다.src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClient.java (1)
1-1: 패키지 재구성 확인NCP 특정 네이밍에서 AI Report 인프라스트럭처로 패키지를 재구성한 것은 헥사고날 아키텍처의 관점에서 적절합니다. 어댑터의 역할과 소속을 더 명확하게 표현하고 있습니다.
Also applies to: 7-9
src/main/java/starlight/application/expertReport/required/ExpertReportCommandPort.java (1)
1-10: 헥사고날 아키텍처 포트 분리 우수Command 작업(save, delete)을 별도 포트 인터페이스로 분리한 것은 CQRS 원칙과 헥사고날 아키텍처를 잘 따르고 있습니다. 포트 인터페이스가 도메인 엔티티에만 의존하고 있어 의존성 방향도 올바릅니다.
src/main/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProvider.java (1)
1-1: 어댑터 모듈 재구성 적절스토리지 어댑터를
aireport.infrastructure네임스페이스로 통합한 것은 모듈 간 관계를 명확히 하고 AI 리포트 관련 인프라를 한 곳에 모으는 좋은 리팩토링입니다.Also applies to: 15-15
src/test/java/starlight/adapter/aireport/infrastructure/webapi/ImageControllerIntegrationTest.java (1)
1-1: 테스트 패키지 재구성 확인테스트 코드의 import들이 새로운 패키지 구조에 맞게 올바르게 업데이트되었습니다.
Also applies to: 14-14, 17-18
src/main/java/starlight/adapter/aireport/webapi/ImageController.java (1)
1-1: 웹 어댑터 재구성 완료컨트롤러를
aireport.webapi패키지로 이동하여 어댑터 레이어의 구조를 명확히 했습니다. 기능 변경 없이 패키지 구조만 개선되었습니다.Also applies to: 9-10
src/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportCommentResponse.java (1)
6-16: DTO 레코드 구조 적절
ExpertReportComment도메인 엔티티를 API 응답으로 변환하는 DTO가 깔끔하게 구현되었습니다. Java record를 활용한 불변 객체 생성과 정적 팩토리 메서드 패턴이 잘 적용되었습니다.한 가지 관찰: 이전
ExpertReportDetailResponse에서는commentType이었던 필드가type으로 간소화되었는데, 이는 레코드 이름 자체에 "Comment"가 포함되어 있어 중복을 줄인 좋은 선택입니다.src/main/java/starlight/adapter/aireport/infrastructure/ocr/infra/PdfDownloadClient.java (2)
1-1: OCR 인프라 패키지 재구성 확인PDF 다운로드 클라이언트를
aireport.infrastructure.ocr네임스페이스로 이동한 것은 OCR 관련 인프라를 논리적으로 그룹화하는 좋은 리팩토링입니다.Also applies to: 8-9
21-21: @qualifier 변경이 올바르게 적용되었습니다.
pdfDownloadRestClientBean이RestClientConfig.java(50번 라인)에 명시적으로 정의되어 있으며, PdfDownloadClient의 Qualifier가 정확하게 매칭됩니다. 60초 타임아웃으로 구성된 경량 클라이언트로, PDF 다운로드 전용 목적에 적합합니다.src/main/java/starlight/application/expertReport/provided/ExpertReportServiceUseCase.java (1)
5-5: 도메인 모델 명명 개선 - 검증 완료
ExpertReportComment로의 변경이 코드베이스 전체에서 완벽하게 적용되었습니다.saveReport메서드의 유일한 호출 지점(ExpertReportController.java)이 일관되게 업데이트되었으며, 인터페이스 정의와 구현부까지 모두 동일한 시그니처를 유지하고 있습니다. 기존ExpertReportDetail참조는 남아있지 않으므로, public API 변경이 완전하고 일관되게 반영되었습니다.src/main/java/starlight/adapter/expertReport/webapi/dto/UpsertExpertReportRequest.java (1)
16-17: 도메인 용어 통일이 잘 반영되었습니다
details에서comments로의 필드명 변경이 도메인 모델과 일관성 있게 적용되었습니다. Record 타입과@Valid애노테이션을 통한 중첩 검증 구조도 적절합니다.src/main/java/starlight/bootstrap/RestClientConfig.java (2)
50-51: RestClient 빈 네이밍 개선이 우수합니다
downloadClient에서pdfDownloadRestClient로 변경하여 빈의 용도(PDF 다운로드)와 기술 스택(RestClient)을 명확히 표현했습니다. 다른 RestClient 빈들(clovaOcrRestClient,clovaStudioRestClient등)과 네이밍 컨벤션도 일관성 있게 통일되었습니다.
61-62: Clova Studio 클라이언트 네이밍 명확화서비스명(Clova Studio)과 기술 타입(RestClient)을 모두 포함하여 빈의 역할이 명확해졌습니다.
src/main/java/starlight/adapter/expertReport/webapi/ExpertReportController.java (3)
9-9: API 문서화 인터페이스 구현이 아키텍처를 개선합니다
ExpertReportApiDoc인터페이스를 통해 Swagger/OpenAPI 명세를 컨트롤러 구현과 분리한 것은 우수한 설계입니다. 이는 헥사고날 아키텍처의 어댑터 레이어에서 API 문서화 관심사를 명확히 분리하는 좋은 패턴입니다.Also applies to: 21-21
58-58: 반환 타입 명시로 타입 안정성이 향상되었습니다
ApiResponse<?>에서ApiResponse<ExpertReportResponse>로 구체화하여 컴파일 타임 타입 체크가 가능해지고, API 소비자에게 명확한 반환 타입 정보를 제공합니다.
62-67: 도메인 용어가 일관되게 정리되었습니다
details에서comments로의 변경이 변수명, 메서드 인자, 엔티티 타입 전반에 걸쳐 일관성 있게 적용되어 코드의 의미가 명확해졌습니다.src/test/java/starlight/adapter/aireport/infrastructure/ocr/infra/ClovaOcrClientTest.java (1)
1-1: 벤더 독립적인 패키지 구조로 개선되었습니다패키지 경로를
starlight.adapter.ncp.*에서starlight.adapter.aireport.infrastructure.ocr.*로 변경하여 벤더(NCP) 종속성을 제거하고 도메인 중심의 구조로 개선했습니다. 이는 헥사고날 아키텍처에서 인프라 어댑터의 교체 가능성을 높이는 우수한 리팩토링입니다.infrastructure레이어를 명시하여 계층 구조도 더욱 명확해졌습니다.Also applies to: 11-14
src/main/java/starlight/domain/expertReport/entity/ExpertReport.java (2)
52-54: 엔티티 관계 필드 변경이 적절합니다
details에서comments로 필드명을 변경하면서@OneToMany관계 설정(cascade = CascadeType.ALL,orphanRemoval = true)을 유지하여 자식 엔티티의 생명주기 관리가 적절하게 유지됩니다.ArrayList초기화로 NPE도 방지하고 있습니다.
123-129: 도메인 메서드 업데이트가 일관성 있게 처리되었습니다
updateComments메서드로 리네이밍하면서 Assert 메시지와 내부 로직(this.comments.clear(),this.comments.addAll())이 일관되게 변경되었습니다.validateCanEdit()호출로 비즈니스 규칙(제출 상태, 만료 여부)도 적절히 검증하고 있습니다.src/main/java/starlight/adapter/expertReport/webapi/mapper/ExpertReportMapper.java (1)
11-22: 매퍼가 새로운 도메인 타입을 올바르게 반영합니다
CreateExpertReportCommentRequest에서ExpertReportComment로 매핑하는 로직이 타입 안정성을 유지하면서 업데이트되었습니다. 헥사고날 아키텍처에서 어댑터 레이어(DTO)와 도메인 레이어(Entity) 간의 변환 책임을 명확히 수행하고 있습니다.src/main/java/starlight/domain/expertReport/entity/ExpertReportComment.java (2)
14-14: 도메인 엔티티 네이밍이 간결하고 명확해졌습니다
ExpertReportDetail에서ExpertReportComment로,commentType필드에서type으로 리네이밍하여 불필요한 중복을 제거하고 의미를 명확히 했습니다. 클래스명 자체가 Comment이므로 필드명에 comment를 중복 표기할 필요가 없습니다.Also applies to: 18-18
23-31: Factory 메서드가 일관성 있게 업데이트되었습니다
create메서드의 파라미터명과 Assert 메시지가 필드명 변경을 정확히 반영하고 있습니다. 파라미터명(type)과 필드명(type)이 일치하여 코드 가독성도 향상되었습니다.src/main/java/starlight/adapter/expertReport/persistence/ExpertReportRepository.java (1)
14-14: EntityGraph 설정이 정확하게 업데이트되었습니다
@EntityGraph(attributePaths = {"comments"})로 변경하여 필드명 변경을 반영했습니다.findByToken과findAllByBusinessPlanIdOrderByCreatedAtDesc메서드 모두에서comments컬렉션을 즉시 로딩하여 N+1 쿼리 문제를 방지하는 설정이 일관되게 적용되었습니다.Also applies to: 17-17
src/main/java/starlight/adapter/expertReport/webapi/dto/ExpertReportResponse.java (1)
20-31: LGTM! details → comments 리네이밍이 일관성 있게 적용되었습니다.
ExpertReportDetailResponse에서ExpertReportCommentResponse로의 변경과 필드명details→comments전환이 도메인 모델과 일관되게 적용되었습니다.fromEntities메서드에서 method reference를 사용한 매핑도 깔끔합니다.src/main/java/starlight/adapter/expertReport/persistence/ExpertReportJpa.java (3)
17-17: 헥사고날 아키텍처 포트 분리가 잘 적용되었습니다.
ExpertReportQueryPort와ExpertReportCommandPort를 분리하여 구현한 것은 CQRS 패턴과 헥사고날 아키텍처 원칙에 부합합니다. 읽기/쓰기 책임이 명확히 분리되어 향후 확장성과 테스트 용이성이 향상됩니다.
22-26: 메서드 네이밍 개선이 좋습니다.
getOrThrow→findByIdOrThrow로 변경하여 JPA 네이밍 컨벤션(findBy...)과 일관성을 유지했습니다.
44-48:findByTokenWithComments메서드명이 EntityGraph 변경과 일치합니다.메서드명이
findByTokenWithDetails에서findByTokenWithComments로 변경되어 도메인 모델 변경(details→comments)과 일관성을 유지합니다.src/main/java/starlight/adapter/expertReport/webapi/swagger/ExpertReportApiDoc.java (1)
22-23: API 문서화 인터페이스 분리가 좋습니다.Controller에서 Swagger 어노테이션을 분리하여 별도 인터페이스로 관리하는 패턴은 코드 가독성과 유지보수성을 높입니다.
src/main/java/starlight/application/expertReport/ExpertReportService.java (2)
42-43: Query/Command 포트 분리가 적절히 적용되었습니다.
ExpertReportQueryPort는 조회 작업에,ExpertReportCommandPort는 저장/삭제 작업에 사용하여 책임 분리가 명확합니다. 이는 헥사고날 아키텍처의 포트-어댑터 패턴을 잘 따르고 있습니다.
65-71:details→comments도메인 모델 변경이 일관되게 적용되었습니다.파라미터 타입
List<ExpertReportComment>와 메서드 호출updateComments가 도메인 모델 변경과 일치합니다.src/test/java/starlight/domain/expertReport/entity/ExpertReportTest.java (3)
107-116: 테스트 케이스가 도메인 모델 변경과 일치합니다.
updateDetails→updateComments,details→comments로의 변경이 테스트에도 일관되게 반영되었습니다. null 입력에 대한 예외 검증도 적절합니다.
118-133:updateComments성공 케이스 테스트가 잘 작성되었습니다.
ExpertReportComment.create()팩토리 메서드를 사용하여 테스트 데이터를 생성하고, 업데이트 후 사이즈 검증을 수행합니다.
150-174:ExpertReportComment엔티티 테스트가 적절히 추가되었습니다.생성 성공 케이스와 빈 content 예외 케이스가 모두 테스트되어 있습니다. accessor 메서드
getType(),getContent()검증도 포함되어 있습니다.src/main/java/starlight/adapter/aireport/infrastructure/ocr/ClovaOcrProvider.java (2)
1-12: 패키지 재구성이 헥사고날 아키텍처 원칙에 부합합니다.
starlight.adapter.ncp.*에서starlight.adapter.aireport.infrastructure.ocr.*로의 패키지 이동은 다음과 같은 장점이 있습니다:
- 기능 중심 패키지 구조:
ncp(기술)보다aireport(기능)를 상위 패키지로 배치- 포트 위치 개선:
OcrProvider가application.aireport.required로 이동하여 outbound port로서의 역할이 명확해짐- 인프라 계층 분리:
infra(클라이언트),util(유틸리티),exception(예외)으로 세분화
41-53: OCR 처리 로직이 잘 구조화되어 있습니다.PDF 다운로드 → 분할 → OCR 호출 → 병합의 흐름이 명확하고, 각 단계가 별도 컴포넌트로 분리되어 있어 테스트와 유지보수가 용이합니다.
src/main/java/starlight/application/order/provided/OrderPaymentServiceUseCase.java (1)
1-19: UseCase 네이밍 컨벤션 적용 확인헥사고날 아키텍처의 애플리케이션 계층에서 UseCase 접미사를 명시적으로 사용하는 것은 좋은 접근입니다. 인터페이스명이
OrderPaymentServiceUseCase로 변경되어 이 인터페이스가 애플리케이션의 유스케이스를 나타낸다는 의도가 명확해졌습니다.src/main/java/starlight/adapter/order/toss/TossClient.java (1)
51-74: 중요한 버그 수정 확인토스 환불(cancel) 메서드에서 발생하는 예외를
TOSS_CLIENT_CANCEL_ERROR로 수정한 것은 정확합니다. 이전에TOSS_CLIENT_CONFIRM_ERROR를 던지던 것은 에러 타입과 실제 동작이 불일치하는 버그였습니다. 수정으로 인해 에러 추적과 디버깅이 명확해졌습니다.src/main/java/starlight/application/order/required/OrderQueryPort.java (1)
1-15: CQRS 패턴 적용을 통한 관심사 분리
OrderQueryPort로 이름을 변경하고 쿼리 책임만 남긴 것은 훌륭한 아키텍처 개선입니다. Command와 Query를 분리하여:
- 읽기 작업만 담당하는 포트로 역할이 명확해짐
required패키지로 이동하여 애플리케이션 계층이 인프라에 요구하는 포트임을 명시OrderCommandPort와 함께 CQRS 패턴을 적용하여 단일 책임 원칙 준수헥사고날 아키텍처의 의도에 부합하는 변경입니다.
src/main/java/starlight/application/usage/required/UsageWalletCommandPort.java (1)
1-8: Usage 도메인의 CQRS 패턴 일관성
UsageWalletCommandPort를usage.required패키지에 추가하여 Usage 도메인에서도 Query/Command 분리 패턴을 일관성 있게 적용하고 있습니다. Order 모듈과 동일한 아키텍처 패턴을 따라 코드베이스의 구조적 일관성이 향상되었습니다.src/main/java/starlight/application/usage/required/UsageHistoryCommandPort.java (1)
1-8: 잘못된 네이밍 수정
save메서드를 포함하는 인터페이스를UsageHistoryQuery로 명명한 것은 잘못된 것이었습니다.UsageHistoryCommandPort로 변경하고required패키지로 이동한 것은 정확한 수정입니다:
- Query는 조회만, Command는 변경 작업을 담당
- 인터페이스명과 실제 동작의 일치
네이밍과 패키지 위치가 이제 헥사고날 아키텍처의 의도와 일치합니다.
src/main/java/starlight/application/order/required/OrderCommandPort.java (1)
1-8: Order 도메인의 CQRS 패턴 완성
OrderCommandPort추가로 Order 도메인의 Query/Command 분리가 완료되었습니다.OrderQueryPort에서 제거된save메서드가 이 포트로 이동하여 책임이 명확히 분리되었고, Usage 도메인과 동일한 아키텍처 패턴을 따라 전체 코드베이스의 일관성이 확보되었습니다.src/main/java/starlight/application/order/required/UsageCreditChargePort.java (1)
1-6: 이 포트 인터페이스는 헥사고날 아키텍처에서 order 모듈이 필요로 하는 아웃바운드 포트로서 적절하게 위치하고 있습니다.실제 코드 구조를 확인한 결과:
OrderPaymentService가UsageCreditChargePort를 의존성으로 주입받아 사용UsageCreditChargeService(usage 모듈)가 이 포트를 구현- order → usage 방향의 명확한 의존성 구조 형성
리뷰 코멘트에서 언급된 "starlight.application.usage.provided.UsageCreditPort"는 코드베이스에 존재하지 않으므로, 패키지 위치와 기존 포트와의 중복에 대한 우려사항은 해당 사항이 없습니다. 현재 설계는 올바릅니다.
Likely an incorrect or invalid review comment.
src/main/java/starlight/adapter/usage/persistence/UsageHistoryJpa.java (1)
1-18: 어댑터 구현체 업데이트 확인클래스명을
UsageHistoryJpa로 간소화하고UsageHistoryCommandPort를 구현하도록 업데이트한 것은 포트 패턴 적용에 부합합니다.UsageHistory 도메인은 설계상 저장(Command) 작업만 필요하며,
UsageHistoryQueryPort는 존재하지 않습니다. UsageWallet과 달리 조회 기능이 필요 없는 것은 각 도메인의 특성에 맞춘 의도된 설계입니다. 따라서 일관성 문제는 없습니다.Likely an incorrect or invalid review comment.
src/main/java/starlight/application/usage/required/UsageWalletQueryPort.java (1)
1-10: LGTM! CQRS 패턴에 맞게 Query 포트가 잘 분리되었습니다.
save()메서드를UsageWalletCommandPort로 분리하여 읽기 전용 인터페이스로 명확하게 정의되었습니다. 헥사고날 아키텍처의 포트 분리 원칙을 잘 따르고 있습니다.src/main/java/starlight/adapter/usage/persistence/UsageWalletJpa.java (1)
11-26: LGTM! 어댑터 구현이 깔끔합니다.
UsageWalletJpa가UsageWalletQueryPort와UsageWalletCommandPort를 모두 구현하여 인프라 레이어에서 두 포트를 통합 관리하는 패턴이 적절합니다.UsageHistoryJpa와 일관된 구조를 따르고 있습니다.src/main/java/starlight/adapter/order/webapi/OrderController.java (1)
23-28: LGTM! 컨트롤러가 UseCase 인터페이스를 의존하도록 개선되었습니다.
OrderPaymentServiceUseCase인터페이스를 주입받음으로써 헥사고날 아키텍처의 포트-어댑터 패턴을 잘 따르고 있습니다. Swagger 문서화도OrderApiDoc인터페이스로 분리하여 컨트롤러 코드가 깔끔해졌습니다.src/main/java/starlight/adapter/order/webapi/swagger/OrderApiDoc.java (2)
221-316:cancelPayment에@SecurityRequirement누락다른 엔드포인트들(
prepareOrder,confirmPayment,getMyPayments)은@SecurityRequirement(name = "Bearer Authentication")가 설정되어 있지만,cancelPayment에는 누락되어 있습니다. 이는OrderController에서AuthDetails가 없는 것과 일치하지만, 보안상 의도된 것인지 확인이 필요합니다.
28-29: LGTM! Swagger 문서를 인터페이스로 분리한 구조가 좋습니다.API 문서를 별도 인터페이스로 분리하여 컨트롤러 코드의 가독성을 높이고, 문서 관리를 용이하게 했습니다. 각 엔드포인트별 상세한 에러 응답 예시도 잘 정의되어 있습니다.
src/main/java/starlight/application/order/OrderPaymentService.java (2)
28-37: LGTM! 포트 기반 의존성 주입으로 잘 리팩터링되었습니다.
OrderQueryPort,OrderCommandPort,UsageCreditChargePort를 통해 헥사고날 아키텍처의 포트 분리 원칙을 잘 따르고 있습니다. 서비스 레이어가 인프라 구현체가 아닌 포트 인터페이스에 의존하도록 개선되었습니다.
55-66: 기존 주문의 변경사항은 JPA dirty checking을 통해 자동으로 저장됩니다.
findByOrderCode()에서 반환된Orders엔티티는 JPA 영속성 컨텍스트에서 관리되는 상태이며, 메서드 전체가 클래스 수준의@Transactional로 보호됩니다.addPaymentAttempt()호출로 인한 엔티티의 변경사항은 트랜잭션 커밋 시 JPA가 자동으로 감지하여 저장합니다. 새 주문의 명시적save()호출(line 65)과의 차이는 스타일의 문제일 뿐, 기존 주문은 표준적인 JPA 패턴으로 올바르게 처리되고 있습니다.Likely an incorrect or invalid review comment.
src/main/java/starlight/adapter/aireport/infrastructure/clova/infra/ClovaStudioClient.java
Outdated
Show resolved
Hide resolved
src/main/java/starlight/adapter/aireport/infrastructure/clova/util/ClovaUtil.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
도메인모델.md (1)
1-451: 포트 인터페이스 분리 구조 문서화 누락PR 목표와 AI Summary에서 "ExpertReportQueryPort 및 ExpertReportCommandPort로 포트 분리" 및 "UsageCreditChargePort 도입"을 언급하셨으나, 현재 도메인 모델 문서에는 구체적인 포트 정의가 미반영되어 있습니다.
제안 사항:
- ExpertReport 섹션에 포트 구조 추가 (예: ExpertReportQueryPort, ExpertReportCommandPort)
- Usage 섹션에 UsageCreditChargePort 명시
- 각 포트의 책임(CQRS 방향성) 문서화
헥사고날 아키텍처의 port-adapter 경계를 명확히 하면, 백엔드 팀의 도메인 이해와 구현 일관성이 향상될 것입니다.
포트 분리가 실제 코드에 적용되었다면, 도메인 모델 문서를 업데이트하여 포트 인터페이스 구조를 명시해주시기 바랍니다. 예를 들어:
### 전문가 리포트 조회 포트(ExpertReportQueryPort) _Port (Driven - Secondary)_ #### 책임 - ExpertReport 조회 및 로드 - EntityGraph를 통한 comments 포함 로드 ### 전문가 리포트 저장 포트(ExpertReportCommandPort) _Port (Driven - Secondary)_ #### 책임 - ExpertReport 저장, 상태 업데이트 - comments 목록 동기화
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProvider.java도메인모델.md용어사전.md
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/starlight/adapter/aireport/infrastructure/storage/NcpPresignedUrlProvider.java
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
도메인모델.md
301-301: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
410-410: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
422-422: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ 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 (5)
용어사전.md (3)
17-18: ExpertReportComment 용어 반영이 명확하고 일관됨.도메인 언어의 변화(Detail → Comment, details → comments)가 용어사전에 정확하게 반영되었습니다. Line 17의 ExpertReport 설명과 Line 18의 새로운 ExpertReportComment 엔트리가 서로 일관성 있게 정의되어 있어 개발자들이 도메인 모델의 의도를 명확하게 이해할 수 있겠습니다.
22-23: UsageWallet, UsageHistory 도입으로 개념 분리 명확화.기존 UsageCredit 개념을 **지갑(잔여 수량 관리)**과 **이력(거래 기록)**으로 명확하게 분리한 설계가 용어사전에 잘 반영되어 있습니다. PR 목표의 "Usage 도메인 재조직 및 UsageCreditChargePort 도입"과 일관되게 정의되어 있어, 개발자와 비즈니스 이해관계자 간의 커뮤니케이션이 명확할 것으로 예상됩니다.
26-26: Review comment is not applicable—no changes needed.용어사전 Line 26의 문서는 정확합니다.
CommentType은 전문가 리포트 코멘트의 타입을 나타내는 enum 클래스명이며, 실제 엔티티의 필드명은type입니다 (ExpertReportComment.java Line 18:private CommentType type;). 문서는 enum 타입명을 올바르게 표기하고 있으므로 별도의 수정이 불필요합니다.Likely an incorrect or invalid review comment.
도메인모델.md (2)
278-289: ExpertReport의 comments 필드 및 메서드명 변경 명확함
details→comments,updateDetails()→updateComments()네이밍 변경이 일관되게 반영되었습니다. 도메인 언어로서 "리포트 코멘트"가 "상세사항"보다 의도를 명확하게 전달하므로 개선된 변경입니다.
300-309: ExpertReportComment 엔티티의 필드명 변경이 완벽하게 구현되어 있습니다검증 결과, 다음 항목들이 모두 올바르게 적용되었음을 확인했습니다:
- JPA 엔티티의
type필드가@Enumerated(EnumType.STRING)과@Column(nullable = false, length = 30)으로 올바르게 매핑됨- Repository의 EntityGraph 경로가
{"comments"}로 정확하게 업데이트됨 (findByToken, findAllByBusinessPlanIdOrderByCreatedAtDesc)- 어댑터 레이어의 DTO/매퍼에서
type필드명이 일관되게 반영됨 (CreateExpertReportCommentRequest, ExpertReportCommentResponse, ExpertReportMapper)기존의
commentType이나ExpertReportDetail참조는 남아있지 않습니다. 도메인 모델 문서의 내용과 실제 구현이 일치합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# UsageWallet/UsageHistory 엔티티 파일 찾기
find . -type f \( -name "*UsageWallet*" -o -name "*UsageHistory*" \) 2>/dev/null | head -20Repository: StartUpLight/STARLIGHT_BE
Length of output: 741
🏁 Script executed:
# 도메인 모델 문서 위치 확인
fd -type f -name "도메인모델.md" -o -name "*domain*model*.md" 2>/dev/nullRepository: StartUpLight/STARLIGHT_BE
Length of output: 51
🏁 Script executed:
# UsageCreditChargePort 관련 파일 검색
rg "UsageCreditChargePort|Usage.*Port" --type java --type md 2>/dev/null | head -30Repository: StartUpLight/STARLIGHT_BE
Length of output: 2774
🏁 Script executed:
# Order 및 Payment 관련 구조 확인
rg "class Order|markPaid|chargeAiReport" --type java --type md 2>/dev/null | head -30Repository: StartUpLight/STARLIGHT_BE
Length of output: 1256
🏁 Script executed:
cat -n ./src/main/java/starlight/domain/order/wallet/UsageWallet.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 2492
🏁 Script executed:
cat -n ./src/main/java/starlight/domain/order/wallet/UsageHistory.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 3313
🏁 Script executed:
cat -n ./src/main/java/starlight/adapter/usage/persistence/UsageHistoryJpa.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 734
🏁 Script executed:
head -60 ./src/main/java/starlight/application/order/OrderPaymentService.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 2456
🏁 Script executed:
cat -n ./src/main/java/starlight/application/usage/UsageCreditChargeService.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 2411
🏁 Script executed:
tail -n +40 ./src/main/java/starlight/application/order/OrderPaymentService.java | head -60Repository: StartUpLight/STARLIGHT_BE
Length of output: 2223
🏁 Script executed:
cat -n ./src/main/java/starlight/adapter/usage/persistence/UsageHistoryRepository.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 363
🏁 Script executed:
cat -n ./src/main/java/starlight/adapter/usage/persistence/UsageWalletRepository.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 470
🏁 Script executed:
# 도메인 모델 문서 해당 섹션 확인
cat -n 도메인모델.md | grep -A 50 "사용 지갑"Repository: StartUpLight/STARLIGHT_BE
Length of output: 1125
🏁 Script executed:
# OrderPaymentService.confirm() 전체 메서드 (markPaid 호출 부분 확인)
sed -n '50,120p' ./src/main/java/starlight/application/order/OrderPaymentService.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 2508
🏁 Script executed:
# 데이터베이스 마이그레이션/스키마 파일 찾기
find . -type f \( -name "*.sql" -o -name "*migration*" -o -name "*schema*" \) 2>/dev/null | grep -i usage | head -20Repository: StartUpLight/STARLIGHT_BE
Length of output: 51
🏁 Script executed:
# Flyway 마이그레이션 파일 확인
find . -type f -path "*/db/migration/*" 2>/dev/null | grep -i usageRepository: StartUpLight/STARLIGHT_BE
Length of output: 51
🏁 Script executed:
# Orders 엔티티의 markPaid() 메서드 확인
grep -A 10 "public void markPaid" ./src/main/java/starlight/domain/order/order/Orders.javaRepository: StartUpLight/STARLIGHT_BE
Length of output: 418
UsageWallet/UsageHistory 필드명 및 문서 정확성 개선 필요
도메인 모델 문서의 필드명이 실제 구현과 불일치합니다. 다음 항목을 수정해주세요:
UsageWallet 필드 정정:
aiReportRemainingCount는 저장 필드가 아닌 계산 속성입니다. 문서에서 실제 필드aiReportChargedCount,aiReportUsedCount를 명시하고, 계산 속성getAiReportRemainingCount()를 별도로 표기하세요.
UsageHistory 필드 및 메서드 정정:
- 문서의
changedCount→ 실제:amount - 문서의
remainingCount→ 실제:balanceAfter - 누락된 팩토리 메서드:
static used(Long userId, Long businessPlanId, int amount, int balanceAfter)추가 orderId필드가 NULL 가능함을 명시 (충전 시 주문 없는 초기 크레딧 시나리오 대비)
조회 성능 고려:
UsageHistory조회 (userId + createdAt, orderId 기반)에 대한 인덱스 정의가 현재 없습니다. 데이터 증가 시 성능 이슈가 발생할 수 있으므로, 데이터베이스 스키마에 복합 인덱스 추가를 검토하세요.
포트 구조 문서화:
- 이미 구현된
UsageCreditChargePort(Command)와UsageWalletQueryPort,UsageWalletCommandPort(Query/Command)를 포트 정의 섹션에 추가하여 헥사고날 아키텍처 구조를 명확히 하세요.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
410-410: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
422-422: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🤖 Prompt for AI Agents
In 도메인모델.md around lines 409 to 431, update the UsageWallet and UsageHistory
sections to match implementation: replace UsageWallet's stored field
aiReportRemainingCount with explicit stored fields aiReportChargedCount and
aiReportUsedCount and document a computed accessor getAiReportRemainingCount();
rename UsageHistory fields changedCount→amount and remainingCount→balanceAfter,
mark orderId as nullable, and add the missing factory method signature static
used(Long userId, Long businessPlanId, int amount, int balanceAfter); also add a
note recommending adding composite DB indexes for UsageHistory queries (userId +
createdAt and orderId) for performance, and list the implemented ports
UsageCreditChargePort (Command), UsageWalletQueryPort and UsageWalletCommandPort
in the ports section to reflect the hexagonal structure.
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 - 해결하려는 문제가 무엇인가요?
adapter/ncp처럼 도메인 관점(airReport)과 어긋난 패키지 위치/명명이 있어 구조를 정리할 필요가 있었습니다.Detail등 일부 용어가 실제 의미(코멘트/피드백)와 달라 도메인 언어를 일치시키고자 했습니다.✅ What - 무엇이 변경됐나요?
adapter/ncp관련 코드를adapter/aireport하위로 이동하고 패키지/설정 클래스 네이밍을 정리했습니다.clovaStudioRestClient,pdfDownloadRestClient)ExpertReportDetail→ExpertReportComment로 용어/클래스 명칭을 정리하고 관련 DTO/Mapper/Repository/EntityGraph를 함께 수정했습니다.UsageCreditChargePort를 도입하고 서비스/포트 네이밍을 정리했습니다.🛠️ How - 어떻게 해결했나요?
🖼️ Attachment
💬 기타 코멘트
./gradlew testGET /v1/experts(categories 파라미터 제거)Summary by CodeRabbit
New Features
Bug Fixes
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.