Skip to content

Conversation

@HyeonJooooo
Copy link
Collaborator

@HyeonJooooo HyeonJooooo commented Nov 30, 2025

#️⃣연관된 이슈

#200

📝작업 내용

구글 클라우드 vertual AI 사용 + Gemini 활용 코드+ 키 추가

스크린샷 (선택)

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

Summary by CodeRabbit

개선사항

  • 외부 링크 추천 기능이 Google Gemini AI로 업그레이드되었습니다.
  • 사용자의 관심사와 선호도에 맞는 더 정확하고 관련성 높은 링크를 추천합니다.
  • 개선된 AI 기술로 더욱 효과적인 콘텐츠 발견을 지원합니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@HyeonJooooo HyeonJooooo linked an issue Nov 30, 2025 that may be closed by this pull request
3 tasks
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 30, 2025

요약

Walkthrough

Google Cloud Vertex AI Gemini 클라이언트 라이브러리 의존성을 추가하고, 이를 활용하는 새로운 Gemini 기반 외부 링크 추천 서비스를 구현했습니다. 기존 Perplexity 서비스를 Gemini 서비스로 대체하여 외부 링크 검색 기능을 통합했습니다.

Changes

응집 / 파일 변경 요약
Gradle 의존성 추가
build.gradle
Google Cloud Vertex AI (Gemini) 클라이언트 라이브러리 com.google.cloud:google-cloud-vertexai:1.14.0 의존성 추가
Gemini 통합 서비스 구현
src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java
새 Spring 서비스 클래스: Vertex AI 클라이언트 초기화, Google Search 도구 설정, 생성 설정(최대 토큰, 온도, JSON MIME) 구성 및 GenerativeModel 구축; searchExternalLinks() 메서드로 시스템/사용자 프롬프트 기반 외부 링크 추천 반환; 도메인 필터링 및 결과 제한 기능 포함; JSON 응답 파싱 및 예외 처리 로직 구현
서비스 공급자 교체
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendServiceImpl.java,
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendWorker.java
PerplexityExternalSearchService 필드 제거 및 GeminiExternalSearchService 필드 추가; 메서드 호출 사이트를 Perplexity에서 Gemini로 변경; 관련 import 업데이트

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

주의 사항:

  • GeminiExternalSearchService의 프롬프트 구성, JSON 파싱 로직, 도메인 필터링 로직 검토 필요
  • Vertex AI 클라이언트 초기화 및 리소스 정리(PostConstruct/PreDestroy) 패턴 확인
  • searchExternalLinks() 메서드의 예외 처리 및 빈 결과 처리 방식 검증
  • 두 개의 서비스 교체 지점(ExternalRecommendServiceImpl, ExternalRecommendWorker)에서 모든 호출이 올바르게 업데이트되었는지 확인

Poem

🐰 새로운 Gemini가 도착했어,
Perplexity는 작별을 고했고,
외부 링크 추천, 더욱 똑똑해졌네!
Google 클라우드의 마법 속에서,
우리의 링크 여행이 시작되어! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Pull request title clearly describes the main change: replacing Perplexity with Google Cloud Vertex AI (Gemini) for external link recommendations.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#200-refactor-external-link

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@HyeonJooooo HyeonJooooo self-assigned this Nov 30, 2025
Copy link
Contributor

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

🧹 Nitpick comments (8)
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendServiceImpl.java (2)

56-70: 주석 및 에러 메시지가 이전 서비스(Perplexity)를 참조합니다.

Gemini 서비스로 전환되었지만, 코드 주석(Line 56)과 에러 메시지(Line 68)가 여전히 "Perplexity"를 참조하고 있습니다. 또한 System.err.println 대신 SLF4J 로거 사용을 권장합니다.

-        // Perplexity 기반 외부 추천 받기
+        // Gemini 기반 외부 추천 받기
         List<RecommendedLinkResponse> external;
         try {
             external = geminiExternalSearchService.searchExternalLinks(
                     recentUrls,
                     tagNames,
                     externalLimit,
                     jobName,
                     gender
             );
         } catch (Exception e) {
             // 🔴 어떤 예외가 와도 외부는 포기하고 빈 리스트로 폴백
-            System.err.println("[Perplexity] 외부 추천 실패: " + e.getMessage());
+            log.warn("[Gemini] 외부 추천 실패: {}", e.getMessage());
             external = List.of();
         }

로거 사용을 위해 클래스에 @Slf4j 어노테이션 추가가 필요합니다:

@Slf4j
@Service
@RequiredArgsConstructor
public class ExternalRecommendServiceImpl implements ExternalRecommendService {

10-11: 주석 처리된 코드를 제거하세요.

더 이상 사용하지 않는 PerplexityExternalSearchService 관련 import와 필드가 주석 처리되어 있습니다. 코드 정리를 위해 완전히 제거하는 것이 좋습니다.

-// import com.umc.linkyou.service.curation.perplexity.PerplexityExternalSearchService;
 import com.umc.linkyou.service.curation.gemini.GeminiExternalSearchService;
-    // private final PerplexityExternalSearchService perplexityExternalSearchService; // Perplexity 사용
     private final GeminiExternalSearchService geminiExternalSearchService; // Gemini 사용

Also applies to: 29-30

src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendWorker.java (2)

36-72: Javadoc, 주석, 로그 메시지가 여전히 Perplexity를 참조합니다.

Gemini 서비스로 전환되었지만 여러 곳에서 "Perplexity"를 참조하고 있습니다:

  • Line 37: Javadoc "Perplexity 호출"
  • Line 62: 주석 "// Perplexity"
  • Line 69, 71: 로그 메시지 "[Perplexity]"

코드 유지보수성을 위해 모두 업데이트가 필요합니다.

     /**
-     * Perplexity 호출 → curation_linku(type=EXTERNAL)에 url/title/imageUrl 저장
+     * Gemini 호출 → curation_linku(type=EXTERNAL)에 url/title/imageUrl 저장
      * 트랜잭션 경계는 이 Worker에서 관리한다.
      */
-        // Perplexity
+        // Gemini
         List<RecommendedLinkResponse> external;
         try {
             long t0 = System.currentTimeMillis();
             external = geminiExternalSearchService.searchExternalLinks(
                     recentUrls, topTags, externalLimit, jobName, gender
             );
-            log.info("[Perplexity] elapsed={}ms", System.currentTimeMillis() - t0);
+            log.info("[Gemini] elapsed={}ms", System.currentTimeMillis() - t0);
         } catch (Exception e) {
-            log.warn("[Perplexity] 외부 추천 실패: {}", e.toString());
+            log.warn("[Gemini] 외부 추천 실패: {}", e.toString());
             external = List.of();
         }

12-13: 주석 처리된 코드를 제거하세요.

ExternalRecommendServiceImpl과 마찬가지로, 더 이상 사용하지 않는 PerplexityExternalSearchService 관련 코드를 완전히 제거하는 것이 좋습니다.

Also applies to: 31-32

src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java (4)

112-136: 시스템 프롬프트를 유저 프롬프트와 분리하여 설정하세요.

현재 시스템 프롬프트와 유저 프롬프트가 문자열 연결로 전달되고 있습니다. Vertex AI GenerativeModel은 setSystemInstruction을 통해 시스템 지시사항을 별도로 설정할 수 있습니다. 이렇게 하면 모델이 역할과 규칙을 더 명확하게 인식합니다.

모델 빌더에서 시스템 지시사항을 설정하거나, 런타임에 Content를 분리하여 전달하는 방식을 고려하세요.


64-68: 검색 결과의 일관성을 위해 temperature 값을 낮추는 것을 고려하세요.

temperature: 0.9는 높은 창의성을 유도하지만, 외부 링크 검색처럼 사실 기반 결과가 필요한 경우에는 일관성이 떨어질 수 있습니다. 0.3~0.5 정도의 값이 더 안정적인 결과를 제공할 수 있습니다.

             GenerationConfig generationConfig = GenerationConfig.newBuilder()
                     .setMaxOutputTokens(2048)
-                    .setTemperature(0.9f) // 창의성(다양한 검색 결과)을 위해 높임
+                    .setTemperature(0.4f) // 검색 결과 일관성을 위해 낮춤
                     .setResponseMimeType("application/json") // JSON 응답 강제
                     .build();

142-154: JSON 파싱 실패에 대한 방어 코드를 강화하세요.

Gemini 응답이 예상 JSON 형식과 다를 경우 파싱 실패가 발생할 수 있습니다. 현재 외부 catch 블록에서 처리되지만, 더 구체적인 로깅이 도움이 될 수 있습니다.

-            // 5. JSON 파싱
-            List<Map<String, String>> parsed = objectMapper.readValue(jsonResponse, new TypeReference<>() {});
+            // 5. JSON 파싱 (Markdown 코드블록 제거)
+            String cleanJson = jsonResponse.trim();
+            if (cleanJson.startsWith("```")) {
+                cleanJson = cleanJson.replaceAll("^```json?\\s*", "").replaceAll("```$", "").trim();
+            }
+            List<Map<String, String>> parsed = objectMapper.readValue(cleanJson, new TypeReference<>() {});

21-21: 사용하지 않는 import를 제거하세요.

java.io.IOException이 import되어 있지만 코드에서 사용되지 않습니다.

-import java.io.IOException;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 305665a and 9c82c59.

📒 Files selected for processing (4)
  • build.gradle (1 hunks)
  • src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java (1 hunks)
  • src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendServiceImpl.java (3 hunks)
  • src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendWorker.java (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java (2)
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendServiceImpl.java (1)
  • Service (20-87)
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendWorker.java (1)
  • Service (22-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Comment on lines +88 to +89
// Google Cloud Vertex AI (Gemini)
implementation 'com.google.cloud:google-cloud-vertexai:1.14.0'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

google-cloud-vertexai latest version maven central 2025

💡 Result:

Latest Maven Central version (Nov 30, 2025): com.google.cloud:google-cloud-vertexai — 1.40.0 (released Nov 15, 2025). [1][2]

Maven coordinate:

com.google.cloud
google-cloud-vertexai
1.40.0

Sources:
[1] Google Cloud Java docs (google-cloud-vertexai overview, 1.40.0).
[2] Maven Central (repo1.maven.org) listing for com/google/cloud/google-cloud-vertexai showing 1.40.0 (2025-11-15).


Update google-cloud-vertexai to the latest version.

The dependency is outdated. Version 1.14.0 is significantly behind the current latest release (1.40.0, released November 15, 2025). Update the dependency to:

implementation 'com.google.cloud:google-cloud-vertexai:1.40.0'

This 26-version gap includes important updates, security patches, and feature improvements that should be incorporated.

🤖 Prompt for AI Agents
In build.gradle around lines 88 to 89 the google-cloud-vertexai dependency is
pinned to 1.14.0 and needs to be updated; change the implementation coordinate
to use the latest release (replace 1.14.0 with 1.40.0) so the project pulls the
updated library with recent fixes and features, then run a dependency
refresh/build to verify no breaking changes and update any code or tests if the
newer client API requires adjustments.

Comment on lines +49 to +83
@PostConstruct
public void init() {
try {
// 1. Vertex AI 클라이언트 초기화
// (참고: 로컬 개발 시 환경변수 GOOGLE_APPLICATION_CREDENTIALS 설정 필수)
this.vertexAI = new VertexAI(projectId, location);

// 2. 구글 검색 도구(Grounding) 설정
Tool googleSearchTool = Tool.newBuilder()
.setGoogleSearchRetrieval(
GoogleSearchRetrieval.newBuilder().build()
)
.build();

// 3. 생성 설정 (JSON 포맷 강제 등)
GenerationConfig generationConfig = GenerationConfig.newBuilder()
.setMaxOutputTokens(2048)
.setTemperature(0.9f) // 창의성(다양한 검색 결과)을 위해 높임
.setResponseMimeType("application/json") // JSON 응답 강제
.build();

// 4. 모델 생성
this.model = new GenerativeModel.Builder()
.setModelName(modelName)
.setVertexAi(vertexAI)
.setTools(Collections.singletonList(googleSearchTool))
.setGenerationConfig(generationConfig)
.build();

log.info("✅ Gemini Search Service 초기화 완료 (Project: {}, Location: {})", projectId, location);

} catch (Exception e) {
log.error("❌ Gemini 초기화 실패: GCP 인증 파일이나 설정을 확인하세요.", e);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

초기화 실패 시 서비스가 비정상 상태로 유지됩니다.

init() 메서드에서 예외 발생 시 로그만 남기고 계속 진행되어 modelnull인 상태로 서비스가 Spring 컨테이너에 등록됩니다. 이후 searchExternalLinks() 호출 시 NullPointerException이 발생할 수 있습니다.

초기화 실패 시 애플리케이션 시작을 중단하거나, searchExternalLinks()에서 model null 체크를 추가하세요:

     @PostConstruct
     public void init() {
         try {
             // ... 초기화 코드 ...
             log.info("✅ Gemini Search Service 초기화 완료 (Project: {}, Location: {})", projectId, location);

         } catch (Exception e) {
-            log.error("❌ Gemini 초기화 실패: GCP 인증 파일이나 설정을 확인하세요.", e);
+            log.error("❌ Gemini 초기화 실패: GCP 인증 파일이나 설정을 확인하세요.", e);
+            throw new IllegalStateException("Gemini 서비스 초기화 실패", e);
         }
     }

또는 searchExternalLinks() 시작 부분에 방어 코드를 추가하세요:

if (this.model == null) {
    log.warn("Gemini 모델이 초기화되지 않음 - 빈 결과 반환");
    return Collections.emptyList();
}
🤖 Prompt for AI Agents
In
src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java
around lines 49–83, the init() catch currently only logs the exception which
leaves the service registered with a null model; update the catch to fail fast
by rethrowing a RuntimeException (wrap the caught Exception with a clear
message) so Spring does not start the app in a broken state, and additionally
add a defensive null-check at the start of searchExternalLinks() that logs a
warning and returns an empty list if this.model is null to avoid
NullPointerException in case initialization still failed.

@HyeonJooooo HyeonJooooo merged commit 9ed812c into develop Nov 30, 2025
2 checks passed
@HyeonJooooo HyeonJooooo mentioned this pull request Dec 1, 2025
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.

REFACTOR: 외부링크추천AI

2 participants