From 7a6c4214cd54cb030d4b0d37f3eec12ccee2c3bd Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Fri, 1 Dec 2023 19:49:46 +0900 Subject: [PATCH 1/7] =?UTF-8?q?New=20:=20=ED=9E=88=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=BB=AC=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EC=A7=80=20=ED=82=A4=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/types/LocalStorageKey.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 client/src/types/LocalStorageKey.ts diff --git a/client/src/types/LocalStorageKey.ts b/client/src/types/LocalStorageKey.ts new file mode 100644 index 0000000..aa2936d --- /dev/null +++ b/client/src/types/LocalStorageKey.ts @@ -0,0 +1,4 @@ +/** + * 검색 기록 관련 키 타입 + */ +export type SearchHistoryKeyType = "alcohol-search-history"; From 68c2958a7bb042922edcaed257e638e4062d0142 Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Fri, 1 Dec 2023 20:04:02 +0900 Subject: [PATCH 2/7] =?UTF-8?q?New=20:=20=EB=A1=9C=EC=BB=AC=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=EC=A7=80=20=EA=B4=80=EB=A6=AC=20Hooks=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/hooks/localStorage/useLocalStorage.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 client/src/hooks/localStorage/useLocalStorage.ts diff --git a/client/src/hooks/localStorage/useLocalStorage.ts b/client/src/hooks/localStorage/useLocalStorage.ts new file mode 100644 index 0000000..d042640 --- /dev/null +++ b/client/src/hooks/localStorage/useLocalStorage.ts @@ -0,0 +1,33 @@ +import { useCallback, useEffect, useState } from "react"; + +const useLocalStorage = (storageKey: string) => { + /** + * 로컬스토리지에 아이템을 stringify해 저장하는 함수 + */ + const setItem = useCallback( + (keyword: T) => { + localStorage.setItem(storageKey, JSON.stringify(keyword)); + }, + [storageKey] + ); + /** + * 로컬 스토리지 아이템을 파싱해서 리턴하는 함수 + */ + const getItems = useCallback((): T | null => { + return JSON.parse(localStorage.getItem(storageKey) || "null"); + }, [storageKey]); + + const [storageValue, setStorageValue] = useState(getItems()); + + // 새로운 값이 저장될 경우, 로컬스토리지에도 같이 저장 + useEffect(() => { + if (!storageValue) { + return; + } + setItem(storageValue); + }, [storageValue]); + + return [storageValue, setStorageValue] as const; +}; + +export default useLocalStorage; From 8f242cb09973aabba256200e35a6f2dea6a2380c Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Fri, 1 Dec 2023 20:05:39 +0900 Subject: [PATCH 3/7] =?UTF-8?q?Refactor=20:=20=ED=9E=88=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EA=B4=80=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20Hooks=20=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/SearchHistory.tsx | 35 ++++-------- .../hooks/searchHistory/useSearchHistory.tsx | 54 +++++++++++++++++++ 2 files changed, 64 insertions(+), 25 deletions(-) create mode 100644 client/src/hooks/searchHistory/useSearchHistory.tsx diff --git a/client/src/components/SearchHistory.tsx b/client/src/components/SearchHistory.tsx index 5b1f382..84dce40 100644 --- a/client/src/components/SearchHistory.tsx +++ b/client/src/components/SearchHistory.tsx @@ -1,34 +1,19 @@ import { Button, Stack, StackProps, Typography } from "@mui/material"; -import { useCallback, useState } from "react"; import XIcon from "@/assets/icons/XIcon.svg"; +import { SearchHistoryKeyType } from "@/types/LocalStorageKey"; +import useSearchHistory from "@/hooks/searchHistory/useSearchHistory"; interface SearchHistoryProps extends Omit { - storageKey: string; - onClick: () => void; + storageKey: SearchHistoryKeyType; + onClick: (keyword: string) => void; } const SearchHistory = ({ storageKey, onClick }: SearchHistoryProps) => { - const getItems = useCallback(() => { - return JSON.parse(localStorage.getItem(storageKey) ?? "[]") as string[]; - }, [storageKey]); - - const [searchHistory, setSearchHistory] = useState(getItems()); - - const removeAll = useCallback(() => { - localStorage.setItem(storageKey, "[]"); - setSearchHistory(getItems()); - }, [storageKey]); - - const removeByKeyword = useCallback( - (keyword: string) => { - const filteredHistory = searchHistory.filter( - (prevKeyword) => prevKeyword !== keyword - ); - localStorage.setItem(storageKey, JSON.stringify(filteredHistory)); - setSearchHistory(getItems()); - }, - [storageKey] - ); + const { + state: searchHistory, + removeAll, + removeByKeyword, + } = useSearchHistory(storageKey); return searchHistory.length > 0 ? ( <> @@ -45,7 +30,7 @@ const SearchHistory = ({ storageKey, onClick }: SearchHistoryProps) => { onClick(keyword)} direction="row" justifyContent="space-between" alignItems="center" diff --git a/client/src/hooks/searchHistory/useSearchHistory.tsx b/client/src/hooks/searchHistory/useSearchHistory.tsx new file mode 100644 index 0000000..6e97d22 --- /dev/null +++ b/client/src/hooks/searchHistory/useSearchHistory.tsx @@ -0,0 +1,54 @@ +import { SearchHistoryKeyType } from "@/types/LocalStorageKey"; +import { useCallback, useEffect } from "react"; +import useLocalStorage from "../localStorage/useLocalStorage"; + +/** + * 로컬스토리지 키를 입력받아 + * 해당 스토리지를 바라보는 state를 리턴 (State 업데이트시 자동으로 반영) + * + * @param storageKey 로컬스토리지 키 + * @returns + */ +const useSearchHistory = (storageKey: SearchHistoryKeyType) => { + const [searchHistory, setSearchHistory] = + useLocalStorage(storageKey); + + useEffect(() => { + if (searchHistory === null) { + setSearchHistory([]); + } + }, []); + + const removeAll = useCallback(() => { + setSearchHistory([]); + }, [storageKey]); + + const removeByKeyword = useCallback( + (keyword: string) => { + const filteredHistory = (searchHistory ?? []).filter( + (prevKeyword) => prevKeyword !== keyword + ); + setSearchHistory(filteredHistory); + }, + [storageKey] + ); + const addSearchHistory = useCallback( + (keyword: string) => { + setSearchHistory((prev) => { + return [ + keyword, + ...(prev ?? []).filter((prevKeyword) => prevKeyword !== keyword), + ]; + }); + }, + [storageKey] + ); + return { + state: searchHistory ?? [], + add: addSearchHistory, + removeAll, + removeByKeyword, + }; +}; + +export default useSearchHistory; From 2a85ffab847031d90cf81117320d587c729a828f Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Fri, 1 Dec 2023 20:06:26 +0900 Subject: [PATCH 4/7] =?UTF-8?q?Refactor=20:=20=EC=8A=A4=EC=BC=88=EB=A0=88?= =?UTF-8?q?=ED=86=A4=20=EC=B2=98=EB=A6=AC=20List=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/wiki/AlcoholList.tsx | 44 ++++++++++++------- .../src/components/wiki/AlcoholPagination.tsx | 15 +++---- .../wiki/searchDrawer/WikiSerachArea.tsx | 29 +++++++----- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/client/src/components/wiki/AlcoholList.tsx b/client/src/components/wiki/AlcoholList.tsx index dc6bb3f..4a54333 100644 --- a/client/src/components/wiki/AlcoholList.tsx +++ b/client/src/components/wiki/AlcoholList.tsx @@ -3,27 +3,41 @@ import AlcoholNameTag from "@/components/wiki/AlcoholNameTag"; import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface"; import { Typography } from "@mui/material"; import { memo } from "react"; +import AlcoholListSkeleton from "./AlcoholListSkeleton"; -const AlcoholList = ({ - data: alcohols, -}: { - data: AlcoholDetailInterface[]; -}) => { - return ( +interface AlcoholList { + data?: AlcoholDetailInterface[]; + onClickElement?: (data: AlcoholDetailInterface) => void; +} +/** + * 술 정보 Array 를 입력받아 List로 맵핑해주는 컴포넌트 + * onClickElement 속성으로 각 엘리먼트 클릭 시 속성을 지정가능 + * @returns + */ +const AlcoholList = ({ data: alcohols, onClickElement }: AlcoholList) => { + return alcohols ? ( <> - {alcohols?.length > 0 ? ( - alcohols.map(({ alcoholName, alcoholNo, alcoholType }) => ( - - )) + {alcohols.length > 0 ? ( + alcohols.map((alcohol) => { + const { alcoholName, alcoholNo, alcoholType } = alcohol; + return ( + { + onClickElement && onClickElement(alcohol); + }} + /> + ); + }) ) : ( 검색 결과가 없어요 )} + ) : ( + ); }; export default memo(AlcoholList); diff --git a/client/src/components/wiki/AlcoholPagination.tsx b/client/src/components/wiki/AlcoholPagination.tsx index dbeae6f..967e9f3 100644 --- a/client/src/components/wiki/AlcoholPagination.tsx +++ b/client/src/components/wiki/AlcoholPagination.tsx @@ -2,19 +2,18 @@ import useGetAlcoholListQuery from "@/queries/alcohol/useGetAlcoholListQuery"; import AlcoholList from "@/components/wiki/AlcoholList"; import { Pagination, Stack } from "@mui/material"; -import AlcoholListSkeleton from "@/components/wiki/AlcoholListSkeleton"; +import usePushToWikiDetail from "@/hooks/wiki/usePushToWikiDetail"; const AlcoholPagenation = () => { - const { data: alcohols, isSuccess } = useGetAlcoholListQuery(); - + const { data: alcohols } = useGetAlcoholListQuery(); + const onClickElementHandler = usePushToWikiDetail(); return ( - {isSuccess ? ( - - ) : ( - - )} + diff --git a/client/src/components/wiki/searchDrawer/WikiSerachArea.tsx b/client/src/components/wiki/searchDrawer/WikiSerachArea.tsx index 7c517fc..a34e85a 100644 --- a/client/src/components/wiki/searchDrawer/WikiSerachArea.tsx +++ b/client/src/components/wiki/searchDrawer/WikiSerachArea.tsx @@ -1,19 +1,24 @@ -import { useEffect, useRef, useState } from "react"; +import { useContext, useEffect, useRef, useState } from "react"; import useDebounce from "@/hooks/useDebounce"; import InputSearchIcon from "@/assets/icons/InputSearchIcon.svg"; import { Stack, TextField } from "@mui/material"; import useGetAlcoholListQuery from "@/queries/alcohol/useGetAlcoholListQuery"; import AlcoholList from "@/components/wiki/AlcoholList"; -import AlcoholListSkeleton from "../AlcoholListSkeleton"; import SearchHistory from "@/components/SearchHistory"; import { ALCOHOL_SEARCH_HISTORY } from "@/const/localstorageKey"; +import WikiPageContext from "@/store/wiki/WikiPageContext"; +import usePushToWikiDetail from "@/hooks/wiki/usePushToWikiDetail"; const WikiSerachArea = () => { + const { setIsSearching } = useContext(WikiPageContext); + const [searchKeyword, setSearchKeyword] = useState(""); const debouncedValue = useDebounce(searchKeyword, 300); - const { data: alcohols, isSuccess } = useGetAlcoholListQuery(debouncedValue); - const inputRef = useRef(null); + const { data: alcohols } = useGetAlcoholListQuery(debouncedValue); + + const onClickElementHandler = usePushToWikiDetail(); + const inputRef = useRef(null); useEffect(() => { inputRef.current?.focus(); }, []); @@ -37,17 +42,17 @@ const WikiSerachArea = () => { {searchKeyword ? ( // 입력중인 경우 - <> - {isSuccess ? ( - - ) : ( - - )} - + { + onClickElementHandler(alcoholData); + setIsSearching(false); + }} + /> ) : ( // 입력이 없는경우 검색기록 표출 console.log("눌림")} + onClick={(keyword) => setSearchKeyword(keyword)} storageKey={ALCOHOL_SEARCH_HISTORY} /> )} From 7dc5233542c5fae1027eb43089d2cc435170dcb4 Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Fri, 1 Dec 2023 20:08:32 +0900 Subject: [PATCH 5/7] =?UTF-8?q?Refactor=20:=20Namtag=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20Presentational=20component=20=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/wiki/AlcoholNameTag.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/client/src/components/wiki/AlcoholNameTag.tsx b/client/src/components/wiki/AlcoholNameTag.tsx index 58bac47..54a6815 100644 --- a/client/src/components/wiki/AlcoholNameTag.tsx +++ b/client/src/components/wiki/AlcoholNameTag.tsx @@ -2,10 +2,8 @@ import { Box, BoxProps, Chip, IconButton, Typography } from "@mui/material"; import PostSeeMoreIcon from "@/assets/icons/PostSeeMoreIcon.svg"; import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface"; import XIcon from "@/assets/icons/XIcon.svg"; -import { useRouter } from "next/navigation"; -import { WIKI_DETAIL } from "@/const/clientPath"; -interface AlcoholNameTagInterface extends BoxProps { +export interface AlcoholNameTagInterface extends BoxProps { alcoholName: AlcoholDetailInterface["alcoholName"]; alcoholType: AlcoholDetailInterface["alcoholType"]; alcoholNo: AlcoholDetailInterface["alcoholNo"]; @@ -21,7 +19,6 @@ const AlcoholNameTag = ({ onClickRemove, ...others }: AlcoholNameTagInterface) => { - const router = useRouter(); return ( ) : ( - router.push(WIKI_DETAIL(String(alcoholNo)))} - > + )} From d865c25c1cb1402b97c5107a53f2228c8277ae7b Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Fri, 1 Dec 2023 20:09:10 +0900 Subject: [PATCH 6/7] =?UTF-8?q?New=20:=20=EC=9C=84=ED=82=A4=20=EB=94=94?= =?UTF-8?q?=ED=85=8C=EC=9D=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=ED=95=A8=EC=88=98=20Hooks=20=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/wiki/usePushToWikiDetail.ts | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 client/src/hooks/wiki/usePushToWikiDetail.ts diff --git a/client/src/hooks/wiki/usePushToWikiDetail.ts b/client/src/hooks/wiki/usePushToWikiDetail.ts new file mode 100644 index 0000000..9880f16 --- /dev/null +++ b/client/src/hooks/wiki/usePushToWikiDetail.ts @@ -0,0 +1,27 @@ +import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface"; +import { useRouter } from "next/navigation"; +import { useCallback } from "react"; +import useSearchHistory from "../searchHistory/useSearchHistory"; +import { ALCOHOL_SEARCH_HISTORY } from "@/const/localstorageKey"; +import { WIKI_DETAIL } from "@/const/clientPath"; +/** + * 검색히스토리에 해당 술을 남기고, 디테일페이지로 이동시키는 함수를 리턴하는 훅 + * @returns 해당 callback함수 + */ +const usePushToWikiDetail = () => { + const { add: addToSearchHistory } = useSearchHistory(ALCOHOL_SEARCH_HISTORY); + const router = useRouter(); + /** + * 검색히스토리에 해당 술을 남기고, 디테일페이지로 이동시키는 함수를 리턴하는 함수 + */ + const onClickElementHandler = useCallback( + ({ alcoholName, alcoholNo }: AlcoholDetailInterface) => { + addToSearchHistory(alcoholName); + router.push(WIKI_DETAIL(String(alcoholNo))); + }, + [addToSearchHistory] + ); + return onClickElementHandler; +}; + +export default usePushToWikiDetail; From fc21cbaad477e2ba43352028b6b1a90e7074462d Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Fri, 1 Dec 2023 20:09:57 +0900 Subject: [PATCH 7/7] =?UTF-8?q?Minor=20:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AA=A8=EB=93=88=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20FIXME=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/newpost/SearchAlcoholInput.tsx | 1 + client/src/components/wiki/searchDrawer/WikiSearchDrawer.tsx | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/components/newpost/SearchAlcoholInput.tsx b/client/src/components/newpost/SearchAlcoholInput.tsx index f3b9a27..e7fecdd 100644 --- a/client/src/components/newpost/SearchAlcoholInput.tsx +++ b/client/src/components/newpost/SearchAlcoholInput.tsx @@ -67,6 +67,7 @@ const SearchAlcoholInput = ({ setAlcoholNo }: SearchAlcoholInputInterface) => { onBlur={() => setIsSearchingAlCohol(false)} autoComplete="off" /> + {/* FIXME List 컴포넌트로 분리 */} {isSearchingAlcohol && ( diff --git a/client/src/components/wiki/searchDrawer/WikiSearchDrawer.tsx b/client/src/components/wiki/searchDrawer/WikiSearchDrawer.tsx index a07c2bd..c57b1c5 100644 --- a/client/src/components/wiki/searchDrawer/WikiSearchDrawer.tsx +++ b/client/src/components/wiki/searchDrawer/WikiSearchDrawer.tsx @@ -1,4 +1,4 @@ -import { SwipeableDrawer, Stack, styled, Box } from "@mui/material"; +import { Stack } from "@mui/material"; import { useContext } from "react"; import WikiPageContext from "@/store/wiki/WikiPageContext"; import WikiSerachArea from "@/components/wiki/searchDrawer/WikiSerachArea"; @@ -7,7 +7,6 @@ import CustomSwipeableDrawer from "@/components/CustomSwipeableDrawer"; const WikiSearchDrawer = () => { const { isSearching, setIsSearching } = useContext(WikiPageContext); - return ( { }; export default WikiSearchDrawer; -