diff --git a/apps/wr/app/api/common-codes/route.ts b/apps/wr/app/api/common-codes/route.ts new file mode 100644 index 000000000..ded38eadf --- /dev/null +++ b/apps/wr/app/api/common-codes/route.ts @@ -0,0 +1,207 @@ +import { SITE_ID } from "@/src/shared/config"; +import { NextResponse } from "next/server"; + +export const revalidate = 60 * 60 * 24; + +type SelectDataType = { + text: string; + val: string; +}; + +type EnvPartSelectDataType = SelectDataType & { + adstDliStrCd: string; +}; + +const codeMapping = { + WR001: "bdstUsgCd", + WR002: "rswtBdstType", + WR003: "rainFcltCd", + WR004: "bdstGrntchClass", + WR005: "rswtPrscCdMthdCd", + WR006: "orgwtKndCd", + WR007: "ruseMngMtd", + WR008: "rusePrcsMthdCd", + WR009: "erctUswtrPrcsMeth", + WR010: "ruseFcltSpot", + WR011: "pplQulity", + WR012: "pplLenght", + WR013: "bdstFilterYn", + WR014: "rainRswtMng", + WR015: "rainFcltFlowRate", + WR016: "dmdUszCd", + WR017: "sewageInFcltUse", + WR018: "sewageOutFclttUse", + WR019: "htdPowerMthdCd", + WR020: "htdUtztnUsgCd", +} as const; + +const defaultHeaders = { + accept: "application/json", + "X-Site-Id": SITE_ID, +}; + +function getServerBaseUrl() { + const port = process.env.PORT ?? process.env.NEXT_PUBLIC_PORT ?? "3000"; + const proxyHost = process.env.NEXT_PUBLIC_PROXY_HOST; + + if (process.env.NODE_ENV === "production" && process.env.HOSTNAME) { + return `http://${process.env.HOSTNAME}:${port}`; + } + + return proxyHost || `http://localhost:${port}`; +} + +async function fetchJson(url: string, cookieHeader?: string) { + const response = await fetch(url, { + method: "GET", + headers: { + ...defaultHeaders, + ...(cookieHeader ? { cookie: cookieHeader } : {}), + }, + next: { revalidate }, + }); + + if (!response.ok) { + throw new Error(`Failed to fetch ${url}: ${response.status}`); + } + + return response.json(); +} + +async function fetchOperationData(path: string, cookieHeader?: string) { + const json = await fetchJson( + `${getServerBaseUrl()}/operation-service${path}`, + cookieHeader + ); + return json.data; +} + +async function fetchCmsData(path: string, cookieHeader?: string) { + return fetchJson(`${getServerBaseUrl()}${path}`, cookieHeader); +} + +async function fetchCommonCodesByKeys(keys: string[], cookieHeader?: string) { + const targets = [...new Set(keys)].filter( + (cd) => Boolean(codeMapping[cd as keyof typeof codeMapping]) + ); + + const entries = await Promise.all( + targets.map(async (cd) => { + const mapping = codeMapping[cd as keyof typeof codeMapping]; + const lwCd = await fetchCmsData( + `/cms-service/api/v1/lwpo-cds/up-cd/${cd}`, + cookieHeader + ); + const list = Array.isArray(lwCd) ? lwCd : (lwCd.content ?? []); + + return [ + mapping, + list.map((it: any) => ({ + text: it.lwpoCdNm, + val: it.lwpoCd, + })), + ] as const; + }) + ); + + return Object.fromEntries(entries) as Record; +} + +export async function GET(request: Request) { + try { + const cookieHeader = request.headers.get("cookie") ?? ""; + const [sgg, city, rvbsn, mdmbsn, lgbsn, lclgvrnmnt, upCd] = + await Promise.all([ + fetchOperationData("/addr/sgg", cookieHeader), + fetchOperationData("/addr/city", cookieHeader), + fetchOperationData("/addr/sarea", cookieHeader), + fetchOperationData("/addr/midArea", cookieHeader), + fetchOperationData("/addr/largeArea", cookieHeader), + fetchOperationData("/addr/govArea", cookieHeader), + fetchCmsData( + "/cms-service/api/v1/up-codes?keyword=&upCd=&cdSe=&useYn=Y&siteId=WR&page=0&size=100&sort=string", + cookieHeader + ), + ]); + + const upWrCode: string[] = (upCd.content ?? []) + .filter((item: any) => item.upCd.slice(0, 2) === "WR") + .map((cd: any) => cd.upCd); + + const lwCds = await fetchCommonCodesByKeys(upWrCode, cookieHeader); + + const payload = { + sggCd: [ + ...((sgg ?? []).map((item: any) => ({ + text: item.gugun || "", + val: String(item.adstDstrCd) || "", + })) as SelectDataType[]), + ], + cityCd: [ + { val: "0", text: "전체" }, + ...((city ?? []).map((item: any) => ({ + val: item.adstDstrCd, + text: item.sido, + })) as SelectDataType[]), + ], + rvbsnCd: [ + { val: "0", text: "전체" }, + ...((rvbsn ?? []).map((item: any) => ({ + val: item.rvbsnCd, + text: item.rvbsn, + })) as SelectDataType[]), + ], + mdmbsnCd: [ + { val: "0", text: "전체" }, + ...((mdmbsn ?? []).map((item: any) => ({ + val: item.rvbsnCd, + text: item.mdmbsn, + })) as SelectDataType[]), + ], + lgbsnCd: [ + { val: "0", text: "전체" }, + ...((lgbsn ?? []).map((item: any) => ({ + val: item.rvbsnCd, + text: item.lgbsn, + })) as SelectDataType[]), + ], + lclgvrnmntCd: [ + { val: "0", text: "전체", adstDliStrCd: "" }, + ...((lclgvrnmnt ?? []).map((item: any) => ({ + val: item.lclgvrnmntCd, + text: item.lclgvrnmntNm, + adstDliStrCd: item.adstDliStrCd || "", + })) as EnvPartSelectDataType[]), + ], + rainFcltCd: lwCds["rainFcltCd"] ?? [], + bdstUsgCd: lwCds["bdstUsgCd"] ?? [], + orgwtKndCd: lwCds["orgwtKndCd"] ?? [], + pplQulity: lwCds["pplQulity"] ?? [], + pplLenght: lwCds["pplLenght"] ?? [], + dmdUszCd: lwCds["dmdUszCd"] ?? [], + sewageOutFclttUse: lwCds["sewageOutFclttUse"] ?? [], + rainRswtMng: lwCds["rainRswtMng"] ?? [], + rainFcltFlowRate: lwCds["rainFcltFlowRate"] ?? [], + ruseMngMtd: lwCds["ruseMngMtd"] ?? [], + rusePrcsMthdCd: lwCds["rusePrcsMthdCd"] ?? [], + erctUswtrPrcsMeth: lwCds["erctUswtrPrcsMeth"] ?? [], + bdstGrntchClass: lwCds["bdstGrntchClass"] ?? [], + htdPowerMthdCd: lwCds["htdPowerMthdCd"] ?? [], + htdUtztnUsgCd: lwCds["htdUtztnUsgCd"] ?? [], + rswtBdstType: lwCds["rswtBdstType"] ?? [], + bdstTotalUsgType: (lwCds["bdstUsgCd"] ?? []).concat(lwCds["rswtBdstType"] ?? []), + }; + + return NextResponse.json(payload, { + headers: { + "Cache-Control": "s-maxage=86400, stale-while-revalidate=3600", + }, + }); + } catch (error) { + console.error("Failed to load common codes:", error); + return NextResponse.json( + { message: "Failed to load common codes" }, + { status: 500 } + ); + } +} diff --git a/apps/wr/app/store/common/code/index.ts b/apps/wr/app/store/common/code/index.ts index 4f7896de0..0ddb7fc8c 100644 --- a/apps/wr/app/store/common/code/index.ts +++ b/apps/wr/app/store/common/code/index.ts @@ -371,18 +371,80 @@ export const codeActionExport: (set: any, get:any) => comCodeActions = (set: any }, initCommonCodes: async () => { if (get().code.isInit) return; + const response = await fetch("/api/common-codes", { + method: "GET", + headers: { + accept: "application/json", + }, + }); - await getSggCd(set); - await getCityCd(set); - await getRegionCd(set); - await getMiddleRegionCd(set); - await getUnitWaterShedCd(set); - await getEnvPartCd(set); - await getCommonUpCd(set); + if (!response.ok) { + throw new Error(`공통 코드 조회 실패: ${response.status}`); + } + + const data = await response.json(); + + lwCds["rainFcltCd"] = data.rainFcltCd ?? lwCds["rainFcltCd"] ?? []; + lwCds["bdstUsgCd"] = data.bdstUsgCd ?? lwCds["bdstUsgCd"] ?? []; + lwCds["orgwtKndCd"] = data.orgwtKndCd ?? lwCds["orgwtKndCd"] ?? []; + lwCds["pplQulity"] = data.pplQulity ?? lwCds["pplQulity"] ?? []; + lwCds["pplLenght"] = data.pplLenght ?? lwCds["pplLenght"] ?? []; + lwCds["dmdUszCd"] = data.dmdUszCd ?? lwCds["dmdUszCd"] ?? []; + lwCds["sewageOutFclttUse"] = + data.sewageOutFclttUse ?? lwCds["sewageOutFclttUse"] ?? []; + lwCds["rainRswtMng"] = data.rainRswtMng ?? lwCds["rainRswtMng"] ?? []; + lwCds["rainFcltFlowRate"] = + data.rainFcltFlowRate ?? lwCds["rainFcltFlowRate"] ?? []; + lwCds["ruseMngMtd"] = data.ruseMngMtd ?? lwCds["ruseMngMtd"] ?? []; + lwCds["rusePrcsMthdCd"] = + data.rusePrcsMthdCd ?? lwCds["rusePrcsMthdCd"] ?? []; + lwCds["erctUswtrPrcsMeth"] = + data.erctUswtrPrcsMeth ?? lwCds["erctUswtrPrcsMeth"] ?? []; + lwCds["bdstGrntchClass"] = + data.bdstGrntchClass ?? lwCds["bdstGrntchClass"] ?? []; + lwCds["htdPowerMthdCd"] = + data.htdPowerMthdCd ?? lwCds["htdPowerMthdCd"] ?? []; + lwCds["htdUtztnUsgCd"] = + data.htdUtztnUsgCd ?? lwCds["htdUtztnUsgCd"] ?? []; + lwCds["rswtBdstType"] = + data.rswtBdstType ?? lwCds["rswtBdstType"] ?? []; + lwCds["bdstTotalUsgType"] = + data.bdstTotalUsgType ?? lwCds["bdstTotalUsgType"] ?? []; set((state) => ({ code: { ...state.code, + sggCd: data.sggCd ?? state.code.sggCd, + cityCd: data.cityCd ?? state.code.cityCd, + rvbsnCd: data.rvbsnCd ?? state.code.rvbsnCd, + mdmbsnCd: data.mdmbsnCd ?? state.code.mdmbsnCd, + lgbsnCd: data.lgbsnCd ?? state.code.lgbsnCd, + lclgvrnmntCd: data.lclgvrnmntCd ?? state.code.lclgvrnmntCd, + rainFcltCd: data.rainFcltCd ?? state.code.rainFcltCd, + bdstUsgCd: data.bdstUsgCd ?? state.code.bdstUsgCd, + orgwtKndCd: data.orgwtKndCd ?? state.code.orgwtKndCd, + pplQulity: data.pplQulity ?? state.code.pplQulity, + pplLenght: data.pplLenght ?? state.code.pplLenght, + dmdUszCd: data.dmdUszCd ?? state.code.dmdUszCd, + sewageOutFclttUse: + data.sewageOutFclttUse ?? state.code.sewageOutFclttUse, + rainRswtMng: data.rainRswtMng ?? state.code.rainRswtMng, + rainFcltFlowRate: + data.rainFcltFlowRate ?? state.code.rainFcltFlowRate, + ruseMngMtd: data.ruseMngMtd ?? state.code.ruseMngMtd, + rusePrcsMthdCd: + data.rusePrcsMthdCd ?? state.code.rusePrcsMthdCd, + erctUswtrPrcsMeth: + data.erctUswtrPrcsMeth ?? state.code.erctUswtrPrcsMeth, + bdstGrntchClass: + data.bdstGrntchClass ?? state.code.bdstGrntchClass, + htdPowerMthdCd: + data.htdPowerMthdCd ?? state.code.htdPowerMthdCd, + htdUtztnUsgCd: + data.htdUtztnUsgCd ?? state.code.htdUtztnUsgCd, + rswtBdstType: data.rswtBdstType ?? state.code.rswtBdstType, + bdstTotalUsgType: + data.bdstTotalUsgType ?? state.code.bdstTotalUsgType, isInit: true, }, })); diff --git a/apps/wr/src/app/providers/app-provider.tsx b/apps/wr/src/app/providers/app-provider.tsx index 63df8349d..33f2e728a 100644 --- a/apps/wr/src/app/providers/app-provider.tsx +++ b/apps/wr/src/app/providers/app-provider.tsx @@ -1,5 +1,6 @@ "use client"; +import { useEffect } from "react"; import { DndProvider } from "react-dnd"; import { MultiBackend, getBackendOptions } from "@minoru/react-dnd-treeview"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; @@ -16,7 +17,10 @@ import i18next from "i18next"; import { I18nextProvider } from "react-i18next"; import { Toaster } from "react-hot-toast"; import koBase from "@/public/locales/ko/message.json"; -import { StoreProvider as WrStoreProvider } from "@/app/store"; +import { + StoreProvider as WrStoreProvider, + useWrStore, +} from "@/app/store"; //import { LoginApi } from "@/src/entities/auth"; //import { useEffect } from "react"; import { setCookie } from "@/src/shared/lib/cookie"; @@ -34,6 +38,18 @@ i18n.init({ }, }); +function CommonCodeInitializer() { + const isInit = useWrStore((state) => state.code.isInit); + const initCommonCodes = useWrStore((state) => state.initCommonCodes); + + useEffect(() => { + if (isInit) return; + initCommonCodes(); + }, [isInit, initCommonCodes]); + + return null; +} + const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => { const queryClient = new QueryClient(); return ( @@ -49,6 +65,7 @@ const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => { > + {children}