Skip to content

[VISION] 이미지 분석 기능 구현 및 설정 구조 통합#6

Merged
s0ooo0k merged 7 commits into
PETTY-HUB:mainfrom
23MinL:main
Apr 14, 2025
Merged

[VISION] 이미지 분석 기능 구현 및 설정 구조 통합#6
s0ooo0k merged 7 commits into
PETTY-HUB:mainfrom
23MinL:main

Conversation

@23MinL
Copy link
Copy Markdown
Contributor

@23MinL 23MinL commented Apr 14, 2025

📜 PR 내용 요약

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

  • AWS Rekognition과 Google Cloud Vision을 이용한 이미지 분석 기능을 구현하고, Gemini API 연동을 통해 반려동물 분석 보고서를 생성하는 전체 흐름을 구성하였습니다.
  • UI 구성 및 업로드 라우팅 처리, 설정 파일 구조 통일, 보안 분리를 포함하여 완성도 있는 초기 구조를 갖추었습니다.

⚒️ 작업 및 변경 내용(상세하게)

변경 내용, 업데이트 및 수정 사항을 자세하게 적어주세요

✅ 기능 구현

  • [feat] AWS Rekognition 및 Google Cloud Vision API 설정 및 의존성 추가
    • AWS SDK 및 Google Cloud Vision 클라이언트 의존성 추가
    • AwsGoogleCloudConfig 클래스에서 Bean 구성
  • [feat] VisionService 구현 - Gemini API 이미지 분석 로직 추가
    • Gemini Vision API를 통한 Base64 이미지 요청 구현
    • 응답 파싱 및 보고서 텍스트 추출 처리
  • [feat] VisionController 및 이미지 업로드 UI 구현
    • 이미지와 반려동물 이름 업로드 처리 (MultipartFile)
    • 결과 메시지 및 분석 결과를 모델에 담아 렌더링

✅ 디자인 및 구조 개선

  • [design] 메인 및 이미지 업로드 UI 개선 및 라우팅 처리
    • index.html, visionUpload.html UI 작성
    • 업로드 버튼, 결과 출력 등 UX 개선
    • IndexController에서 업로드 진입 라우팅 처리
  • [chore] RestTemplate Bean 등록을 위한 설정 클래스 추가
    • RestTemplateConfig에서 RestTemplate 빈 등록 (Gemini API 호출용)
  • [chore] 공통 application.yml 구조 통합 및 비밀 설정 분리
    • application.yml: 공통 환경 프로파일 정의 및 구조 통일
    • application-secret.yml: Gemini, AWS, Google 키 분리
    • spring.profiles.active=secret 구조로 보안성 강화

📚 기타 참고 사항

리뷰 포인트, 참고 사항, 빌드 관련 내용 기타 사항을 자세히 적어주세요

  • credentials.json 및 application-secret.yml은 절대 커밋되지 않도록 .gitignore 등록 필수
  • 추후 API Key들은 환경 변수 혹은 GitHub Actions secrets로 관리 예정
  • /upload, /api/vision/analyze 라우팅 구조는 Front ↔ Back 테스트 용이하게 구성됨
  • 테스트용 키를 사용하고 있으므로 운영 시 Secret Manager로 연동 권장

Summary by CodeRabbit

  • New Features

    • 애완동물 이미지를 업로드하면 상세 분석 결과와 리포트를 제공하는 기능이 추가되었습니다.
    • 새 업로드 페이지를 통해 이미지와 애완동물 이름을 간편하게 제출할 수 있습니다.
    • 이미지 분석을 위한 API 통합 기능이 추가되었습니다.
  • Style

    • 홈 화면 타이틀과 환영 메시지가 업데이트되어 사용자 인터페이스가 개선되었습니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

이번 변경사항은 클라우드 기반 이미지 분석 기능 도입을 위한 전반적인 수정이다.
.gitignore 파일에 새로운 섹션이 추가되어 application-dev.ymlcredentials.json 파일이 Git에서 무시되도록 설정되었다. build.gradle에는 Google Cloud Vision 및 AWS Rekognition 의존성이 추가되었다. IndexController, VisionController, VisionService에 이미지 업로드 및 분석 관련 메소드가 도입되었고, AWS 및 Google Cloud 클라이언트 설정을 위한 AwsGoogleCloudConfig 클래스와 HTTP 요청을 위한 RestTemplateConfig 클래스가 새로 추가되었다. application.yml 설정 파일과 HTML 템플릿(index.html, visionUpload.html) 변경으로 사용자 인터페이스가 보완되었다.

Changes

파일 경로 변경 내용
.gitignore VS Code 섹션에 application-dev.yml, credentials.json 추가
build.gradle Google Cloud Vision (com.google.cloud:google-cloud-vision:3.33.0) 및 AWS Rekognition 의존성 추가
.../IndexController.java visionUploadPage 메소드 추가 (GET /vision/upload 요청 처리)
.../config/AwsGoogleCloudConfig.java AWS Rekognition 및 Google Cloud Vision 클라이언트 빈(rekognitionClient, imageAnnotatorClient) 생성 클래스 추가
.../config/RestTemplateConfig.java RestTemplate 빈을 생성하는 설정 클래스 추가
.../vision/VisionController.java 이미지 분석 처리용 analyzeImage (POST /analyze) 및 업로드 폼 제공용 showUploadForm 메소드 추가
.../vision/VisionService.java Gemini API 관련 필드(geminiApiKey, geminiApiUrl) 및 이미지 분석/리포트 생성 메소드(createPetReport, analyzeImage, createEnhancedPromptForReport, extractReport) 추가
.../application.yml Spring 활성 프로파일(secret), datasource (PostgreSQL), HikariCP, JPA 및 Hibernate 설정 추가, application-secret.yml 임포트
.../templates/index.html 제목 수정("🐶 PETTY"에서 "🐶 PETTY 메인"으로), 헤더 텍스트 변경 및 이미지 업로드 폼 추가
.../templates/visionUpload.html 이미지 분석을 위한 업로드 폼 HTML 템플릿 새로 추가

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant VC as VisionController
    participant VS as VisionService
    participant Gemini as Gemini API
    User->>VC: GET /vision/upload 요청
    VC->>User: 업로드 폼 응답 (visionUpload view)
    User->>VC: POST /api/vision/analyze (파일, petName 전달)
    VC->>VS: analyzeImage(파일, petName) 호출
    VS->>VS: 파일 처리 및 payload 작성
    VS->>Gemini: API 요청 (이미지, 텍스트 데이터 전송)
    Gemini-->>VS: JSON 응답 전달
    VS->>VS: extractReport로 응답 파싱
    VS-->>VC: 분석 결과 리포트 반환
    VC->>User: 결과 포함 view 응답 전송
Loading

Poem

나는 귀여운 토끼, 코드 숲을 뛰놀며 🐇
새로운 의존성, 설정들이 피어나는 날,
이미지가 춤추고, 리포트가 노래하는,
클라우드와 API가 꿈꾸는 세상에서,
나의 발자취 따라 코딩의 즐거움이 번지네,
오늘도 행복한 토끼, 변화와 함께 hop hop!

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc65336 and 11fe1a4.

📒 Files selected for processing (4)
  • .gitignore (1 hunks)
  • build.gradle (1 hunks)
  • src/main/resources/application.yml (1 hunks)
  • src/main/resources/templates/index.html (3 hunks)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (9)
src/main/java/io/github/petty/IndexController.java (1)

17-20: 메소드 주석 추가 필요

vision 업로드 페이지로 연결하는 새 메소드가 추가되었습니다. 코드 일관성을 위해 기존 메소드(인덱스 페이지)처럼 주석을 추가하는 것이 좋겠습니다.

+    // 비전 이미지 업로드 페이지
    @GetMapping("/vision/upload")
    public String visionUploadPage() {
        return "visionUpload";
    }
src/main/resources/templates/index.html (1)

11-13: 업로드 버튼 스타일 개선 권장

이미지 업로드 기능으로 이동하는 버튼이 추가되었습니다. 하지만 버튼에 스타일이 적용되어 있지 않습니다. 사용자 경험 향상을 위해 간단한 스타일을 추가하는 것이 좋겠습니다.

<form method="get" action="/vision/upload">
-    <button type="submit">이미지 업로드하러 가기</button>
+    <button type="submit" style="padding: 10px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">이미지 업로드하러 가기</button>
</form>

더 나은 방법으로는 별도의 CSS 파일을 사용하여 스타일을 분리하는 것이 유지보수에 더 효과적입니다.

src/main/resources/templates/visionUpload.html (1)

1-29: 사용자 경험 개선을 위한 기본 스타일 추가가 필요합니다.

