[PIPELINE] Vision + Pipeline 통합#27
Conversation
…LINE # Conflicts: # src/main/java/io/github/petty/pipeline/controller/PipelineController.java
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary by CodeRabbit
Walkthrough이번 변경에서는 기존의 파이프라인 컨트롤러( Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UnifiedFlowController
participant VisionUseCase
participant VisionServiceImpl
participant TogetherPromptBuilder
participant RecommendService
User->>UnifiedFlowController: POST /flow/analyze (file, petName, location)
UnifiedFlowController->>VisionUseCase: interim(file.bytes, petName)
VisionUseCase-->>UnifiedFlowController: interimResult
UnifiedFlowController->>VisionServiceImpl: analyze(file, petName)
VisionServiceImpl-->>UnifiedFlowController: visionReport
UnifiedFlowController->>TogetherPromptBuilder: buildPrompt(visionReport, location)
TogetherPromptBuilder-->>UnifiedFlowController: promptJson
UnifiedFlowController->>RecommendService: recommend(promptMap)
RecommendService-->>UnifiedFlowController: recommendation
UnifiedFlowController-->>User: unifiedFlow.html (interimResult, visionReport, recommendation)
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (9)
src/main/java/io/github/petty/pipeline/service/PromptGeneratorServiceImpl.java (2)
25-38: JSON 처리 로직 개선기존 문자열 포맷팅 방식에서 JSON 객체로 변환 후 처리하는 방식으로 개선된 것은 더 안전하고 유지보수 가능한 접근법입니다. 로깅을 추가한 것도 디버깅에 도움이 됩니다.
하지만 petInfoString을 생성하는 방식에서 중괄호를 감싸는 부분이 불필요할 수 있습니다. extractedPetInfoJson이 이미 JSON 형식이라면, 이를 다시 중괄호로 감싸지 않고 직접 매핑하는 것이 더 간결할 수 있습니다.
다음과 같이 로직을 간소화하는 것을 고려해보세요:
- // 기본 문자열 - String petInfoString = """ - { - %s - } - """.formatted(extractedPetInfoJson); - // JSON 으로 변환 - Map<String, Object> petInfoMap = mapper.readValue(petInfoString, new TypeReference<>() {}); + // JSON 으로 직접 변환 + Map<String, Object> petInfoMap = mapper.readValue(extractedPetInfoJson, new TypeReference<>() {});
41-46: 주석 처리된 코드 제거 권장더 이상 사용하지 않는 코드가 주석 처리되어 있습니다. 코드 가독성과 유지보수를 위해 이러한 주석 처리된 코드는 제거하는 것이 좋습니다.
-// return String.format(""" -// { -// %s, -// "location": "%s" -// } -// """, extractedPetInfoJson, location);src/main/resources/templates/unifiedFlow.html (2)
1-19: HTML 구조와 스타일링 적절함HTML 문서의 기본 구조와 스타일링이 잘 정의되어 있습니다. 반응형 디자인 요소가 부족하지만, 기본적인 사용성은 확보되어 있습니다.
모바일 환경에서의 사용성을 향상시키기 위해 viewport 메타 태그를 추가하는 것을 고려해보세요:
<meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>통합 여행 추천 파이프라인</title>
58-159: 지역 드롭다운 기능 개선 필요지역 드롭다운 기능은 잘 구현되어 있지만, 사용자 경험을 더 향상시킬 수 있는 몇 가지 개선 사항이 있습니다:
- 키보드 접근성(접근성 개선)
- 로딩 상태 표시(UX 개선)
- 드롭다운 항목 선택 시 Enter 키 지원
키보드 접근성과 사용성을 향상시키기 위해 다음과 같은 코드를 추가할 수 있습니다:
locationInput.addEventListener('input', function () { // 기존 코드 유지 }); +// 키보드 접근성 추가 +locationInput.addEventListener('keydown', function(e) { + if (e.key === 'ArrowDown' && dropdownList.style.display === 'block') { + // 첫 번째 항목으로 포커스 이동 + const firstItem = dropdownList.querySelector('li'); + if (firstItem) firstItem.focus(); + e.preventDefault(); + } +}); + +// 드롭다운 항목에 키보드 이벤트 추가 +dropdownList.addEventListener('keydown', function(e) { + if (e.key === 'Enter') { + const focusedItem = document.activeElement; + if (focusedItem && focusedItem.tagName === 'LI') { + locationInput.value = focusedItem.textContent; + dropdownList.style.display = 'none'; + e.preventDefault(); + } + } +});또한 드롭다운 항목의 접근성을 향상시키기 위해 각 li 항목에 tabindex를 추가하는 것이 좋습니다:
filtered.forEach(region => { const li = document.createElement('li'); li.textContent = region; + li.setAttribute('tabindex', '0'); li.addEventListener('click', () => { locationInput.value = region; dropdownList.style.display = 'none'; }); dropdownList.appendChild(li); });src/main/java/io/github/petty/pipeline/support/TogetherPromptBuilder.java (1)
25-32: 프롬프트 구조 개선프롬프트 구조가 개선되어 고양이는 항상 맹수 여부를 false로 반환하도록 명시적으로 지시하고 있습니다. 이는 PR 설명에서 언급된 버그(고양이가 맹수로 잘못 분류되는 문제)를 해결하는 데 도움이 됩니다.
하지만 프롬프트 문자열 구성이 여러 개의 문자열 연결로 복잡하게 구성되어 있습니다. 가독성과 유지보수를 위해 텍스트 블록(Text block)을 사용하는 것이 더 좋을 것 같습니다.
다음과 같이 텍스트 블록을 사용하여 프롬프트를 더 가독성 있게 구성할 수 있습니다:
- String extractedPetInfoJson = togetherService.answer( - visionReport + " -> 이 문장에서 반려동물의 이름(name), 종(species), 무게(weight), 맹수 여부(is_danger(only true or false))를 JSON 형식으로 작성 + " + - "만약 반려동물의 종과 무게를 보았을 때, 입마개가 필요할 것 같다면 맹수 여부를 'true'로 작성 + " + "고양이는 맹수 여부를 false로 작성 " + - "무게는 kg 단위를 반드시 포함 " + "no markdown " + "-> 양식에 맞춰서 작성 " + """ - "name": "???", - "species": "???", - "weight": "???", - "is_danger": "???" - """ + " -> 부가 설명이나 그 어떠한 텍스트 없이 양식의 빈 항목을 채워서 답변할 것." + String extractedPetInfoJson = togetherService.answer( + """ + %s + + -> 이 문장에서 반려동물의 이름(name), 종(species), 무게(weight), 맹수 여부(is_danger(only true or false))를 JSON 형식으로 작성하세요. + 만약 반려동물의 종과 무게를 보았을 때, 입마개가 필요할 것 같다면 맹수 여부를 'true'로 작성하세요. + 고양이는 맹수 여부를 반드시 false로 작성하세요. + 무게는 kg 단위를 반드시 포함하세요. + no markdown + + -> 양식에 맞춰서 작성하세요: + "name": "???", + "species": "???", + "weight": "???", + "is_danger": "???" + + -> 부가 설명이나 그 어떠한 텍스트 없이 양식의 빈 항목을 채워서 답변할 것. + """.formatted(visionReport)src/main/java/io/github/petty/pipeline/controller/PipelineController.java (2)
36-42: 파라미터 주석 업데이트가 필요합니다.기존 주석은 더 이상 사용하지 않는 파라미터를 설명하고 있습니다. 새로운 파라미터에 대한 명확한 주석이 필요합니다. 또한 불필요한 공백이 있어 처리 흐름을 파악하기 어렵게 만듭니다.
- // form 으로 받은 임시 Vision 보고서 -// @RequestParam("visionReport") String visionReport, // form 으로 받은 사용자 위치 @RequestParam("location") String location, - // 실제 VisionServiceImpl 에서 사용하는 요소 + // 반려동물 이미지 파일 @RequestParam("file") MultipartFile file, + // 반려동물 이름 @RequestParam("petName") String pet, Model model -
46-51: 중복 로깅 및 일관성 문제.jsonPrompt 변수를 로깅한 후 다시 promptMapper.toString()을 로깅하고 있습니다. 또한 visionReport 생성 후 로깅이 누락되어 있어 UnifiedFlowController와 로깅 일관성이 없습니다.
-// String prompt = togetherPromptBuilder.buildPrompt(visionReport, location); String visionReport = visionService.analyze(file, pet); + log.info("📄 Vision Report: {}", visionReport); + log.info("📌 location = {}", location); String jsonPrompt = togetherPromptBuilder.buildPrompt(visionReport, location); - log.info(jsonPrompt);src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java (2)
52-53: 중복 로깅이 있습니다.location 값을 48번 줄과 52번 줄에서 중복해서 로깅하고 있습니다. 불필요한 중복은 제거해야 합니다.
String jsonPrompt = togetherPromptBuilder.buildPrompt(visionReport, location); - log.info("📌 location = {}", location); Map<String, String> promptMapper = new ObjectMapper().readValue(jsonPrompt, new TypeReference<>() {});
62-64: 오류 메시지 개선.오류 메시지가 너무 일반적입니다. 사용자에게 더 구체적인 오류 정보를 제공하는 것이 도움이 됩니다. 또한 오류 유형에 따라 다른 메시지를 표시하는 것도 고려해 볼 수 있습니다.
} catch (Exception e) { log.error("❌ 분석 중 오류 발생", e); - model.addAttribute("error", "분석 및 추천 중 오류가 발생했습니다."); + String errorMessage = "분석 및 추천 중 오류가 발생했습니다: "; + if (e instanceof IOException) { + errorMessage += "파일 처리 중 오류가 발생했습니다."; + } else if (e instanceof IllegalArgumentException) { + errorMessage += e.getMessage(); + } else { + errorMessage += "서버 내부 오류가 발생했습니다."; + } + model.addAttribute("error", errorMessage);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/io/github/petty/pipeline/controller/PipelineController.java(3 hunks)src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java(1 hunks)src/main/java/io/github/petty/pipeline/service/PromptGeneratorService.java(1 hunks)src/main/java/io/github/petty/pipeline/service/PromptGeneratorServiceImpl.java(1 hunks)src/main/java/io/github/petty/pipeline/support/TogetherPromptBuilder.java(2 hunks)src/main/resources/templates/unifiedFlow.html(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java (1)
src/main/java/io/github/petty/llm/service/VectorStoreService.java (1)
lombok(18-74)
🔇 Additional comments (11)
src/main/java/io/github/petty/pipeline/service/PromptGeneratorService.java (2)
3-4: JsonProcessingException 추가 적절히 반영됨JsonProcessingException을 추가하여 JSON 처리 관련 예외를 명시적으로 다루도록 한 것은 좋은 접근입니다. 이는 구현 클래스에서 JSON 파싱 오류를 적절히 처리할 수 있게 해줍니다.
6-6: 메서드 시그니처 업데이트 적절함generatePrompt 메서드가 JsonProcessingException을 던지도록 수정한 것은 PromptGeneratorServiceImpl 클래스의 변경사항과 일치하며, 예외 처리를 더 명확하게 합니다.
src/main/java/io/github/petty/pipeline/service/PromptGeneratorServiceImpl.java (3)
3-10: 적절한 임포트 추가Jackson 라이브러리와 관련된 클래스들과 로깅을 위한 임포트가 적절히 추가되었습니다. TypeReference를 사용하여 Map 구조로 변환하는 접근 방식이 타입 안전성을 높여줍니다.
12-12: 로깅 기능 추가 적절함@log 어노테이션을 추가하여 로깅 기능을 구현한 것은 디버깅과 모니터링에 도움을 줄 수 있습니다.
18-18: 예외 처리 시그니처 업데이트 적절함generatePrompt 메서드가 JsonProcessingException을 던지도록 수정한 것은 인터페이스의 변경사항과 일치하며, 예외 처리를 더 명확하게 합니다.
src/main/resources/templates/unifiedFlow.html (2)
22-36: 폼 구조 적절함폼의 구조가 잘 정의되어 있으며, 필요한 입력 필드와 유효성 검사가 포함되어 있습니다. 멀티파트 인코딩 타입이 올바르게 설정되어 파일 업로드를 지원합니다.
38-56: 결과 표시 블록 구조 적절함중간 분석 결과, 최종 Vision 분석 보고서, 추천 여행지, 오류 등 다양한 결과를 조건부로 표시하는 구조가 잘 구현되어 있습니다.
src/main/java/io/github/petty/pipeline/support/TogetherPromptBuilder.java (1)
7-11: 로깅 기능 추가 적절함@log 어노테이션을 추가하여 로깅 기능을 구현한 것은 디버깅과 모니터링에 도움을 줄 수 있는 좋은 개선사항입니다.
src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java (3)
1-22: 새로운 컨트롤러 구현이 잘 되었습니다.통합 흐름을 위한 새 컨트롤러가 적절하게 구현되었습니다. @RequestMapping("/flow")을 사용하여 기존 컨트롤러와의 충돌을 방지한 것은 좋은 접근 방식입니다.
34-40: 메서드 이름과 URL이 직관적입니다.analyze 메서드와 /analyze URL이 직관적이고 목적에 맞게 잘 명명되었습니다. 파라미터도 명확하게 정의되어 있습니다.
56-60: 뷰 모델 속성 설정이 잘 구현되었습니다.분석 결과를 단계별로 모델에 추가하여 사용자가 전체 과정을 확인할 수 있도록 한 것은 좋은 접근 방식입니다. 특히 중간 결과(interim)를 포함한 것은 사용자 경험 측면에서 유용합니다.
| @Log | ||
| public class PromptGeneratorServiceImpl implements PromptGeneratorService { | ||
|
|
||
| ObjectMapper mapper = new ObjectMapper(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
ObjectMapper 인스턴스화 방식 개선 필요
ObjectMapper 인스턴스를 클래스 레벨에서 생성하는 것은 좋지만, final 키워드가 누락되어 있습니다. 또한 스프링의 의존성 주입을 사용하는 것이 권장됩니다.
- ObjectMapper mapper = new ObjectMapper();
+ private final ObjectMapper mapper = new ObjectMapper();또는 더 나은 방법:
- ObjectMapper mapper = new ObjectMapper();
+ @Autowired
+ private ObjectMapper mapper;그리고 Configuration 클래스에 Bean을 등록:
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}| public String buildPrompt(String visionReport, String location) throws Exception { | ||
| try { | ||
| if (visionReport.isEmpty()) { | ||
| log.info("Vision API 에서 필수 정보를 받아오지 못했습니다."); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
로그 레벨 조정 필요
Vision API에서 필수 정보를 받아오지 못한 상황은 일반 정보(info)보다는 경고(warning) 또는 오류(severe) 수준의 로그로 기록하는 것이 더 적절합니다.
- log.info("Vision API 에서 필수 정보를 받아오지 못했습니다.");
+ log.warning("Vision API 에서 필수 정보를 받아오지 못했습니다.");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| log.info("Vision API 에서 필수 정보를 받아오지 못했습니다."); | |
| log.warning("Vision API 에서 필수 정보를 받아오지 못했습니다."); |
| return promptGeneratorService.generatePrompt(extractedPetInfoJson, location); | ||
|
|
||
| } catch (RuntimeException e) { | ||
| log.info("TogetherPromptBuilder 내부 오류."); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
로그 레벨 조정 필요
내부 오류 상황에서도 로그 레벨을 info가 아닌 severe나 warning으로 설정하는 것이 더 적절합니다. 또한 예외 정보를 포함하면 디버깅에 더 도움이 됩니다.
- log.info("TogetherPromptBuilder 내부 오류.");
+ log.severe("TogetherPromptBuilder 내부 오류: " + e.getMessage());|
|
||
| import io.github.petty.pipeline.service.PromptGeneratorService; | ||
| import io.github.petty.pipeline.service.TogetherService; | ||
| import io.grpc.lb.v1.LoadBalancerGrpc; |
There was a problem hiding this comment.
사용되지 않는 임포트 제거 필요
io.grpc.lb.v1.LoadBalancerGrpc 임포트가 추가되었지만 코드에서 사용되지 않고 있습니다. 코드 가독성과 유지보수를 위해 사용되지 않는 임포트는 제거해야 합니다.
-import io.grpc.lb.v1.LoadBalancerGrpc;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import io.grpc.lb.v1.LoadBalancerGrpc; |
|
|
||
| private final TogetherPromptBuilder togetherPromptBuilder; | ||
| private final RecommendService recommendService; | ||
| private final VisionServiceImpl visionService; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
인터페이스 대신 구현 클래스에 직접 의존하고 있습니다.
VisionServiceImpl에 직접 의존하기보다는 인터페이스(VisionUseCase)에 의존하는 것이 좋은 설계 방식입니다. 이렇게 하면 의존성 역전 원칙(DIP)을 준수하며 유지보수성이 향상됩니다.
- private final VisionServiceImpl visionService;
+ private final VisionUseCase visionService;| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import io.github.petty.llm.service.RecommendService; | ||
| import io.github.petty.pipeline.support.TogetherPromptBuilder; | ||
| import io.github.petty.vision.service.VisionServiceImpl; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
불필요하게 구현 클래스를 직접 주입받고 있습니다.
VisionServiceImpl 대신 인터페이스(예: VisionService)를 주입받는 것이 더 좋은 방식입니다. 이렇게 하면 구현체를 쉽게 교체할 수 있고 단위 테스트시 모킹이 용이해집니다.
-import io.github.petty.vision.service.VisionServiceImpl;
+import io.github.petty.vision.port.in.VisionUseCase;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import io.github.petty.vision.service.VisionServiceImpl; | |
| -import io.github.petty.vision.service.VisionServiceImpl; | |
| +import io.github.petty.vision.port.in.VisionUseCase; |
| private final VisionUseCase visionUseCase; | ||
| private final VisionServiceImpl visionService; | ||
| private final TogetherPromptBuilder togetherPromptBuilder; | ||
| private final RecommendService recommendService; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
인터페이스와 구현체를 함께 주입받고 있습니다.
VisionUseCase 인터페이스와 VisionServiceImpl 구현체를 동시에 주입받고 있는데, 이는 의존성 주입 원칙에 맞지 않습니다. VisionServiceImpl이 VisionUseCase를 구현한다면 둘 중 하나만 주입받아야 합니다.
private final VisionUseCase visionUseCase;
- private final VisionServiceImpl visionService;
private final TogetherPromptBuilder togetherPromptBuilder;
private final RecommendService recommendService;이후 analyze 메서드에서도 visionService 대신 visionUseCase를 사용하도록 수정해야 합니다:
// 2. Vision 보고서 생성
- String visionReport = visionService.analyze(file, petName);
+ String visionReport = visionUseCase.analyze(file, petName);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private final VisionUseCase visionUseCase; | |
| private final VisionServiceImpl visionService; | |
| private final TogetherPromptBuilder togetherPromptBuilder; | |
| private final RecommendService recommendService; | |
| // In UnifiedFlowController.java, remove the implementation field: | |
| private final VisionUseCase visionUseCase; | |
| private final TogetherPromptBuilder togetherPromptBuilder; | |
| private final RecommendService recommendService; | |
| // …later in your analyze(...) method, switch to using the interface: | |
| // 2. Vision 보고서 생성 | |
| String visionReport = visionUseCase.analyze(file, petName); |
| // 1. 중간 종 추론 결과 | ||
| String interim = visionUseCase.interim(file.getBytes(), petName); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
예외 처리 추가 필요.
visionUseCase.interim() 호출 시 발생할 수 있는 IOException에 대한 예외 처리가 필요합니다. file.getBytes()에서 IOException이 발생할 수 있습니다.
try {
// 1. 중간 종 추론 결과
- String interim = visionUseCase.interim(file.getBytes(), petName);
+ byte[] fileBytes;
+ try {
+ fileBytes = file.getBytes();
+ } catch (IOException e) {
+ log.error("❌ 파일 읽기 실패", e);
+ throw new RuntimeException("파일을 읽는 중 오류가 발생했습니다.", e);
+ }
+ String interim = visionUseCase.interim(fileBytes, petName);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 1. 중간 종 추론 결과 | |
| String interim = visionUseCase.interim(file.getBytes(), petName); | |
| try { | |
| // 1. 중간 종 추론 결과 | |
| byte[] fileBytes; | |
| try { | |
| fileBytes = file.getBytes(); | |
| } catch (IOException e) { | |
| log.error("❌ 파일 읽기 실패", e); | |
| throw new RuntimeException("파일을 읽는 중 오류가 발생했습니다.", e); | |
| } | |
| String interim = visionUseCase.interim(fileBytes, petName); |
📜 PR 내용 요약
⚒️ 작업 및 변경 내용
📚 기타 참고 사항
UnifiedFlowController입니다. (경로 : pipeline/controller)unifiedFlow.html입니다.