Skip to content

[LLM] RAG 지역 기반 필터링 검색 추가 및 PIPELINE+LLM 파트 통합#23

Merged
s0ooo0k merged 11 commits into
PETTY-HUB:mainfrom
s0ooo0k:main
Apr 23, 2025
Merged

[LLM] RAG 지역 기반 필터링 검색 추가 및 PIPELINE+LLM 파트 통합#23
s0ooo0k merged 11 commits into
PETTY-HUB:mainfrom
s0ooo0k:main

Conversation

@s0ooo0k
Copy link
Copy Markdown
Member

@s0ooo0k s0ooo0k commented Apr 23, 2025

📜 PR 내용 요약

지역 기반 필터링을 구현하여 응답 속도를 높였고, PIPELINE과 통합하여 VISION에서 받은 이미지 보고서로 바로 관광지 검색 결과를 추천하게 하였습니다.

  • 임의로 생성한 VISION 보고서와 위치 정보를 입력하였을 때 다음과 같이 결과가 나오는 것을 테스트 했습니다.
// 프롬프트
쫑쫑이는 3살 말티즈이며, 순한 편입니다. 하얀색 털을 가지고 있으며 4kg 정도로 추정됩니다.
강원특별자치도 강릉시

image

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

Part1. RAG 지역 기반 필터링 검색

  • Qdrant 벡터 검색의 정확도를 높이기 위해 지역(areaCode) 기반 필터 조건 추가
    🧷 Spring AI Vector DB의 filterExpression 관련 Docs
  • AreaCode, ContentType : enum을 도입하여 DB의 areaCode와 실제 지역 이름 매칭 재사용 가능하게 구성
  • PIPELINE 파트 통합을 위한 입출력 형식 통일

Part2. PIPELINE + LLM 파트 통합

  • RecommendControllerRecommendServiceImpl, RecommendService로 분리
  • PromptGeneratorServiceImpl 일부 수정(, 오류)
    • ,이 없어 JSON 출력 결과 파싱이 제대로 되지 않는 이슈를 해결했습니다.
  • PipelineController를 통한 LLM 파트와 통합
  • 시군구 코드와 주소 정보를 메타데이터에 추가하여 향후 시군구 기반 확장 검색이 가능하게 구성했습니다.

📚 기타 참고 사항 및 리뷰 포인트

  • 실행 시 Open AI Key가 필요합니다. 과금 관련 이슈로 현재 yml 파일에서 제외하였으므로, 테스트 시 문의 부탁 드립니다.
  • RecommendController에서 처리하던 추천 로직을 PIPELINE 파트와 통합을 위하여 서비스 위임하여 분리했습니다. 이 부분에서 오류가 없는지 확인 부탁드립니다.

🔧 추후 확장 예정

  • 현재 글로만 나오는 검색 결과 방식을 이미지 동시 출력 혹은 상세 페이지 연결 방식으로 전환하려 합니다.

s0ooo0k added 11 commits April 22, 2025 03:09
- AreaCode, ContentType enum 추가로 코드 변환 추가
- 사용자 입력 location 기반 areaCode 추출 및 필터 적용
- 유사도 검색 시 지역 필터 반영으로 추천 결과 정확도 향상
[feat] 지역 기반 필터링 도입 및 추천 정확도 향상을 위한 구조 개선
- 시군구 enum 추가
- sigunguCode metadata에 추가 저장
- RecommendController를 RecommendServiceImpl로 분리
- PromptGeneratorServiceImpl 일부 수정(, 오류)
- PipelineController를 통한 LLM 파트와 통합
[feat] PIPELINE+LLM 파트 통합 및 RecommendController 로직 분리
@PETTY-HUB PETTY-HUB deleted a comment from coderabbitai Bot Apr 23, 2025
@s0ooo0k
Copy link
Copy Markdown
Member Author

s0ooo0k commented Apr 23, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2025

Summary by CodeRabbit

  • 신규 기능

    • 대한민국 행정구역 코드 및 콘텐츠 유형을 나타내는 enum이 추가되었습니다.
    • 반려동물 추천 기능이 RecommendService 및 RecommendServiceImpl로 분리되어 서비스화되었습니다.
  • 버그 수정

    • 프롬프트 생성 시 JSON 형식 오류를 수정하여 올바른 구문을 보장합니다.
  • 기능 개선

    • 추천 컨트롤러와 파이프라인 컨트롤러가 새로운 RecommendService를 사용하도록 개선되었습니다.
    • 임베딩 서비스의 텍스트 준비 및 문서 생성 방식이 자연스러운 설명과 풍부한 메타데이터로 개선되었습니다.
    • 유사도 검색 시 지역 기반 필터링 및 상세 로깅이 추가되었습니다.
    • 콘텐츠 조회 개수가 10개에서 20개로 확대되었습니다.
  • 스타일/사용성

    • 추천 입력 폼에 반려동물 유형 필드명이 species로 변경되고, 맹견 여부 선택 드롭다운이 추가되었습니다.
    • recommend.html에 lang="ko" 속성이 추가되었습니다.

Walkthrough

이번 변경 사항에서는 추천 로직의 구조적 리팩토링과 관련 유틸리티 기능의 확장이 이루어졌습니다. 추천 컨트롤러는 추천 서비스로 로직을 위임하도록 수정되었고, 추천 서비스 인터페이스 및 구현체가 새롭게 추가되었습니다. 행정구역 코드와 콘텐츠 유형을 다루는 enum이 도입되어 데이터 표준화가 강화되었습니다. 임베딩 서비스와 벡터스토어 서비스의 텍스트 준비, 문서 생성, 로깅 기능이 개선되었으며, 파이프라인 컨트롤러는 JSON 기반 프롬프트 파싱 및 추천 서비스 연동 방식으로 변경되었습니다. 프론트엔드 폼에도 입력 필드 명칭 및 옵션이 추가되었습니다.

Changes

파일/경로 요약 변경 요약
build.gradle 파일 끝의 공백 줄 삭제
.../common/AreaCode.java, .../common/ContentType.java 행정구역 코드 및 콘텐츠 유형 enum 신설, 코드/이름 기반 변환 메서드 추가
.../controller/RecommendController.java 추천 로직을 RecommendService로 위임, VectorStoreService/ChatService 의존성 제거, 프롬프트 생성 메서드 삭제
.../service/RecommendService.java, .../service/RecommendServiceImpl.java RecommendService 인터페이스 및 구현체 신설, 프롬프트 조립·지역 필터링·문서 검색·챗 응답 생성 통합
.../service/EmbeddingService.java 텍스트 준비 방식 자연어화, 문서 생성 메서드 추가, Javadoc 보강
.../service/VectorStoreService.java 임베딩 모델 필드 제거, 유사도 검색+필터 메서드 활성화 및 로깅 추가
.../pipeline/controller/PipelineController.java TogetherPromptBuilder 결과를 JSON 파싱 후 RecommendService로 위임, 로깅 추가
.../pipeline/service/PromptGeneratorServiceImpl.java JSON 문자열 포맷에 콤마 추가
.../tour/repository/ContentRepository.java 상위 10개 → 20개 콘텐츠 조회로 메서드 변경
.../resources/templates/recommend.html <html lang="ko"> 속성 추가, 반려동물 종류 필드명 변경, 맹견 여부 선택 필드 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RecommendController
    participant RecommendServiceImpl
    participant VectorStoreService
    participant ChatService

    User->>RecommendController: 추천 요청 (promptMap)
    RecommendController->>RecommendServiceImpl: recommend(promptMap)
    RecommendServiceImpl->>RecommendServiceImpl: buildPrompt(promptMap)
    RecommendServiceImpl->>RecommendServiceImpl: buildRegion(promptMap)
    RecommendServiceImpl->>VectorStoreService: findSimilarWithFilter(query, k, filter)
    VectorStoreService-->>RecommendServiceImpl: 유사 문서 리스트 반환
    RecommendServiceImpl->>ChatService: generate(prompt, 유사 문서)
    ChatService-->>RecommendServiceImpl: 추천 결과 반환
    RecommendServiceImpl-->>RecommendController: 추천 결과 반환
    RecommendController-->>User: 추천 결과 응답