현재 HTML은 기능적으로는 문제가 없지만, 사용자 경험을 향상시키기 위한 기본적인 스타일이 없습니다. 최소한의 스타일을 추가하는 것이 좋겠습니다.

<head>
    <meta charset="UTF-8">
    <title>🐾 PETTY - 동물 이미지 분석</title>
+    <style>
+        body {
+            font-family: 'Arial', sans-serif;
+            max-width: 800px;
+            margin: 0 auto;
+            padding: 20px;
+        }
+        h1 {
+            color: #4CAF50;
+        }
+        form {
+            margin: 20px 0;
+            padding: 15px;
+            border: 1px solid #e0e0e0;
+            border-radius: 5px;
+        }
+        button {
+            padding: 8px 15px;
+            background-color: #4CAF50;
+            color: white;
+            border: none;
+            border-radius: 4px;
+            cursor: pointer;
+        }
+        pre {
+            background-color: #f5f5f5;
+            padding: 15px;
+            border-radius: 5px;
+            white-space: pre-wrap;
+        }
+        a {
+            color: #4CAF50;
+            text-decoration: none;
+        }
+    </style>
</head>
src/main/resources/application.yml (1)

11-16: JPA 설정에 대한 고려 사항이 있습니다.

ddl-auto: update 설정은 개발 환경에서는 편리하지만, 프로덕션 환경에서는 위험할 수 있습니다. 환경에 따라 이 값을 달리 설정하는 것이 좋습니다.

프로덕션 환경에서는 validate 또는 none으로 설정하는 것이 안전합니다. 환경별 설정을 도입하는 것을 고려해보세요:

spring:
  profiles:
    active: secret
    
  # ... 기존 설정 ...
  
  jpa:
    hibernate:
      # 프로필에 따라 달라지는 설정
      ddl-auto: ${HIBERNATE_DDL_AUTO:update}

그리고 각 환경별 프로필 설정에서 적절한 값을 지정하세요.

src/main/java/io/github/petty/config/AwsGoogleCloudConfig.java (2)

42-64: 예외 처리가 개선될 수 있습니다.

현재 예외 처리가 일반적인 IOException에 대해서만 되어 있습니다. 더 구체적인 예외 유형을 사용하면 문제 해결이 더 쉬워질 수 있습니다.

Google Cloud Vision API 클라이언트 생성 실패 시 보다 구체적인 예외 처리를 추가하는 것이 좋습니다:

         try (InputStream credentialsStream = getClass().getClassLoader().getResourceAsStream(googleCredentialsPath)) {
             if (credentialsStream == null) {
                 throw new IOException("classpath에서 " + googleCredentialsPath + "을(를) 찾을 수 없습니다.");
             }

             GoogleCredentials credentials = GoogleCredentials.fromStream(credentialsStream);

             ImageAnnotatorSettings settings = ImageAnnotatorSettings.newBuilder()
                     .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
                     .build();

             return ImageAnnotatorClient.create(settings);
+        } catch (com.google.api.gax.rpc.ApiException e) {
+            log.error("Google Cloud Vision API 오류: {}", e.getMessage(), e);
+            throw e;
+        } catch (com.google.auth.oauth2.GoogleCredentialsException e) {
+            log.error("Google 자격 증명 오류: {}", e.getMessage(), e);
+            throw e;
         } catch (IOException e) {
             log.error("Google Cloud Vision 클라이언트 생성 실패. 경로를 확인해주세요: {}", googleCredentialsPath, e);
             throw e;
         }

19-65: 자격 증명 파일 누락 시 실패 복구 전략 필요

자격 증명 파일이 누락되거나 잘못된 경우 애플리케이션이 시작되지 않을 수 있습니다. 개발 모드에서 더 유연한 복구 전략을 고려해보세요.

개발 환경에서 클라이언트 생성 실패 시 MockClient나 NullClient를 반환하는 방식을 고려해볼 수 있습니다. 이는 프로필 조건부 빈 생성을 통해 구현할 수 있습니다.

@Bean
@Profile("!prod") // 프로덕션이 아닌 환경에서만 적용
public ImageAnnotatorClient fallbackImageAnnotatorClient() {
    log.warn("개발 환경용 Mock ImageAnnotatorClient를 사용합니다. 프로덕션에서는 사용하지 마세요.");
    // 개발용 Mock 클라이언트 반환 로직
    return null; // 실제로는 Mock 구현체 제공
}

이를 통해 개발 과정에서 실제 클라이언트가 없어도 다른 기능을 테스트할 수 있습니다.

src/main/java/io/github/petty/vision/VisionController.java (2)

13-14: 필드 주입보다 생성자 주입을 고려해보세요.
@Autowired 필드 주입 대신 생성자 주입을 사용하면, 테스트 및 유지보수성이 더 좋아집니다.

- @Autowired
- private VisionService visionService;
+ private final VisionService visionService;

public VisionController(VisionService visionService) {
    this.visionService = visionService;
}

20-33: 입력값 검증 로직이 적절하지만, 파일 포맷 혹은 크기 확인도 고려해주세요.
현재 이름과 파일이 비어있는지 여부만 확인하고 있는데, 이미지의 포맷이나 크기가 예상 범위를 벗어나는지도 점검하면 안전성이 높아집니다.

src/main/java/io/github/petty/vision/VisionService.java (1)

73-81: 파일 업로드 시 포맷 검증 및 에러 처리 강화 제안
파일 포맷(JPEG, PNG 등)이나 파일 크기 제한 등도 확인하면 보안 측면에서 더 안정적입니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19741a3 and dc65336.

📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • build.gradle (1 hunks)
  • src/main/java/io/github/petty/IndexController.java (1 hunks)
  • src/main/java/io/github/petty/config/AwsGoogleCloudConfig.java (1 hunks)
  • src/main/java/io/github/petty/config/RestTemplateConfig.java (1 hunks)
  • src/main/java/io/github/petty/vision/VisionController.java (1 hunks)
  • src/main/java/io/github/petty/vision/VisionService.java (1 hunks)
  • src/main/resources/application.yml (1 hunks)
  • src/main/resources/templates/index.html (1 hunks)
  • src/main/resources/templates/visionUpload.html (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/io/github/petty/vision/VisionController.java (1)
src/main/java/io/github/petty/IndexController.java (1)
  • Controller (7-21)
🔇 Additional comments (13)
build.gradle (2)

45-46: Google Cloud Vision 의존성이 추가되었습니다.

Vision API를 활용한 이미지 분석을 위해 Google Cloud Vision 라이브러리가 적절히 추가되었습니다.


48-50: AWS Rekognition 의존성이 추가되었습니다.

AWS SDK를 통한 이미지 분석을 위해 필요한 Rekognition 의존성이 정상적으로 추가되었습니다. BOM을 통해 버전 관리를 일관되게 하는 좋은 접근법입니다.

src/main/resources/application.yml (2)

1-3: 환경 프로필 설정이 적절합니다.

프로필을 secret으로 설정하여 민감한 정보를 별도 파일로 관리하는 접근 방식이 좋습니다. 이는 보안 모범 사례를 따르고 있습니다.


5-8: 데이터베이스 연결 설정이 일부 누락되었습니다.

드라이버 클래스와 커넥션 풀 설정은 있지만, JDBC URL, 사용자 이름, 비밀번호가 누락되어 있습니다. 이는 아마도 application-secret.yml에 있을 것으로 예상됩니다.

데이터베이스 연결 설정이 application-secret.yml에 정의되어 있는지 확인해주세요. 다음과 같은 속성이 필요합니다:

  • spring.datasource.url
  • spring.datasource.username
  • spring.datasource.password
src/main/java/io/github/petty/config/AwsGoogleCloudConfig.java (3)

24-28: 구성 속성이 올바르게 주입되고 있습니다.

AWS 리전과 Google 자격 증명 경로를 외부 설정 파일에서 주입받는 방식으로 구현했습니다. 이는 좋은 접근 방식입니다.

하지만 이러한 속성들이 application-secret.yml에 정의되어 있는지 확인이 필요합니다:

  • aws.region
  • google.credentials.path

30-40: AWS Rekognition 클라이언트 설정이 적절합니다.

AWS Rekognition 클라이언트가 올바르게 구성되었습니다. 기본 자격 증명 공급자와 설정된 리전을 사용합니다.


47-63:

Details

✅ Verification successful

자격 증명 파일이 .gitignore에 포함되어 있는지 확인 필요

민감한 credentials.json 파일이 버전 관리 시스템에 실수로 포함되지 않도록 .gitignore에 추가되었는지 확인하세요.


🏁 Script executed:

#!/bin/bash
# .gitignore에 credentials.json이 포함되어 있는지 확인
grep "credentials.json" .gitignore || echo "경고: credentials.json이 .gitignore에 없습니다."

Length of output: 51


검증 완료: credentials.json 파일 관리 양호

스크립트 실행 결과, 민감한 credentials.json 파일이 이미 .gitignore에 포함되어 있어 버전 관리 시스템에 실수로 포함될 위험이 없음을 확인했습니다. 해당 부분은 수정할 필요가 없습니다.

src/main/java/io/github/petty/vision/VisionController.java (3)

9-10: API 경로 명시가 명확하여 유지보수에 유리합니다.
API 경로를 명확히 지정해주셔서, 기능 범위가 분명해지고 향후 유지보수가 수월해집니다.


44-52: 예외 처리 분리가 잘 되어 있습니다.
IllegalArgumentException과 일반 Exception을 구별하여 오류 메시지를 세분화한 점이 좋습니다. 다만, 운영 단계에서 예외 메시지를 사용자에게 직접 노출할 때는 보안 및 사용성 측면을 유의해주세요.


57-61: IndexController와 중복 가능성에 주의해주세요.
/vision/upload 경로가 IndexController에서도 매핑되고 있으니, 라우트 충돌이나 중복 기능 여부를 확인하고 필요시 하나로 정리하는 방안을 검토해주세요.

src/main/java/io/github/petty/vision/VisionService.java (3)

24-31: 민감한 키 관리에 주의가 필요합니다.
API 키와 URL을 사용 중이므로, 환경 변수나 시크릿 매니저 등을 통해 안전하게 관리하는 방안을 고려해보세요.


83-107: createEnhancedPromptForReport 메서드의 멀티라인 문자열 활용이 적절합니다.
한글 메시지를 포함한 상세한 프롬프트 구성이 돋보입니다. 단, 국제화나 다국어 지원이 필요한 경우 추가적인 구조화가 필요할 수 있습니다.


109-136: extractReport 메서드는 예외 처리와 로깅이 잘 구성되었습니다.
JSON 구조가 다를 경우에도 적절히 에러 메시지를 반환하도록 설계되어 있어, 안정성이 높습니다.

Comment thread .gitignore Outdated
Comment on lines +38 to +41

#Vision test API
application-dev.yml
credentials.json No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

민감한 설정 파일 관리 방식 개선 필요

Vision API 관련 설정 파일을 .gitignore에 추가한 것은 좋습니다. 하지만 PR 목표에서 언급된 application-secret.yml이 누락되어 있습니다. 또한 개별 파일을 지정하는 것보다 패턴을 사용하는 것이 더 효과적입니다.

다음과 같이 수정하는 것을 권장합니다:

### VS Code ###
.vscode/

#Vision test API
-application-dev.yml
-credentials.json
+application-*.yml
+!application.yml
+*.json
+!package.json
+!package-lock.json
📝 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.

Suggested change
#Vision test API
application-dev.yml
credentials.json
### VS Code ###
.vscode/
#Vision test API
application-*.yml
!application.yml
*.json
!package.json
!package-lock.json

Comment on lines +1 to +14
package io.github.petty.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

RestTemplate 설정 개선 필요

RestTemplate 빈을 생성하는 기본 설정은 잘 구현되었지만, 프로덕션 환경에서는 타임아웃, 연결 풀링, 오류 처리 등의 추가 설정이 필요합니다. 또한 클래스 목적에 대한 주석도 추가하는 것이 좋겠습니다.

package io.github.petty.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

+/**
+ * HTTP 요청을 위한 RestTemplate 설정
+ * Vision API와 같은 외부 서비스 호출에 사용됩니다.
+ */
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
-        return new RestTemplate();
+        return new RestTemplate(clientHttpRequestFactory());
+    }
+    
+    private ClientHttpRequestFactory clientHttpRequestFactory() {
+        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+        factory.setConnectTimeout(5000);
+        factory.setReadTimeout(10000);
+        return factory;
    }
}
📝 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.

