"use client"; import { create } from "zustand"; import { getRawJson } from "@/components/lib/api"; import type { SelectDataType } from "@common/business_components"; type OnsemiroAddrStoreState = { govArea: SelectDataType[]; city: SelectDataType[]; sgg: SelectDataType[]; fetchGovArea: () => Promise; fetchCity: () => Promise; fetchSgg: () => Promise; }; /** 시도 SelectBox에 적용할 행정구역 표준 표시 순서다. */ const CITY_SORT_ORDER = [ "서울특별시", "부산광역시", "대구광역시", "인천광역시", "광주광역시", "대전광역시", "울산광역시", "세종특별자치시", "경기도", "강원특별자치도", "충청북도", "충청남도", "전북특별자치도", "전라남도", "경상북도", "경상남도", "제주특별자치도", ]; /** 공통 응답 래퍼와 배열 응답을 동일한 목록으로 정규화한다. */ const toResponseList = (response: unknown) => { if (Array.isArray(response)) return response; if (response && typeof response === "object") { const data = (response as { data?: unknown }).data; if (Array.isArray(data)) return data; } return []; }; /** ONSEMIRO 주소 원본 필드를 공통 SelectBox 옵션으로 변환한다. */ const toSelectOption = (item: Record) => { const textCandidate = [ item.text, item.name, item.label, item.govAreaNm, item.govAreaName, item.gugun, item.sggNm, item.sggName, item.lclgvrnmntNm, item.sido, item.cityNm, item.cityName, item.korNm, ].find((value) => typeof value === "string" && value.trim()); const valueCandidate = [ item.val, item.code, item.id, item.govAreaCd, item.govAreaCode, item.adstDstrCd, item.sggCd, item.sggCode, item.lclgvrnmntCd, item.cityCd, item.cityCode, ].find( (value) => value !== null && value !== undefined && String(value).trim(), ); if (!textCandidate || valueCandidate === undefined) return null; return { text: String(textCandidate).trim(), val: String(valueCandidate).trim(), }; }; /** 시도 옵션을 표준 순서로 정렬하고 나머지는 한글명으로 정렬한다. */ const compareCityOptions = (left: SelectDataType, right: SelectDataType) => { const leftIndex = CITY_SORT_ORDER.indexOf(left.text); const rightIndex = CITY_SORT_ORDER.indexOf(right.text); if (leftIndex !== -1 && rightIndex !== -1) return leftIndex - rightIndex; if (leftIndex !== -1) return -1; if (rightIndex !== -1) return 1; return left.text.localeCompare(right.text, "ko"); }; /** 주소 API를 호출해 공통 SelectBox 옵션 목록을 만든다. */ const loadSelectOptions = async (url: string) => { const response = await getRawJson(url); return toResponseList(response) .map((item) => item && typeof item === "object" ? toSelectOption(item as Record) : null, ) .filter((item): item is SelectDataType => Boolean(item)); }; /** ONSEMIRO 환경청·시도·시군구 선택목록을 공유하는 상태 저장소다. */ const useOnsemiroAddrStore = create((set) => ({ govArea: [], city: [], sgg: [], /** 환경청 선택목록을 조회해 한글명 순으로 저장한다. */ fetchGovArea: async () => { const govArea = await loadSelectOptions( "/sewerage-service/api/v1/onsemiro/addr/govArea", ); set({ govArea: govArea.sort((left, right) => left.text.localeCompare(right.text, "ko"), ), }); }, /** 시도 선택목록을 조회해 표준 행정구역 순서로 저장한다. */ fetchCity: async () => { const city = await loadSelectOptions( "/sewerage-service/api/v1/onsemiro/addr/city", ); set({ city: city.sort(compareCityOptions), }); }, /** 시군구 선택목록을 조회해 한글명 순으로 저장한다. */ fetchSgg: async () => { const sgg = await loadSelectOptions( "/sewerage-service/api/v1/onsemiro/addr/sgg", ); set({ sgg: sgg.sort((left, right) => left.text.localeCompare(right.text, "ko")), }); }, })); export default useOnsemiroAddrStore;