Loading

Possibly related PRs

Poem

🐰
추천의 길을 새로이 짓고
enum으로 지역과 유형을 빚고
프롬프트는 JSON으로 변신했네
서비스 계층에서 논리 펼치고
맹견도, 소형견도 모두 환영!
PETTY의 봄, 코드에 꽃이 피었네

🌸

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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 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.

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: 4

🧹 Nitpick comments (10)
src/main/java/io/github/petty/llm/common/AreaCode.java (1)

41-47: 이름 기반 지역 매핑 메서드에 개선 가능한 부분이 있습니다.

현재 구현은 지역명이 포함되는 경우 첫 번째로 매칭되는 지역을 반환합니다. 그러나 일부 지역명이 다른 지역명에 포함될 수 있어 모호성이 발생할 수 있습니다(예: "경기도 부산시"와 같이 입력되면 "경기도"가 반환됨).

정확한 매칭을 위해 다음과 같은 개선을 고려해보세요:

public static AreaCode fromName(String name) {
+    if (name == null) return ETC;
+    
+    // 정확한 일치 먼저 시도
+    for (AreaCode a : values()) {
+        if (name.equals(a.name))
+            return a;
+    }
+    
+    // 포함 관계로 검색
     for (AreaCode a : values()) {
-        if (name != null && name.contains(a.name))
+        if (name.contains(a.name))
             return a;
     }
     return ETC;
}
src/main/java/io/github/petty/llm/service/RecommendService.java (1)

5-7: 인터페이스 구조 및 문서화 개선 제안

인터페이스가 간결하게 설계되어 있어 좋습니다. 그러나 메서드와 파라미터에 대한 Javadoc 문서화가 없어 해당 서비스의 용도와 입출력 규격을 이해하기 어렵습니다. 또한 Map<String, String>은 유연하지만 타입 안전성이 떨어집니다.

다음과 같이 문서화와 함께 필요한 경우 별도 DTO 클래스 도입을 고려해보세요:

 public interface RecommendService {
+    /**
+     * 사용자의 입력 프롬프트를 바탕으로 추천 결과를 생성합니다.
+     * 
+     * @param promptMap 사용자 입력 프롬프트 정보를 담은 맵 (species, location, weight 등)
+     * @return 생성된 추천 결과 문자열
+     */
     String recommend(Map<String, String> promptMap);
 }

장기적으로는 다음과 같은 DTO 클래스 사용을 고려해보세요:

public record RecommendRequest(
    String species,
    String location,
    String weight,
    Boolean isDanger,
    String info
) {}
src/main/resources/templates/recommend.html (1)

77-82: 맹견 여부 필드 추가 적절

맹견 여부를 체크하는 드롭다운 추가는 추천 시스템에 중요한 맥락 정보를 제공합니다. 다만 사용자 입력 값 검증 로직이 누락되어 있습니다.

