[VISION] 반려동물 이미지 분석/보고서/추천 플로우 개선 및 폰트 적용#40
Conversation
Summary by CodeRabbit
Walkthrough세션 기반 데이터 흐름과 예외 처리가 대폭 개선되었습니다. 주요 컨트롤러에서 플래시 속성 대신 세션을 일관적으로 사용하도록 변경되었으며, 이미지와 분석 결과, 추천 결과 등이 세션에 저장됩니다. 템플릿과 자바스크립트도 사용자 경험과 에러 처리를 강화하는 방향으로 리팩터링되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser
participant VisionController
participant UnifiedFlowController
participant Session
User->>Browser: 이미지 및 이름 입력 후 업로드
Browser->>VisionController: /vision/species (파일, 이름, 세션)
VisionController->>Session: 이미지, 이름, Base64 저장
VisionController->>Browser: 중간 결과 반환
Browser->>VisionController: /vision/analyze (파일, 이름, 세션)
VisionController->>Session: 분석 결과 저장
VisionController->>Browser: 분석 완료 응답
Browser->>UnifiedFlowController: /analyze/report (세션)
UnifiedFlowController->>Session: 분석 결과, 이미지, 이름 조회
UnifiedFlowController->>Browser: 리포트 페이지 렌더링
User->>Browser: 추천 요청
Browser->>UnifiedFlowController: /recommend (세션)
UnifiedFlowController->>Session: 추천 결과 저장
UnifiedFlowController->>Browser: 추천 결과 반환 및 세션 정리
Possibly related PRs
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. 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 (
|
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java (1)
27-28: VisionController와 동일한 아키텍처 문제가 있습니다.이 컨트롤러도 추상화와 구현체를 동시에 의존하고 있습니다. VisionController에 대한 리뷰 코멘트와 동일한 리팩토링이 필요합니다.
🧹 Nitpick comments (9)
src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java (1)
183-186: 세션 정리가 불완전합니다.추천 결과 표시 후 세션에서
recommendationResult와visionReport만 제거하고 있습니다. 메모리 효율성을 위해petImageBase64와petName도 함께 제거해야 합니다.// 사용 후 세션에서 제거 (선택 사항) session.removeAttribute("recommendationResult"); session.removeAttribute("visionReport"); + session.removeAttribute("petImageBase64"); + session.removeAttribute("petName");src/main/resources/templates/visionUpload.html (8)
6-9: CDN 리소스에 SRI 및crossorigin속성 추가 고려
Bootstrap CDN 사용 시 보안 강화를 위해 Subresource Integrity(integrity)와crossorigin="anonymous"속성 추가를 권장합니다.
65-111: 프리포맷 텍스트 및 폼 컨트롤 스타일 개선 제안
<pre>요소에 최대 높이 및 스크롤 처리가 잘 되어 있습니다..btn-primary,.form-label,.form-control등 폼 요소 스타일도 직관적입니다.- 다만, 버튼 포커스(focus) 상태에 대한 별도 스타일 추가나, 시각장애인을 위한 대비(contrast) 검사 등을 고려해 보세요.
140-160: 애니메이션 및 성공 메시지 스타일 권장사항
로딩 펄스와 키프레임은 적절하게 정의되었습니다.
.success-message역시 잘 꾸며져 있지만, 브라우저 호환성 확보를 위해 벤더 프리픽스(-webkit-,-moz-)를 추가 검토해 보세요.
165-173: 오프라인 알림에 ARIA 속성 추가 제안
#offlineAlert는 사용자에게 중요한 상태 정보를 제공합니다.role="alert"또는aria-live="assertive"를 추가해 보세요.
243-266: 온라인 상태 감지 로직 보완 제안
navigator.onLine은 일부 브라우저에서 완전 신뢰하기 어려울 수 있습니다. 네트워크 요청 타임아웃 체크를 추가하는 방법을 검토해 보세요.
268-299:alert()대신 인라인 피드백 UI 사용 고려
파일 타입/크기 검증 시alert()를 호출하는 대신, 페이지 내#errorMessage영역을 재활용해 사용자 경험을 일관되게 유지하는 것이 좋습니다.
302-335: 토큰 갱신 시 동시성 처리 주의
여러 요청이 동시에 401을 받을 경우 중복 갱신 요청이 발생할 수 있습니다. 갱신 플래그를 설정하거나 큐(queue)로 관리하는 패턴을 도입하는 것을 권장합니다.
337-389: 첫 번째 분석 단계 상태 관리 개선 제안
폼 전체를display: none처리하기보다는, 제출 버튼 비활성화(disabled) 및 스피너 토글만으로도 충분한 피드백이 가능합니다. 불필요한 DOM 트리 탐색(parentElement.parentElement)을 줄이는 방법을 추천합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java(4 hunks)src/main/java/io/github/petty/vision/adapter/in/VisionController.java(3 hunks)src/main/java/io/github/petty/vision/helper/PromptFactory.java(1 hunks)src/main/resources/templates/visionUpload.html(1 hunks)
🔇 Additional comments (6)
src/main/java/io/github/petty/vision/helper/PromptFactory.java (1)
81-81: 프롬프트 개선이 적절합니다!"사용자 친화적으로"라는 표현을 추가하여 생성되는 보고서가 보호자에게 더 친근하고 이해하기 쉬운 형태로 제공되도록 개선한 것이 좋습니다.
src/main/resources/templates/visionUpload.html (5)
2-4: 레이아웃 데코레이터 및 XML 네임스페이스 설정이 적절합니다.
Thymeleaf 레이아웃 데코레이터와 XML 네임스페이스 선언이 올바르게 적용되어 있습니다.
35-63: 카드·프리뷰·스피너·결과 컨테이너 스타일 적절
.card-body,#imagePreview img,.spinner-border,#spinnerContainer,#result등 주요 컨테이너 스타일이 일관성 있게 정의되어 있습니다.
174-196: 반려동물 정보 입력 폼 마크업 적절
<label for>와id가 정확히 매핑되어 있고,required속성도 잘 설정되어 있습니다. UX/폼 유효성 검사 측면에서 이상 없습니다.
201-238: 로딩 스피너 및 결과 UI 구조 적합
스피너와 결과 표시 영역이 명확히 분리되어 있어 사용자 피드백 흐름이 잘 구현되었습니다.
391-426: 두 번째 분석 및 리다이렉션 처리 적절
세션 기반 흐름과 사용자 피드백이 잘 조합되어 있습니다. 예외 처리도 상세해 문제 없습니다.
| private final VisionUseCase vision; | ||
| private final VisionServiceImpl visionService; |
There was a problem hiding this comment.
아키텍처 문제: 추상화와 구현체를 동시에 의존하고 있습니다.
컨트롤러가 VisionUseCase(추상화)와 VisionServiceImpl(구현체)를 모두 의존하는 것은 의존성 역전 원칙(DIP)을 위반합니다. 이는 결합도를 높이고 테스트를 어렵게 만듭니다.
VisionUseCase 인터페이스에 필요한 모든 메서드를 정의하고, 컨트롤러는 인터페이스만 의존하도록 리팩토링하는 것을 권장합니다.
🤖 Prompt for AI Agents
In src/main/java/io/github/petty/vision/adapter/in/VisionController.java at
lines 23-24, the controller depends on both the abstraction VisionUseCase and
the concrete implementation VisionServiceImpl, violating the dependency
inversion principle. Remove the dependency on VisionServiceImpl and ensure the
controller only depends on the VisionUseCase interface. Refactor the code to use
VisionUseCase exclusively for all required methods, improving modularity and
testability.
| // UnifiedFlowController와 호환되도록 세션에 데이터 저장 | ||
| session.setAttribute("petName", petName); | ||
|
|
||
| // 파일을 임시로 저장 (나중에 analyze에서 사용) | ||
| try { | ||
| byte[] imageBytes = file.getBytes(); | ||
| session.setAttribute("tempImageBytes", imageBytes); | ||
|
|
||
| // 이미지를 Base64로 인코딩하여 세션에 저장 | ||
| String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes); | ||
| session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64); | ||
| } catch (IOException e) { | ||
| log.warn("이미지 저장 실패", e); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
메모리 사용량 최적화가 필요합니다.
현재 구현은 동일한 이미지를 두 가지 형태(원본 바이트와 Base64 문자열)로 세션에 저장하고 있어 메모리를 비효율적으로 사용합니다. 대용량 이미지의 경우 세션 메모리 부족 문제가 발생할 수 있습니다.
다음 중 하나의 방법을 고려해보세요:
- Base64 인코딩된 데이터만 저장하고 필요시 디코딩
- 파일 시스템이나 캐시 서버에 임시 저장 후 참조 ID만 세션에 저장
- 이미지 크기 제한 설정
- // 파일을 임시로 저장 (나중에 analyze에서 사용)
- try {
- byte[] imageBytes = file.getBytes();
- session.setAttribute("tempImageBytes", imageBytes);
-
- // 이미지를 Base64로 인코딩하여 세션에 저장
- String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes);
- session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64);
- } catch (IOException e) {
- log.warn("이미지 저장 실패", e);
- }
+ // 이미지를 Base64로 인코딩하여 세션에 저장 (메모리 효율성을 위해 한 번만 저장)
+ try {
+ byte[] imageBytes = file.getBytes();
+ String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes);
+ session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64);
+ } catch (IOException e) {
+ log.warn("이미지 저장 실패", e);
+ throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "이미지 처리 중 오류가 발생했습니다.");
+ }📝 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.
| // UnifiedFlowController와 호환되도록 세션에 데이터 저장 | |
| session.setAttribute("petName", petName); | |
| // 파일을 임시로 저장 (나중에 analyze에서 사용) | |
| try { | |
| byte[] imageBytes = file.getBytes(); | |
| session.setAttribute("tempImageBytes", imageBytes); | |
| // 이미지를 Base64로 인코딩하여 세션에 저장 | |
| String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes); | |
| session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64); | |
| } catch (IOException e) { | |
| log.warn("이미지 저장 실패", e); | |
| } | |
| // UnifiedFlowController와 호환되도록 세션에 데이터 저장 | |
| session.setAttribute("petName", petName); | |
| // 이미지를 Base64로 인코딩하여 세션에 저장 (메모리 효율성을 위해 한 번만 저장) | |
| try { | |
| byte[] imageBytes = file.getBytes(); | |
| String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes); | |
| session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64); | |
| } catch (IOException e) { | |
| log.warn("이미지 저장 실패", e); | |
| throw new ResponseStatusException( | |
| HttpStatus.INTERNAL_SERVER_ERROR, | |
| "이미지 처리 중 오류가 발생했습니다." | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/io/github/petty/vision/adapter/in/VisionController.java around
lines 45 to 58, the code stores the same image in the session both as raw bytes
and as a Base64 string, causing inefficient memory use. To fix this, choose one
approach: either store only the Base64-encoded string in the session and decode
it when needed, or save the image temporarily on the file system or a cache
server and store only a reference ID in the session. Additionally, consider
implementing an image size limit to prevent excessive memory consumption.
| try { | ||
| byte[] imageBytes = file.getBytes(); | ||
| String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes); | ||
| session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64); | ||
| } catch (Exception e) { | ||
| log.warn("이미지 저장 실패", e); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
이미지 인코딩 로직이 중복되어 있습니다.
VisionController의 /species 엔드포인트에서 이미 이미지를 Base64로 인코딩하여 세션에 저장하고 있습니다. 여기서 다시 동일한 작업을 수행하는 것은 불필요한 중복입니다.
이미 세션에 저장된 이미지를 재사용하거나, 공통 유틸리티 메서드로 추출하는 것을 권장합니다.
- // 이미지를 Base64로 인코딩하여 세션에 저장
- try {
- byte[] imageBytes = file.getBytes();
- String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes);
- session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64);
- } catch (Exception e) {
- log.warn("이미지 저장 실패", e);
- }
+ // VisionController에서 이미 저장한 이미지가 없는 경우에만 저장
+ if (session.getAttribute("petImageBase64") == null) {
+ try {
+ byte[] imageBytes = file.getBytes();
+ String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes);
+ session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64);
+ } catch (Exception e) {
+ log.error("이미지 저장 실패", e);
+ throw new RuntimeException("이미지 처리 중 오류가 발생했습니다.", e);
+ }
+ }📝 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.
| try { | |
| byte[] imageBytes = file.getBytes(); | |
| String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes); | |
| session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64); | |
| } catch (Exception e) { | |
| log.warn("이미지 저장 실패", e); | |
| } | |
| // VisionController에서 이미 저장한 이미지가 없는 경우에만 저장 | |
| if (session.getAttribute("petImageBase64") == null) { | |
| try { | |
| byte[] imageBytes = file.getBytes(); | |
| String imageBase64 = java.util.Base64.getEncoder().encodeToString(imageBytes); | |
| session.setAttribute("petImageBase64", "data:image/jpeg;base64," + imageBase64); | |
| } catch (Exception e) { | |
| log.error("이미지 저장 실패", e); | |
| throw new RuntimeException("이미지 처리 중 오류가 발생했습니다.", e); | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/java/io/github/petty/pipeline/controller/UnifiedFlowController.java
around lines 59 to 65, the image Base64 encoding logic is duplicated from
VisionController's /species endpoint. To fix this, remove the redundant encoding
here and instead reuse the Base64 image string already stored in the session.
Alternatively, extract the encoding logic into a shared utility method and call
that method from both controllers to avoid duplication.
| /* 중간 보고서 스타일 개선 */ | ||
| #interim { | ||
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||
| color: white; | ||
| padding: 1.5rem; | ||
| border-radius: var(--border-radius-lg, 12px); | ||
| margin-bottom: 1rem; | ||
| text-align: center; | ||
| font-weight: 500; | ||
| box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); | ||
| transition: all 0.3s ease; | ||
| } | ||
|
|
||
| #interim.hidden { | ||
| opacity: 0; | ||
| transform: translateY(-20px); | ||
| pointer-events: none; | ||
| } | ||
|
|
||
| #analyzingMessage { | ||
| font-style: italic; | ||
| color: var(--secondary-text-color, #6c757d); | ||
| text-align: center; | ||
| margin: 1rem 0; | ||
| font-size: 0.9rem; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
ARIA 라이브 리전 도입으로 접근성 강화
중간 보고서(#interim)와 분석 메시지(#analyzingMessage)는 동적으로 업데이트되므로, 스크린 리더가 즉시 읽도록 aria-live="polite"나 role="status" 속성 추가를 권장합니다.
🤖 Prompt for AI Agents
In src/main/resources/templates/visionUpload.html around lines 113 to 138, the
#interim and #analyzingMessage elements are dynamically updated but lack ARIA
live region attributes. To improve accessibility, add aria-live="polite" or
role="status" attributes to these elements so screen readers announce updates
immediately.
| <style> | ||
| /* 전체 폰트 설정 */ | ||
| * { | ||
| font-family: 'HakgyoansimDunggeunmisoTTF-B', BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | ||
| } | ||
|
|
||
| /* body 배경색을 두 번째 이미지와 동일하게 설정 */ | ||
| body { | ||
| background-color: var(--background-color, #f8f9fa); | ||
| } | ||
|
|
||
| /* 페이지 전용 스타일 */ | ||
| .vision-container { | ||
| max-width: 600px; | ||
| margin: 0 auto; | ||
| } | ||
|
|
||
| .card { | ||
| margin-top: 2rem; | ||
| border-radius: var(--border-radius-lg, 12px); | ||
| box-shadow: var(--box-shadow-light, 0 4px 6px rgba(0, 0, 0, 0.1)); | ||
| border: none; | ||
| } |
There was a problem hiding this comment.
웹 폰트 로딩 누락 및 스타일 분리 제안
전역 폰트를 지정하셨으나, 실제로 @font-face 또는 CDN <link>를 통해 폰트 파일을 임포트하는 부분이 없습니다. 이대로면 기본 시스템 폰트로 대체될 수 있습니다.
또한, 전역 스타일과 레이아웃 전용 스타일을 별도의 CSS 파일로 분리하면 유지보수가 용이해집니다.
🤖 Prompt for AI Agents
In src/main/resources/templates/visionUpload.html around lines 11 to 33, the
global font-family is set but the custom font 'HakgyoansimDunggeunmisoTTF-B' is
not imported via @font-face or a CDN link, causing fallback to system fonts. Fix
this by adding an appropriate @font-face rule or a CDN link to load the font
file before using it. Additionally, move the global and layout-specific styles
into separate CSS files to improve maintainability and link them properly in the
HTML.
|
PR 잘 검토했습니다. 반려동물 이미지 분석부터 보고서 생성, 추천까지 이어지는 전체 플로우의 일관성을 높이고 사용자 경험을 개선하시려는 고민과 노력이 코드 전반에서 느껴집니다. 주요 변경 사항들과 긍정적인 효과들을 아래와 같이 정리해 보았습니다. ✨ 핵심 플로우 통합 및 데이터 관리 효율성 증대:
🎨 사용자 경험(UX) 및 인터페이스 정교화:
이렇게 많은 개선과 더불어, (몇몇 코드 분석 도구나 일반적인 권장 사항들도 참고하여) 앞으로 우리 시스템을 더욱 견고하고 발전시켜 나갈 몇 가지 주요 지점들을 함께 논의해보면 좋을 것 같습니다:
이번 PR을 통해 시스템 전반의 완성도와 사용자 경험이 크게 향상된 것 같습니다. 중요한 개선 작업 진행하시느라 정말 수고 많으셨습니다! |
리뷰
|
📜 PR 내용 요약
⚒️ 작업 및 변경 내용(상세하게)
1. PromptFactory.java
(종, 품종, 외형, 무게, 맹수 여부, 감정·행동 등 주요 항목 포함)
2. VisionController.java
3. UnifiedFlowController.java
(이전 단계 데이터 재사용 및 예외 발생 시 graceful fallback 처리)
4. visionUpload.html
(카드 영역 전체에 font-family 선언)
📚 기타 참고 사항
리뷰 포인트:
빌드/배포: