Skip to content

Conversation

@KiSeungMin
Copy link
Member

@KiSeungMin KiSeungMin commented Aug 18, 2025

✔️ 연관 이슈

📝 작업 내용

  • 위, 경도와 가게 이름을 전달받았을 때, 웨이블존 여부를 반환하는 API 성능 개선
  • 기존 방식
    • 거리가 30m 이내면서 글자가 일정 부분 일치하면 true
  • 개선된 방식
    • 거리 필터 우선 적용 -> 30m 내부 데이터에 대해서만 문자열 일치 여부 검증
    • 문자열 일치 여부를 memory를 사용해서 계산하도록 개선

스크린샷 (선택)

웨이블존 더미 데이터 1000개에 대한 응답 시간 비교입니다. 꽤 유의미한 결과여서 알고리즘을 개선하기로 결정했습니다.
(랜덤 값이어서 두 실험의 위,경도가 다릅니다.)

  • 개선 전
2025-08-18 18-17-24
  • 개선 후
2025-08-18 18-18-43

Summary by CodeRabbit

  • 신기능
    • 없음
  • 리팩터링
    • 근접 영역 검색 로직을 개선하여 지리적 후보 선별 후 텍스트 유사도 평가로 매칭 정확도와 응답 성능을 향상.
    • 결과 내 거리 표시가 제공되지 않거나 비어 있을 수 있습니다.
  • 테스트
    • 통합 테스트 데이터 규모를 확대(100→1000)하고, 요청-응답 시간 측정을 위한 성능 로그를 추가.

@KiSeungMin KiSeungMin self-assigned this Aug 18, 2025
@KiSeungMin KiSeungMin added the 💡 feature 기능 구현 및 개발 label Aug 18, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 18, 2025

Walkthrough

Elasticsearch 기반의 가중치/퍼지/와일드카드 복합 쿼리를 제거하고, 30m 내 후보 조회(ES geoDistance) 후 메모리 내 텍스트 유사도 검사(정규화, 포함, Levenshtein, Jaccard)로 첫 매칭을 반환하도록 변경. 거리 계산/매핑 제거. 통합 테스트는 샘플 수 확대(100→1000)와 성능 로그 추가, 거리 검증 삭제.

Changes