Suggested change
package io.github.petty.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package io.github.petty.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* HTTP 요청을 위한 RestTemplate 설정
* Vision API와 같은 외부 서비스 호출에 사용됩니다.
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(10000);
return factory;
}
}


<form method="post" enctype="multipart/form-data" action="/api/vision/analyze">
<label>반려동물 이름: <input type="text" name="petName" placeholder="반려동물 이름 입력" required /></label><br/><br/>
<label>이미지 선택: <input type="file" name="file" required /></label>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

파일 업로드 타입 제한 추가가 필요합니다.

현재 파일 업로드 입력란에 파일 타입 제한이 없어 사용자가 이미지가 아닌 파일을 업로드할 수 있습니다. 이미지 파일로 제한하는 것이 좋습니다.

-    <label>이미지 선택: <input type="file" name="file" required /></label>
+    <label>이미지 선택: <input type="file" name="file" accept="image/*" required /></label>
📝 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.

Suggested change
<label>이미지 선택: <input type="file" name="file" required /></label>
<label>이미지 선택: <input type="file" name="file" accept="image/*" required /></label>

Comment on lines +11 to +15
<form method="post" enctype="multipart/form-data" action="/api/vision/analyze">
<label>반려동물 이름: <input type="text" name="petName" placeholder="반려동물 이름 입력" required /></label><br/><br/>
<label>이미지 선택: <input type="file" name="file" required /></label>
<button type="submit">분석하기</button>
</form>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

