diff --git a/src/api/recentsearch/deleteRecentSearch.ts b/src/api/recentsearch/deleteRecentSearch.ts index 7983b4b9..773b41cd 100644 --- a/src/api/recentsearch/deleteRecentSearch.ts +++ b/src/api/recentsearch/deleteRecentSearch.ts @@ -10,11 +10,10 @@ export interface DeleteRecentSearchResponse { export const deleteRecentSearch = async ( recentSearchId: number, - userId: number, ): Promise => { try { const response = await apiClient.delete( - `/recent-searches/${recentSearchId}?userId=${userId}`, + `/recent-searches/${recentSearchId}`, ); return response.data; } catch (error) { diff --git a/src/components/search/BookSearchResult.tsx b/src/components/search/BookSearchResult.tsx index 4d6c9d6d..41ce500a 100644 --- a/src/components/search/BookSearchResult.tsx +++ b/src/components/search/BookSearchResult.tsx @@ -9,6 +9,7 @@ interface BookSearchResultProps { hasMore?: boolean; isLoading?: boolean; lastBookElementCallback?: (node: HTMLDivElement | null) => void; + totalElements?: number; } export function BookSearchResult({ @@ -17,6 +18,7 @@ export function BookSearchResult({ hasMore = false, isLoading = false, lastBookElementCallback, + totalElements, }: BookSearchResultProps) { const navigate = useNavigate(); @@ -32,7 +34,7 @@ export function BookSearchResult({ return ( - {type === 'searching' ? <> : 전체 {searchedBookList.length}} + {type === 'searching' ? <> : 전체 {totalElements}} {isEmptySearchedBookList() ? ( @@ -43,7 +45,7 @@ export function BookSearchResult({ ) : ( searchedBookList.map((book, index) => ( navigate(`/search/book/${book.isbn}`)} ref={ index === searchedBookList.length - 1 && lastBookElementCallback diff --git a/src/pages/feed/UserSearch.tsx b/src/pages/feed/UserSearch.tsx index 6dc819ee..c48a778b 100644 --- a/src/pages/feed/UserSearch.tsx +++ b/src/pages/feed/UserSearch.tsx @@ -61,9 +61,7 @@ const UserSearch = () => { const handleDelete = async (recentSearchId: number) => { try { - const userId = 1; // 임시 userId - - const response = await deleteRecentSearch(recentSearchId, userId); + const response = await deleteRecentSearch(recentSearchId); if (response.isSuccess) { setRecentSearches(prev => prev.filter(item => item.recentSearchId !== recentSearchId)); diff --git a/src/pages/groupSearch/GroupSearch.tsx b/src/pages/groupSearch/GroupSearch.tsx index d0afff69..b1536faa 100644 --- a/src/pages/groupSearch/GroupSearch.tsx +++ b/src/pages/groupSearch/GroupSearch.tsx @@ -289,8 +289,7 @@ const GroupSearch = () => { handleDelete={(term: string) => { const x = recentSearches.find(i => i.searchTerm === term); if (!x) return; - const userId = 1; - deleteRecentSearch(x.recentSearchId, userId).then(res => { + deleteRecentSearch(x.recentSearchId).then(res => { if (res.isSuccess) { setRecentSearches(prev => prev.filter(it => it.recentSearchId !== x.recentSearchId), diff --git a/src/pages/search/Search.tsx b/src/pages/search/Search.tsx index fb5a28ec..0bd2d32a 100644 --- a/src/pages/search/Search.tsx +++ b/src/pages/search/Search.tsx @@ -30,10 +30,10 @@ const Search = () => { const [searchResults, setSearchResults] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isInitialized, setIsInitialized] = useState(false); - + const [totalElements, setTotalElements] = useState(0); // 무한스크롤 관련 상태 const [hasMore, setHasMore] = useState(true); - const [page, setPage] = useState(1); + const [page, setPage] = useState(1); // 1부터 시작 const [isLoadingMore, setIsLoadingMore] = useState(false); const [recentSearches, setRecentSearches] = useState([]); @@ -59,30 +59,8 @@ const Search = () => { } }; - useEffect(() => { - fetchRecentSearches(); - }, []); - - // 무한스크롤을 위한 Intersection Observer 설정 - const lastBookElementCallback = useCallback( - (node: HTMLDivElement | null) => { - if (isLoadingMore || !hasMore) return; - - if (observerRef.current) observerRef.current.disconnect(); - - observerRef.current = new IntersectionObserver(entries => { - if (entries[0].isIntersecting && hasMore && !isLoadingMore) { - loadMore(); - } - }); - if (node) observerRef.current.observe(node); - lastBookElementRef.current = node; - }, - [isLoadingMore, hasMore], - ); - // 추가 데이터 로드 함수 - const loadMore = async () => { + const loadMore = useCallback(async () => { if (!searchTerm.trim() || isLoadingMore || !hasMore) return; try { @@ -97,8 +75,8 @@ const Search = () => { if (newResults.length > 0) { setSearchResults(prev => [...prev, ...newResults]); setPage(nextPage); - // 더 이상 데이터가 없으면 hasMore를 false로 설정 - setHasMore(newResults.length === 10); // size가 10이므로 + // API 응답의 last 필드를 사용하여 hasMore 설정 + setHasMore(!response.data.last); } else { setHasMore(false); } @@ -112,14 +90,36 @@ const Search = () => { } finally { setIsLoadingMore(false); } - }; + }, [searchTerm, isLoadingMore, hasMore, page, isFinalized]); + + // 무한스크롤을 위한 Intersection Observer 설정 + const lastBookElementCallback = useCallback( + (node: HTMLDivElement | null) => { + if (isLoadingMore || !hasMore) return; + + if (observerRef.current) observerRef.current.disconnect(); + + observerRef.current = new IntersectionObserver(entries => { + if (entries[0].isIntersecting && hasMore && !isLoadingMore) { + loadMore(); + } + }); + if (node) observerRef.current.observe(node); + lastBookElementRef.current = node; + }, + [isLoadingMore, hasMore, loadMore], + ); + + useEffect(() => { + fetchRecentSearches(); + }, []); const handleChange = (value: string) => { setSearchTerm(value); setIsFinalized(false); setIsSearching(value.trim() !== ''); setHasMore(true); // 새로운 검색 시 hasMore 초기화 - setPage(1); // 페이지 초기화 + setPage(1); // 페이지를 1로 초기화 if (value.trim()) { setSearchParams({ q: value.trim() }, { replace: true }); @@ -157,17 +157,18 @@ const Search = () => { } setIsLoading(true); - setPage(1); // 검색 시 페이지 초기화 + setPage(1); // 검색 시 페이지를 1로 초기화 setHasMore(true); // 검색 시 hasMore 초기화 try { - const response = await getSearchBooks(term, 1, isManualSearch); + const response = await getSearchBooks(term, 1, isManualSearch); // 첫 페이지는 1 if (response.isSuccess) { const convertedResults = convertToSearchedBooks(response.data.searchResult); setSearchResults(convertedResults); - // 더 이상 데이터가 없으면 hasMore를 false로 설정 - setHasMore(convertedResults.length === 10); // size가 10이므로 + // API 응답의 last 필드를 사용하여 hasMore 설정 + setHasMore(!response.data.last); + setTotalElements(response.data.totalElements); } else { console.log('검색 실패:', response.message); setSearchResults([]); @@ -208,9 +209,7 @@ const Search = () => { const handleDelete = async (recentSearchId: number) => { try { - const userId = 1; // 임시 userId - - const response = await deleteRecentSearch(recentSearchId, userId); + const response = await deleteRecentSearch(recentSearchId); if (response.isSuccess) { setRecentSearches(prev => prev.filter(item => item.recentSearchId !== recentSearchId)); @@ -293,6 +292,7 @@ const Search = () => { hasMore={hasMore} isLoading={isLoadingMore} lastBookElementCallback={lastBookElementCallback} + totalElements={totalElements} /> )}