Skip to content

Conversation

@yeonjy
Copy link
Contributor

@yeonjy yeonjy commented May 30, 2025

#️⃣ 연관된 이슈

📝 작업 내용

  • trimImageUrl() 구현
  • URI에 이미지 확장자가 없는 경우 예외처리

📸 스크린샷

스크린샷 2025-05-31 오전 1 47 05

💬 리뷰 요구사항

Summary by CodeRabbit

  • 신규 기능
    • 이미지 파일 확장자가 없는 경우에 대한 새로운 오류 메시지가 추가되었습니다.
  • 문서화
    • 이미지 분석 API에 이미지 확장자가 없을 때의 오류 응답(400) 안내가 추가되었습니다.

@yeonjy yeonjy requested a review from zyovn May 30, 2025 16:47
@yeonjy yeonjy self-assigned this May 30, 2025
@yeonjy yeonjy added the 🐛bug Something isn't working label May 30, 2025
@coderabbitai
Copy link

coderabbitai bot commented May 30, 2025

Walkthrough

이미지 파일 확장자가 없는 경우를 처리하기 위해 새로운 에러 코드가 추가되었고, 이미지 분석 요청 시 URL에서 확장자를 감지하여 잘라내는 로직이 도입되었습니다. 또한, Swagger 문서에 확장자 누락 시 발생하는 400 에러 응답이 명시적으로 추가되었습니다.

Changes

파일/경로 변경 요약
src/main/java/com/webeye/backend/global/error/ErrorCode.java 이미지 확장자 누락 에러 코드(NO_IMAGE_FILE_EXTENSION_FOUND) 추가
src/main/java/com/webeye/backend/imageanalysis/infrastructure/OpenAiClient.java 이미지 URL에서 확장자 감지 및 자르기, 확장자 없을 시 예외 처리 추가
src/main/java/com/webeye/backend/imageanalysis/presentation/swagger/ImageAnalysisSwagger.java Swagger에 400 에러 응답(확장자 없음) 명시적으로 추가

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller
    participant OpenAiClient
    participant ErrorHandler

    Client->>Controller: 이미지 분석 요청 (image URL 포함)
    Controller->>OpenAiClient: explainImage(imageUrl)
    OpenAiClient->>OpenAiClient: trimImageUrl(imageUrl)
    alt 확장자 존재
        OpenAiClient->>OpenAiClient: URL 확장자 기준으로 자름
        OpenAiClient->>Controller: 분석 결과 반환
        Controller->>Client: 200 OK + 결과
    else 확장자 없음
        OpenAiClient->>ErrorHandler: BusinessException(NO_IMAGE_FILE_EXTENSION_FOUND)
        ErrorHandler->>Client: 400 Bad Request + 에러 메시지
    end
Loading

Suggested reviewers

  • zyovn

Poem

🐇
확장자 없는 이미지,
토끼가 깡총 놀라네!
에러 코드 추가하고
Swagger에 기록했지.
이제는 안심하고
URL을 잘라내니,
분석도 문제 없어요—토끼 춤을 춰요!
🖼️✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need 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)

  • @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 generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @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.

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

@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: 1

🔭 Outside diff range comments (1)
src/main/java/com/webeye/backend/imageanalysis/infrastructure/OpenAiClient.java (1)

192-206: 💡 Verification agent

🧩 Analysis chain

다른 이미지 분석 메서드들에서도 URL 검증을 고려해보세요.

현재 trimImageUrlexplainImage에서만 사용되고 있습니다. 다른 이미지 분석 메서드들(explainAllergy, explainNutrition, explainCosmetic 등)에서도 동일한 URL 검증이 필요할 수 있습니다.

다른 메서드들에서 URL 검증 필요성을 확인해보겠습니다:

만약 다른 메서드들에서도 URL 검증이 필요하다면, callWithStructuredOutput 메서드 내부에서 URL들을 전처리하는 것이 더 일관성 있는 접근법일 수 있습니다.


