From b494fdd1a6224c7118dc452dbf500c23c457ec2e Mon Sep 17 00:00:00 2001 From: swcho Date: Mon, 4 Aug 2025 18:44:48 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=95=98=EC=88=98=EB=8F=84=EC=A0=95?= =?UTF-8?q?=EB=B9=84=EA=B8=B0=EB=B3=B8=EA=B3=84=ED=9A=8D=20=EB=B3=B4?= =?UTF-8?q?=EA=B3=A0=EC=84=9C=20=EB=AA=A8=EB=8B=AC=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/popupComponent/SwtrmtFclt.tsx | 116 ++++------ .../map/searchModal/reportTab.tsx | 218 +++++++++--------- .../map/searchModal/searchModalReportApi.ts | 161 ++++--------- 3 files changed, 204 insertions(+), 291 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/SwtrmtFclt.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/SwtrmtFclt.tsx index 6d182072b..366218ec1 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/SwtrmtFclt.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/SwtrmtFclt.tsx @@ -1,88 +1,62 @@ 'use client' -import React, { useState,useEffect } from 'react' -import Modal from '@/components/ui/modal'; +import React, { useState } from 'react' import { VerticalTable } from '@/components/ui/verticalTable'; -import { UiTable } from '@common/business_components/ui'; +import { UiModal } from '@common/business_components/ui'; import { Button } from '@common/components/ui'; -import { useDcTree, flattenFolders, getFileDownload, addFileName, type FlattenedFolder} from '../../searchModal/searchModalReportApi'; +import ReportTab from '../../searchModal/reportTab'; // 하수처리시설 const headList = [ { id: "title", title: "하수도정비 기본계획보고서" }, ]; -const modalHeadList = [ - { id: "no", title: "번호" }, - { id: "year", title: "승인연도" }, - { id: "cad", title: '평면도' }, - { id: "fileNm", title: "파일명" }, - { id: "date", title: "입력시간" }, -] const SwtrmtFclt = ({ gisObject }: { gisObject: any }) => { const [isModalOpen, setIsModalOpen] = useState(false); - const [modalTitle, setModalTitle] = useState(''); - const [dcDataOne, setDcDataOne] = useState({}); - const openModal = (title: string) => { - setIsModalOpen(true); - setModalTitle(title) - }; - const closeModal = () => { - setIsModalOpen(false); - }; - const ctpvNm = gisObject?.ctpv_nm; - //const reportOriginData = useDcTree(ctpvNm); - const reportOriginData = useDcTree("강원특별자치도"); - useEffect(() => { - const fetch = async () => { - - if (!reportOriginData || reportOriginData.length === 0) { - return; - } - const reportData = flattenFolders(reportOriginData); - let filterReportData = [...reportData]; - if (gisObject && gisObject.sgg_nm !== undefined && gisObject.sgg_nm !== "") { // 시군구검색 - //const sggNm = gisObject?.sgg_nm; - const sggNm = "강릉시"; - filterReportData = filterReportData.filter(item => item.sigungu === sggNm); - } - addFileName(filterReportData).then(data => { - filterReportData = data.filter(item => item.filePath !== undefined && item.filePath !== ""); - if (filterReportData !== undefined && filterReportData.length > 0) { - setDcDataOne(filterReportData[0]); - } - }); - console.log("filterReportData", filterReportData); - }; - fetch(); - }, [reportOriginData]); - - // 하수도정비기본계획 filedownload 핸들러 - const handleFileDownloadClick = () => { - if (dcDataOne && dcDataOne !== undefined && dcDataOne.fileSn !== undefined && dcDataOne.fileSn !== "" - && dcDataOne.filePath !== undefined && dcDataOne.filePath !== "" && dcDataOne.fileName !== undefined && dcDataOne.fileName !== "" ) { - console.log("dcDataOne", dcDataOne); - getFileDownload(String(dcDataOne.fileSn), String(dcDataOne.fileName), String(dcDataOne.filePath)); - } - }; - //cad: , + const ctpvNm = gisObject?.ctpv_nm; + const sggNm = gisObject?.sgg_nm; + return (
- handleFileDownloadClick()}>다운로드 - }} headName='디지털 자료관 연계정보' tableData={[]} headlist={headList} pageSize={20} total={0} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void { - throw new Error("Function not implemented."); - }}> - {/* -
- -
-
*/} -
+ setIsModalOpen(true)}>조회 + }} + headName='디지털 자료관 연계정보' + tableData={[]} + headlist={headList} + pageSize={20} + total={0} + cellClick={undefined} + isEditable={true} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }} + /> + + } + useDirectRender={true} + /> + ) } diff --git a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx index 5ca3f7059..a4a50e89c 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx @@ -4,9 +4,9 @@ import { Button, Label } from '@common/components'; import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox'; import { SelectBox, UiTable } from '@common/business_components/ui'; import LoadingOverlay from '@/components/ui/LoadingBar'; - -import { addFileName, flattenFolders, getFileDownload, useCityCd, useDcTree, useSggCd, reportGubuns } from './searchModalReportApi'; -import { type SelectDataType } from '../commonStore'; +import { addFileName, flattenFolders, getFileDownload, getDcTree, useDcTree, reportGubuns } from './searchModalReportApi'; +import useStore from '../../../store/useStore'; +import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'; // 하수도정비 기본계획 보고서 @@ -15,18 +15,19 @@ export const reportTableHeadlist = [ { id: "sido", title: "시도" }, { id: "sigungu", title: "시군구" }, { id: "approvalYear", title: "승인년도" }, - { id: "plantZoneGugun", title: "구분" }, + { id: "plantZoneGubun", title: "구분" }, { id: "reportGubun", title: "보고서구분" }, { id: "fileName", title: "파일명" }, { id: "regDate", title: "등록일자" }, ]; - - -interface BasicTabProps { +interface ReportTabProps { open?: boolean; + isSearch?: boolean; + ctpvNm?: string; + sggNm?: string; } -const ReportTab: React.FC = ({ open }) => { +const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sggNm }) => { const [isLoading, setIsLoading] = useState(false); const [isResetPage, setIsResetPage] = useState(false); @@ -36,24 +37,20 @@ const ReportTab: React.FC = ({ open }) => { const [tableCount, setTableCount] = useState(0); const [curPage, setCurPage] = useState(1); const [yearFilter, setYearFilter] = useState({ start: "", end: "" }); - const [ctpvCd, setCtpvCd] = useState('0'); // 하수정비기본계획 report - const [sigungu, setSigungu] = useState('0'); // 하수정비기본계획 report - const [filteredSiguns, setFilteredSiguns] = useState([{ val: '0', text: '전체', sido: '0' }]); - const [gubun, setGubun] = useState(""); // 하수정비기본계획 report - const [reportGubun, setReportGubun] = useState("0"); // 하수정비기본계획 report - + const [ctpvCd, setCtpvCd] = useState(0); + const [sggCd, setSggCd] = useState(0); + const [reportGubun, setReportGubun] = useState("0"); + // const [gubun, setGubun] = useState(""); + + const { ctpv, sgg } = useStore(); // 하수정비기본계획 report - const sidos = useCityCd(); - const siguns = useSggCd(); const reportOriginData = useDcTree(""); //:Folder[] - const dynamicHeadlist: { id: string, title: string }[] = reportTableHeadlist; - useEffect(() => { // filterParams 초기화 - setCtpvCd('0'); - setSigungu('0'); + setCtpvCd(0); + setSggCd(0); setReportGubun('0'); setYearFilter({ start: "", end: "" }); setShowTable(false); @@ -64,27 +61,39 @@ const ReportTab: React.FC = ({ open }) => { setIsResetSelection(true); // 선택 초기화 }, [open]); + useEffect(() => { + let newCtpvCd = ctpv.find((n) => n.name === ctpvNm)?.id || 0; + let newSggCd = sgg.find((n) => n.name === sggNm)?.id || 0; + + setCtpvCd(newCtpvCd); + setSggCd(newSggCd); + }, [ctpvNm, sggNm]); + + // ctpvCd, sggCd가 업데이트된 후에 fetchFilteredData 실행 + useEffect(() => { + if (!isSearch && ctpvCd && sggCd) { + fetchFilteredData(); + } + }, [reportOriginData, ctpvCd, sggCd, isSearch]); + // 페이지 변경 시마다 데이터 fetch useEffect(() => { // showTable이 true일 때만 fetch (최초 렌더 방지) setIsResetPage(false); - setIsResetSelection(true); // 선택 초기화 - if (showTable) fetchFilteredData(); - }, [curPage]); - - const handleCtpvCdChange = (val: string | number) => { // 하수도정비기본계획.시도 - const sidoOption = sidos.find((n) => n.val === val); - setCtpvCd(String(val)); - if (val !== '0') { - const filterSiguns = siguns.filter(item => item.sido === sidoOption?.text); - filterSiguns.unshift({ val: '0', text: '전체', sido: '0' }); - setFilteredSiguns(filterSiguns); - } - setSigungu("0"); - setGubun(""); - setYearFilter({ start: "", end: "", }); - } + setIsResetSelection(true); + if (showTable) fetchFilteredData(); + + setIsResetSelection(false); + }, [curPage]); + + const handleCtpvCdChange = (sidoId: number) => { + setCtpvCd(sidoId); + setSggCd(0); + setReportGubun('0'); + setYearFilter({ start: "", end: "", }); + // setGubun(""); + } // 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화 const handleSearch = () => { @@ -96,7 +105,7 @@ const ReportTab: React.FC = ({ open }) => { }; // 하수도정비기본계획 테이블 cellClick 핸들러 - const handleReportCellClick = (index: number | undefined ) => { + const handleReportCellClick = (index: number | undefined) => { console.log(index); if (typeof index === "number" && tableData[index]) { const reportData = tableData[index]; @@ -107,23 +116,23 @@ const ReportTab: React.FC = ({ open }) => { } } }; - + // 검색 API 호출 함수 const fetchFilteredData = async () => { setIsLoading(true); // 로딩 시작 setIsResetSelection(true); // 선택 초기화 - + try { - //const reportOriginData = await getDcTree('경기도'); const reportData = flattenFolders(reportOriginData); let filterReportData = reportData; - if (ctpvCd !== "0") { // 시도검색 - const sidoOption = sidos.find((n) => n.val === ctpvCd); - filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text); + + if (ctpvCd !== 0) { // 시도검색 + const sidoOption = ctpv.find((n) => n.id === ctpvCd); + filterReportData = filterReportData.filter(item => item.sido === sidoOption?.name); } - if (sigungu !== "0") { // 시군구검색 - const sigunguOption = filteredSiguns.find((n) => n.val === sigungu); - filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text); + if (sggCd !== 0) { // 시군구검색 + const sigunguOption = sgg.find((n) => n.id === sggCd); + filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.name); } if (reportGubun !== "0") { // 보고서구분검색 const reportGubunOption = reportGubuns.find((n) => n.val === reportGubun); @@ -139,14 +148,16 @@ const ReportTab: React.FC = ({ open }) => { return (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" && item.approvalYear <= yearFilter.end + "년" : null; }); } - if (gubun !== "") { // 구분검색 - filterReportData = filterReportData.filter(item => (item.plantZoneGugun) ? String(item.plantZoneGugun).includes(String(gubun)) : null); - } + // if (gubun !== "") { // 구분검색 (추후 사용할 수도 있을 것 같아 남겨둠) + // filterReportData = filterReportData.filter(item => (item.plantZoneGubun) ? String(item.plantZoneGubun).includes(String(gubun)) : null); + // } console.log('report count:', filterReportData.length); + setTableCount(filterReportData.length || 0); + filterReportData = filterReportData.slice((curPage - 1) * 10, curPage * 10); // paging filterReportData = filterReportData?.map((item, index) => ({ ...item, no: (curPage - 1) * 10 + index + 1 })); - //const fileList = await getDcFile("CLS20250530000041"); + addFileName(filterReportData).then(data => { filterReportData = data; setTableData(filterReportData || []); @@ -159,69 +170,70 @@ const ReportTab: React.FC = ({ open }) => { setTableData([]); } finally { setIsLoading(false); // 로딩 종료 + setIsResetSelection(false); } }; return ( <> -
-
- - - - setSigungu(String(val))} - selectContentClass="z-[101]" - /> -
-
- - setReportGubun(String(val))} - selectContentClass="z-[101]" labelClass='whitespace-nowrap text-xl text-gray-600 pr-2' - /> - {/* 승인년도 필터 */} - setYearFilter({ ...yearFilter, start: range.start, end: range.end })} - startLabel="승인년도" endLabel="" - /> - {/* 조회 버튼 */} -
- +
+ {isSearch && ( + <> +
+ { handleCtpvCdChange(sidoId) }} + onSigunguChange={setSggCd} + setIsLoading={setIsLoading} + /> +
+ + )} +
+ + setReportGubun(String(val))} + selectContentClass="z-[101]" labelClass='whitespace-nowrap text-xl text-gray-600 pr-2' + /> + {/* 승인년도 필터 */} + setYearFilter({ ...yearFilter, start: range.start, end: range.end })} + startLabel="승인년도" endLabel="" + /> + {/* 조회 버튼 */} +
+ +
-
-
- {/* 테이블2 영역 (가로/세로 스크롤 가능) */} -
- - { - return (<>조회 결과 ({tableCount.toLocaleString('ko-KR')})건); - })() - } - tableData={tableData} - headlist={dynamicHeadlist} - pageSize={10} - total={tableCount} - cellClick={handleReportCellClick} - onPageChange={setCurPage} - resetPageNo={isResetPage} - resetSelection={isResetSelection} - /> +
+ {/* 테이블2 영역 (가로/세로 스크롤 가능) */} +
+ + { + return (<>조회 결과 ({tableCount.toLocaleString('ko-KR')})건); + })() + } + tableData={tableData} + headlist={reportTableHeadlist} + pageSize={10} + total={tableCount} + cellClick={handleReportCellClick} + onPageChange={setCurPage} + resetPageNo={isResetPage} + resetSelection={isResetSelection} + /> +
-
); }; diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts b/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts index 9610b4264..4a5df24a1 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts @@ -1,39 +1,35 @@ -import {useState, useEffect} from 'react'; +import { useState, useEffect } from 'react'; import { getDcData } from '@/components/lib/api'; -const ADDR_URL = process.env.NEXT_PUBLIC_ADDR_URL; export type Folder = { - clsfSystmSn: string; - upClsfSystmSn: string; - clsfSystmNm: string; - children: Folder[]; - }; - + clsfSystmSn: string; + upClsfSystmSn: string; + clsfSystmNm: string; + children: Folder[]; +}; export type FlattenedFolder = { - [key: string]: string|number|undefined; -}; - -type SelectDataType = { - text: string; - val: string; - sido?: string; // 시도 코드 (선택 사항) + [key: string]: string | number | undefined; +}; +export type SelectDataType = { + text: string; + val: string; + sido?: string; // 시도 코드 (선택 사항) } - type ReportDataFile = { - no : number; - fileTitle : string; - regDt : string; - fileSn : string; - filePath : string; + no: number; + fileTitle: string; + regDt: string; + fileSn: string; + filePath: string; } const isYear = (value: string): boolean => { return /^[0-9]{4}년$/.test(value); }; +export const reportGubuns = [{ val: '0', text: '전체' }, { val: '1', text: '보고서' } + , { val: '2', text: '요약보고서' }, { val: '3', text: '부록' }, { val: '4', text: '기타' }]; -export const reportGubuns = [{val: '0', text: '전체'}, {val: '1', text: '보고서'} - , {val: '2', text: '요약보고서'} , {val: '3', text: '부록'}, {val: '4', text: '기타'}]; - + /** * Flatten a hierarchical folder structure into a flat array. * @param data - The hierarchical folder data to flatten. @@ -41,18 +37,20 @@ export const reportGubuns = [{val: '0', text: '전체'}, {val: '1', text: '보 */ export function flattenFolders(data: Folder[]): FlattenedFolder[] { const result: FlattenedFolder[] = []; - const exceptNodeNm = ["부분","통합","전체"]; - const addNodeNm = ["보고서","요약보고서","부록","기타"]; // level5 - const columnNms: { [key: number]: string } = { 0: "root", 1:"sido", 2:"sigungu", 3:"approvalYear" - ,4:"plantZoneGugun", 5:"reportGubun", 7:"fileName", 8:"regDate" }; + const exceptNodeNm = ["부분", "통합", "전체"]; + const addNodeNm = ["보고서", "요약보고서", "부록", "기타"]; // level5 + const columnNms: { [key: number]: string } = { + 0: "root", 1: "sido", 2: "sigungu", 3: "approvalYear" + , 4: "plantZoneGubun", 5: "reportGubun", 7: "fileName", 8: "regDate" + }; function traverse( node: Folder, - path: { [key: string]: string| number }, + path: { [key: string]: string | number }, level: number ) { if (level === 2 && isYear(node.clsfSystmNm)) { - level++; + level++; } if (level === 4 && addNodeNm.includes(node.clsfSystmNm)) { level++; // 레벨을 증가시켜 하위값에 추가되도록 함. @@ -79,7 +77,7 @@ export function flattenFolders(data: Folder[]): FlattenedFolder[] { } } - if (data && data.length > 0 && data[0].clsfSystmNm == "기본계획") { // data가 비어있지 않고 children이 있는 경우에만 처리 + if (data && data.length > 0 && data[0]?.clsfSystmNm == "기본계획") { // data가 비어있지 않고 children이 있는 경우에만 처리 data.forEach((root) => traverse(root, {}, 0)); } else { data.forEach((root) => traverse(root, {}, 1)); @@ -87,11 +85,11 @@ export function flattenFolders(data: Folder[]): FlattenedFolder[] { return result; } -export const getDcTree = async (clsfSystmNm:string) => { +export const getDcTree = async (clsfSystmNm: string) => { const result = await getDcData("/api/v1/dc/clsf?clsfSystmNm=" + clsfSystmNm); return result; }; -export const useDcTree = (clsfSystmNm:string) => { +export const useDcTree = (clsfSystmNm: string) => { const [data, setData] = useState([]); useEffect(() => { @@ -109,31 +107,31 @@ export const useDcTree = (clsfSystmNm:string) => { return data; }; -export const getDcFile = async (clsfSystmSn:string) => { - const queryString = 'clsfSystmSn='+clsfSystmSn; +export const getDcFile = async (clsfSystmSn: string) => { + const queryString = 'clsfSystmSn=' + clsfSystmSn; const result = await getDcData(`/api/v1/dc/file${queryString ? `?${queryString}` : ''}`); return result; }; -export const addFileName = async (data:FlattenedFolder[]) => { +export const addFileName = async (data: FlattenedFolder[]) => { const fileReportData = await Promise.all( data.map(async (item) => { - const filenameArr:ReportDataFile[] = await getDcFile(String(item.clsfSystmSn)); + const filenameArr: ReportDataFile[] = await getDcFile(String(item.clsfSystmSn)); const f = filenameArr[0]; - const result = {...item, fileName:f?.fileTitle, regDate:f?.regDt, fileSn:f?.fileSn, filePath:f?.filePath}; - + const result = { ...item, fileName: f?.fileTitle, regDate: f?.regDt, fileSn: f?.fileSn, filePath: f?.filePath }; + return result; }) ); return fileReportData; }; -export const getDcFileprcs = async (fileSn:string) => { - const queryString = 'fileSn='+fileSn; +export const getDcFileprcs = async (fileSn: string) => { + const queryString = 'fileSn=' + fileSn; const result = await getDcData(`/api/v1/dc/fileprcs${queryString ? `?${queryString}` : ''}`); return result; }; -export const getDcFileDownload = async (filePath:string) => { - const queryString = 'filePath='+ encodeURIComponent(filePath); +export const getDcFileDownload = async (filePath: string) => { + const queryString = 'filePath=' + encodeURIComponent(filePath); const response = await fetch(`/dc-service/api/v1/dc/file/download?${queryString}`, { method: 'GET', }); @@ -143,7 +141,7 @@ export const getDcFileDownload = async (filePath:string) => { const blob = await response.blob(); // PDF, image 등은 blob으로 받아야 함 return blob; }; -export const fileDownload = (data:Blob, fileName:string) => { // Blob데이터 임시link로 연결 +export const fileDownload = (data: Blob, fileName: string) => { // Blob데이터 임시link로 연결 const url = window.URL.createObjectURL(data); const link = document.createElement('a'); link.href = url; @@ -153,80 +151,9 @@ export const fileDownload = (data:Blob, fileName:string) => { // Blob데이터 link.remove(); window.URL.revokeObjectURL(url); } -export const getFileDownload = async (fileSn:string, fileName:string, filePath:string) => { - if (filePath === "") alert("filePath 정보 없으므로 다운로드 불가:filesn"+ fileSn); +export const getFileDownload = async (fileSn: string, fileName: string, filePath: string) => { + if (filePath === "") alert("filePath 정보 없으므로 다운로드 불가:filesn" + fileSn); const blobData = await getDcFileDownload(filePath); // 실제 다운로드 API fileDownload(blobData, fileName); } -// 시도 데이터 -export const getCityCd = async (): Promise => { - try { - const response = await fetch(`${ADDR_URL}/api/v1/addr/city`, { method: "GET", headers: { accept: "application/json" } }); - if (!response.ok) throw new Error(`시도 데이터 오류: ${response.status}`); - const res = await response.json(); - const city = res.data; - let citys:SelectDataType[] = city.map((item: {adstDstrCd:string, sido:string}) => ({ - val: item.adstDstrCd, - text: item.sido, - })); - citys = citys.sort((a:SelectDataType, b:SelectDataType) => a.text.localeCompare(b.text)); - citys.unshift({ val: '0', text: '전체' }); - return citys; - } catch (error) { - console.error('시도 데이터를 가져오는데 실패했습니다:', error); - return [{ val: '0', text: '전체', sido: '0' }]; - } - }; - -export const useSggCd = () => { - const [data, setData] = useState([]); - - useEffect(() => { - const fetch = async () => { - const result = await getSggCd(); - if (result) { - setData(result); - } - console.log("useSggCd호출"); - }; - fetch(); - }, []); - return data; -}; - -// 시군구 데이터 -export const getSggCd = async (): Promise => { - try { - const response = await fetch(`${ADDR_URL}/api/v1/addr/sgg`, { method: "GET", headers: { accept: "application/json" } }); - if (!response.ok) throw new Error(`시군구 데이터 오류: ${response.status}`); - const res = await response.json(); - const sgg = res.data; - const sggCd = sgg.map((item: {adstDstrCd:string, sido:string, gugun:string}) => ({ - val: item.adstDstrCd, - sido: item.sido, - text: item.gugun, - })); - sggCd.unshift({ val: '0', text: '전체' }); // "전체" 옵션 추가 - return sggCd; - } catch (error) { - console.error('시군구 데이터를 가져오는데 실패했습니다:', error); - return [{ val: '0', text: '전체', sido: '0' }]; - } -}; - -export const useCityCd = () => { - const [data, setData] = useState([]); - - useEffect(() => { - const fetch = async () => { - const result = await getCityCd(); - if (result) { - setData(result); - } - console.log("useCityCd호출"); - }; - fetch(); - }, []); - return data; -};