Skip to content

Commit

Permalink
Merge branch 'main' into feat/#486
Browse files Browse the repository at this point in the history
  • Loading branch information
gxxrxn committed Feb 19, 2024
2 parents add7a81 + ff66f15 commit dd70084
Show file tree
Hide file tree
Showing 20 changed files with 572 additions and 37 deletions.
13 changes: 13 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const baseURL = process.env.NEXT_PUBLIC_API_URL;
const aladinURL = process.env.ALADIN_OPEN_API_URI;
const ALADIN_API_KEY = process.env.ALADIN_OPEN_API_TTBKEY;

/** @type {import('next').NextConfig} */
const nextConfig = {
Expand All @@ -15,6 +17,11 @@ const nextConfig = {
source: '/service-api/:url*',
destination: `${baseURL}/api/:url*`,
},
{
source: '/aladin-api',
has: [{ type: 'query', key: 'QueryType', value: '(?<QueryType>.*)' }],
destination: `${aladinURL}/api/ItemList.aspx?ttbkey=${ALADIN_API_KEY}&QueryType=:QueryType&MaxResults=10&start=1&SearchTarget=Book&output=JS&Version=20131101`,
},
];
},
async redirects() {
Expand Down Expand Up @@ -46,6 +53,12 @@ const nextConfig = {
port: '',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'image.aladin.co.kr',
port: '',
pathname: '/**',
},
],
},
};
Expand Down
6 changes: 3 additions & 3 deletions public/icons/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/apis/book/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
APISearchedBook,
APISearchedBookPagination,
APIRecentSearches,
APIBestSellerRes,
} from '@/types/book';
import bookshelfAPI from '../bookshelf';
import { publicApi } from '../core/axios';
Expand Down Expand Up @@ -94,6 +95,11 @@ const bookAPI = {
`/service-api/bookshelves/${bookshelfId}/books/${bookId}`
)
),

getBestSellers: () =>
publicApi.get<APIBestSellerRes>(
`/aladin-api?QueryType=Bestseller&Cover=Big`
),
};

export default bookAPI;
100 changes: 85 additions & 15 deletions src/app/book/search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,93 @@
'use client';

import { VStack, Text } from '@chakra-ui/react';
import { useEffect, useState } from 'react';
import { useInView } from 'react-intersection-observer';

import BookSearch from '@/ui/BookSearch';
import useBookSearchQuery from '@/queries/book/useBookSearchQuery';
import useRecentSearchesQuery from '@/queries/book/useRecentSearchesQuery';

import useDebounceValue from '@/hooks/useDebounce';
import { isAuthed } from '@/utils/helpers/auth';

import TopHeader from '@/v1/base/TopHeader';
import BookSearchInput from '@/v1/bookSearch/BookSearchInput';
import BestSellers from '@/v1/bookSearch/BestSellers';
import RecentSearch from '@/v1/bookSearch/RecentSearch';
import BookSearchResults from '@/v1/bookSearch/SearchResult';

/**
* @todo
* recentSearchedInfo 계속해서 refetch되는 현상 고치기
*/

const BookSearch = () => {
const [inputSearchValue, setInputSearchValue] = useState<string>('');

const queryKeyword = useDebounceValue(inputSearchValue, 1000);

const { ref: inViewRef, inView } = useInView();

const bookSearchInfo = useBookSearchQuery({
query: queryKeyword,
page: 1,
pageSize: 12,
});
const recentSearchesInfo = useRecentSearchesQuery({ enabled: isAuthed() });

const searchedBooks = bookSearchInfo.isSuccess
? bookSearchInfo.data.pages.flatMap(page => page.searchBookResponseList)
: [];
const recentSearches = recentSearchesInfo.isSuccess
? recentSearchesInfo.data.bookRecentSearchResponses
: undefined;

const handleInputValueChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
if (!event.target) return;

const inputValue = event.target.value;

setInputSearchValue(inputValue.trim());
};

useEffect(() => {
if (inView && bookSearchInfo.hasNextPage) {
bookSearchInfo.fetchNextPage();
}
}, [
bookSearchInfo.fetchNextPage,
inView,
bookSearchInfo.hasNextPage,
queryKeyword,
bookSearchInfo,
]);

const BookPage = () => {
return (
<VStack gap="1rem">
<Text
alignSelf="flex-start"
fontSize="2rem"
fontWeight="800"
color="main"
>
Discover
</Text>
<BookSearch />
</VStack>
<>
<TopHeader text={'Discover'} />
<article className="flex h-full w-full flex-col gap-[3.8rem]">
<BookSearchInput
value={inputSearchValue}
onChange={handleInputValueChange}
/>
{inputSearchValue ? (
<>
<BookSearchResults searchedBooks={searchedBooks} />
<div ref={inViewRef} />
</>
) : (
<>
<RecentSearch
recentSearches={recentSearches}
setInputSearchValue={setInputSearchValue}
/>
<BestSellers />
</>
)}
</article>
</>
);
};