🏁 Script executed:

#!/bin/bash
# 이미지 분석 관련 메서드들에서 URL 사용 패턴 확인
rg -A 10 -B 5 "callWithStructuredOutput.*urls" src/main/java/com/webeye/backend/imageanalysis/infrastructure/OpenAiClient.java

Length of output: 5134


URL 전처리 로직을 모든 메서드에 일관되게 적용하세요

현재 trimImageUrlexplainImage에서만 사용되고 있어, 외부에서 전달된 URL 리스트(List<String> urls)를 처리하지 않고 있습니다. 모든 이미지 분석 메서드에서 동일한 전처리를 적용하거나, callWithStructuredOutput 내부로 중앙화하여 한 번만 처리하도록 변경하는 것이 좋습니다.

• 대상 파일:

  • src/main/java/com/webeye/backend/imageanalysis/infrastructure/OpenAiClient.java
    • 메서드: explainAllergy, explainNutrition, explainCosmetic, explainHealthFood
    • 호출부: return callWithStructuredOutput(urls, …);

• 개선안 (예시)

 private <T> T callWithStructuredOutput(List<String> urls, ImageAnalysisPrompt prompt, Class<T> clazz) {
-    BeanOutputConverter<T> outputConverter = new BeanOutputConverter<>(clazz);
+    // URL 전처리: 공백·특수문자 제거 등
+    List<String> trimmedUrls = urls.stream()
+        .map(this::trimImageUrl)
+        .toList();
+    BeanOutputConverter<T> outputConverter = new BeanOutputConverter<>(clazz);

-    for (String imageUrl : urls) {
+    for (String imageUrl : trimmedUrls) {
         MimeType extension = ImageMimeType.fromExtension(extractFileExtension(imageUrl));
         promptUserSpec.media(extension, new UrlResource(imageUrl));
     }

위와 같이 중앙화하면 추후 메서드 추가 시에도 URL 검증 코드를 반복 작성할 필요가 없어집니다.

🧹 Nitpick comments (1)
src/main/java/com/webeye/backend/imageanalysis/presentation/swagger/ImageAnalysisSwagger.java (1)

23-26: API 에러 응답 문서화가 적절히 추가되었습니다.

새로운 에러 케이스에 대한 명시적 문서화로 API 사용자에게 도움이 됩니다. 에러 메시지도 ErrorCode와 일치하여 일관성이 있습니다.

다른 가능한 에러 상황들도 문서화를 고려해보세요:

  • 잘못된 이미지 URL (400)
  • OpenAI 서버 에러 (500)
  • 지원하지 않는 이미지 형식 (400)
             @ApiResponse(
                     responseCode = "400",
                     description = "이미지 확장자가 없습니다."
+            ),
+            @ApiResponse(
+                     responseCode = "400", 
+                     description = "잘못된 이미지 URL이거나 지원하지 않는 이미지 형식입니다."
+            ),
+            @ApiResponse(
+                     responseCode = "500",
+                     description = "OpenAI 서버 에러가 발생했습니다."
             )
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6509d02 and 0a2f0f8.

📒 Files selected for processing (3)
  • src/main/java/com/webeye/backend/global/error/ErrorCode.java (1 hunks)
  • src/main/java/com/webeye/backend/imageanalysis/infrastructure/OpenAiClient.java (3 hunks)
  • src/main/java/com/webeye/backend/imageanalysis/presentation/swagger/ImageAnalysisSwagger.java (1 hunks)
🔇 Additional comments (3)
src/main/java/com/webeye/backend/global/error/ErrorCode.java (1)

16-16: 새로운 이미지 확장자 에러 코드가 적절히 추가되었습니다.

기존의 FILE_EXTENSION_NOT_FOUND와 구별되는 용도로 잘 정의되었습니다. 에러 메시지도 명확하고 HTTP 상태 코드도 적절합니다.

src/main/java/com/webeye/backend/imageanalysis/infrastructure/OpenAiClient.java (2)

30-30: import 문의 wildcard 사용이 적절합니다.

여러 ErrorCode 상수를 사용하고 있어 wildcard import가 코드 가독성을 향상시킵니다.


205-205: 이미지 URL 전처리 로직이 적절히 추가되었습니다.

trimImageUrl 호출로 이미지 URL의 유효성을 보장하여 안정성이 향상되었습니다.

Comment on lines +240 to +251
private String trimImageUrl(String url) {
for (ImageMimeType type : ImageMimeType.values()) {
String extension = "." + type.name().toLowerCase();
int index = url.toLowerCase().indexOf(extension);
if (index != -1) {
int endIndex = index + extension.length();
return url.substring(0, endIndex);
}
}
throw new BusinessException(NO_IMAGE_FILE_EXTENSION_FOUND);
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

trimImageUrl 메서드 구현을 개선할 수 있습니다.

현재 구현은 기본적으로 작동하지만 몇 가지 개선사항이 있습니다:

  1. 성능 최적화: 모든 ImageMimeType을 순회하는 대신 더 효율적인 방법 사용
  2. 정확성 향상: 현재는 URL 어디서든 확장자를 찾지만, URL의 path 부분에서만 찾는 것이 더 정확
  3. 코드 간소화: 로직을 더 간결하게 작성 가능

다음과 같이 개선을 제안합니다:

 private String trimImageUrl(String url) {
-    for (ImageMimeType type : ImageMimeType.values()) {
-        String extension = "." + type.name().toLowerCase();
-        int index = url.toLowerCase().indexOf(extension);
-        if (index != -1) {
-            int endIndex = index + extension.length();
-            return url.substring(0, endIndex);
-        }
-    }
-    throw new BusinessException(NO_IMAGE_FILE_EXTENSION_FOUND);
+    try {
+        // URL에서 path 부분만 추출 (쿼리 파라미터 제거)
+        String urlPath = url.split("\\?")[0];
+        
+        // path에서 파일명 추출
+        String fileName = urlPath.substring(urlPath.lastIndexOf('/') + 1);
+        
+        // 확장자 추출
+        int dotIndex = fileName.lastIndexOf('.');
+        if (dotIndex == -1) {
+            throw new BusinessException(NO_IMAGE_FILE_EXTENSION_FOUND);
+        }
+        
+        String extension = fileName.substring(dotIndex + 1).toLowerCase();
+        
+        // ImageMimeType에서 지원하는 확장자인지 확인
+        boolean isValidExtension = Arrays.stream(ImageMimeType.values())
+            .anyMatch(type -> type.name().toLowerCase().equals(extension));
+            
+        if (!isValidExtension) {
+            throw new BusinessException(UNSUPPORTED_IMAGE_TYPE);
+        }
+        
+        // 확장자까지만 포함하는 URL 반환
+        int extensionEndIndex = urlPath.lastIndexOf('.') + extension.length() + 1;
+        return url.substring(0, extensionEndIndex);
+        
+    } catch (Exception e) {
+        if (e instanceof BusinessException) {
+            throw e;
+        }
+        throw new BusinessException(NO_IMAGE_FILE_EXTENSION_FOUND);
+    }
 }

이렇게 하면:

  • URL의 path 부분에서만 확장자를 찾아 더 정확함
  • 쿼리 파라미터가 있어도 올바르게 처리됨
  • 지원하지 않는 확장자에 대해서도 적절한 에러 처리
  • 성능 개선 (순회 대신 직접 확장자 추출)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/com/webeye/backend/imageanalysis/infrastructure/OpenAiClient.java
around lines 240 to 251, improve the trimImageUrl method by extracting the file
extension only from the URL's path segment instead of searching the entire URL
string. Parse the URL to isolate the path, then check if it ends with any
supported ImageMimeType extension. Return the substring up to the extension's
end if found, otherwise throw the BusinessException. This approach enhances
accuracy by ignoring query parameters, improves performance by avoiding full
iteration over all types, and simplifies the code logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants