Skip to content

검색 기본 정렬과 URL 상태 반영#17

Merged
SmileJune merged 1 commit into
mainfrom
ai/search-url-state-main
Jun 1, 2026
Merged

검색 기본 정렬과 URL 상태 반영#17
SmileJune merged 1 commit into
mainfrom
ai/search-url-state-main

Conversation

@SmileJune
Copy link
Copy Markdown
Owner

@SmileJune SmileJune commented May 31, 2026

승인된 내용

Refs #2

검색 결과 이해성과 탐색 흐름을 개선하는 후속 작업으로, 사용자가 명시적으로 요청한 검색 기본 정렬 전환과 검색 상태 URL 반영을 적용합니다.

변경 사항

  • 검색어가 있는 검색은 기본 정렬을 관련도순으로 사용합니다.
  • 검색어, 정렬, 페이지, 필터 상태를 URL query string에 반영합니다.
  • URL 직접 진입 시 검색 상태를 복원합니다.
  • 브라우저 뒤로가기/앞으로가기 시 URL 기준으로 검색 상태를 다시 적용합니다.
  • docs/search-design.mddocs/development-log.md에 동작 방식과 검증 기록을 남겼습니다.

의도적으로 제외한 것

  • 백엔드 검색 알고리즘 변경은 포함하지 않았습니다.
  • Elasticsearch 쿼리/인덱스 구조 변경은 포함하지 않았습니다.
  • 인증, 결제, 데이터베이스 마이그레이션, 인프라 변경은 포함하지 않았습니다.
  • 자동 머지는 없습니다.

검증

  • apps/web에서 npm run build 성공
  • 동일 구현 브랜치에서 로컬 백엔드/프론트엔드 실행 후 확인:
    • /?q=RAG&sort=relevance 직접 진입 시 RAG, 관련도순, 결과 목록 복원
    • 다음 페이지 이동 시 /?q=RAG&sort=relevance&page=2 반영
    • 뒤로가기 시 1페이지 상태 복원
    • 추천 검색어 RAG 클릭 시 /?q=RAG&sort=relevance 반영

사람이 확인할 방법

  1. PR 브랜치를 체크아웃합니다.
  2. 백엔드와 웹 앱을 로컬에서 실행합니다.
  3. /?q=RAG&sort=relevance로 직접 진입해 검색어/정렬/결과가 복원되는지 확인합니다.
  4. 페이지 이동과 브라우저 뒤로가기로 URL과 화면 상태가 같이 바뀌는지 확인합니다.

Summary by CodeRabbit

  • New Features
    • Search state (query, filters, sorting, and pagination) is now synchronized with the URL, enabling users to bookmark and share search results.
    • Browser back/forward navigation now correctly restores previous search states.
    • Improved sort defaults: displays latest results when browsing without a query and relevance-ranked results when searching with keywords.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

SearchExperience now reads search state from URL query parameters on mount and keeps browser history synchronized as users modify queries, sorts, filters, and pagination. Sort logic adapts based on entry point: browsing by company uses latest-first; searching by query uses relevance-first. Browser back/forward navigation restores prior search states.

Changes

Search state URL synchronization

Layer / File(s) Summary
Type contracts and URL utilities
apps/web/app/components/SearchExperience.tsx
SearchUrlState and RunSearchOptions types added. Utility functions parse window.location.search into state, compute default sort based on query presence, build search URLs, and sync via history.pushState.
Component initialization and browser history
apps/web/app/components/SearchExperience.tsx
Mount reads currentSearchUrlState from URL, sets filters, loads favorites, and runs initial search with URL updates disabled. popstate listener re-runs searches when browser navigation changes the URL.
URL synchronization in search execution
apps/web/app/components/SearchExperience.tsx
runSearch accepts options parameter; options.updateUrl controls whether syncSearchUrl is called. Query is normalized, committedQueryRef set, and state (query/sort/filters/page) updated before fetch.
Company facet preservation during results
apps/web/app/components/SearchExperience.tsx
Company facets from API results replace current facets only when no sources are selected or current list is empty; otherwise existing facets are retained.
Sort behavior for different entry points
apps/web/app/components/SearchExperience.tsx
Form submission uses sortForSubmittedQuery helper to choose sort based on query match. Suggestion and quick-tag selection use defaultSortForQuery to apply relevance for new queries and latest for topic browsing.
Feature documentation
docs/development-log.md, docs/search-design.md
Development log and design document record URL parameter list (q, sort, page, source, technology, problem, content_type), sort behavior rules by entry point, and state restoration on mount and browser navigation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through history's lane,
With URLs that remember the search domain,
From query to latest, from fresh to refine,
Each click syncs the state, oh how they align!
Browser buttons now bound to the thread,
Where search dreams are shared and back paths are read.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: implementing default sort behavior for searches and reflecting search state in the URL, which are the primary objectives of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ai/search-url-state-main

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.

