From 66faaa21900ca87094984c935f311cb7154530a5 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Tue, 13 May 2025 18:38:59 +0900 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20=EC=84=9C=EC=A0=90=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A0=95=EB=B3=B4=20api=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/zip.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/zip.api.ts b/src/api/zip.api.ts index e2e113c..a21ee85 100644 --- a/src/api/zip.api.ts +++ b/src/api/zip.api.ts @@ -52,7 +52,7 @@ export const likeZip = async (bookstoreId: number) => { // 서점 상세 정보 export const getZipDetail = async (bookstoreId: number, type: string, sortFiled: string) => { try { - const response = await instance.get(`api/bookstores/${bookstoreId}/details?type=${type}&sortFiled?=${sortFiled}`); + const response = await instance.get(`api/bookstores/${bookstoreId}/details?type=${type}&sortField=${sortFiled}`); if (response.status == 200) { return response.data; } From 6f0776b0f4eb5658100bbb651c1fbe76010f5044 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Tue, 13 May 2025 18:39:12 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20=EB=B6=81=EC=8A=A4=EB=83=85=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ScrollContext.tsx | 11 ++++++++ src/components/layout.tsx | 35 ++++++++++++++------------ src/pages/Booksnap/BookSnap.tsx | 43 ++++++++++++++------------------ 3 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 src/components/ScrollContext.tsx diff --git a/src/components/ScrollContext.tsx b/src/components/ScrollContext.tsx new file mode 100644 index 0000000..0518af4 --- /dev/null +++ b/src/components/ScrollContext.tsx @@ -0,0 +1,11 @@ +import { createContext, useContext, useRef } from 'react'; + +export const ScrollContext = createContext | null>(null); + +export const useScrollRef = () => { + const context = useContext(ScrollContext); + if (!context) { + throw new Error('useScrollRef must be used within a ScrollProvider'); + } + return context; +}; diff --git a/src/components/layout.tsx b/src/components/layout.tsx index db925a9..b67014a 100644 --- a/src/components/layout.tsx +++ b/src/components/layout.tsx @@ -1,9 +1,10 @@ import { Outlet } from 'react-router-dom'; import MenuBar from './Common/MenuBar'; import { useEffect, useRef, useState } from 'react'; +import { ScrollContext } from './scrollContext'; const Layout = () => { - const headerHeight = useRef(null); + const mainRef = useRef(null); const menuBarHeight = useRef(null); const [heights, setHeights] = useState(0); @@ -24,21 +25,23 @@ const Layout = () => { }, []); return ( -
-
- -
-
- -
-
+ +
+
+ +
+
+ +
+
+
); }; diff --git a/src/pages/Booksnap/BookSnap.tsx b/src/pages/Booksnap/BookSnap.tsx index de11e1e..988897d 100644 --- a/src/pages/Booksnap/BookSnap.tsx +++ b/src/pages/Booksnap/BookSnap.tsx @@ -7,6 +7,7 @@ import WriteButton from '../../components/Booksnap/WriteButton'; import { getReview } from '../../api/booksnap.api'; import Toast from '../../components/Common/Toast'; import BooksnapHeader from '../../components/Header/BooksnapHeader'; +import { useScrollRef } from '../../components/scrollContext'; const BookSnap = () => { const [filter, setFilter] = useState('createdAt'); @@ -14,9 +15,9 @@ const BookSnap = () => { const [page, setPage] = useState(1); const [isLast, setIsLast] = useState(false); const [isBottom, setIsBottom] = useState(false); - const mainRef = useRef(null); const isLastRef = useRef(false); const [isLoading, setIsLoading] = useState(false); + const mainRef = useScrollRef(); // 리뷰 목록 받아오기 const getReviews = async () => { @@ -59,32 +60,26 @@ const BookSnap = () => { isLastRef.current = isLast; }, [isLast]); - // 바닥 감지 - const detectBottom = () => { - if (mainRef.current) { + useEffect(() => { + const handleScroll = () => { + if (!mainRef.current) return; + const { scrollTop, clientHeight, scrollHeight } = mainRef.current; - return scrollHeight > clientHeight && scrollTop + clientHeight >= scrollHeight - 1; - } - return false; - }; + if (scrollTop + clientHeight >= scrollHeight - 100 && !isBottom && !isLastRef.current) { + setIsBottom(true); + setPage((prev) => prev + 1); + } + }; - // 스크롤이 바닥에 있고, 마지막 페이지가 아니면 page + 1 - const handleScrollEvent = () => { - if (detectBottom() && !isBottom && !isLastRef.current) { - setIsBottom(true); - setPage((prev) => prev + 1); + const el = mainRef.current; + if (el) { + el.addEventListener('scroll', handleScroll); } - }; - // 스크롤 이벤트 등록 - useEffect(() => { - if (mainRef.current) { - mainRef.current.addEventListener('scroll', handleScrollEvent); - return () => { - mainRef.current?.removeEventListener('scroll', handleScrollEvent); - }; - } - }, []); + return () => { + el?.removeEventListener('scroll', handleScroll); + }; + }, [mainRef, isBottom]); if (isLoading) { return ; @@ -95,7 +90,7 @@ const BookSnap = () => { } return ( -
+
{/* 헤더 */}
From 033f501da22259b25f565fa3631c9653d3f14c9c Mon Sep 17 00:00:00 2001 From: yongaricode Date: Tue, 13 May 2025 22:29:43 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=EB=B6=81=EC=8A=A4=EB=83=85=20?= =?UTF-8?q?=EC=B1=85=20=EB=A6=AC=EB=B7=B0=20=EA=B2=80=EC=83=89=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/booksnap.api.ts | 12 ++++++++++++ src/components/Header/BookSearchHeader.tsx | 4 +++- src/pages/Booksnap/BookSnap.tsx | 18 ++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/api/booksnap.api.ts b/src/api/booksnap.api.ts index 8a818a5..b12a8cf 100644 --- a/src/api/booksnap.api.ts +++ b/src/api/booksnap.api.ts @@ -115,3 +115,15 @@ export const searchBookstore = async (query: string | null) => { console.log(error); } }; + +// 책 리뷰검색하기 +export const searchReview = async (bookName: string) => { + try { + const response = await instance.get(`/api/search?bookName=${bookName}`); + if (response.status === 200) { + return response.data; + } + } catch (err) { + console.log(err); + } +}; diff --git a/src/components/Header/BookSearchHeader.tsx b/src/components/Header/BookSearchHeader.tsx index ab55acf..f3bf5fd 100644 --- a/src/components/Header/BookSearchHeader.tsx +++ b/src/components/Header/BookSearchHeader.tsx @@ -8,7 +8,9 @@ const BookSearchHeader = () => { const [searchWord, setSearchWord] = useState(''); const nav = useNavigate(); - const handleSearch = () => {}; + const handleSearch = () => { + nav(`/booksnap?query=${searchWord}`); + }; return (
diff --git a/src/pages/Booksnap/BookSnap.tsx b/src/pages/Booksnap/BookSnap.tsx index 988897d..5e40d4d 100644 --- a/src/pages/Booksnap/BookSnap.tsx +++ b/src/pages/Booksnap/BookSnap.tsx @@ -4,10 +4,11 @@ import ReviewPreview from '../../components/Booksnap/ReviewPreview'; import { BooksnapPreview } from '../../model/booksnap.model'; import Loading from '../Loading'; import WriteButton from '../../components/Booksnap/WriteButton'; -import { getReview } from '../../api/booksnap.api'; +import { getReview, searchReview } from '../../api/booksnap.api'; import Toast from '../../components/Common/Toast'; import BooksnapHeader from '../../components/Header/BooksnapHeader'; -import { useScrollRef } from '../../components/scrollContext'; +import { useScrollRef } from '../../components/ScrollContext'; +import { useSearchParams } from 'react-router-dom'; const BookSnap = () => { const [filter, setFilter] = useState('createdAt'); @@ -18,6 +19,16 @@ const BookSnap = () => { const isLastRef = useRef(false); const [isLoading, setIsLoading] = useState(false); const mainRef = useScrollRef(); + const [searchParams] = useSearchParams(); + const query = searchParams.get('query'); + + useEffect(() => { + if (query) { + searchReview(query).then((data) => { + setReview(data.booksnapPreview); + }); + } + }, [query]); // 리뷰 목록 받아오기 const getReviews = async () => { @@ -36,6 +47,7 @@ const BookSnap = () => { // filter가 변경될 때 상태 초기화 및 getReviews 호출 useEffect(() => { + if (query) return; setReview([]); setIsLast(false); setIsBottom(false); @@ -49,6 +61,8 @@ const BookSnap = () => { // page가 변경될 때만 getReviews 호출 useEffect(() => { + if (query) return; + if (page !== 1 || review.length === 0) { // 🔄 리뷰가 없거나 페이지가 1이 아닐 때만 호출 getReviews(); From f5efac72eb3a9c4fa2141a54cd600dea65b5a654 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Tue, 13 May 2025 22:35:48 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=EC=B1=85=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/booksnap.api.ts | 15 +++++++++++++++ src/components/Header/BookSearchHeader.tsx | 2 ++ src/pages/Booksnap/BookSearch.tsx | 11 ++--------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/api/booksnap.api.ts b/src/api/booksnap.api.ts index b12a8cf..abd71f7 100644 --- a/src/api/booksnap.api.ts +++ b/src/api/booksnap.api.ts @@ -127,3 +127,18 @@ export const searchReview = async (bookName: string) => { console.log(err); } }; + +// 책 검색 기록 저장 +export const postSearchHistory = async (searchType: string, searchWord: string) => { + try { + const response = await instance.post(`/api/search-history`, { + searchType: searchType, + searchWord: searchWord, + }); + if (response.status === 200) { + return response.data; + } + } catch (err) { + console.log(err); + } +}; diff --git a/src/components/Header/BookSearchHeader.tsx b/src/components/Header/BookSearchHeader.tsx index f3bf5fd..c289135 100644 --- a/src/components/Header/BookSearchHeader.tsx +++ b/src/components/Header/BookSearchHeader.tsx @@ -3,6 +3,7 @@ import Arrow from '../../../public/icons/menu-bar/ArrowLeft.svg?react'; import { useNavigate } from 'react-router-dom'; import SearchBar from '../Zip/SearchBar'; +import { postSearchHistory } from '../../api/booksnap.api'; const BookSearchHeader = () => { const [searchWord, setSearchWord] = useState(''); @@ -10,6 +11,7 @@ const BookSearchHeader = () => { const handleSearch = () => { nav(`/booksnap?query=${searchWord}`); + postSearchHistory('booktitle', searchWord); }; return ( diff --git a/src/pages/Booksnap/BookSearch.tsx b/src/pages/Booksnap/BookSearch.tsx index 6e32deb..093f3ba 100644 --- a/src/pages/Booksnap/BookSearch.tsx +++ b/src/pages/Booksnap/BookSearch.tsx @@ -1,23 +1,16 @@ import RecentSearch from '../../components/Booksnap/RecentSearch'; import Tag from '../../components/Common/Tag'; -import SearchBar from '../../components/Zip/SearchBar'; -import { useState } from 'react'; -import Arrow from '../../../public/icons/menu-bar/ArrowLeft.svg?react'; -import { useNavigate } from 'react-router-dom'; -import Header from '../../components/Common/Header'; import BookSearchHeader from '../../components/Header/BookSearchHeader'; const BookSearch = () => { - const nav = useNavigate(); - const bookname = ['구원의 날', '지구에서 한아뿐', '사이키쿠스오']; const recent = ['하이큐 10권', '우리가 빛의 속도로 갈 수 없다면', '천 개의 파랑']; return ( -
+
{/* 헤더 */} -
+
{/* 인기 검색어 */}

zipzip이들이 많이 찾아본 리뷰

From 02bfed3ca5329ae3da82d71e522004e36a135c90 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Tue, 13 May 2025 22:44:27 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=EC=B1=85=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=96=B4=20=EB=B6=88=EB=9F=AC=EC=98=A4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/booksnap.api.ts | 12 ++++++++++++ src/pages/Booksnap/BookSearch.tsx | 17 +++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/api/booksnap.api.ts b/src/api/booksnap.api.ts index abd71f7..3938b1e 100644 --- a/src/api/booksnap.api.ts +++ b/src/api/booksnap.api.ts @@ -142,3 +142,15 @@ export const postSearchHistory = async (searchType: string, searchWord: string) console.log(err); } }; + +// 최근 검색어 불러오기 +export const getRecentSearch = async (searchtype: string, page: number, size: number) => { + try { + const response = await instance.get(`/api/search-history?searchtype=${searchtype}&page=${page}&size=${size}`); + if (response.status === 200) { + return response.data; + } + } catch (err) { + console.log(err); + } +}; diff --git a/src/pages/Booksnap/BookSearch.tsx b/src/pages/Booksnap/BookSearch.tsx index 093f3ba..7b2b6e3 100644 --- a/src/pages/Booksnap/BookSearch.tsx +++ b/src/pages/Booksnap/BookSearch.tsx @@ -1,10 +1,23 @@ +import { useEffect, useState } from 'react'; +import { getRecentSearch } from '../../api/booksnap.api'; import RecentSearch from '../../components/Booksnap/RecentSearch'; import Tag from '../../components/Common/Tag'; import BookSearchHeader from '../../components/Header/BookSearchHeader'; +interface RecentType { + id: number; + searchWord: string; +} + const BookSearch = () => { const bookname = ['구원의 날', '지구에서 한아뿐', '사이키쿠스오']; - const recent = ['하이큐 10권', '우리가 빛의 속도로 갈 수 없다면', '천 개의 파랑']; + const [recent, setRecent] = useState([]); + + useEffect(() => { + getRecentSearch('booktitle', 1, 10).then((data) => { + setRecent(data.data.searchHistory); + }); + }, []); return (
@@ -25,7 +38,7 @@ const BookSearch = () => {

최근 검색

{recent.map((book, index) => ( - + ))}
From 48100875a9ea5871d027604db9f4afe2c0ff2756 Mon Sep 17 00:00:00 2001 From: yongaricode Date: Tue, 13 May 2025 22:47:16 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EB=B6=80=ED=82=A4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/instance.ts | 4 +++- src/pages/Bookie.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/instance.ts b/src/api/instance.ts index b84d20b..f12f8be 100644 --- a/src/api/instance.ts +++ b/src/api/instance.ts @@ -24,10 +24,11 @@ instance.interceptors.request.use( // 응답 인터셉터 instance.interceptors.response.use( (response) => { - const { accessToken, refreshToken } = response.data.data || {}; + const { accessToken, refreshToken, nickname } = response.data.data || {}; if (accessToken && refreshToken) { localStorage.setItem('accessToken', accessToken); localStorage.setItem('refreshToken', refreshToken); + localStorage.setItem('nickname', nickname); } return response; }, @@ -57,6 +58,7 @@ instance.interceptors.response.use( console.log('Token 갱신 실패:', refreshErr); localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); + localStorage.removeItem('nickname'); window.location.href = '/login'; return Promise.reject(refreshErr); } diff --git a/src/pages/Bookie.tsx b/src/pages/Bookie.tsx index 1d39f27..1240a88 100644 --- a/src/pages/Bookie.tsx +++ b/src/pages/Bookie.tsx @@ -30,7 +30,7 @@ const Bookie = () => { const [isComposing, setIsComposing] = useState(false); const [isLoading, setIsLoading] = useState(false); const [loadingMessage, setLoadingMessage] = useState(''); - const userName = '이구역독서짱'; + const userName = localStorage.getItem('nickname'); const [systemRes, setSystemRes] = useState([ { text: `안녕하세요! ${userName}님이 좋아하실만한책을 추천해드리는 Bookie입니다! 더 많은 정보를 알려주시면, 책을 찾아드릴게요.`, From 794223359e08a01bc773e7ef83cbe19d168f0abf Mon Sep 17 00:00:00 2001 From: yongaricode Date: Tue, 13 May 2025 23:02:09 +0900 Subject: [PATCH 7/7] =?UTF-8?q?fix:=20import=ED=95=9C=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=8E=A0=EB=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layout.tsx b/src/components/layout.tsx index b67014a..656a38d 100644 --- a/src/components/layout.tsx +++ b/src/components/layout.tsx @@ -1,7 +1,7 @@ import { Outlet } from 'react-router-dom'; import MenuBar from './Common/MenuBar'; import { useEffect, useRef, useState } from 'react'; -import { ScrollContext } from './scrollContext'; +import { ScrollContext } from './ScrollContext'; const Layout = () => { const mainRef = useRef(null);