[VISION] 이미지 분석 기능 구현 및 설정 구조 통합#6
Conversation
|
Caution Review failedThe pull request is closed. Walkthrough이번 변경사항은 클라우드 기반 이미지 분석 기능 도입을 위한 전반적인 수정이다. Changes
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 응답 전송
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (4)
🪧 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: 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
📒 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 구조가 다를 경우에도 적절히 에러 메시지를 반환하도록 설계되어 있어, 안정성이 높습니다.
|
|
||
| #Vision test API | ||
| application-dev.yml | ||
| credentials.json No newline at end of file |
There was a problem hiding this comment.
🛠️ 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.
| #Vision test API | |
| application-dev.yml | |
| credentials.json | |
| ### VS Code ### | |
| .vscode/ | |
| #Vision test API | |
| application-*.yml | |
| !application.yml | |
| *.json | |
| !package.json | |
| !package-lock.json |
| 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(); | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ 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.
| 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> |
There was a problem hiding this comment.
🛠️ 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.
| <label>이미지 선택: <input type="file" name="file" required /></label> | |
| <label>이미지 선택: <input type="file" name="file" accept="image/*" required /></label> |
| <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> |
There was a problem hiding this comment.
🛠️ 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.
| <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> |
| config: | ||
| import: "optional:classpath:application-secret.yml" |
There was a problem hiding this comment.
💡 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에 추가해주시기 바랍니다.
| try (InputStream credentialsStream = getClass().getClassLoader().getResourceAsStream("credentials.json")) { | ||
| if (credentialsStream == null) { | ||
| throw new IOException("classpath에서 credentials.json을 찾을 수 없습니다."); | ||
| } | ||
|
|
There was a problem hiding this comment.
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.
| 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 + "을(를) 찾을 수 없습니다."); | |
| } |
| 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 분석 중 오류 발생"; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
createPetReport 메서드가 길어질 우려가 있습니다.
- 이미지 인코딩, 2) 요청 페이로드 구성, 3) API 호출, 4) 응답 처리 로직을 적절히 나누면 가독성과 유지보수성이 향상됩니다.
|
[VISION] 이미지 분석 기능 구현 및 설정 구조 통합
[feat] PIPELINE+LLM 파트 통합 및 RecommendController 로직 분리

📜 PR 내용 요약
⚒️ 작업 및 변경 내용(상세하게)
✅ 기능 구현
✅ 디자인 및 구조 개선
📚 기타 참고 사항
Summary by CodeRabbit
New Features
Style