@SmileJune SmileJune requested a review from Copilot May 31, 2026 23:13
@SmileJune SmileJune marked this pull request as ready for review May 31, 2026 23:13
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds URL-backed search state and improves default sort behavior for keyword searches in the web search experience.

Changes:

  • Uses relevance as the default sort for new non-empty searches while preserving user-selected sort across related interactions.
  • Syncs query, sort, page, and filter state to the URL and restores state on initial load/back-forward navigation.
  • Documents the new URL query behavior and verification notes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
apps/web/app/components/SearchExperience.tsx Adds URL parsing/sync helpers and wires search state restoration/history handling into the main search component.
docs/search-design.md Documents default sort behavior and supported URL query parameters.
docs/development-log.md Records implementation scope and manual verification results.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/components/SearchExperience.tsx (1)

608-661: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent stale search responses from overwriting newer state.

runSearch can run concurrently (submit/filter/page/popstate). Without request ordering/cancellation, an older response may resolve later and replace the latest UI state.

💡 Proposed fix (request-sequence guard)
@@
   const suggestAbortRef = useRef<AbortController | null>(null);
   const committedQueryRef = useRef<string | null>(null);
+  const searchRequestSeqRef = useRef(0);
@@
   async function runSearch(
@@
   ) {
+    const requestSeq = ++searchRequestSeqRef.current;
     const trimmedQuery = nextQuery.trim();
@@
     try {
@@
       const data = (await response.json()) as SearchResponse;
+      if (requestSeq !== searchRequestSeqRef.current) {
+        return;
+      }
       setResult(data);
@@
     } catch (error) {
+      if (requestSeq !== searchRequestSeqRef.current) {
+        return;
+      }
       setResult(null);
       setErrorMessage(error instanceof Error ? error.message : "검색 중 오류가 발생했습니다.");
     } finally {
-      setIsLoading(false);
+      if (requestSeq === searchRequestSeqRef.current) {
+        setIsLoading(false);
+      }
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/components/SearchExperience.tsx` around lines 608 - 661,
runSearch can return out-of-order results which overwrite newer UI state; add a
request-sequencing guard by introducing a ref like latestRequestIdRef (increment
before each fetch), capture the current id at start of runSearch, and after any
await (before setResult, setCompanyFacets, setErrorMessage, setIsLoading) verify
the captured id === latestRequestIdRef.current; if not, discard the response.
Update runSearch (and any early returns on non-ok responses or catches) to only
apply state when the sequence id matches so stale responses cannot override
newer state.
🧹 Nitpick comments (1)
apps/web/app/components/SearchExperience.tsx (1)

650-654: ⚡ Quick win

Keep company facets fresh when filters/query change.

Line 651 currently keeps old companyFacets whenever any source is selected, so facet counts/recommendation flags can become stale across subsequent searches.

🔧 Proposed simplification
-      setCompanyFacets((currentFacets) =>
-        nextFilters.sources.length === 0 || currentFacets.length === 0
-          ? data.facets.sources
-          : currentFacets,
-      );
+      setCompanyFacets(data.facets.sources);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/components/SearchExperience.tsx` around lines 650 - 654, The
current update to company facets uses a conditional that preserves currentFacets
whenever any source is selected, which lets counts/flags become stale; change
the setter to always assign the latest facets from data.facets.sources when
filters or query change (i.e., replace the ternary in the setCompanyFacets call
so it unconditionally returns data.facets.sources rather than currentFacets),
referencing setCompanyFacets, nextFilters, and data.facets.sources.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/web/app/components/SearchExperience.tsx`:
- Around line 608-661: runSearch can return out-of-order results which overwrite
newer UI state; add a request-sequencing guard by introducing a ref like
latestRequestIdRef (increment before each fetch), capture the current id at
start of runSearch, and after any await (before setResult, setCompanyFacets,
setErrorMessage, setIsLoading) verify the captured id ===
latestRequestIdRef.current; if not, discard the response. Update runSearch (and
any early returns on non-ok responses or catches) to only apply state when the
sequence id matches so stale responses cannot override newer state.

---

Nitpick comments:
In `@apps/web/app/components/SearchExperience.tsx`:
- Around line 650-654: The current update to company facets uses a conditional
that preserves currentFacets whenever any source is selected, which lets
counts/flags become stale; change the setter to always assign the latest facets
from data.facets.sources when filters or query change (i.e., replace the ternary
in the setCompanyFacets call so it unconditionally returns data.facets.sources
rather than currentFacets), referencing setCompanyFacets, nextFilters, and
data.facets.sources.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 65b9cf8c-7f9c-4efc-87cd-02370ddfc39b

📥 Commits

Reviewing files that changed from the base of the PR and between b162298 and d3f0fb2.

📒 Files selected for processing (3)
  • apps/web/app/components/SearchExperience.tsx
  • docs/development-log.md
  • docs/search-design.md

@SmileJune SmileJune merged commit 3a4bf33 into main Jun 1, 2026
3 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.

2 participants