Skip to content

[#592] [도서 검색] 검색 키워드 및 결과 유지 기능 구현 #593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions public/icons/close.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: 5 additions & 1 deletion scripts/server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require('fs');
const https = require('https');
const path = require('path');
const { parse } = require('url');
const { execSync } = require('child_process');
const next = require('next');
Expand All @@ -14,7 +15,10 @@ const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();

const SELF_CERTIFICATES_PATH = {
ca: `${execSync('mkcert -CAROOT').toString().replace(/\s/g, '')}/rootCA.pem`,
ca: path.resolve(
execSync('mkcert -CAROOT').toString().replace(/\s$/, ''),
'./rootCA.pem'
),
key: './.certificates/localhost-key.pem',
cert: './.certificates/localhost.pem',
};
Expand Down
19 changes: 18 additions & 1 deletion src/app/book/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import bookAPI from '@/apis/book';

import SSRSafeSuspense from '@/components/SSRSafeSuspense';
import useDebounceValue from '@/hooks/useDebounce';
import useQueryParams from '@/hooks/useQueryParams';
import { checkAuthentication } from '@/utils/helpers';

import Loading from '@/v1/base/Loading';
Expand All @@ -28,17 +29,32 @@ type FormValues = {
searchValue: string;
};

const KEYWORD = 'keyword';

const BookSearchPage = () => {
const { getQueryParam, setQueryParams, removeQueryParam } = useQueryParams();

const { register, watch, setValue } = useForm<FormValues>({
mode: 'all',
defaultValues: {
searchValue: '',
searchValue: getQueryParam(KEYWORD) ?? '',
},
});

const watchedKeyword = watch('searchValue');
const debouncedKeyword = useDebounceValue(watchedKeyword, 1000);

/* debounce된 keyword값에 따라 queryParameter를 수정하는 useEffect */
useEffect(() => {
const queryValue = getQueryParam(KEYWORD);

if (debouncedKeyword) {
setQueryParams({ [KEYWORD]: debouncedKeyword });
} else if (!debouncedKeyword && queryValue) {
removeQueryParam(KEYWORD, 'replace');
}
}, [debouncedKeyword, getQueryParam, setQueryParams, removeQueryParam]);

/* TopHeader가 사라졌을 때 input의 위치 top: 5.8rem */
const inputPositionClasses = watchedKeyword && 'sticky top-[5.8rem]';

Expand All @@ -59,6 +75,7 @@ const BookSearchPage = () => {
}`}
>
<Input
type="search"
inputStyle="line"
leftIconType="search"
placeholder="책 제목, 작가를 검색해보세요"
Expand Down
70 changes: 70 additions & 0 deletions src/hooks/useQueryParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useRouter, useSearchParams } from 'next/navigation';
import { useCallback } from 'react';

type RouteOptions = 'push' | 'replace';

type QueryParams = { [key: string]: string };

const useQueryParams = () => {
const router = useRouter();
const searchParams = useSearchParams();

const queryParams = searchParams.toString();

const getQueryParam = useCallback(
(queryKey: string) => {
const queryParam = searchParams.get(queryKey);

return queryParam;
},
[searchParams]
);

const setQueryParams = useCallback(
(queryParams: QueryParams, option?: RouteOptions) => {
const prevParams = new URLSearchParams(searchParams.toString());

for (const queryKey in queryParams) {
prevParams.set(queryKey, queryParams[queryKey]);
}

const newQueryParams = prevParams.toString();

switch (option) {
case 'replace':
router.replace(`?${newQueryParams}`, { shallow: true });
return;
case 'push':
default:
router.push(`?${newQueryParams}`, { shallow: true });
return;
}
},
[router, searchParams]
);

const removeQueryParam = useCallback(
(queryKey: string, option?: RouteOptions) => {
const prevParams = new URLSearchParams(searchParams.toString());
if (!prevParams.has(queryKey)) return;
prevParams.delete(queryKey);

const newQueryParams = prevParams.toString();

switch (option) {
case 'replace':
router.replace(`?${newQueryParams}`, { shallow: true });
return;
case 'push':
default:
router.push(`?${newQueryParams}`, { shallow: true });
return;
}
},
[router, searchParams]
);

return { queryParams, getQueryParam, setQueryParams, removeQueryParam };
};

export default useQueryParams;
13 changes: 13 additions & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
}

input[type='date'] {
appearance: none;
-webkit-appearance: none;
}

Expand All @@ -67,8 +68,20 @@
}
/* FireFox */
input[type='number'] {
appearance: none;
-moz-appearance: textfield;
}

input[type='search']::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
width: 2.1rem;
height: 2.1rem;
cursor: pointer;
background-image: url('/icons/close.svg');
background-position: center;
background-repeat: no-repeat;
}
}

@layer utilities {
Expand Down
2 changes: 1 addition & 1 deletion src/v1/base/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const getInputStyleClasses = (inputStyle: InputStyle) => {
const getLeftIconClass = (iconType?: LeftIconType) => {
switch (iconType) {
case 'search':
return 'px-[1rem] before:relative before:top-[0.3rem] before:pr-[1rem] before:content-search';
return 'px-[1rem] before:h-[2.4rem] before:w-[2rem] before:relative before:top-[0.2rem] before:mr-[1rem] before:content-search';
default:
return '';
}
Expand Down
6 changes: 3 additions & 3 deletions src/v1/bookSearch/BestSellers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,10 @@ const BestSellerSkeleton = () => {
export const BestSellersSkeleton = () => {
return (
<Skeleton>
<section className="flex flex-col gap-[1.7rem]">
<section className="flex flex-col gap-[1.5rem]">
<Skeleton.Text width="7rem" fontSize="2xlarge" />
<ul className="flex w-full gap-[1rem] pb-[1rem]">
<Skeleton.Rect width="5.5rem" height="2.7rem" rounded="large" />
<ul className="flex w-full gap-[1rem]">
<Skeleton.Rect width="5.5rem" height="2.5rem" rounded="large" />
</ul>
<ul className="flex w-[12.8rem] flex-row justify-around">
<Skeleton.Text width="2.5rem" fontSize="xsmall" />
Expand Down
10 changes: 5 additions & 5 deletions src/v1/bookSearch/RecentSearchList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ export default RecentSearchList;
export const RecentSearchListSkeleton = () => {
return (
<Skeleton>
<section className="flex animate-pulse flex-col gap-[1.7rem] rounded-[0.5rem]">
<section className="flex animate-pulse flex-col gap-[1.5rem] rounded-[0.5rem]">
<Skeleton.Text width="8rem" fontSize="2xlarge" />
<ul className="flex w-full gap-[1rem] pb-[1rem]">
<Skeleton.Rect width="7.55rem" height="2.8rem" rounded="full" />
<Skeleton.Rect width="7.55rem" height="2.8rem" rounded="full" />
<Skeleton.Rect width="7.55rem" height="2.8rem" rounded="full" />
<Skeleton.Rect width="7.55rem" height="2.8rem" rounded="full" />
<Skeleton.Rect width="7.55rem" height="3.3rem" rounded="full" />
<Skeleton.Rect width="7.55rem" height="3.3rem" rounded="full" />
<Skeleton.Rect width="7.55rem" height="3.3rem" rounded="full" />
<Skeleton.Rect width="7.55rem" height="3.3rem" rounded="full" />
</ul>
</section>
</Skeleton>
Expand Down
Loading