폼 구성이 적절히 구현되었지만 보안 개선이 필요합니다.

파일 업로드 폼이 올바르게 구현되었습니다. 하지만 CSRF 보호를 위한 토큰이 누락되어 있습니다. Spring Security를 사용하고 있으므로 CSRF 보호를 추가하는 것이 좋습니다.

-<form method="post" enctype="multipart/form-data" action="/api/vision/analyze">
+<form method="post" enctype="multipart/form-data" action="/api/vision/analyze" th:action="@{/api/vision/analyze}">
+    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
     <label>반려동물 이름: <input type="text" name="petName" placeholder="반려동물 이름 입력" required /></label><br/><br/>
     <label>이미지 선택: <input type="file" name="file" required /></label>
     <button type="submit">분석하기</button>
📝 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.

Suggested change
<form method="post" enctype="multipart/form-data" action="/api/vision/analyze">
<label>반려동물 이름: <input type="text" name="petName" placeholder="반려동물 이름 입력" required /></label><br/><br/>
<label>이미지 선택: <input type="file" name="file" required /></label>
<button type="submit">분석하기</button>
</form>
<form method="post" enctype="multipart/form-data" action="/api/vision/analyze" th:action="@{/api/vision/analyze}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<label>반려동물 이름: <input type="text" name="petName" placeholder="반려동물 이름 입력" required /></label><br/><br/>
<label>이미지 선택: <input type="file" name="file" required /></label>
<button type="submit">분석하기</button>
</form>

Comment on lines +18 to +19
config:
import: "optional:classpath:application-secret.yml"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

추가 설정 파일 가져오기가 올바르게 설정되었습니다.

민감한 정보를 담은 application-secret.yml 파일을 선택적으로 가져오도록 설정한 것이 적절합니다. 보안을 위해 이 파일이 버전 관리 시스템에 포함되지 않도록 .gitignore에 추가되었는지 확인하세요.

아래 명령을 실행하여 .gitignore에 application-secret.yml이 포함되어 있는지 확인해주세요:


🏁 Script executed:

#!/bin/bash
# .gitignore에 application-secret.yml이 포함되어 있는지 확인
grep "application-secret.yml" .gitignore || echo "경고: application-secret.yml이 .gitignore에 없습니다."

Length of output: 141


보안 위험: application-secret.yml 파일의 .gitignore 등록 필요

  • src/main/resources/application.yml 파일에 선택적 설정 파일 가져오기가 올바르게 구성되었습니다.
  • 다만, 스크립트 실행 결과 application-secret.yml 파일이 현재 .gitignore에 포함되어 있지 않은 것으로 확인되었습니다.
  • 민감한 정보를 포함할 수 있는 이 파일이 버전 관리 시스템에 포함되지 않도록, 반드시 .gitignore에 추가해주시기 바랍니다.