export default BookPage;
export default BookSearch;
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const RootLayout = ({ children }: { children: ReactNode }) => {
</head>
<body className="app-layout">
<ContextProvider>
<main className={`${LineSeedKR.variable} font-lineseed `}>
<main className={`${LineSeedKR.variable} font-lineseed`}>
{children}
</main>
</ContextProvider>
Expand Down
1 change: 1 addition & 0 deletions src/queries/book/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const bookKeys = {
details: () => [...bookKeys.all, 'detail'] as const,
detail: (bookId: APIBook['bookId']) =>
[...bookKeys.details(), bookId] as const,
bestSeller: () => [...bookKeys.all, 'bestSeller'],
bookmark: (bookId: APIBook['bookId']) =>
[...bookKeys.detail(bookId), 'bookmark'] as const,
comments: (bookId: APIBook['bookId']) =>
Expand Down
16 changes: 16 additions & 0 deletions src/queries/book/useBestSellersQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useQuery } from '@tanstack/react-query';
import type { QueryOptions } from '@/types/query';

import bookAPI from '@/apis/book';
import type { APIBestSellerRes } from '@/types/book';

import bookKeys from './key';

const useBestSellersQuery = (options?: QueryOptions<APIBestSellerRes>) =>
useQuery(
bookKeys.bestSeller(),
() => bookAPI.getBestSellers().then(({ data }) => data),
options
);

export default useBestSellersQuery;
52 changes: 52 additions & 0 deletions src/stories/bookSearch/BestSellers.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Meta, StoryObj } from '@storybook/react';
import BestSellers from '@/v1/bookSearch/BestSellers';

const meta: Meta<typeof BestSellers> = {
title: 'bookSearch/BestSellers',
component: BestSellers,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof BestSellers>;

type BestSellerProps = {
isbn: string;
title: string;
author: string;
cover: string;
bestRank: number;
link: string;
};

const BESTSELLER: BestSellerProps = {
isbn: '9791162242742',
title: '리팩터링',
author: '마틴 파울러',
cover:
'https://search1.kakaocdn.net/thumb/R120x174.q85/?fname=http%3A%2F%2Ft1.daumcdn.net%2Flbook%2Fimage%2F5326912%3Ftimestamp%3D20231207165435',
bestRank: 1,
link: 'https://search.daum.net/search?w=bookpage&bookId=5326912&q=%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81',
};

export const Default: Story = {
args: {
bestSellers: [BESTSELLER, BESTSELLER, BESTSELLER],
searchRange: 'WEEKLY',
},
};

export const MonthlyBestSeller: Story = {
args: {
bestSellers: [],
searchRange: 'MONTHLY',
},
};

export const YearlyBestSeller: Story = {
args: {
bestSellers: [],
searchRange: 'YEARLY',
},
};
18 changes: 18 additions & 0 deletions src/stories/bookSearch/BookSearchInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Meta, StoryObj } from '@storybook/react';
import BookSearchInput from '@/v1/bookSearch/BookSearchInput';

const meta: Meta<typeof BookSearchInput> = {
title: 'bookSearch/BookSearchInput',
component: BookSearchInput,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof BookSearchInput>;

export const Default: Story = {
args: {
value: '',
},
};
34 changes: 34 additions & 0 deletions src/stories/bookSearch/RecentSearch.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Meta, StoryObj } from '@storybook/react';
import RecentSearch from '@/v1/bookSearch/RecentSearch';

const meta: Meta<typeof RecentSearch> = {
title: 'bookSearch/RecentSearch',
component: RecentSearch,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof RecentSearch>;

export const Default: Story = {
args: {
recentSearches: undefined,
setInputSearchValue: () => alert('선택한 검색어 검색!'),
},
};

export const RecentSearches: Story = {
args: {
recentSearches: [
{ keyword: '21', modifiedAt: 'now' },
{ keyword: 'I Love It', modifiedAt: 'now' },
{ keyword: 'D (Half Moon)', modifiedAt: 'now' },
{ keyword: 'What 2 Do', modifiedAt: 'now' },
{ keyword: 'Bonnie & Clyde', modifiedAt: 'now' },
{ keyword: '풀어', modifiedAt: 'now' },
{ keyword: '어때', modifiedAt: 'now' },
],
setInputSearchValue: () => alert('선택한 검색어 검색!'),
},
};
32 changes: 32 additions & 0 deletions src/stories/bookSearch/SearchResult.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Meta, StoryObj } from '@storybook/react';
import type { APISearchedBook } from '@/types/book';
import SearchResult from '@/v1/bookSearch/SearchResult';

const meta: Meta<typeof SearchResult> = {
title: 'bookSearch/SearchResult',
component: SearchResult,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof SearchResult>;

const SEARCHED_BOOK: APISearchedBook = {
title: '리팩터링',
author: '마틴 파울러',
isbn: '9791162242742',
contents:
'지난 20년간 전 세계 프로그래머에게 리팩터링의 교본이었던 이 책의 1판은, 기존 코드의 디자인을 개선하고 소프트웨어 유지 관리 능력을 향상시켰으며 기존 코드를 이해하기 쉽게 만드는 데 도움을 주었습니다. 간절히 기다려온 이번 개정판에는 프로그래밍 환경의 중요한 변화가 대거 반영되었습니다. 새로운 리팩터링 카탈로그를 자바스크립트 코드로 제시합니다. 리팩터링 원칙부터 클래스 없이 리팩터링하는 방법과 데이터 조직화, 조건부 로직 간소화 방법을 다룹니다. 또한',
url: 'https://search.daum.net/search?w=bookpage&bookId=5326912&q=%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81',
imageUrl:
'https://search1.kakaocdn.net/thumb/R120x174.q85/?fname=http%3A%2F%2Ft1.daumcdn.net%2Flbook%2Fimage%2F5326912%3Ftimestamp%3D20231207165435',
publisher: '한빛미디어',
apiProvider: 'KAKAO',
};

export const Default: Story = {
args: {
searchedBooks: [SEARCHED_BOOK, SEARCHED_BOOK, SEARCHED_BOOK, SEARCHED_BOOK],
},
};
Loading

0 comments on commit dd70084

Please sign in to comment.