diff --git a/App.tsx b/App.tsx
index 0abb01c..40550d2 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,12 +1,16 @@
-import * as React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { useFonts } from "expo-font";
+import * as React from "react";
-import TabNavigation from "./src/components/navBar/TabNavigation";
-import OnboardingOverlay from "./src/components/libary/OnboardingOverlay";
import AsyncStorage from "@react-native-async-storage/async-storage";
+import OnboardingOverlay from "./src/components/libary/OnboardingOverlay";
+import TabNavigation from "./src/components/navBar/TabNavigation";
+
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
export default function App() {
+ const queryClient = new QueryClient();
+
//처음 방문 시 온보딩 화면 실행
const [showOnboarding, setShowOnboarding] = React.useState(false);
@@ -35,11 +39,13 @@ export default function App() {
if (!fontsLoaded) return null;
return (
-
-
- {showOnboarding && (
-
- )}
-
+
+
+
+ {showOnboarding && (
+
+ )}
+
+
);
}
diff --git a/package-lock.json b/package-lock.json
index c4c77cf..d41dc02 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@react-navigation/bottom-tabs": "^6.6.0",
"@react-navigation/native": "^6.1.17",
"@react-navigation/native-stack": "^6.10.0",
+ "@tanstack/react-query": "^5.64.2",
"axios": "^1.7.9",
"expo": "~51.0.24",
"expo-camera": "~15.0.14",
@@ -6958,6 +6959,32 @@
"node": ">=4"
}
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.64.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.64.2.tgz",
+ "integrity": "sha512-hdO8SZpWXoADNTWXV9We8CwTkXU88OVWRBcsiFrk7xJQnhm6WRlweDzMD+uH+GnuieTBVSML6xFa17C2cNV8+g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.64.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.64.2.tgz",
+ "integrity": "sha512-3pakNscZNm8KJkxmovvtZ4RaXLyiYYobwleTMvpIGUoKRa8j8VlrQKNl5W8VUEfVfZKkikvXVddLuWMbcSCA1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.64.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
diff --git a/package.json b/package.json
index e37385a..45a7216 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"@react-navigation/bottom-tabs": "^6.6.0",
"@react-navigation/native": "^6.1.17",
"@react-navigation/native-stack": "^6.10.0",
+ "@tanstack/react-query": "^5.64.2",
"axios": "^1.7.9",
"expo": "~51.0.24",
"expo-camera": "~15.0.14",
diff --git a/src/api/book/deleteKeyword.ts b/src/api/book/deleteKeyword.ts
new file mode 100644
index 0000000..b5b4269
--- /dev/null
+++ b/src/api/book/deleteKeyword.ts
@@ -0,0 +1,7 @@
+import api from "..";
+
+//최근 검색어 삭제
+export const deleteKeyword = async (keywordId: number): Promise => {
+ const response = await api.delete(`/api/v1/book/keyword/${keywordId}`);
+ return response.data;
+};
diff --git a/src/api/book/getBestSeller.ts b/src/api/book/getBestSeller.ts
new file mode 100644
index 0000000..819ccae
--- /dev/null
+++ b/src/api/book/getBestSeller.ts
@@ -0,0 +1,22 @@
+import api from "..";
+import { RequestBestSeller } from "../../types/search/bestbook";
+import { ResponseBookSearchResult } from "../../types/search/search";
+
+// 베스트셀러 조회
+export const getBestSeller = async ({
+ category = 0,
+ size = 12,
+}: RequestBestSeller): Promise => {
+ try {
+ const response = await api.get(`/api/v1/book/best-seller`, {
+ params: {
+ category,
+ size,
+ },
+ });
+ return response.data;
+ } catch (error) {
+ console.error("Error fetching best sellers:", error);
+ return undefined;
+ }
+};
diff --git a/src/api/book/getKeyword.ts b/src/api/book/getKeyword.ts
new file mode 100644
index 0000000..9adda3f
--- /dev/null
+++ b/src/api/book/getKeyword.ts
@@ -0,0 +1,15 @@
+import api from "..";
+import { ResponseResentSearch } from "../../types/search/resentSearch";
+
+//최근 검색어 조회
+export const getKeyword = async (): Promise<
+ ResponseResentSearch | undefined
+> => {
+ try {
+ const response = await api.get(`/api/v1/book/keyword`);
+ return response.data;
+ } catch (e) {
+ console.log(e);
+ return undefined;
+ }
+};
diff --git a/src/api/book/getSearch.ts b/src/api/book/getSearch.ts
new file mode 100644
index 0000000..375f7d2
--- /dev/null
+++ b/src/api/book/getSearch.ts
@@ -0,0 +1,15 @@
+import api from "..";
+import { ResponseBookSearchResult } from "../../types/search/search";
+
+// 검색 조회
+export const getSearch = async (
+ keyword: string
+): Promise => {
+ try {
+ const response = await api.get(`api/v1/book/search?keyword=${keyword}`);
+ return response.data;
+ } catch (e) {
+ console.log(e);
+ return undefined;
+ }
+};
diff --git a/src/assets/data/dummyRecentSearchList.ts b/src/assets/data/dummyRecentSearchList.ts
deleted file mode 100644
index 2da144f..0000000
--- a/src/assets/data/dummyRecentSearchList.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const dummyRecentSearchList = [
- "난",
- "사실",
- "책 같은 건",
- "안 읽습니다.",
- "ㅋㅋㅋㅋ",
-];
diff --git a/src/components/search/BestSellerBook.tsx b/src/components/search/BestSellerBook.tsx
index 5be058e..b2f24c1 100644
--- a/src/components/search/BestSellerBook.tsx
+++ b/src/components/search/BestSellerBook.tsx
@@ -1,7 +1,7 @@
import React from "react";
-import { Text, View, Image } from "react-native";
-import { BestBook } from "../../types/search/bestbook";
+import { Image, Text, View } from "react-native";
import { styles } from "../../styles/search/BestSellerBookStyle";
+import { BestBook } from "../../types/search/bestbook";
const BestSellerBook = ({ id, title, image, name }: BestBook) => {
return (
@@ -10,7 +10,7 @@ const BestSellerBook = ({ id, title, image, name }: BestBook) => {
{id}
-
+
{title}
diff --git a/src/components/search/BookCollection.tsx b/src/components/search/BookCollection.tsx
index faece37..ab7dd79 100644
--- a/src/components/search/BookCollection.tsx
+++ b/src/components/search/BookCollection.tsx
@@ -1,25 +1,28 @@
import { ScrollView, View } from "react-native";
-import BestSellerBook from "./BestSellerBook";
-import { dummyList } from "../../assets/data/dummyBestBookList";
+import { useBestSeller } from "../../hooks/book/useBestSeller";
import { styles } from "../../styles/search/BestSellerStyle";
+import BestSellerBook from "./BestSellerBook";
const BookCollection = () => {
+ const { data } = useBestSeller({});
+ const bestSellerList = data.item;
+
// 3*4 배열을 만들기 위해 3개씩 푸시함.
const groupedBooks = [];
- for (let i = 0; i < dummyList.length; i += 3) {
- groupedBooks.push(dummyList.slice(i, i + 3));
+ for (let i = 0; i < bestSellerList.length; i += 3) {
+ groupedBooks.push(bestSellerList.slice(i, i + 3));
}
return (
- {groupedBooks.map((group, index) => (
-
- {group.map((book) => (
+ {groupedBooks.map((group, groupIndex) => (
+
+ {group.map((book, index) => (
))}
diff --git a/src/components/search/RecentSearch.tsx b/src/components/search/RecentSearch.tsx
index a4267d9..0feebb0 100644
--- a/src/components/search/RecentSearch.tsx
+++ b/src/components/search/RecentSearch.tsx
@@ -1,16 +1,36 @@
-import React, { useState } from "react";
-import { View, Text, ScrollView } from "react-native";
+import { useFocusEffect } from "@react-navigation/native";
+import React, { useCallback } from "react";
+import { ScrollView, Text, View } from "react-native";
import { RecentSearchText } from "../../constans/search";
+import { useDeleteKeyword, useGetKeyword } from "../../hooks/book/useKeyword";
import { styles } from "../../styles/search/RecentSearchStyle";
-import { dummyRecentSearchList } from "../../assets/data/dummyRecentSearchList";
import RecentSearchCard from "./RecentSearchCard";
const RecentSearch = () => {
- const [searchList, setSearchList] = useState(dummyRecentSearchList);
+ const { data, refetch } = useGetKeyword();
+ const searchList = data.information;
- // 같은 이름이 있을 경우 delete함수가 안 먹히는데 이는 어처피 api호출로 해결될 문제로 보임.
- const handleDeleteCard = (text: string) => {
- setSearchList(searchList.filter((item) => item !== text));
+ const { mutate } = useDeleteKeyword();
+
+ useFocusEffect(
+ useCallback(() => {
+ refetch();
+ }, [])
+ );
+
+ const handleDeleteKeyword = (keywordId: number) => {
+ mutate(
+ { keywordId },
+ {
+ onSuccess: (data) => {
+ console.log("Success");
+ refetch();
+ },
+ onError: (error) => {
+ console.log("Error", error.message);
+ },
+ }
+ );
};
return (
@@ -23,9 +43,9 @@ const RecentSearch = () => {
>
{searchList.map((search) => (
handleDeleteKeyword(search.keywordId)}
/>
))}
diff --git a/src/components/search/ResultBookCard.tsx b/src/components/search/ResultBookCard.tsx
index 1eaddef..0f340e6 100644
--- a/src/components/search/ResultBookCard.tsx
+++ b/src/components/search/ResultBookCard.tsx
@@ -1,6 +1,6 @@
-import { View, Image, Text } from "react-native";
-import { ResultBookProp } from "../../types/search";
+import { Image, Text, View } from "react-native";
import { styles } from "../../styles/search/ResultBookCardStyle";
+import { ResultBookProp } from "../../types/search";
const ResultBookCard = ({
image,
title,
@@ -9,7 +9,7 @@ const ResultBookCard = ({
}: ResultBookProp) => {
return (
-
+
{title}
diff --git a/src/hooks/book/useBestSeller.ts b/src/hooks/book/useBestSeller.ts
new file mode 100644
index 0000000..5b66fc0
--- /dev/null
+++ b/src/hooks/book/useBestSeller.ts
@@ -0,0 +1,17 @@
+import {
+ useSuspenseQuery,
+ UseSuspenseQueryResult,
+} from "@tanstack/react-query";
+import { ResponseBookSearchResult } from "../../types/search/bestbook";
+import { getBestSeller } from "./../../api/book/getBestSeller";
+
+// 베스트 셀러 조회
+export function useBestSeller({
+ category = 0,
+ size = 12,
+}): UseSuspenseQueryResult {
+ return useSuspenseQuery({
+ queryKey: ["GetBestSeller"],
+ queryFn: () => getBestSeller({ category, size }),
+ });
+}
diff --git a/src/hooks/book/useKeyword.ts b/src/hooks/book/useKeyword.ts
new file mode 100644
index 0000000..8c5db4d
--- /dev/null
+++ b/src/hooks/book/useKeyword.ts
@@ -0,0 +1,26 @@
+import {
+ useMutation,
+ useSuspenseQuery,
+ UseSuspenseQueryResult,
+} from "@tanstack/react-query";
+import { deleteKeyword } from "../../api/book/deleteKeyword";
+import { getKeyword } from "../../api/book/getKeyword";
+import { ResponseResentSearch } from "../../types/search/resentSearch";
+
+//최근 검색어 조회
+export function useGetKeyword(): UseSuspenseQueryResult<
+ ResponseResentSearch,
+ Error
+> {
+ return useSuspenseQuery({
+ queryKey: ["GetResentKeyword"],
+ queryFn: () => getKeyword(),
+ });
+}
+
+// 최근 검색어 삭제
+export function useDeleteKeyword() {
+ return useMutation<{}, Error, { keywordId: number }>({
+ mutationFn: ({ keywordId }) => deleteKeyword(keywordId),
+ });
+}
diff --git a/src/hooks/book/useSearch.ts b/src/hooks/book/useSearch.ts
new file mode 100644
index 0000000..27cb08e
--- /dev/null
+++ b/src/hooks/book/useSearch.ts
@@ -0,0 +1,16 @@
+import {
+ useSuspenseQuery,
+ UseSuspenseQueryResult,
+} from "@tanstack/react-query";
+import { getSearch } from "../../api/book/getSearch";
+import { ResponseBookSearchResult } from "../../types/search/search";
+
+//최근 검색어 조회
+export function useSearch(
+ keyword: string
+): UseSuspenseQueryResult {
+ return useSuspenseQuery({
+ queryKey: ["GetSearch"],
+ queryFn: () => getSearch(keyword),
+ });
+}
diff --git a/src/pages/searchPage/SearchResultPage.tsx b/src/pages/searchPage/SearchResultPage.tsx
index fa64f36..73aa05e 100644
--- a/src/pages/searchPage/SearchResultPage.tsx
+++ b/src/pages/searchPage/SearchResultPage.tsx
@@ -1,8 +1,8 @@
import React from "react";
import { ScrollView, View } from "react-native";
-import { dummyList } from "../../assets/data/dummyBestBookList";
import ResultBookCard from "../../components/search/ResultBookCard";
import SearchHeader from "../../components/search/SearchHeader";
+import { useSearch } from "../../hooks/book/useSearch";
import { styles } from "../../styles/search/SearchResultPageStyle";
import { BestSellerPageRouteProp } from "../../types/navigation/navigation";
@@ -12,19 +12,20 @@ type Props = {
const SearchResultPage: React.FC = ({ route }) => {
const { query } = route.params;
-
+ const { data } = useSearch(query);
+ const searchList = data.item;
return (
- {dummyList.map((book) => (
+ {searchList.map((book, index) => (
))}
diff --git a/src/styles/search/BestSellerStyle.ts b/src/styles/search/BestSellerStyle.ts
index 8f59cf5..8d1595c 100644
--- a/src/styles/search/BestSellerStyle.ts
+++ b/src/styles/search/BestSellerStyle.ts
@@ -23,6 +23,6 @@ export const styles = StyleSheet.create({
marginVertical: 10,
},
bestSellerContainer: {
- marginBottom: 20,
+ marginBottom: 100,
},
});
diff --git a/src/types/search/bestbook.ts b/src/types/search/bestbook.ts
index b29db62..ef74e63 100644
--- a/src/types/search/bestbook.ts
+++ b/src/types/search/bestbook.ts
@@ -19,3 +19,26 @@ export interface BestSellerKeywordProp {
onFocus: (text: string) => void;
checked: boolean;
}
+
+// 베스트 셀러 요청 props
+export interface RequestBestSeller {
+ category?: number;
+ size?: number;
+}
+
+// 각 책 항목에 대한 타입
+interface BookItem {
+ title: string; // 책 제목
+ author: string; // 저자
+ isbn13: string; // ISBN13
+ cover: string; // 책 표지 이미지 URL
+ bestRank: number; // 베스트 순위
+}
+
+// 전체 응답 타입
+export interface ResponseBookSearchResult {
+ totalResults: number; // 총 결과 수
+ startIndex: number; // 시작 인덱스
+ itemsPerPage: number; // 한 페이지당 항목 수
+ item: BookItem[]; // 책 항목 배열
+}
diff --git a/src/types/search/resentSearch.ts b/src/types/search/resentSearch.ts
new file mode 100644
index 0000000..4710383
--- /dev/null
+++ b/src/types/search/resentSearch.ts
@@ -0,0 +1,9 @@
+interface ResentSearchInformation {
+ keywordId: number; // 키워드의 고유 ID
+ content: string; // 키워드 내용
+}
+
+export interface ResponseResentSearch {
+ check: boolean; // 체크 여부
+ information: ResentSearchInformation[]; // 키워드 정보를 포함하는 배열
+}
diff --git a/src/types/search/search.ts b/src/types/search/search.ts
new file mode 100644
index 0000000..65da20f
--- /dev/null
+++ b/src/types/search/search.ts
@@ -0,0 +1,17 @@
+// 단일 책 정보에 대한 타입
+interface BookItem {
+ title: string; // 책 제목
+ author: string; // 저자
+ pubDate: string; // 출판일
+ isbn13: string; // ISBN13 번호
+ cover: string; // 표지 이미지 URL
+ publisher: string; // 출판사
+}
+
+// 검색 결과 전체에 대한 타입
+export interface ResponseBookSearchResult {
+ totalResults: number; // 전체 검색 결과 수
+ startIndex: number; // 시작 인덱스
+ itemsPerPage: number; // 페이지당 아이템 수
+ item: BookItem[]; // 책 정보 배열
+}