From c62414e61942aed5189433fbc49e82e6c5049901 Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Thu, 5 Jun 2025 16:45:57 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EC=A7=80=EB=B6=95=EC=9E=AC=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=ED=8C=9D=EC=97=85=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 구 지붕재 적합성 pdf 페이지와 연계된 내용 제거 및 수정 - crypto-js 라이브러리 제거 --- package-lock.json | 9 --- package.json | 2 - src/components/popup/SuitableDetailPopup.tsx | 15 ++--- src/components/suitable/SuitableList.tsx | 3 +- src/hooks/useSuitable.ts | 59 ++++++-------------- src/store/useSuitableStore.ts | 18 ++---- 6 files changed, 28 insertions(+), 78 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98a452c..50673cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "@tanstack/react-query-devtools": "^5.71.0", "@types/nodemailer": "^6.4.17", "axios": "^1.8.4", - "crypto-js": "^4.2.0", "env-cmd": "^10.1.0", "iron-session": "^8.0.4", "lucide": "^0.503.0", @@ -33,7 +32,6 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", - "@types/crypto-js": "^4.2.2", "@types/mysql": "^2.15.27", "@types/node": "^20", "@types/react": "^19", @@ -2142,13 +2140,6 @@ "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==", "license": "MIT" }, - "node_modules/@types/crypto-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", - "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/mysql": { "version": "2.15.27", "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", diff --git a/package.json b/package.json index 9ed3d18..064f055 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "@tanstack/react-query-devtools": "^5.71.0", "@types/nodemailer": "^6.4.17", "axios": "^1.8.4", - "crypto-js": "^4.2.0", "env-cmd": "^10.1.0", "iron-session": "^8.0.4", "lucide": "^0.503.0", @@ -40,7 +39,6 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", - "@types/crypto-js": "^4.2.2", "@types/mysql": "^2.15.27", "@types/node": "^20", "@types/react": "^19", diff --git a/src/components/popup/SuitableDetailPopup.tsx b/src/components/popup/SuitableDetailPopup.tsx index e4635c0..461ac1b 100644 --- a/src/components/popup/SuitableDetailPopup.tsx +++ b/src/components/popup/SuitableDetailPopup.tsx @@ -5,16 +5,14 @@ import { useCallback, useEffect, useState } from 'react' import SuitableDetailPopupButton from './SuitableDetailPopupButton' import { useSuitable } from '@/hooks/useSuitable' import { usePopupController } from '@/store/popupController' -import { useSuitableStore } from '@/store/useSuitableStore' -import { useSpinnerStore } from '@/store/spinnerStore' import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable' export default function SuitableDetailPopup() { const popupController = usePopupController() - const { toCodeName, toSuitableDetail, suitableCheckIcon, suitableCheckMemo, selectedSuitables, isSelectedSuitablesLoading } = useSuitable() - const { setSelectedItemsSearching } = useSuitableStore() + const { toCodeName, toSuitableDetail, suitableCheckIcon, suitableCheckMemo, getSelectedSuitables } = useSuitable() const [openItems, setOpenItems] = useState>(new Set()) + const [selectedSuitables, setSelectedSuitables] = useState([]) /* 아이템 열기/닫기 */ const toggleItemOpen = useCallback((itemId: number) => { @@ -25,13 +23,10 @@ export default function SuitableDetailPopup() { }) }, []) - /* 데이터 로딩 상태 처리 */ useEffect(() => { - useSpinnerStore.getState().setIsShow(isSelectedSuitablesLoading) - }, [isSelectedSuitablesLoading]) - - useEffect(() => { - setSelectedItemsSearching(true) + getSelectedSuitables().then((res) => { + setSelectedSuitables(res) + }) }, []) return ( diff --git a/src/components/suitable/SuitableList.tsx b/src/components/suitable/SuitableList.tsx index 918776a..a0dc5be 100644 --- a/src/components/suitable/SuitableList.tsx +++ b/src/components/suitable/SuitableList.tsx @@ -21,7 +21,7 @@ export default function SuitableList() { isLoading, suitableCheckIcon, } = useSuitable() - const { selectedItems, addSelectedItem, removeSelectedItem, setSelectedItemsSearching } = useSuitableStore() + const { selectedItems, addSelectedItem, removeSelectedItem } = useSuitableStore() const [openItems, setOpenItems] = useState>(new Set()) const observerTarget = useRef(null) @@ -49,7 +49,6 @@ export default function SuitableList() { /* 아이템 클릭 */ const handleItemClick = useCallback( (mainId: number, detailId?: number, detailIds?: Set): void => { - setSelectedItemsSearching(false) isItemSelected(mainId, detailId) ? removeSelectedItem(mainId, detailId) : addSelectedItem(mainId, detailId, detailIds) }, [isItemSelected, addSelectedItem, removeSelectedItem], diff --git a/src/hooks/useSuitable.ts b/src/hooks/useSuitable.ts index f525d61..ce6c638 100644 --- a/src/hooks/useSuitable.ts +++ b/src/hooks/useSuitable.ts @@ -1,5 +1,4 @@ -import { useInfiniteQuery, useQuery } from '@tanstack/react-query' -import { SHA256 } from 'crypto-js' +import { useInfiniteQuery } from '@tanstack/react-query' import { transformObjectKeys } from '@/libs/axios' import { useSuitableStore } from '@/store/useSuitableStore' import { useAxios } from './useAxios' @@ -19,7 +18,6 @@ export function useSuitable() { clearSearchKeyword, selectedItems, clearSelectedItems, - selectedItemsSearching, } = useSuitableStore() const getSuitables = async ({ @@ -137,42 +135,6 @@ export function useSuitable() { enabled: selectedCategory !== '' || searchKeyword !== '', }) - const serializeSelectedItems = (): { ids: string; detailIds: string } => { - const ids: string[] = [] - const detailIds: string[] = [] - for (const [key, value] of selectedItems) { - ids.push(String(key)) - for (const id of value) detailIds.push(String(id)) - } - return { ids: ids.join(','), detailIds: detailIds.length > 0 ? detailIds.join(',') : '' } - } - - const getSelectedItemsHash = (): string => { - const entries = Array.from(selectedItems.entries()) - .map(([key, value]) => `${key}:${Array.from(value).sort().join(',')}`) - .sort() - .join('|') - return SHA256(entries).toString() - } - - const { - data: selectedSuitables, - isLoading: isSelectedSuitablesLoading, - // refetch: refetchSelectedSuitables, - } = useQuery({ - queryKey: ['suitables', 'selectedItems', getSelectedItemsHash(), selectedItemsSearching], - queryFn: async () => { - const { ids, detailIds } = serializeSelectedItems() - return await getSuitableDetails(ids, detailIds) - }, - staleTime: Infinity, - gcTime: Infinity, - enabled: selectedItemsSearching, - refetchOnMount: false, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - }) - const clearSuitableStore = ({ items = false, category = false, keyword = false }: { items?: boolean; category?: boolean; keyword?: boolean }) => { if (items) clearSelectedItems() if (category) clearSelectedCategory() @@ -198,6 +160,21 @@ export function useSuitable() { return `${value}で設置可` } + const serializeSelectedItems = (): { ids: string; detailIds: string } => { + const ids: string[] = [] + const detailIds: string[] = [] + for (const [key, value] of selectedItems) { + ids.push(String(key)) + for (const id of value) detailIds.push(String(id)) + } + return { ids: ids.join(','), detailIds: detailIds.length > 0 ? detailIds.join(',') : '' } + } + + const getSelectedSuitables = async (): Promise => { + const { ids, detailIds } = serializeSelectedItems() + return await getSuitableDetails(ids, detailIds) + } + const downloadSuitablePdf = async (): Promise => { try { const { ids, detailIds } = serializeSelectedItems() @@ -240,7 +217,6 @@ export function useSuitable() { return { getSuitables, getSuitableIds, - getSuitableDetails, getSuitableCommCode, toCodeName, toSuitableDetail, @@ -250,8 +226,7 @@ export function useSuitable() { hasNextPage, isFetchingNextPage, isLoading, - selectedSuitables, - isSelectedSuitablesLoading, + getSelectedSuitables, clearSuitableStore, suitableCheckIcon, suitableCheckMemo, diff --git a/src/store/useSuitableStore.ts b/src/store/useSuitableStore.ts index c0b6ca9..8ecd343 100644 --- a/src/store/useSuitableStore.ts +++ b/src/store/useSuitableStore.ts @@ -35,10 +35,6 @@ interface SuitableState { removeSelectedItem: (mainId: number, detailId?: number) => void /* 선택된 아이템 모두 제거 */ clearSelectedItems: () => void - /* 선택된 아이템 검색 상태 */ - selectedItemsSearching: boolean - /* 선택된 아이템 검색 상태 설정 */ - setSelectedItemsSearching: (value: boolean) => void } export const useSuitableStore = create((set) => ({ @@ -47,7 +43,6 @@ export const useSuitableStore = create((set) => ({ selectedCategory: '' as string, searchKeyword: '' as string, selectedItems: new Map() as Map>, - selectedItemsSearching: false as boolean, /* 공통코드 설정 */ setSuitableCommCode: (headCode: string, commCode: CommCode[]) => @@ -68,7 +63,7 @@ export const useSuitableStore = create((set) => ({ /* 선택된 아이템 추가 */ addSelectedItem: (mainId: number, detailId?: number, detailIds?: Set) => { if (detailId) { - // 디테일(하위) 아이템 추가 + /* 디테일(하위) 아이템 추가 */ set((state) => { const detailSet = state.selectedItems.get(mainId) || new Set() detailSet.add(detailId) @@ -76,7 +71,7 @@ export const useSuitableStore = create((set) => ({ return { selectedItems: state.selectedItems } }) } else { - // 메인(상위) 아이템 추가 + /* 메인(상위) 아이템 추가 */ set((state) => { state.selectedItems.set(mainId, detailIds || new Set()) return { selectedItems: state.selectedItems } @@ -101,16 +96,16 @@ export const useSuitableStore = create((set) => ({ const newSelectedItems = new Map(state.selectedItems) if (!detailId) { - // 메인(상위) 아이템 제거 + /* 메인(상위) 아이템 제거 */ newSelectedItems.delete(mainId) return { selectedItems: newSelectedItems } } - // 디테일(하위) 아이템 제거 + /* 디테일(하위) 아이템 제거 */ const detailSet = state.selectedItems.get(mainId) || new Set() detailSet.delete(detailId) - // 디테일(하위)하위 아이템이 모두 제거되면 메인 아이템도 제거 + /* 디테일(하위)하위 아이템이 모두 제거되면 메인 아이템도 제거 */ detailSet.size === 0 ? newSelectedItems.delete(mainId) : newSelectedItems.set(mainId, detailSet) return { selectedItems: newSelectedItems } @@ -119,7 +114,4 @@ export const useSuitableStore = create((set) => ({ /* 선택된 아이템 모두 제거 */ clearSelectedItems: () => set({ selectedItems: new Map() as Map> }), - - /* 선택된 아이템 검색 상태 설정 */ - setSelectedItemsSearching: (value: boolean) => set({ selectedItemsSearching: value }), }))