Comment on lines +48 to +52
try (InputStream credentialsStream = getClass().getClassLoader().getResourceAsStream("credentials.json")) {
if (credentialsStream == null) {
throw new IOException("classpath에서 credentials.json을 찾을 수 없습니다.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Google 자격 증명 파일 경로 불일치가 있습니다.

클래스에 주입된 googleCredentialsPath 속성을 사용하지 않고 하드코딩된 "credentials.json" 경로를 사용하고 있습니다. 이는 속성 주입이 의미가 없게 됩니다.

-        try (InputStream credentialsStream = getClass().getClassLoader().getResourceAsStream("credentials.json")) {
+        try (InputStream credentialsStream = getClass().getClassLoader().getResourceAsStream(googleCredentialsPath)) {
             if (credentialsStream == null) {
-                throw new IOException("classpath에서 credentials.json을 찾을 수 없습니다.");
+                throw new IOException("classpath에서 " + googleCredentialsPath + "을(를) 찾을 수 없습니다.");
             }
📝 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.

Suggested change
try (InputStream credentialsStream = getClass().getClassLoader().getResourceAsStream("credentials.json")) {
if (credentialsStream == null) {
throw new IOException("classpath에서 credentials.json을 찾을 수 없습니다.");
}
try (InputStream credentialsStream = getClass().getClassLoader().getResourceAsStream(googleCredentialsPath)) {
if (credentialsStream == null) {
throw new IOException("classpath에서 " + googleCredentialsPath + "을(를) 찾을 수 없습니다.");
}

Comment on lines +33 to +71
public String createPetReport(String petName, byte[] imageData, List<String> rekognitionLabels, List<String> googleVisionLabels) {
try {
String prompt = createEnhancedPromptForReport(petName, rekognitionLabels, googleVisionLabels);
String base64Image = Base64.getEncoder().encodeToString(imageData);

Map<String, Object> payload = Map.of(
"contents", List.of(Map.of(
"parts", List.of(
Map.of("text", prompt),
Map.of("inline_data", Map.of(
"mime_type", "image/jpeg",
"data", base64Image
))
)
))
);

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

// ✅ 이 부분이 핵심 수정 부분입니다.
String fullUrl = geminiApiUrl + "?key=" + geminiApiKey;

HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);

ResponseEntity<String> response = restTemplate.postForEntity(fullUrl, request, String.class);

if (response.getStatusCode() != HttpStatus.OK) {
log.error("Gemini API 요청 실패: Status = {}, Body = {}", response.getStatusCode(), response.getBody());
return "Gemini 분석 요청 실패";
}

return extractReport(petName, response.getBody());

} catch (Exception e) {
log.error("Gemini 보고서 생성 실패", e);
return "Gemini 분석 중 오류 발생";
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

createPetReport 메서드가 길어질 우려가 있습니다.

  1. 이미지 인코딩, 2) 요청 페이로드 구성, 3) API 호출, 4) 응답 처리 로직을 적절히 나누면 가독성과 유지보수성이 향상됩니다.

@taehyun32
Copy link
Copy Markdown
Member

  • AWS Rekognition, Google Vision, Gemini API를 각자의 강점을 살려 통합한 점이 인상 깊었습니다.
  • 각 서비스의 특성을 이해하고 조합하신 점에서 기획과 설계에 대한 고민이 느껴졌습니다.
  • RestTemplate 설정을 분리하고 의존성 주입과 설정 클래스로 관리한 구조도 좋았습니다.
  • 확장성과 유지보수 측면에서 유리한 접근이라고 생각합니다.

@LimPark996
Copy link
Copy Markdown
Contributor

전체 코드를 다 살펴보지는 못하였으나, 코드 래빗 + PR된 전체 내용은 확인하였습니다.

수고 많으셨습니다. 감사합니다.

우선 크게 고쳐야되는 부분이 필요해보이지는 않습니다.

다만, 아래 사진에서와 같이 이미지 파일 외의 타입으로 사용자가 입력할 수 있는 상황을 고려하여 타입 제한하는 것이 좋아보입니다.
물론, 이미 처리를 해두셨다면 넘겨주시면 좋겠습니다.

image

마지막으로 코드 래빗에서 .gitignore 관련하여 파일을 내부에 작성하라는 문구를 봤습니다. 잘 처리해주셨을 것이라 생각합니다.

마치겠습니다.

@s0ooo0k s0ooo0k merged commit 6b5c7dd into PETTY-HUB:main Apr 14, 2025
1 of 2 checks passed
23MinL pushed a commit to 23MinL/PETTY-BACK-VISION that referenced this pull request Apr 23, 2025
[VISION] 이미지 분석 기능 구현 및 설정 구조 통합
s0ooo0k added a commit that referenced this pull request Apr 23, 2025
[feat] PIPELINE+LLM 파트 통합 및 RecommendController 로직 분리
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants