From 6ec1d3fa9f1288e86114eabf3c439fa1ea7a77c2 Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Tue, 20 May 2025 16:39:06 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A7=80=EB=B6=95=EC=9E=AC=20=EC=A0=81?= =?UTF-8?q?=ED=95=A9=EC=84=B1=20=EC=A1=B0=ED=9A=8C=20raw=20query=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/suitable/list/route.ts | 119 +++++++------- src/app/api/suitable/list/test/route.ts | 71 -------- src/app/suitable-test/layout.tsx | 24 --- src/app/suitable-test/page.tsx | 9 - src/components/suitable/Suitable.tsx | 6 +- src/components/suitable/SuitableList.tsx | 15 +- src/components/suitable/SuitableListRaw.tsx | 173 -------------------- src/components/suitable/SuitableRaw.tsx | 118 ------------- src/hooks/useSuitable.ts | 46 +++--- src/hooks/useSuitableRaw.ts | 109 ------------ src/types/Suitable.ts | 21 +-- 11 files changed, 95 insertions(+), 616 deletions(-) delete mode 100644 src/app/api/suitable/list/test/route.ts delete mode 100644 src/app/suitable-test/layout.tsx delete mode 100644 src/app/suitable-test/page.tsx delete mode 100644 src/components/suitable/SuitableListRaw.tsx delete mode 100644 src/components/suitable/SuitableRaw.tsx delete mode 100644 src/hooks/useSuitableRaw.ts diff --git a/src/app/api/suitable/list/route.ts b/src/app/api/suitable/list/route.ts index 32eac33..edc5579 100644 --- a/src/app/api/suitable/list/route.ts +++ b/src/app/api/suitable/list/route.ts @@ -1,82 +1,75 @@ import { NextRequest, NextResponse } from 'next/server' import { prisma } from '@/libs/prisma' -import { SUITABLE_HEAD_CODE, type SuitableMain } from '@/types/Suitable' +import { type Suitable } from '@/types/Suitable' export async function GET(request: NextRequest) { try { const searchParams = request.nextUrl.searchParams + + const pageNumber = parseInt(searchParams.get('pageNumber') || '0') + const itemPerPage = parseInt(searchParams.get('itemPerPage') || '0') + if (pageNumber === 0 || itemPerPage === 0) { + return NextResponse.json({ error: '페이지 번호와 페이지당 아이템 수가 필요합니다' }, { status: 400 }) + } + + const ids = searchParams.get('ids') const category = searchParams.get('category') const keyword = searchParams.get('keyword') - let MainWhereCondition: any = {} - const whereCondition: string[] = [] - const params: string[] = [] + let query = ` + SELECT + msm.id + , msm.product_name + , msm.manu_ft_cd + , msm.roof_mt_cd + , msm.roof_sh_cd + , details.detail + FROM ms_suitable_main msm + LEFT JOIN ( + SELECT + msd.main_id + , ( + SELECT + msd_json.id + , msd_json.trestle_mfpc_cd + , msd_json.trestle_manufacturer_product_name + , msd_json.memo + FROM ms_suitable_detail msd_json + WHERE msd.main_id = msd_json.main_id + FOR JSON PATH + ) AS detail + FROM ms_suitable_detail msd + GROUP BY msd.main_id + ) AS details + ON msm.id = details.main_id + --mainIds AND details.main_id IN (:mainIds) + WHERE 1=1 + --mainIds AND msm.id IN (:mainIds) + --roofMtCd AND msm.roof_mt_cd = ':roofMtCd' + --productName AND msm.product_name LIKE '%:productName%' + ORDER BY msm.product_name + OFFSET (@P1 - 1) * @P2 ROWS + FETCH NEXT @P2 ROWS ONLY; + ` + + // 검색 조건 설정 + if (ids) { + query = query.replaceAll('--mainIds ', '') + query = query.replaceAll(':mainIds', ids) + } if (category) { - whereCondition.push(`${SUITABLE_HEAD_CODE.ROOF_MT_CD} = @P1`) - params.push(category) - MainWhereCondition[SUITABLE_HEAD_CODE.ROOF_MT_CD] = category + query = query.replace('--roofMtCd ', '') + query = query.replace(':roofMtCd', category) } if (keyword) { - whereCondition.push('PRODUCT_NAME LIKE @P2') - params.push(`%${keyword}%`) - MainWhereCondition['PRODUCT_NAME'] = { - contains: keyword, - } + query = query.replace('--productName ', '') + query = query.replace(':productName', keyword) } - const startTime = performance.now() - console.log(`쿼리 (main table) 시작 시간: ${startTime}ms`) // @ts-ignore - const suitable = await prisma.MS_SUITABLE_MAIN.findMany({ - select: { - ID: true, - PRODUCT_NAME: true, - ROOF_MT_CD: true, - }, - where: MainWhereCondition, - orderBy: { - PRODUCT_NAME: 'asc', - }, - }) + const suitable: Suitable[] = await prisma.$queryRawUnsafe(query, pageNumber, itemPerPage) - const endTime = performance.now() - console.log(`쿼리 (main table) 종료 시간: ${endTime - startTime}ms`) - - const mainIds: number[] = suitable.map((item: SuitableMain) => item.id) - - - const startTime2 = performance.now() - console.log(`쿼리 (detail table) 시작 시간: ${startTime2}ms`) - let detailQuery = ` - SELECT - msd.main_id - , ( - SELECT - msd_json.id - , msd_json.trestle_mfpc_cd - , msd_json.trestle_manufacturer_product_name - , msd_json.memo - FROM ms_suitable_detail msd_json - WHERE msd.main_id = msd_json.main_id - FOR JSON PATH - ) AS detail - FROM ms_suitable_detail msd - -- WHERE 1=1 - GROUP BY msd.main_id - ` - if (whereCondition.length > 0) { - detailQuery = detailQuery.replace('-- WHERE 1=1', `WHERE msd.main_id IN @P1`) - } - // @ts-ignore - const detail = await prisma.$queryRawUnsafe(detailQuery, ...mainIds) - - const endTime2 = performance.now() - console.log(`쿼리 (detail table) 종료 시간: ${endTime2 - startTime2}ms`) - - const endTime3 = performance.now() - console.log(`쿼리 총 실행 시간: ${endTime3 - startTime}ms`) - - return NextResponse.json({ suitable, detail }) + return NextResponse.json(suitable) } catch (error) { console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) diff --git a/src/app/api/suitable/list/test/route.ts b/src/app/api/suitable/list/test/route.ts deleted file mode 100644 index e4688bd..0000000 --- a/src/app/api/suitable/list/test/route.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server' -import { prisma } from '@/libs/prisma' -import { SUITABLE_HEAD_CODE, type Suitable } from '@/types/Suitable' - -export async function GET(request: NextRequest) { - try { - const searchParams = request.nextUrl.searchParams - const category = searchParams.get('category') - const keyword = searchParams.get('keyword') - - const whereCondition: string[] = [] - const params: string[] = [] - if (category) { - whereCondition.push(`${SUITABLE_HEAD_CODE.ROOF_MT_CD} = @P1`) - params.push(category) - } - if (keyword) { - whereCondition.push('PRODUCT_NAME LIKE @P2') - params.push(`%${keyword}%`) - } - - const startTime = performance.now() - console.log(`쿼리 시작 시간: ${startTime}ms`) - - let query = ` - SELECT - msm.id - , msm.product_name - , msm.manu_ft_cd - , msm.roof_mt_cd - , msm.roof_sh_cd - , details.detail - FROM ms_suitable_main msm - LEFT JOIN ( - SELECT - msd.main_id - , ( - SELECT - msd_json.id - , msd_json.trestle_mfpc_cd - , msd_json.trestle_manufacturer_product_name - , msd_json.memo - FROM ms_suitable_detail msd_json - WHERE msd.main_id = msd_json.main_id - FOR JSON PATH - ) AS detail - FROM ms_suitable_detail msd - GROUP BY msd.main_id - ) AS details - ON msm.id = details.main_id - -- AND details.main_id IN (#mainIds) - -- WHERE 1=1 - ORDER BY msm.product_name` - - // 검색 조건 추가 - if (whereCondition.length > 0) { - query = query.replace('-- WHERE 1=1', `WHERE ${whereCondition.join(' AND ')}`) - } - - // @ts-ignore - const suitable: Suitable[] = await prisma.$queryRawUnsafe(query, ...params) - - const endTime = performance.now() - console.log(`쿼리 실행 시간: ${endTime - startTime}ms`) - - return NextResponse.json(suitable) - } catch (error) { - console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) - return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) - } -} diff --git a/src/app/suitable-test/layout.tsx b/src/app/suitable-test/layout.tsx deleted file mode 100644 index e5e7c3f..0000000 --- a/src/app/suitable-test/layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { ReactNode } from 'react' - -interface SuitableLayoutProps { - children: ReactNode -} - -export default function layout({ children }: SuitableLayoutProps) { - return ( - <> -
-
-
-
-
この適合表は参考資料として使用してください.
-
詳細やお問い合わせは1:1お問い合わせをご利用ください.
-
屋根材の選択or屋根材名を直接入力してください.
-
-
- {children} -
-
- - ) -} diff --git a/src/app/suitable-test/page.tsx b/src/app/suitable-test/page.tsx deleted file mode 100644 index a5299fe..0000000 --- a/src/app/suitable-test/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import SuitableRaw from '@/components/suitable/SuitableRaw' - -export default function page() { - return ( - <> - - - ) -} diff --git a/src/components/suitable/Suitable.tsx b/src/components/suitable/Suitable.tsx index 36a397f..15cda44 100644 --- a/src/components/suitable/Suitable.tsx +++ b/src/components/suitable/Suitable.tsx @@ -2,13 +2,13 @@ import Image from 'next/image' import { useEffect, useState } from 'react' -import SuitableList from './SuitableList' +import SuitableListRaw from './SuitableList' import { useSuitable } from '@/hooks/useSuitable' import { useSuitableStore } from '@/store/useSuitableStore' import type { CommCode } from '@/types/CommCode' import { SUITABLE_HEAD_CODE } from '@/types/Suitable' -export default function Suitable() { +export default function SuitableRaw() { const [reference, setReference] = useState(true) const { getSuitableCommCode, refetchBySearch } = useSuitable() @@ -110,7 +110,7 @@ export default function Suitable() { - + ) diff --git a/src/components/suitable/SuitableList.tsx b/src/components/suitable/SuitableList.tsx index 18d94e5..bbb8e03 100644 --- a/src/components/suitable/SuitableList.tsx +++ b/src/components/suitable/SuitableList.tsx @@ -6,7 +6,7 @@ import SuitableButton from './SuitableButton' import SuitableNoData from './SuitableNoData' import { useSuitable } from '@/hooks/useSuitable' import { useSuitableStore } from '@/store/useSuitableStore' -import { SUITABLE_HEAD_CODE, type SuitableMain, type SuitableDetail } from '@/types/Suitable' +import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable' // 한 번에 로드할 아이템 수 const ITEMS_PER_PAGE = 100 @@ -14,9 +14,8 @@ const ITEMS_PER_PAGE = 100 export default function SuitableList() { const { toCodeName, suitableSearchResults, isSearchLoading, toSuitableDetail } = useSuitable() const { selectedItems, addSelectedItem, removeSelectedItem } = useSuitableStore() - const [openItems, setOpenItems] = useState>(new Set()) - const [visibleItems, setVisibleItems] = useState([]) + const [visibleItems, setVisibleItems] = useState([]) const [page, setPage] = useState(1) const [isLoadingMore, setIsLoadingMore] = useState(false) const observerTarget = useRef(null) @@ -32,7 +31,7 @@ export default function SuitableList() { // 초기 데이터 로드 useEffect(() => { if (suitableSearchResults) { - const initialItems = suitableSearchResults.suitable.slice(0, ITEMS_PER_PAGE) + const initialItems = suitableSearchResults.slice(0, ITEMS_PER_PAGE) setVisibleItems(initialItems) setPage(1) } @@ -46,7 +45,7 @@ export default function SuitableList() { const nextPage = page + 1 const startIndex = (nextPage - 1) * ITEMS_PER_PAGE const endIndex = startIndex + ITEMS_PER_PAGE - const nextItems = suitableSearchResults.suitable.slice(startIndex, endIndex) + const nextItems = suitableSearchResults.slice(startIndex, endIndex) if (nextItems.length > 0) { setIsLoadingMore(true) @@ -108,7 +107,7 @@ export default function SuitableList() { // 메모이제이션된 아이템 렌더링 const renderItem = useCallback( - (item: SuitableMain) => { + (item: Suitable) => { const isSelected = isItemSelected(item.id) const isOpen = openItems.has(item.id) @@ -124,7 +123,7 @@ export default function SuitableList() {
    - {toSuitableDetail(item.id).map((subItem: SuitableDetail) => ( + {toSuitableDetail(item.detail).map((subItem: SuitableDetail) => (
  • @@ -158,7 +157,7 @@ export default function SuitableList() { return
    Loading...
    } - if (!suitableSearchResults?.suitable.length) { + if (!suitableSearchResults?.length) { return } diff --git a/src/components/suitable/SuitableListRaw.tsx b/src/components/suitable/SuitableListRaw.tsx deleted file mode 100644 index 6dc7f36..0000000 --- a/src/components/suitable/SuitableListRaw.tsx +++ /dev/null @@ -1,173 +0,0 @@ -'use client' - -import Image from 'next/image' -import { useState, useEffect, useRef, useCallback, useMemo } from 'react' -import SuitableButton from './SuitableButton' -import SuitableNoData from './SuitableNoData' -import { useSuitableRaw, type Suitable } from '@/hooks/useSuitableRaw' -import { useSuitableStore } from '@/store/useSuitableStore' -import { SUITABLE_HEAD_CODE, type SuitableDetail } from '@/types/Suitable' - -// 한 번에 로드할 아이템 수 -const ITEMS_PER_PAGE = 100 - -export default function SuitableListRaw() { - const { toCodeName, suitableSearchResults, isSearchLoading, toSuitableDetail } = useSuitableRaw() - const { selectedItems, addSelectedItem, removeSelectedItem } = useSuitableStore() - const [openItems, setOpenItems] = useState>(new Set()) - const [visibleItems, setVisibleItems] = useState([]) - const [page, setPage] = useState(1) - const [isLoadingMore, setIsLoadingMore] = useState(false) - const observerTarget = useRef(null) - - // 선택된 아이템 확인 함수 메모이제이션 - const isItemSelected = useCallback( - (itemId: number) => { - return selectedItems.some((selected) => selected === itemId) - }, - [selectedItems], - ) - - // 초기 데이터 로드 - useEffect(() => { - if (suitableSearchResults) { - const initialItems = suitableSearchResults.slice(0, ITEMS_PER_PAGE) - setVisibleItems(initialItems) - setPage(1) - } - }, [suitableSearchResults]) - - // Intersection Observer 설정 - useEffect(() => { - const observer = new IntersectionObserver( - (entries) => { - if (entries[0].isIntersecting && suitableSearchResults && !isLoadingMore) { - const nextPage = page + 1 - const startIndex = (nextPage - 1) * ITEMS_PER_PAGE - const endIndex = startIndex + ITEMS_PER_PAGE - const nextItems = suitableSearchResults.slice(startIndex, endIndex) - - if (nextItems.length > 0) { - setIsLoadingMore(true) - setVisibleItems((prev) => [...prev, ...nextItems]) - setPage(nextPage) - setIsLoadingMore(false) - } - } - }, - { - threshold: 0.2, - }, - ) - - if (observerTarget.current) { - observer.observe(observerTarget.current) - } - - return () => observer.disconnect() - }, [page, suitableSearchResults, isLoadingMore]) - - const handleItemClick = useCallback( - (itemId: number) => { - isItemSelected(itemId) ? removeSelectedItem(itemId) : addSelectedItem(itemId) - }, - [isItemSelected, addSelectedItem, removeSelectedItem], - ) - - const toggleItemOpen = useCallback((itemId: number) => { - setOpenItems((prev) => { - const newOpenItems = new Set(prev) - newOpenItems.has(itemId) ? newOpenItems.delete(itemId) : newOpenItems.add(itemId) - return newOpenItems - }) - }, []) - - // TODO: 추후 지붕재 적합성 데이터 CUD 구현 시 ×, ー 데이터 관리 필요 - const suitableCheck = useCallback((value: string) => { - if (value === '×') { - return ( -
    - -
    - ) - } else if (value === 'ー') { - return ( -
    - -
    - ) - } else { - return ( -
    - -
    - ) - } - }, []) - - // 메모이제이션된 아이템 렌더링 - const renderItem = useCallback( - (item: Suitable) => { - const isSelected = isItemSelected(item.id) - const isOpen = openItems.has(item.id) - - return ( -
    -
    -
    - handleItemClick(item.id)} /> - -
    -
    - -
    -
    -
      - {toSuitableDetail(item.detail).map((subItem: SuitableDetail) => ( -
    • -
      -
      - - -
      -
      - {suitableCheck(subItem.trestleManufacturerProductName)} - {subItem.memo && ( -
      - -
      - )} -
      -
      -
    • - ))} -
    -
    - ) - }, - [isItemSelected, openItems, handleItemClick, toggleItemOpen, suitableCheck, toCodeName, toSuitableDetail], - ) - - // 메모이제이션된 아이템 리스트 - const renderedItems = useMemo(() => { - return visibleItems.map(renderItem) - }, [visibleItems, renderItem]) - - if (isSearchLoading) { - return
    Loading...
    - } - - if (!suitableSearchResults?.length) { - return - } - - return ( - <> - {renderedItems} -
    - {isLoadingMore &&
    데이터를 불러오는 중...
    } -
    - - - ) -} diff --git a/src/components/suitable/SuitableRaw.tsx b/src/components/suitable/SuitableRaw.tsx deleted file mode 100644 index d48dfea..0000000 --- a/src/components/suitable/SuitableRaw.tsx +++ /dev/null @@ -1,118 +0,0 @@ -'use client' - -import Image from 'next/image' -import { useEffect, useState } from 'react' -import SuitableListRaw from './SuitableListRaw' -import { useSuitableRaw } from '@/hooks/useSuitableRaw' -import { useSuitableStore } from '@/store/useSuitableStore' -import type { CommCode } from '@/types/CommCode' -import { SUITABLE_HEAD_CODE } from '@/types/Suitable' - -export default function SuitableRaw() { - const [reference, setReference] = useState(true) - - const { getSuitableCommCode, refetchBySearch } = useSuitableRaw() - const { suitableCommCode, selectedCategory, setSelectedCategory, searchValue, setSearchValue, setIsSearch, clearSelectedItems } = useSuitableStore() - - const handleInputSearch = async () => { - if (!searchValue.trim()) { - alert('屋根材の製品名を入力してください。') - return - } - setIsSearch(true) - refetchBySearch() - } - - const handleInputClear = () => { - setSearchValue('') - setIsSearch(false) - refetchBySearch() - } - - useEffect(() => { - refetchBySearch() - }, [selectedCategory]) - - useEffect(() => { - getSuitableCommCode() - return () => { - setSelectedCategory('') - setSearchValue('') - clearSelectedItems() - } - }, []) - - return ( -
    - 테스트1 페이지 -
    - -
    -
    -
    - setSearchValue(e.target.value)} - /> - {searchValue &&
    -
    -
    -
    -
    -
    凡例
    -
    - -
    -
    -
      -
    • -
      -
      - -
      - 設置可能 -
      -
    • -
    • -
      -
      - -
      - 設置不可 -
      -
    • -
    • -
      -
      - -
      - お問い合わせ -
      -
    • -
    • -
      -
      - -
      - 備考 -
      -
    • -
    -
    - -
    -
    - ) -} diff --git a/src/hooks/useSuitable.ts b/src/hooks/useSuitable.ts index 4bdd9b2..ece8d2e 100644 --- a/src/hooks/useSuitable.ts +++ b/src/hooks/useSuitable.ts @@ -2,19 +2,27 @@ import { useQuery } from '@tanstack/react-query' import { axiosInstance, transformObjectKeys } from '@/libs/axios' import { useSuitableStore } from '@/store/useSuitableStore' import { useCommCode } from './useCommCode' -import { SUITABLE_HEAD_CODE, type SuitableDetailGroup, type SuitableMain, type Suitable, type SuitableDetail } from '@/types/Suitable' +import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable' export function useSuitable() { const { getCommCode } = useCommCode() const { selectedCategory, searchValue, suitableCommCode, setSuitableCommCode, isSearch } = useSuitableStore() - const getSuitables = async (): Promise => { + const getSuitables = async (): Promise => { try { - const response = await axiosInstance(null).get('/api/suitable/list') + const response = await axiosInstance(null).get('/api/suitable/list', { + params: { + pageNumber: 1, + itemPerPage: 1000, + ids: '', + category: '', + keyword: '', + }, + }) return response.data } catch (error) { console.error('지붕재 데이터 로드 실패:', error) - return { suitable: [], detail: [] } + return [] } } @@ -42,12 +50,8 @@ export function useSuitable() { return commCode?.find((item) => item.code === code)?.codeJp || '' } - const toSuitableDetail = (mainId: number): SuitableDetail[] => { + const toSuitableDetail = (suitableDetailString: string): SuitableDetail[] => { try { - const suitableDetailString = suitableList?.detail.find((item) => item.mainId === mainId)?.detail - if (!suitableDetailString) { - return [] - } const suitableDetailArray = transformObjectKeys(JSON.parse(suitableDetailString)) as SuitableDetail[] if (!Array.isArray(suitableDetailArray)) { throw new Error('suitableDetailArray is not an array') @@ -59,7 +63,7 @@ export function useSuitable() { } } - const { data: suitableList, isLoading: isInitialLoading } = useQuery({ + const { data: suitableList, isLoading: isInitialLoading } = useQuery({ queryKey: ['suitables', 'list'], queryFn: async () => await getSuitables(), staleTime: 1000 * 60 * 10, // 10분 @@ -70,23 +74,19 @@ export function useSuitable() { data: suitableSearchResults, refetch: refetchBySearch, isLoading: isSearchLoading, - } = useQuery({ + } = useQuery({ queryKey: ['suitables', 'search', selectedCategory, isSearch], queryFn: async () => { if (!isSearch && !selectedCategory) { - // 검색 상태가 아니면 초기 데이터 반환 임시처리 - return isInitialLoading ? await getSuitables() : suitableList ?? { suitable: [], detail: [] } + return isInitialLoading ? await getSuitables() : suitableList ?? [] // 검색 상태가 아니면 초기 데이터 반환 임시처리 } else { - const filteredSuitable = suitableList?.suitable.filter((item: SuitableMain) => { - const categoryMatch = !selectedCategory || item.roofMtCd === selectedCategory - const searchMatch = !searchValue || item.productName.includes(searchValue) - return categoryMatch && searchMatch - }) ?? [] - const mainIds = filteredSuitable.map((item: SuitableMain) => item.id) - const filteredDetail = suitableList?.detail.filter((item: SuitableDetailGroup) => { - return mainIds.includes(item.mainId) - }) ?? [] - return { suitable: filteredSuitable, detail: filteredDetail } + return ( + suitableList?.filter((item: Suitable) => { + const categoryMatch = !selectedCategory || item.roofMtCd === selectedCategory + const searchMatch = !searchValue || item.productName.includes(searchValue) + return categoryMatch && searchMatch + }) ?? [] + ) } }, staleTime: 1000 * 60 * 10, diff --git a/src/hooks/useSuitableRaw.ts b/src/hooks/useSuitableRaw.ts deleted file mode 100644 index a962244..0000000 --- a/src/hooks/useSuitableRaw.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { axiosInstance, transformObjectKeys } from '@/libs/axios' -import { useSuitableStore } from '@/store/useSuitableStore' -import { useCommCode } from './useCommCode' -import { SUITABLE_HEAD_CODE, type SuitableDetail } from '@/types/Suitable' - -export type Suitable = { - id: number - productName: string - manuFtCd: string - roofMtCd: string - roofShCd: string - detail: string -} - -export function useSuitableRaw() { - const { getCommCode } = useCommCode() - const { selectedCategory, searchValue, suitableCommCode, setSuitableCommCode, isSearch } = useSuitableStore() - - const getSuitables = async (): Promise => { - try { - const response = await axiosInstance(null).get('/api/suitable/list/test') - return response.data - } catch (error) { - console.error('지붕재 데이터 로드 실패:', error) - return [] - } - } - - // const updateSearchResults = async (selectedCategory: string | undefined, searchValue: string | undefined): Promise => { - // try { - // const response = await axiosInstance(null).get('/api/suitable/list', { params: { selectedCategory, searchValue } }) - // return response.data - // } catch (error) { - // console.error('지붕재 데이터 검색 실패:', error) - // return [] - // } - // } - - const getSuitableCommCode = () => { - const headCodes = Object.values(SUITABLE_HEAD_CODE) as SUITABLE_HEAD_CODE[] - for (const code of headCodes) { - getCommCode(code).then((res) => { - setSuitableCommCode(code, res) - }) - } - } - - const toCodeName = (headCode: string, code: string): string => { - const commCode = suitableCommCode.get(headCode) - return commCode?.find((item) => item.code === code)?.codeJp || '' - } - - const toSuitableDetail = (suitableDetailString: string): SuitableDetail[] => { - try { - const suitableDetailArray = transformObjectKeys(JSON.parse(suitableDetailString)) as SuitableDetail[] - if (!Array.isArray(suitableDetailArray)) { - throw new Error('suitableDetailArray is not an array') - } - return suitableDetailArray - } catch (error) { - console.error('지붕재 데이터 파싱 실패:', error) - return [] - } - } - - const { data: suitableList, isLoading: isInitialLoading } = useQuery({ - queryKey: ['suitables', 'list'], - queryFn: async () => await getSuitables(), - staleTime: 1000 * 60 * 10, // 10분 - gcTime: 1000 * 60 * 10, // 10분 - }) - - const { - data: suitableSearchResults, - refetch: refetchBySearch, - isLoading: isSearchLoading, - // } = useQuery({ - } = useQuery({ - queryKey: ['suitables', 'search', selectedCategory, isSearch], - queryFn: async () => { - if (!isSearch && !selectedCategory) { - return isInitialLoading ? await getSuitables() : suitableList ?? [] // 검색 상태가 아니면 초기 데이터 반환 임시처리 - } else { - return ( - suitableList?.filter((item: Suitable) => { - const categoryMatch = !selectedCategory || item.roofMtCd === selectedCategory - const searchMatch = !searchValue || item.productName.includes(searchValue) - return categoryMatch && searchMatch - }) ?? [] - ) - } - }, - staleTime: 1000 * 60 * 10, - gcTime: 1000 * 60 * 10, - enabled: true, - }) - - return { - getSuitables, - getSuitableCommCode, - toCodeName, - toSuitableDetail, - suitableList, - suitableSearchResults, - refetchBySearch, - isSearchLoading, - } -} diff --git a/src/types/Suitable.ts b/src/types/Suitable.ts index 2e3563b..e3966c8 100644 --- a/src/types/Suitable.ts +++ b/src/types/Suitable.ts @@ -25,20 +25,11 @@ export type SuitableDetail = { memo: string } -// export type Suitable = { -// id: number -// productName: string -// manuFtCd: string -// roofMtCd: string -// roofShCd: string -// detail: string -// } - -export type SuitableDetailGroup = { - mainId: number +export type Suitable = { + id: number + productName: string + manuFtCd: string + roofMtCd: string + roofShCd: string detail: string } -export type Suitable = { - suitable: SuitableMain[] - detail: SuitableDetailGroup[] -} \ No newline at end of file