Cohort / File(s) Summary
Search repo: two-phase similarity pipeline
src/main/java/.../WaybleZoneQuerySearchRepository.java
ES 다중 should 쿼리 제거 → geoDistance로 최대 10개 후보 조회(findNearbyZones) 후 isTextSimilar로 필터링. Levenshtein/Jaccard 유사도 계산 유틸 추가. distance 정렬/매핑 제거, DTO에 distance null 전달.
Tests: scale-up and perf logging
src/test/java/.../WaybleZoneSearchApiIntegrationTest.java
SAMPLES 100→1000. /validate 테스트에 시간 측정 및 로그 출력 추가. 거리 임계/정확도 검증 삭제.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Service/Repo as WaybleZoneQuerySearchRepository
  participant ES as Elasticsearch

  Client->>Service/Repo: findSimilarWaybleZone(name, lat, lon)
  Service/Repo->>ES: geoDistance query (within 30m, size=10)
  ES-->>Service/Repo: Candidate documents
  loop In-memory similarity checks
    Service/Repo->>Service/Repo: normalize + contains/Levenshtein/Jaccard
  end
  Service/Repo-->>Client: First matching zone (distance=null) or null
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
사용자 장소 기능: 내 장소 요약 조회, 장소 내 웨이블존 페이지 조회, 웨이블존 삭제, Eager Loading 최적화 [#154] 변경 파일이 검색 레포지토리와 검색 통합 테스트에 한정됨.
페이징/정렬 이슈 해결 (0-based 위임) [#154] 관련 컨트롤러/요청 처리 코드 변경 없음.
색상(color)/저장수(savedCount) 필드 추가 및 증감 처리 [#154] 엔티티/리포지토리/서비스 레벨 변경 없음.
리뷰 작성 시 user_id 제거 및 보안 컨텍스트 사용 [#154] 리뷰 작성 API/DTO/보안 컨텍스트 관련 변경 없음.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
ES 퍼지/와일드카드 기반 findSimilarWaybleZone를 2단계(geoDistance + in-memory 유사도)로 변경 (src/main/java/.../WaybleZoneQuerySearchRepository.java) 링크된 이슈는 사용자 장소/리뷰 도메인 리팩터링으로, 검색 레포지토리의 텍스트 유사도/쿼리 전략 변경과 무관함.
WaybleZoneSearchApiIntegrationTest에서 샘플 수 확대 및 성능 로깅 추가, 거리 검증 제거 (src/test/java/.../WaybleZoneSearchApiIntegrationTest.java) 이슈 범위는 사용자 장소 API/리뷰 변경 검증이며, 검색 성능/거리 검증 테스트 변경과 직접적 연관이 없음.

Possibly related PRs

Suggested labels

🤔 test, 🔧 refactor

Suggested reviewers

  • zyovn
  • seung-in-Yoo
  • wonjun-lee-fcwj245

Poem

토끼 발로 탁탁, 코를 킁킁—
이름은 글자로, 거리는 발걸음.
가까운 후보 모아놓고,
레벤·자카드로 콕 집어내지!
숫자는 천으로, 속도는 찰칵!
오늘도 버그는 당근처럼 바삭 🥕✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/seungmin

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 0

🔭 Outside diff range comments (1)
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (1)

593-599: distance가 null일 때 String.format("%.3f", null)로 인한 NPE 가능성

/validate 경로는 distance를 null로 반환하도록 변경되었는데, 현재 로그 라인이 double 포맷을 강제해 NPE가 발생할 수 있습니다. null-safe 출력으로 수정하세요.

-        System.out.println("  거리: " + String.format("%.3f km", dto.distance()));
+        Double distance = dto.distance();
+        System.out.println("  거리: " + (distance == null ? "N/A" : String.format("%.3f km", distance)));
🧹 Nitpick comments (6)
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (1)

83-83: 샘플 수 1000개는 CI에서 부담 가능성 → 시스템 프로퍼티로 조정 가능하게

성능 실험 의도는 이해하지만, CI/로컬 환경에 따라 시간/메모리 비용이 커질 수 있습니다. 기본값은 유지하되, 환경별로 조절 가능하도록 하는 편이 안전합니다.

아래처럼 시스템 프로퍼티(예: IT_SAMPLES)로 오버라이드 가능하게 하세요.

-    private static final int SAMPLES = 1000;
+    private static final int SAMPLES = Integer.parseInt(System.getProperty("IT_SAMPLES", "1000"));
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (5)

129-147: 첫 매칭 반환은 비결정적일 수 있음 → 후보를 거리 오름차순으로 정렬 권장

현재 findNearbyZones가 정렬 없이 반환되므로 ES 내부 순서에 따라 첫 매칭이 달라질 수 있습니다. 근접 후보에서 텍스트 매칭을 하되, 거리 오름차순으로 정렬 후 첫 매칭을 선택하면 결정성이 올라가고 UX도 개선됩니다.

아래 findNearbyZones 정렬 추가 제안을 함께 적용하세요.


153-168: 후보 조회: 거리 정렬 추가 + 후보 상한 여유 확보

  • 정렬: 거리 오름차순 정렬을 추가해 결정성/일관성 향상.
  • 상한: 30m 내 ‘보통 10개 미만’ 가정은 밀집 지역에서 깨질 수 있습니다. 30m에서도 10개 이상이 가능하므로 상한을 조금 늘려 안전하게 가져오길 권장합니다(예: 50).
     private List<WaybleZoneDocument> findNearbyZones(WaybleZoneSearchConditionDto cond) {
         Query geoQuery = Query.of(q -> q
                 .geoDistance(gd -> gd
                         .field("address.location")
                         .location(loc -> loc.latlon(ll -> ll
                                 .lat(cond.latitude())
                                 .lon(cond.longitude())
                         ))
                         .distance("30m")
                 )
         );
 
-        NativeQuery nativeQuery = NativeQuery.builder()
-                .withQuery(geoQuery)
-                .withPageable(PageRequest.of(0, 10)) // 30m 이내는 보통 10개 미만
-                .build();
+        SortOptions geoSort = SortOptions.of(s -> s
+                .geoDistance(gds -> gds
+                        .field("address.location")
+                        .location(GeoLocation.of(gl -> gl
+                                .latlon(ll -> ll
+                                        .lat(cond.latitude())
+                                        .lon(cond.longitude())
+                                )
+                        ))
+                        .order(SortOrder.Asc)
+                )
+        );
+
+        NativeQuery nativeQuery = NativeQuery.builder()
+                .withQuery(geoQuery)
+                .withSort(geoSort)
+                .withPageable(PageRequest.of(0, 50)) // 밀집 지역 고려 여유분 확보
+                .build();

가능하면 매직 넘버는 상수로 올리길 권장합니다.

추가(클래스 상단 예시):

private static final String VALIDATION_RADIUS = "30m";
private static final int VALIDATION_CANDIDATE_LIMIT = 50;

그리고 distance("30m"), PageRequest.of(0, 50) 부분을 상수로 교체하세요.


181-206: 짧은 질의(1글자 등)에서 과매칭 가능성 → contains 조건 최소 길이 가드 권장

normalize 후 1~2글자 질의는 거의 모든 후보에 매칭될 수 있습니다. contains 기반 판정을 최소 길이 가드로 약화시키면 오탐을 줄일 수 있습니다.

-        // 2. 포함 관계 (기존 wildcard와 유사)
-        if (normalizedZone.contains(normalizedSearch) ||
-            normalizedSearch.contains(normalizedZone)) {
+        // 2. 포함 관계 (기존 wildcard와 유사) — 너무 짧은 토큰은 과매칭 방지
+        boolean zoneContainsSearch = normalizedSearch.length() >= 2 && normalizedZone.contains(normalizedSearch);
+        boolean searchContainsZone = normalizedZone.length() >= 2 && normalizedSearch.contains(normalizedZone);
+        if (zoneContainsSearch || searchContainsZone) {
             return true;
         }

211-215: 정규화에 유니코드 정규화(NFKC) 추가 제안

특수문자/전각/반각/조합형 등의 표현 차이를 줄이려면 유니코드 정규화(NFKC)를 먼저 수행하는 것이 안전합니다.

-    private String normalize(String text) {
-        return text.replaceAll("\\s+", "")           // 공백 제거
-                  .replaceAll("[^가-힣a-zA-Z0-9]", "") // 특수문자 제거
-                  .toLowerCase();
-    }
+    private String normalize(String text) {
+        String n = java.text.Normalizer.normalize(text, java.text.Normalizer.Form.NFKC);
+        return n.replaceAll("\\s+", "")              // 공백 제거
+                .replaceAll("[^가-힣a-zA-Z0-9]", "")  // 특수문자 제거
+                .toLowerCase();
+    }

259-277: 자카드 유사도: 문자 집합보다는 2-그램(바이그램) 기반이 검색 품질에 유리

한글/혼합 문자열에서 문자 단위 집합은 구분력이 낮습니다. 2-그램 기반 자카드가 실제 “부분 유사”를 더 잘 반영합니다.

-    private double calculateJaccardSimilarity(String s1, String s2) {
+    private double calculateJaccardSimilarity(String s1, String s2) {
         if (s1.isEmpty() && s2.isEmpty()) {
             return 1.0;
         }
         if (s1.isEmpty() || s2.isEmpty()) {
             return 0.0;
         }
 
-        Set<Character> set1 = s1.chars().mapToObj(c -> (char) c).collect(java.util.stream.Collectors.toSet());
-        Set<Character> set2 = s2.chars().mapToObj(c -> (char) c).collect(java.util.stream.Collectors.toSet());
+        java.util.Set<String> set1 = new java.util.HashSet<>();
+        java.util.Set<String> set2 = new java.util.HashSet<>();
+        for (int i = 0; i < s1.length() - 1; i++) set1.add(s1.substring(i, i + 2));
+        for (int i = 0; i < s2.length() - 1; i++) set2.add(s2.substring(i, i + 2));
+        if (set1.isEmpty() && set2.isEmpty()) return 1.0;
+        if (set1.isEmpty() || set2.isEmpty()) return 0.0;
 
-        Set<Character> intersection = new java.util.HashSet<>(set1);
+        java.util.Set<String> intersection = new java.util.HashSet<>(set1);
         intersection.retainAll(set2);
 
-        Set<Character> union = new java.util.HashSet<>(set1);
+        java.util.Set<String> union = new java.util.HashSet<>(set1);
         union.addAll(set2);
 
         return (double) intersection.size() / union.size();
     }
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c5e7a17 and d022f97.

📒 Files selected for processing (2)
  • src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (3 hunks)
  • src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (3)
src/main/java/com/wayble/server/explore/service/WaybleZoneSearchService.java (1)
  • Service (15-49)
src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java (2)
  • searchPersonalWaybleZones (48-162)
  • Repository (30-163)
src/main/java/com/wayble/server/wayblezone/repository/WaybleZoneRepositoryImpl.java (1)
  • WaybleZoneRepositoryImpl (19-140)
🔇 Additional comments (3)
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (1)

539-556: 요청-응답 시간 측정 추가: 방향성과 위치 적절함

호출 직전에 startTime, 응답 직후 endTime 측정 구간이 적절하며, 성능 회귀 감지에 도움이 됩니다. 유지해도 좋습니다.

src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (2)

22-22: Set import 적절

자카드 유사도 계산을 위한 Set 사용에 필요한 import 추가, 문제 없습니다.


145-146: distance null 반환 시 서버측 NPE/직렬화 문제 없음—클라이언트 null 처리만 검증 필요

  • WaybleZoneSearchResponseDtodistance 필드는 원시 타입 double이 아닌 객체형 Double으로 선언되어 있어, 서버에서 null을 반환해도 NPE나 직렬화 오류가 발생하지 않습니다.
  • Jackson 등의 기본 설정에서도 null 값을 허용하므로 서버 측에서는 안전합니다.
  • 프론트엔드 및 기타 소비자 코드에서 distance == null인 경우를 적절히 처리(예: 기본값 대체 또는 null 체크)하고 있는지 한번 더 검증해 주세요.

@KiSeungMin KiSeungMin merged commit e6ce328 into develop Aug 18, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💡 feature 기능 구현 및 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants