fix: 책 검색 무한스크롤 로직 수정 및 최근검색어 API 수정#164
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough최근검색 삭제 API에서 userId 파라미터가 제거되었고 호출부들이 이에 맞춰 단일 인자로 변경되었습니다. 검색 화면은 무한 스크롤과 totalElements 반영 로직이 추가/개편되었고, BookSearchResult는 totalElements 프로퍼티로 헤더 카운트를 표시합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant U as 사용자
participant UI as Search.tsx
participant API as 검색 API
participant Comp as BookSearchResult
U->>UI: 키워드 입력/검색 실행
UI->>API: getSearchBooks(term, page=1, manual?)
API-->>UI: { content, last, totalElements }
UI->>UI: setResults, setHasMore(!last), setTotalElements
UI->>Comp: render(results, totalElements, lastBookElementCallback)
Note over UI,Comp: IntersectionObserver로 sentinel 관찰
Comp-->>UI: lastBookElementCallback(교차 감지)
UI->>UI: if (hasMore && !isLoadingMore) loadMore()
UI->>API: getSearchBooks(term, page+1)
API-->>UI: { content, last }
UI->>UI: appendResults, setHasMore(!last), setPage(page+1)
UI->>Comp: re-render
sequenceDiagram
participant U as 사용자
participant UI as UserSearch/GroupSearch
participant API as RecentSearch API
U->>UI: 최근검색 항목 삭제 클릭
UI->>API: DELETE /recent-searches/{recentSearchId}
API-->>UI: 200 OK
UI->>UI: 상태에서 해당 항목 제거 후 갱신
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
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 unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/components/search/BookSearchResult.tsx (1)
37-37: totalElements 미지정 시 '전체 undefined' 노출 가능 — 안전한 폴백 및 표시 최적화 제안API에서 totalElements가 아직 세팅되지 않았거나 전달되지 않는 경우가 있을 수 있어 '전체 undefined'가 노출될 수 있습니다. 검색 결과 배열 길이를 폴백으로 사용하고, 숫자 포매팅(+ 단위)을 적용하면 사용자 경험이 좋아집니다.
아래처럼 처리하는 것을 제안합니다.
- {type === 'searching' ? <></> : <ResultHeader>전체 {totalElements}</ResultHeader>} + {type === 'searching' ? ( + <></> + ) : ( + <ResultHeader> + 전체 {(typeof totalElements === 'number' ? totalElements : searchedBookList.length).toLocaleString()}권 + </ResultHeader> + )}src/pages/search/Search.tsx (3)
63-93: loadMore 흐름 전반은 적절하나, jank 완화에는 '고유 key 보장'이 우선요청 중복 방지(isLoadingMore), 종료 여부(hasMore) 가드는 적절합니다. 다만 현재 무한스크롤 버벅임의 주요 원인은 리스트 key 중복 가능성(페이지 append 시 id 재사용)입니다. BookSearchResult에서 key를 isbn으로 바꾸는 대응을 최우선으로 적용해 주세요. 적용 후에도 미세한 버벅임이 남는다면 IntersectionObserver 프리패치 폭을 확대하는 최적화를 병행하세요(아래 코멘트 참조).
95-112: IntersectionObserver 프리패치 폭 확대로 스크롤 버벅임 완화뷰포트에 거의 닿은 뒤에야 요청이 나가면 사용자 체감이 끊깁니다. rootMargin을 넉넉히 주고 threshold를 낮춰 사전 로드를 유도하세요. 또한 동일 노드에 대해 불필요한 disconnect/observe 반복을 줄이는 것도 도움이 됩니다.
아래처럼 옵션 추가를 권장합니다.
- observerRef.current = new IntersectionObserver(entries => { - if (entries[0].isIntersecting && hasMore && !isLoadingMore) { - loadMore(); - } - }); + observerRef.current = new IntersectionObserver( + entries => { + if (entries[0].isIntersecting && hasMore && !isLoadingMore) { + loadMore(); + } + }, + { + root: null, + rootMargin: '300px 0px', // 뷰포트 300px 전에 미리 로드 + threshold: 0.01, + }, + );추가로, lastBookElementRef가 현재 읽히지 않습니다. 사용하지 않는다면 정리하는 편이 낫습니다.
122-123: 새 검색 시작 시 totalElements 초기화 권장새 키워드 입력 시 이전 검색의 totalElements가 일시적으로 남아 있을 수 있습니다(특히 빠른 전환 시). 헤더 폴백을 적용하더라도 상태를 0으로 초기화하면 잔여 표기 가능성을 줄일 수 있습니다.
setHasMore(true); // 새로운 검색 시 hasMore 초기화 setPage(1); // 페이지를 1로 초기화 + setTotalElements(0); // 총 개수 초기화
📜 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.
📒 Files selected for processing (5)
src/api/recentsearch/deleteRecentSearch.ts(1 hunks)src/components/search/BookSearchResult.tsx(3 hunks)src/pages/feed/UserSearch.tsx(1 hunks)src/pages/groupSearch/GroupSearch.tsx(1 hunks)src/pages/search/Search.tsx(7 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/pages/groupSearch/GroupSearch.tsx (1)
src/api/recentsearch/deleteRecentSearch.ts (1)
deleteRecentSearch(11-23)
src/pages/feed/UserSearch.tsx (1)
src/api/recentsearch/deleteRecentSearch.ts (1)
deleteRecentSearch(11-23)
src/pages/search/Search.tsx (2)
src/api/books/getSearchBooks.ts (2)
getSearchBooks(41-59)convertToSearchedBooks(61-70)src/api/recentsearch/deleteRecentSearch.ts (1)
deleteRecentSearch(11-23)
🔇 Additional comments (11)
src/pages/feed/UserSearch.tsx (1)
64-65: 삭제 API 시그니처 변경 반영 완료userId 제거된 새로운 시그니처에 맞게 호출부가 정리되었습니다. 후속 흐름(상태 업데이트, 에러 로깅)도 일관적입니다.
src/pages/groupSearch/GroupSearch.tsx (1)
292-299: 삭제 API 호출부 정리 OK하드코딩된 userId 제거 및 단일 인자 호출로 정돈되었습니다. 응답 처리도 기존 로직과 합치됩니다.
src/api/recentsearch/deleteRecentSearch.ts (1)
16-17: 엔드포인트 정정 LGTM쿼리 파라미터 제거 후 RESTful한 경로로 정리되어 명확합니다. 에러 로깅/전파도 기존대로 적절합니다.
src/pages/search/Search.tsx (8)
33-34: totalElements 상태 추가 LGTM총 개수를 별도 상태로 관리하여 헤더 표시와 분리한 방향성 좋습니다.
36-37: 페이지를 1부터 시작하도록 명시 — 적절합니다서버/UX 기준에 맞춘 명시적 초기화로 혼선을 줄일 수 있습니다.
113-116: 최근검색어 초기 로딩 훅 복구 OK마운트 시점 fetch로 UX를 보완합니다. 에러 시 빈 배열 처리도 일관적입니다.
160-161: 검색 시작 시 페이지/hasMore 초기화 적절초기 페이지를 고정하고 hasMore를 리셋하는 흐름은 안정적입니다.
164-165: isFinalized 파라미터 전달 의도 재확인 권장현재 isManualSearch 값을 그대로 isFinalized로 API에 전달합니다. "사용자가 명시적으로 검색 버튼을 눌렀을 때만(isFinalized=true) 최근검색어 기록" 등의 목적이라면 합리적입니다. 백엔드 기대값과 일치하는지 한 번만 확인해 주세요.
170-172: hasMore/totalElements 서버 값 반영 LGTM서버의 last/totalElements를 신뢰해 상태를 갱신하는 방식은 페이징 상태와 헤더 표기를 정확히 유지합니다.
212-213: 최근검색어 삭제 API 시그니처 변경 반영 OK단일 인자 호출로 간소화되었고, 성공 시 상태 필터링도 일관적으로 동작합니다.
295-296: BookSearchResult로 totalElements 전달 OK — 헤더 폴백과 병행 권장props 전달은 적절합니다. 상단 코멘트의 폴백/포매팅까지 함께 적용하면 일시적 값 부재에도 안전합니다.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/components/search/BookSearchResult.tsx (1)
48-48: key에 index 포함은 잠재적 재마운트/DIFF 비용 증가 — ISBN 단독 키 권장이전 리뷰에서도 안내된 내용과 유사합니다. index를 키에 섞으면 중간 삽입/정렬/필터링 시 컴포넌트 재마운트가 늘어나고, 가상 DOM diff 효율이 떨어질 수 있습니다. ISBN이 고유하다면 ISBN 단독 키가 가장 안정적입니다.
수정 제안:
- key={`book-${book.isbn}-${index}`} + key={book.isbn}만약 API 특성상 동일 ISBN이 중복으로 내려오는 경우가 있다면, 렌더 전 단계에서 de-dup을 하거나, 부득이한 경우에만 보조 식별자를 결합하는 방식을 고려해 주세요.
🧹 Nitpick comments (3)
src/components/search/BookSearchResult.tsx (3)
46-56: 이미지 lazy-loading/async decoding으로 스크롤 버벅임 완화무한스크롤에서 다량의 이미지 로딩은 주요 jank 원인입니다. 이미지에 lazy/async 속성을 부여하면 즉시 체감 성능이 개선됩니다.
부분 수정 제안:
- <Cover src={book.coverUrl} alt={`${book.title} 커버`} /> + <Cover src={book.coverUrl} alt={`${book.title} 커버`} loading="lazy" decoding="async" />또는 styled 정의에서 기본 attrs로 강제하는 방법:
// 대체안: styled 정의 자체를 attrs로 교체 const Cover = styled.img.attrs({ loading: 'lazy', decoding: 'async' })` width: 80px; height: 107px; object-fit: cover; `;
46-55: [선택] 결과가 많다면 리스트 가상화(windowing) 도입 고려무한스크롤에서 DOM 노드 수가 커지면 어떤 최적화보다 windowing 효과가 큽니다. react-window/react-virtuoso 도입 시 렌더/리플로우/이미지 로딩 모두 체감 감소합니다. 도입 난이도 대비 성능 이득이 커서, 버벅임 이슈가 지속되면 우선순위 높게 고려를 권장합니다.
12-22: BookSearchResult의 totalElements를 필수 Prop로 변경 권장검색 페이지에서는 항상
totalElements를 전달하고 있으므로,BookSearchResultProps에서 optional 처리(totalElements?: number)를 제거하고 필수로 정의하는 편이 안전합니다. 필요하다면 컴포넌트 시그니처에 기본값(totalElements = 0)을 지정하세요.
- 파일: src/components/search/BookSearchResult.tsx
type BookSearchResultProps의totalElements?: number→totalElements: number로 수정- (선택) 함수 파라미터 디폴트:
totalElements = 0추가- 변경 후
Search.tsx호출부에서 타입 오류가 없는지 확인
📜 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.
📒 Files selected for processing (1)
src/components/search/BookSearchResult.tsx(4 hunks)
| <Wrapper> | ||
| <List> | ||
| {type === 'searching' ? <></> : <ResultHeader>전체 {searchedBookList.length}</ResultHeader>} | ||
| {type === 'searching' ? <></> : <ResultHeader>전체 {totalElements}</ResultHeader>} |
There was a problem hiding this comment.
🛠️ Refactor suggestion
전체 undefined 표시 가능 — totalElements 미전달/초기값 시 Fallback 필요
totalElements가 optional인데 바로 출력되고 있어, 값이 아직 준비되지 않은 렌더 타이밍에 "전체 undefined"가 노출될 수 있습니다. length를 안전한 Fallback으로 사용해 주세요.
적용 제안:
- {type === 'searching' ? <></> : <ResultHeader>전체 {totalElements}</ResultHeader>}
+ {type === 'searching' ? null : (
+ <ResultHeader>전체 {totalElements ?? searchedBookList.length}</ResultHeader>
+ )}🤖 Prompt for AI Agents
In src/components/search/BookSearchResult.tsx around line 37, totalElements is
optional and may be undefined during initial render causing "전체 undefined" to
appear; update the rendering to use a safe fallback (e.g., display totalElements
?? items?.length ?? 0) so that when totalElements is not provided it falls back
to the items array length (or 0) before rendering the ResultHeader.
|
LGTM! |
#️⃣연관된 이슈
없음
📝작업 내용
스크린샷
💬리뷰 요구사항
무한스크롤이 약간 버벅이는 현상이 느껴지는데 이 부분을 해결해야할 것 같습니다.
Summary by CodeRabbit
New Features
Refactor