From a46b185c16cb477fc4d2eeb882d6838ef4847843 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 23 Mar 2026 11:45:34 +0900 Subject: [PATCH] =?UTF-8?q?[st]=20refactor:=20=EC=8B=9C=EB=8F=84/=EC=8B=9C?= =?UTF-8?q?=EA=B5=B0=EA=B5=AC=20=EC=85=80=EB=A0=89=ED=8A=B8=20API=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 --- .../verification/HbStatisticsVerification.tsx | 6 +- .../verification/ImrVerificationHistory.tsx | 4 +- .../verification/detail/RegionalGrid.tsx | 81 ++++++++----------- .../verification/detail/ScatterChart.tsx | 57 ++++++------- .../st/src/entities/statistics/api/queries.ts | 21 +++++ ...i.ts => statistics-analysis-master-api.ts} | 22 ++++- apps/st/src/entities/statistics/index.ts | 1 + .../statistics-analysis-master-interface.ts | 10 +++ 8 files changed, 122 insertions(+), 80 deletions(-) rename apps/st/src/entities/statistics/api/{analysis-history-api.ts => statistics-analysis-master-api.ts} (56%) diff --git a/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx b/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx index 3bb9d22af..776e9b7b6 100644 --- a/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx +++ b/apps/st/src/components/statistics/verification/HbStatisticsVerification.tsx @@ -24,7 +24,7 @@ import type { } from "@/entities/statistics"; import { verificationHistoryColumns } from "@/entities/statistics"; import { analysisApi } from "@/entities/statistics/api/analysis-api"; -import { analysisHistoryApi } from "@/entities/statistics/api/analysis-history-api"; +import { stAnlsMstApi } from "@/entities/statistics/api/statistics-analysis-master-api"; import { verificationTargetManagementApi } from "@/entities/statistics/api/verification-target-management-service"; import type { VerificationTargetCodeResponse } from "@/entities/statistics/model/verification-target-management-interface"; import { @@ -202,7 +202,7 @@ const HbStatisticsVerification = () => { params.verifyDtEnd = dayjs(to).format("YYYY-MM-DD"); } - return analysisHistoryApi.getAnalysisHistoryList(params); + return stAnlsMstApi.getAnalysisHistoryList(params); }, refetchInterval: (query) => { const data = query.state.data; @@ -242,7 +242,7 @@ const HbStatisticsVerification = () => { ], queryFn: async () => { if (!outlierDialog.anlsSn) return []; - const response = await analysisHistoryApi.getOutlierList( + const response = await stAnlsMstApi.getOutlierList( outlierDialog.anlsSn, ); return response; diff --git a/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx b/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx index 8944b779b..583f141dc 100644 --- a/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx +++ b/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx @@ -33,7 +33,7 @@ import { imrDetailGridColumnInfo, imrVerificationHistoryColumns, } from "@/entities/statistics"; -import { analysisHistoryApi } from "@/entities/statistics/api/analysis-history-api"; +import { stAnlsMstApi } from "@/entities/statistics/api/statistics-analysis-master-api"; import { imrApi } from "@/entities/statistics/api/imr-api"; import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api"; import { @@ -130,7 +130,7 @@ const ImrVerificationHistory = () => { historyPagination, ], queryFn: () => - analysisHistoryApi.getAnalysisHistoryList({ + stAnlsMstApi.getAnalysisHistoryList({ ...appliedSearchParams, page: historyPagination.pageIndex, size: historyPagination.pageSize, diff --git a/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx b/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx index a932cfb3e..e1c0ab3da 100644 --- a/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx +++ b/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx @@ -1,11 +1,14 @@ import React, { useEffect, useMemo, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; - -import { ENDPOINT } from "@/shared/config"; -import { CodeApi } from "@/entities/code/api/code-service"; -import type { StAnlsRsltResponse } from "@/entities/statistics"; -import { useStAnlsRsltList } from "@/entities/statistics/api/queries"; +import type { + AdstZoneResponseDto, + StAnlsRsltResponse, +} from "@/entities/statistics"; +import { + useStAnlsRsltList, + useStAnlsSidoCombo, + useStAnlsSigunguCombo, +} from "@/entities/statistics/api/queries"; import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api"; import { regionalGridColumnInfo as columnInfo } from "@/entities/statistics/model/regional-grid-columns"; import { @@ -22,6 +25,8 @@ import type { AppliedFilter } from "./HbVerificationDetail"; import DataGrid from "@/components/module/DataGrid"; +const EMPTY_ADST_LIST: AdstZoneResponseDto[] = []; + interface RegionalGridProps { anlsSn: number; vrfcTrgtSn: string | null; @@ -52,27 +57,10 @@ const RegionalGrid: React.FC = ({ Record[] >([]); - const { data: regionCodes } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CODE, "LAESECD"], - queryFn: () => CodeApi.getCode("LAESECD"), - }); - - const sidoOptions = useMemo(() => { - if (!regionCodes) return []; - return regionCodes.filter( - (code) => code.lwpoCd.startsWith("G3") && code.lwpoCd.length === 4, - ); - }, [regionCodes]); - - const gugunOptions = useMemo(() => { - if (!regionCodes || !selectedSido) return []; - - const sidoPrefix = selectedSido; - return regionCodes.filter( - (code) => - code.lwpoCd.startsWith(sidoPrefix) && code.lwpoCd.length === 7, - ); - }, [regionCodes, selectedSido]); + const { data: sidoData } = useStAnlsSidoCombo(); + const { data: gugunData } = useStAnlsSigunguCombo(selectedSido); + const sidoOptions = sidoData ?? EMPTY_ADST_LIST; + const gugunOptions = gugunData ?? EMPTY_ADST_LIST; const { data: resultData, isLoading } = useStAnlsRsltList( anlsSn, @@ -90,22 +78,22 @@ const RegionalGrid: React.FC = ({ data = showAllOnEmptyFilter ? data : []; } else { if (appliedFilter.sido) { - const sidoName = sidoOptions.find( - (opt) => opt.lwpoCd === appliedFilter.sido, - )?.lwpoCdNm; - if (sidoName) { - data = data.filter((item) => item.sido === sidoName); + const sidoCd = sidoOptions.find( + (opt) => opt.adstCd === appliedFilter.sido, + )?.adstCd; + if (sidoCd) { + data = data.filter((item) => + item.admdstCd.startsWith(sidoCd.slice(0, 2)), + ); } } if (appliedFilter.gugun) { - const gugunName = gugunOptions.find( - (opt) => opt.lwpoCd === appliedFilter.gugun, - )?.lwpoCdNm; - if (gugunName) { - data = data.filter((item) => - gugunName.includes(item.gugun), - ); + const gugunCd = gugunOptions.find( + (opt) => opt.adstCd === appliedFilter.gugun, + )?.adstCd; + if (gugunCd) { + data = data.filter((item) => item.admdstCd === gugunCd); } } @@ -135,9 +123,6 @@ const RegionalGrid: React.FC = ({ otlrYn: mapOtlrToLabel(item.otlrYnCd), })); - setGridRowData( - mappedData as unknown as Record[], - ); return mappedData; }, [ resultData, @@ -198,6 +183,10 @@ const RegionalGrid: React.FC = ({ } }; + useEffect(() => { + setGridRowData(rowData as Record[]); + }, [rowData]); + useEffect(() => { handleReset(); }, [vrfcTrgtSn]); @@ -215,8 +204,8 @@ const RegionalGrid: React.FC = ({ {sidoOptions.map((opt) => ( - - {opt.lwpoCdNm} + + {opt.adstNm} ))} @@ -231,8 +220,8 @@ const RegionalGrid: React.FC = ({ {gugunOptions.map((opt) => ( - - {opt.lwpoCdNm} + + {opt.adstNm} ))} diff --git a/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx b/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx index 57eba1445..abba89ed1 100644 --- a/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx +++ b/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx @@ -1,7 +1,6 @@ import React, { useMemo } from "react"; import { Scatter } from "react-chartjs-2"; -import { useQuery } from "@tanstack/react-query"; import { Chart as ChartJS, type ChartData, @@ -17,8 +16,6 @@ import { } from "chart.js"; import zoomPlugin from "chartjs-plugin-zoom"; -import { ENDPOINT } from "@/shared/config"; -import { CodeApi } from "@/entities/code/api/code-service"; import type { StAnlsRsltResponse } from "@/entities/statistics"; import { useStAnlsChartData, @@ -55,29 +52,28 @@ interface DataPoint extends LinePoint { item: StAnlsRsltResponse; } +function matchesAdmdstFilter( + admdstCd: string, + sidoCd: string | null | undefined, + gugunCd: string | undefined, +): boolean { + const gugun = (gugunCd ?? "").trim(); + if (gugun) { + return admdstCd === gugun; + } + const sido = (sidoCd ?? "").trim(); + if (!sido) { + return true; + } + return admdstCd.startsWith(sido.slice(0, 2)); +} + const ScatterChart: React.FC = ({ anlsSn, vrfcTrgtSn, appliedFilter, onPointClick, }) => { - const { data: regionCodes } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CODE, "LAESECD"], - queryFn: () => CodeApi.getCode("LAESECD"), - }); - - const filterNames = useMemo(() => { - if (!regionCodes) return { sido: null, gugun: null }; - return { - sido: - regionCodes.find((c) => c.lwpoCd === appliedFilter.sido) - ?.lwpoCdNm || null, - gugun: - regionCodes.find((c) => c.lwpoCd === appliedFilter.gugun) - ?.lwpoCdNm || null, - }; - }, [regionCodes, appliedFilter.sido, appliedFilter.gugun]); - const { data: resultData, isLoading: isLoadingGrid } = useStAnlsRsltList( anlsSn, vrfcTrgtSn, @@ -113,12 +109,13 @@ const ScatterChart: React.FC = ({ const outlierPoints: DataPoint[] = []; rawPoints.forEach((item) => { - // Apply Filters (Sido, Gugun, Status) - if (filterNames.sido && item.sido !== filterNames.sido) { - return; - } - - if (filterNames.gugun && !filterNames.gugun.includes(item.gugun)) { + if ( + !matchesAdmdstFilter( + item.admdstCd, + appliedFilter.sido, + appliedFilter.gugun, + ) + ) { return; } @@ -249,7 +246,13 @@ const ScatterChart: React.FC = ({ }; return { data, options }; - }, [resultData, chartData, filterNames, appliedFilter.status]); + }, [ + resultData, + chartData, + appliedFilter.sido, + appliedFilter.gugun, + appliedFilter.status, + ]); return (
diff --git a/apps/st/src/entities/statistics/api/queries.ts b/apps/st/src/entities/statistics/api/queries.ts index bd8510421..9a389bcbf 100644 --- a/apps/st/src/entities/statistics/api/queries.ts +++ b/apps/st/src/entities/statistics/api/queries.ts @@ -2,8 +2,28 @@ import { useQuery } from "@tanstack/react-query"; import { ENDPOINT } from "@/shared/config"; +import { stAnlsMstApi } from "./statistics-analysis-master-api"; import { stAnlsRsltApi } from "./statistics-analysis-result-api"; +export const useStAnlsSidoCombo = () => { + return useQuery({ + queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, "sido"], + queryFn: () => stAnlsMstApi.getSidoCombo(), + }); +}; + +export const useStAnlsSigunguCombo = (sidoCd: string) => { + return useQuery({ + queryKey: [ + ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, + "sigungu", + sidoCd, + ], + queryFn: () => stAnlsMstApi.getSigunguCombo(sidoCd), + enabled: Boolean(sidoCd), + }); +}; + export const useStAnlsRsltList = ( anlsSn: number, vrfcTrgtSn: string | null, @@ -21,6 +41,7 @@ export const useStAnlsRsltList = ( vrfcTrgtSn: Number(vrfcTrgtSn), page: 0, size: 1000, + sort: "admdstCd,asc", }), enabled: !!anlsSn && !!vrfcTrgtSn, }); diff --git a/apps/st/src/entities/statistics/api/analysis-history-api.ts b/apps/st/src/entities/statistics/api/statistics-analysis-master-api.ts similarity index 56% rename from apps/st/src/entities/statistics/api/analysis-history-api.ts rename to apps/st/src/entities/statistics/api/statistics-analysis-master-api.ts index 316684f99..19a6bee1c 100644 --- a/apps/st/src/entities/statistics/api/analysis-history-api.ts +++ b/apps/st/src/entities/statistics/api/statistics-analysis-master-api.ts @@ -2,12 +2,13 @@ import { client } from "@/shared/api"; import { ENDPOINT } from "@/shared/config"; import type { + AdstZoneResponseDto, StAnlsMstListApiResponse, StAnlsMstSearchParams, StAnlsOutlierListResponse, } from "../model/statistics-analysis/statistics-analysis-master-interface"; -export class AnalysisHistoryApiService { +export class StAnlsMstApiService { public async getAnalysisHistoryList( params: StAnlsMstSearchParams, ): Promise { @@ -30,6 +31,23 @@ export class AnalysisHistoryApiService { return response.data; } + + public async getSidoCombo(): Promise { + const response = await client.get( + `${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sido`, + ); + + return response.data; + } + + public async getSigunguCombo(sidoCd: string): Promise { + const response = await client.get( + `${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sigungu`, + { params: { sidoCd } }, + ); + + return response.data; + } } -export const analysisHistoryApi = new AnalysisHistoryApiService(); +export const stAnlsMstApi = new StAnlsMstApiService(); diff --git a/apps/st/src/entities/statistics/index.ts b/apps/st/src/entities/statistics/index.ts index 82cc2d995..44ce398c1 100644 --- a/apps/st/src/entities/statistics/index.ts +++ b/apps/st/src/entities/statistics/index.ts @@ -28,6 +28,7 @@ export { type StAnlsDtlSearchParams, } from "./model/statistics-analysis/statistics-analysis-detail-interface"; export { + type AdstZoneResponseDto, type StAnlsMstListApiResponse, type StAnlsMstResponse, type StAnlsMstResponseDto, diff --git a/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts b/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts index f8a539146..046ba891b 100644 --- a/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts @@ -1,5 +1,15 @@ import type { PageableList, PageableParams } from "@/shared/model"; +/** + * 행정구역(시도·시군구) 콤보 응답 DTO + */ +export interface AdstZoneResponseDto { + /** 행정구역 코드 */ + adstCd: string; + /** 행정구역 명 */ + adstNm: string; +} + /** * 분석 현황 마스터 응답 DTO */