폼 제출 전 클라이언트 측 유효성 검사를 추가하는 것이 좋습니다:

 form.addEventListener("submit", async function(e) {
     e.preventDefault();
+    
+    const formData = new FormData(form);
+    // 필수 입력 필드 검증
+    if (!formData.get('species') || !formData.get('location')) {
+        alert('종과 위치는 필수 입력 항목입니다.');
+        return;
+    }
 
-    const formData = new FormData(form);
     const json = Object.fromEntries(formData.entries());
src/main/java/io/github/petty/llm/service/VectorStoreService.java (1)

51-68: 필터링 기능 및 로깅 개선 구현 적절

필터 표현식을 사용한 유사 콘텐츠 검색 기능이 잘 구현되었습니다. 상세한 로깅을 추가한 것도 디버깅과 문제 해결에 도움이 될 것입니다. 다만 몇 가지 개선점이 있습니다:

  1. filterExpression 파라미터에 대한 문서화가 부족합니다.
  2. 대량의 결과를 로깅할 경우 성능 영향이 있을 수 있습니다.

다음과 같이 개선해보세요:

+    /**
+     * 필터 조건을 사용한 유사 콘텐츠 검색
+     * 
+     * @param query 검색 쿼리
+     * @param k 반환할 최대 결과 수
+     * @param filterExpression 필터 표현식 (예: "metadata.areaCode=='1'")
+     * @return 필터링된 유사 문서 목록
+     */
     public List<Document> findSimilarWithFilter(String query, int k, String filterExpression) {
         SearchRequest searchRequest = SearchRequest.builder()
                 .query(query)
                 .topK(k)
                 .similarityThreshold(0.1)
                 .filterExpression(filterExpression)
                 .build();

         List<Document> results = vectorStore.similaritySearch(searchRequest);

-        log.info("유사 콘텐츠 검색 결과");
+        if (log.isDebugEnabled()) {
+            log.debug("유사 콘텐츠 검색 결과 (총 {}개)", results.size());
+            for (int i = 0; i < results.size(); i++) {
+                Document doc = results.get(i);
+                log.debug("▶ 결과 {}: ID={}, Metadata={}, Content={}",
+                        i + 1, doc.getId(), doc.getMetadata(), doc.getText());
+            }
+        } else {
+            log.info("유사 콘텐츠 검색 결과: {}개 항목 검색됨", results.size());
+        }
-        for (int i = 0; i < results.size(); i++) {
-            Document doc = results.get(i);
-            log.info("▶ 결과 {}: ID={}, Metadata={}, Content={}",
-                    i + 1, doc.getId(), doc.getMetadata(), doc.getText());
-        }
         return results;
     }
src/main/java/io/github/petty/llm/service/RecommendServiceImpl.java (2)

7-9: 불필요한 컨트롤러 관련 임포트가 있습니다.

서비스 클래스에서 컨트롤러 관련 어노테이션이나 클래스는 필요하지 않습니다. 다음 임포트를 제거해주세요:

-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;

67-78: 지역 필터링 로직 개선 가능성

현재 buildRegion 메서드는 위치 문자열의 첫 번째 부분만 처리하고 있어 도시나 구체적인 지역 정보가 무시됩니다. 더 세분화된 지역 필터링을 위해 개선이 필요합니다.

private String buildRegion(String location) {
    // ETC (지역 없을 때 대비)
    if (location == null || location.isBlank()) return "areaCode == 0";

    String[] parts = location.trim().split(" ");
    if (parts.length == 0) return "areaCode == 0";

    String areaName = parts[0];
    AreaCode areaCode = AreaCode.fromName(areaName);
+    
+    // 시/군/구 정보가 있는 경우 추가 필터링 로직
+    if (parts.length > 1) {
+        // TODO: 시/군/구 코드로 추가 필터링 구현
+        // 예: return "areaCode == %d AND sigunguCode == %d".formatted(areaCode.getCode(), sigunguCode);
+    }
    
    return "areaCode == %d".formatted(areaCode.getCode());
}

시/군/구 기반 필터링을 추가하는 것이 향후 계획에 맞는지 확인해주세요. PR 목표에 따르면 지역 기반 필터링 기능을 추가하고 있으며, 지구 코드와 주소 정보를 포함한 메타데이터를 추가하여 지구 기반 검색의 향후 확장을 지원한다고 합니다.

src/main/java/io/github/petty/llm/controller/RecommendController.java (1)

3-5: 사용하지 않는 임포트가 있습니다.

AreaCode를 임포트했지만 컨트롤러에서 사용하지 않습니다. 사용하지 않는 임포트는 제거해주세요.

-import io.github.petty.llm.common.AreaCode;
src/main/java/io/github/petty/llm/service/EmbeddingService.java (3)

37-41: 잠재적인 NullPointerException 위험이 감소했습니다.

변수 추출과 null 체크를 통해 코드의 안정성이 향상되었습니다. 변수명의 일관성을 위해 소문자로 시작하는 것이 좋겠습니다.

String title = content.getTitle();
String addr1 = content.getAddr1() != null ? content.getAddr1() : "";
String addr2 = content.getAddr2() != null ? content.getAddr2() : "";
-String ContentTypeName = ContentType.fromCode(content.getContentTypeId()).getName();
+String contentTypeName = ContentType.fromCode(content.getContentTypeId()).getName();

44-45: 자연스러운 텍스트 생성 방식이 개선되었습니다.

장소와 관련된 정보를 자연스러운 문장으로 구성하는 방식이 사용자 경험을 향상시킵니다. 한 가지 개선점은 한글의 조사 처리입니다.

-sb.append("%s은/는 %s %s에 위치한 %s 장소입니다.\n"
+sb.append("%s%s %s %s에 위치한 %s 장소입니다.\n"
-        .formatted(title, addr1, addr2, ContentTypeName));
+        .formatted(title, getJosaForKorean(title), addr1, addr2, contentTypeName));

// 추가할 메서드
private String getJosaForKorean(String word) {
    if (word == null || word.isEmpty()) return "은/는";
    
    char lastChar = word.charAt(word.length() - 1);
    // 한글의 유니코드 범위 내에 있는지 확인
    if (lastChar >= '가' && lastChar <= '힣') {
        // 받침이 있는지 확인 (유니코드 값을 이용)
        if ((lastChar - '가') % 28 == 0) return "는"; // 받침 없음
        else return "은"; // 받침 있음
    }
    return "은/는"; // 한글이 아닌 경우 기본값
}

79-101: 메타데이터를 풍부하게 포함한 문서 생성 로직이 추가되었습니다.

문서 생성 시 지역 코드, 시군구 코드, 주소 등의 메타데이터를 포함하여 향후 지역 기반 필터링을 위한 기반이 잘 마련되었습니다. 지역 코드와 콘텐츠 타입에 대한 상수 사용을 고려해보세요.

// 메타데이터에 원본 ID, 관련 데이터 저장
Map<String, Object> metadata = new HashMap<>();
metadata.put("contentId", result.contentId());
metadata.put("title", content.getTitle());

// areaCode (지역 관련 추가)
-metadata.put("areaCode", content.getAreaCode());
+metadata.put("areaCode", content.getAreaCode()); // 상수 키 사용 고려: MetadataKeys.AREA_CODE
// sigunguCode (시군구 추가)
-metadata.put("sigunguCode", content.getSigunguCode());
+metadata.put("sigunguCode", content.getSigunguCode()); // 상수 키 사용 고려: MetadataKeys.SIGUNGU_CODE
// 지역 String 추가
-metadata.put("address", content.getAddr1());
+metadata.put("address", content.getAddr1()); // 상수 키 사용 고려: MetadataKeys.ADDRESS
// contentType (콘텐츠 관련 추가)
-metadata.put("contentType", content.getContentTypeId());
+metadata.put("contentType", content.getContentTypeId()); // 상수 키 사용 고려: MetadataKeys.CONTENT_TYPE

다음과 같은 상수 클래스 추가를 고려해보세요:

public final class MetadataKeys {
    private MetadataKeys() {} // 인스턴스화 방지
    
    public static final String CONTENT_ID = "contentId";
    public static final String TITLE = "title";
    public static final String AREA_CODE = "areaCode";
    public static final String SIGUNGU_CODE = "sigunguCode";
    public static final String ADDRESS = "address";
    public static final String CONTENT_TYPE = "contentType";
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between adcf207 and d9d7728.

📒 Files selected for processing (12)
  • build.gradle (1 hunks)
  • src/main/java/io/github/petty/llm/common/AreaCode.java (1 hunks)
  • src/main/java/io/github/petty/llm/common/ContentType.java (1 hunks)
  • src/main/java/io/github/petty/llm/controller/RecommendController.java (2 hunks)
  • src/main/java/io/github/petty/llm/service/EmbeddingService.java (2 hunks)
  • src/main/java/io/github/petty/llm/service/RecommendService.java (1 hunks)
  • src/main/java/io/github/petty/llm/service/RecommendServiceImpl.java (1 hunks)
  • src/main/java/io/github/petty/llm/service/VectorStoreService.java (4 hunks)
  • src/main/java/io/github/petty/pipeline/controller/PipelineController.java (3 hunks)
  • src/main/java/io/github/petty/pipeline/service/PromptGeneratorServiceImpl.java (1 hunks)
  • src/main/java/io/github/petty/tour/repository/ContentRepository.java (1 hunks)
  • src/main/resources/templates/recommend.html (2 hunks)
🔇 Additional comments (11)
build.gradle (1)

69-69: 빌드스크립트에 영향 없는 공백 제거
파일 말미의 개행 문자가 제거되었으며, 의존성이나 빌드 로직에는 전혀 영향이 없습니다. 포맷 수정만 승인합니다.

src/main/java/io/github/petty/pipeline/service/PromptGeneratorServiceImpl.java (1)

16-18: JSON 구문 오류 수정이 잘 이루어졌습니다.

포맷 문자열에 콤마가 추가되어 JSON 구문 오류를 해결했습니다. 이전에는 extractedPetInfoJson과 "location" 필드 사이에 콤마가 없어 JSON 파싱 오류가 발생했을 것입니다.

src/main/java/io/github/petty/llm/common/ContentType.java (1)

1-30: 콘텐츠 타입 열거형 구현이 잘 되었습니다.

콘텐츠 타입을 표준화하는 열거형을 추가하여 코드와 이름을 매핑하는 방식이 적절합니다. fromCode 메서드는 코드가 없거나 일치하는 값이 없을 때 ETC로 기본 반환하여 안정성을 확보했습니다.

src/main/java/io/github/petty/llm/common/AreaCode.java (2)

1-30: 지역 코드 열거형이 잘 구현되었습니다.

한국 행정구역을 표준화하는 열거형을 추가하여 코드와 이름을 매핑하는 방식이 적절합니다. Lombok 어노테이션을 활용한 코드 간소화도 좋습니다.


31-39: 코드 기반 지역 매핑 메서드가 잘 구현되었습니다.

코드값을 통해 지역 열거형을 조회하는 메서드가 적절히 구현되었습니다. null 체크와 기본값 처리가 잘 되어 있습니다.

src/main/java/io/github/petty/tour/repository/ContentRepository.java (1)

27-28: 테스트 데이터 수 증가가 적절히 이루어졌습니다.

테스트에 사용되는 콘텐츠 조회 수를 10개에서 20개로 증가시켰습니다. 이는 지역 기반 필터링 구현과 관련하여 더 다양한 테스트 데이터를 확보하기 위한 적절한 변경으로 보입니다.

src/main/resources/templates/recommend.html (2)

3-3: 접근성 향상을 위한 언어 속성 추가 적절

HTML 태그에 lang="ko" 속성을 추가한 것은 접근성과 SEO 측면에서 좋은 변경입니다.


74-74: 필드명 변경 적절

type에서 species로 변경한 것은 의미적으로 더 명확하고 적절합니다.

src/main/java/io/github/petty/pipeline/controller/PipelineController.java (1)

3-6: RecommendService 의존성 주입 적절

RecommendService 인터페이스를 주입받아 사용하는 구조로 변경한 것은 관심사 분리와 책임 위임 측면에서 좋은 접근입니다. 필요한 import들도 적절히 추가되었습니다.

Also applies to: 15-16, 23-23

src/main/java/io/github/petty/llm/service/VectorStoreService.java (1)

39-39: 메서드 주석 변경 적절

메서드 주석을 "유사한 콘텐츠 검색 (SearchRequest 기반)"에서 "유사도 검색"으로 간결하게 변경한 것은 적절합니다.

src/main/java/io/github/petty/llm/controller/RecommendController.java (1)

22-28: 서비스 위임 패턴이 잘 적용되었습니다.

컨트롤러에서 비즈니스 로직을 분리하여 서비스 계층으로 위임하는 좋은 예입니다. 이렇게 하면 책임이 명확하게 분리되고 테스트가 용이해집니다.

Comment on lines +39 to 46
// String prompt = togetherPromptBuilder.buildPrompt(visionReport, location);
String jsonPrompt = togetherPromptBuilder.buildPrompt(visionReport, location);
log.info(jsonPrompt);
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> promptMapper = objectMapper.readValue(jsonPrompt, new TypeReference<>() {});
log.info(promptMapper.toString());
String prompt = recommendService.recommend(promptMapper);
model.addAttribute("recommendation", prompt);
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

JSON 파싱 및 서비스 위임 로직 개선 필요

JSON 프롬프트를 파싱하고 RecommendService로 위임하는 로직은 적절하나, 몇 가지 개선이 필요합니다:

  1. 주석 처리된 코드는 제거하는 것이 좋습니다.
  2. JSON 파싱 예외 처리가 세분화되어 있지 않습니다.
  3. ObjectMapper는 클래스 필드로 선언하는 것이 효율적입니다.

다음과 같이 개선해보세요:

-//            String prompt = togetherPromptBuilder.buildPrompt(visionReport, location);
-            String jsonPrompt = togetherPromptBuilder.buildPrompt(visionReport, location);
-            log.info(jsonPrompt);
-            ObjectMapper objectMapper = new ObjectMapper();
-            Map<String, String> promptMapper = objectMapper.readValue(jsonPrompt, new TypeReference<>() {});
-            log.info(promptMapper.toString());
-            String prompt = recommendService.recommend(promptMapper);
+            String jsonPrompt = togetherPromptBuilder.buildPrompt(visionReport, location);
+            log.info("생성된 JSON 프롬프트: {}", jsonPrompt);
+            
+            try {
+                ObjectMapper objectMapper = new ObjectMapper();
+                Map<String, String> promptMapper = objectMapper.readValue(jsonPrompt, new TypeReference<>() {});
+                log.info("파싱된 프롬프트 맵: {}", promptMapper);
+                
+                String prompt = recommendService.recommend(promptMapper);
+                model.addAttribute("recommendation", prompt);
+                return "pipeline";
+            } catch (JsonProcessingException e) {
+                log.error("JSON 프롬프트 파싱 중 오류 발생", e);
+                model.addAttribute("error", "프롬프트 형식이 올바르지 않습니다.");
+                return "pipeline";
+            }

또는 ObjectMapper를 클래스 필드로 선언하는 방식:

 public class PipelineController {
 
     private final TogetherPromptBuilder togetherPromptBuilder;
     private final RecommendService recommendService;
+    private final ObjectMapper objectMapper = new ObjectMapper();

@@ -1,5 +1,6 @@
package io.github.petty.llm.service;

import groovy.util.logging.Slf4j;
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

중복된 로깅 어노테이션 제거 필요

Slf4j 어노테이션이 두 가지 다른 패키지(groovy.util.logging.Slf4jlombok.extern.slf4j.Slf4j)에서 중복 import 및 사용되고 있습니다. 이는 혼란을 야기할 수 있습니다.

다음과 같이 수정해주세요:

-import groovy.util.logging.Slf4j;
+import lombok.extern.slf4j.Slf4j;
 import io.github.petty.tour.entity.Content;
 import lombok.RequiredArgsConstructor;
 import org.springframework.ai.document.Document;
 import org.springframework.ai.embedding.EmbeddingModel;
 ...

-@lombok.extern.slf4j.Slf4j
 @Slf4j
 @Service
 @RequiredArgsConstructor

Also applies to: 18-19

Comment on lines +33 to +38
try {
String result = chatService.generateFromPrompt(userPrompt, docs);
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
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

예외 처리를 개선해야 합니다.

현재 모든 예외를 RuntimeException으로 래핑하고 있어 원인 파악이 어렵습니다. 구체적인 예외 타입을 사용하고 의미 있는 메시지를 추가하세요.

try {
    String result = chatService.generateFromPrompt(userPrompt, docs);
    return result;
} catch (Exception e) {
-    throw new RuntimeException(e);
+    throw new RuntimeException("추천 생성 중 오류가 발생했습니다: " + e.getMessage(), 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.

Suggested change
try {
String result = chatService.generateFromPrompt(userPrompt, docs);
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
String result = chatService.generateFromPrompt(userPrompt, docs);
return result;
} catch (Exception e) {
- throw new RuntimeException(e);
+ throw new RuntimeException("추천 생성 중 오류가 발생했습니다: " + e.getMessage(), e);
}

Comment on lines +52 to +56
if(isDanger.equals("true")) {
sb.append("맹견이에요\n");
} else {
sb.append("소형견, 중형견이에요\n");
}
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

NullPointerException 발생 가능성이 있습니다.

isDanger 변수가 null일 경우 equals() 메서드 호출 시 NullPointerException이 발생할 수 있습니다. null 체크 로직을 추가하세요.

-if(isDanger.equals("true")) {
+if("true".equals(isDanger)) {
    sb.append("맹견이에요\n");
} else {
    sb.append("소형견, 중형견이에요\n");
}

또는 null 체크를 명시적으로 추가:

-if(isDanger.equals("true")) {
+if(isDanger != null && isDanger.equals("true")) {
    sb.append("맹견이에요\n");
} else {
    sb.append("소형견, 중형견이에요\n");
}
📝 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
if(isDanger.equals("true")) {
sb.append("맹견이에요\n");
} else {
sb.append("소형견, 중형견이에요\n");
}
if("true".equals(isDanger)) {
sb.append("맹견이에요\n");
} else {
sb.append("소형견, 중형견이에요\n");
}

@Juyoung8563
Copy link
Copy Markdown
Contributor

리뷰

  • generatePrompt의 오류를 수정해주셔서 감사합니다.
  • 재사용이 가능하도록 enum을 채택한 점이 인상 깊었습니다.
  • 책임 분할과 전체적인 통합을 진행하시느라 고생 많으셨습니다.

제안

  • NullPointerException 관련 부분만 수정하면 좋을 것 같습니다!

좋은 코드 작성해주셔서 감사합니다 고생 많으셨습니다.

@s0ooo0k
Copy link
Copy Markdown
Member Author

s0ooo0k commented Apr 23, 2025

  • NullPointerException 관련 부분만 수정하면 좋을 것 같습니다!

확인했습니다, null 예외 처리를 전반적으로 추가하도록 하겠습니다!

더불어 CodeRabbit 리뷰에서, PipelineController 개선 부분도 제안하여 함께 최종 수정 거치면 될 것 같습니다.
빠른 리뷰 감사합니다

@23MinL
Copy link
Copy Markdown
Contributor

23MinL commented Apr 23, 2025

✅ 코드 리뷰

  • 추천 로직의 구조 개선, 지역 기반 필터링 기능 추가, 그리고 PIPELINE+LLM 파트의 통합이 핵심으로 진행 한 것을 확인했습니다.
  • 구조 개선, enum 도입, 필터링 로직 추가 등 전반적으로 코드 품질이 좋아졌습니다.
  • EmbeddingService에서 null 체크와 변수명 일관성(소문자 시작)으로 코드 안정성이 높아졌습니다.
  • 한글 조사 처리 메서드(getJosaForKorean)가 추가되어, 자연스러운 문장 생성이 가능한 것을 확인 했습니다.
  • 시/군/구 세분화 필터링을 개선하면 좋을 것 같습니다.

전반적으로 기능 추가와 구조 개선이 잘 된 것을 확인했습니다. 세부 로직과 코드 품질 측면에서 보완하면 더욱 완성도 높은 코드가 될 것 같습니다.

@s0ooo0k
Copy link
Copy Markdown
Member Author

s0ooo0k commented Apr 23, 2025

시/군/구 세분화 필터링을 개선하면 좋을 것 같습니다.

확인했습니다! 금요일까지 추가할 수 있도록 해보겠습니다

@s0ooo0k s0ooo0k merged commit 9d2ef66 into PETTY-HUB:main Apr 23, 2025
2 checks passed
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.

3 participants