하수처리시설 레이어 기준 검색 동기화

This commit is contained in:
kiminchan
2026-06-26 17:29:16 +09:00
parent 37ace8b34c
commit 23b13e3fdc
3 changed files with 219 additions and 116 deletions
+60 -1
View File
@@ -182,6 +182,9 @@ const getFlowDirectionModeByPathname = (pathname: string | null): FlowDirectionM
const AGED_PIPE_FOCUS_PADDING: [number, number, number, number] = [120, 120, 120, 120];
const DEFAULT_FEATURE_FIT_PADDING: [number, number, number, number] = [50, 50, 50, 50];
const SWAGE_PRCS_FCLT_LAYER_TYPE = "tb_il_swage_prcs_fclt";
type SwagePrcsFcltLayerMode = "all" | "gte500" | "lt500" | "none";
// 레이어 설정 응답이 lyrTyNm/lyrTypeNm/lyr_type_nm처럼 혼재되어 내려오는 경우를 단일 기준으로 정규화한다.
const getLayerTypeName = (layerInfo: any) =>
@@ -191,6 +194,45 @@ const getLayerTypeName = (layerInfo: any) =>
layerInfo?.lyr_type_nm ??
"",
).trim();
const getLayerFilterSql = (layerInfo: any) =>
String(
layerInfo?.filtrSql ??
layerInfo?.filtr_sql ??
layerInfo?.flterSql ??
layerInfo?.filterSql ??
layerInfo?.filter_sql ??
"",
)
.replace(/\s+/g, "")
.toLowerCase();
const getSwagePrcsFcltCapacityKind = (layerInfo: any): Exclude<SwagePrcsFcltLayerMode, "all" | "none"> | null => {
if (getLayerTypeName(layerInfo) !== SWAGE_PRCS_FCLT_LAYER_TYPE) return null;
const layerName = String(layerInfo?.lyrNm ?? layerInfo?.name ?? "").replace(/\s+/g, "");
const filterSql = getLayerFilterSql(layerInfo);
if (filterSql.includes("fclt_cpct>=500") || layerName.includes("500이상") || layerName.includes("500톤이상")) return "gte500";
if (filterSql.includes("fclt_cpct<500") || layerName.includes("500미만") || layerName.includes("500톤미만")) return "lt500";
return null;
};
const getSwagePrcsFcltLayerMode = (checkedNodesData: any[]): SwagePrcsFcltLayerMode => {
const checkedKinds = new Set(
checkedNodesData
.map((node) => getSwagePrcsFcltCapacityKind(node?.data ?? node))
.filter(Boolean),
);
const hasGte500 = checkedKinds.has("gte500");
const hasLt500 = checkedKinds.has("lt500");
if (hasGte500 && hasLt500) return "all";
if (hasGte500) return "gte500";
if (hasLt500) return "lt500";
return "none";
};
// 유하방향 노드는 타입명 또는 표시명으로 식별한다. DB DML 반영 전후 응답 차이를 모두 흡수하기 위한 판별 함수이다.
const isFlowDirectionLayerInfo = (layerInfo: any) => {
@@ -2240,6 +2282,7 @@ const Map = () => {
const [isAgedPipeChecked, setIsAgedPipeChecked] = useState(false);
const [isSinkholeChecked, setIsSinkholeChecked] = useState(false);
const [isFloodHistoryChecked, setIsFloodHistoryChecked] = useState(false);
const [swagePrcsFcltLayerMode, setSwagePrcsFcltLayerMode] = useState<SwagePrcsFcltLayerMode>("all");
const [activeLayerSettingOrder, setActiveLayerSettingOrder] = useState<string[]>([]);
const [draftAgedPipeFilters, setDraftAgedPipeFilters] =
useState<AgedPipeFilters>(AGED_PIPE_DEFAULT_FILTERS);
@@ -3616,7 +3659,21 @@ const Map = () => {
setIsSearchModalOpen(false);
};
const facilityTypes = [{ text: "하수처리시설", val: "tb_il_swage_prcs_fclt" }];
const swagePrcsFcltSearchFilter = useMemo(() => {
switch (swagePrcsFcltLayerMode) {
case "gte500":
return { label: "하수처리시설(500톤 이상)", cqlFilter: "fclt_cpct >= 500" };
case "lt500":
return { label: "하수처리시설(500톤 미만)", cqlFilter: "fclt_cpct < 500" };
case "none":
return { label: "하수처리시설(레이어 꺼짐)", disabled: true };
case "all":
default:
return { label: "하수처리시설(전체)" };
}
}, [swagePrcsFcltLayerMode]);
const facilityTypes = [{ text: "하수처리시설", val: SWAGE_PRCS_FCLT_LAYER_TYPE }];
const fetchAgedPipeRows = useCallback(
async (page: number, filters: AgedPipeFilters, size: number) => {
@@ -4482,6 +4539,7 @@ const Map = () => {
const handleCheckedLayersChange = useCallback((checkedIds: string[], checkedNodesData: any[]) => {
checkedNodesDataRef.current = checkedNodesData;
setCheckedLayerLegendVersion((version) => version + 1);
setSwagePrcsFcltLayerMode(getSwagePrcsFcltLayerMode(checkedNodesData));
// 유하방향은 일반 setLayerOnMap 대상이 아니므로 체크 노드 여부를 별도로 추적한다.
const flowDirectionNode = checkedNodesData.find((node) =>
@@ -6629,6 +6687,7 @@ const Map = () => {
gisMaps={gisMaps}
facilityTypes={facilityTypes}
sidoSearchZoom={isAgedPipeChecked ? AGED_PIPE_GUIDE_MIN_ZOOM : undefined}
facilityLayerFilter={swagePrcsFcltSearchFilter}
/>
}
waterDepth={undefined}
@@ -0,0 +1,28 @@
# FE-MAP 하수처리시설 검색 동기화 구현기록
## 1. 작업 목적
- 지도 레이어 트리의 하수처리시설 500톤 기준 레이어 선택 상태와 상단 시설물 검색 조건을 동기화한다.
## 2. 수정 대상
- `FE_APP_ROOT/app/(map-layout)/map/page.tsx`
- `FE_REPO_ROOT/packages/common/business_components/src/ui/map/searchBar/procSearchBar.tsx`
- `COMMON_DOCS_ROOT/DB작업.sql`
## 3. 구현 내용
- 하수처리시설 하위 레이어명은 DB `tb_sn_lyr_stng`의 `lyr_nm`을 그대로 사용하고, 프론트 강제 치환은 제거했다.
- 레이어 선택 상태에 따라 시설물 검색 셀렉트 문구를 `하수처리시설(전체)`, `하수처리시설(500톤 이상)`, `하수처리시설(500톤 미만)`, `하수처리시설(레이어 꺼짐)`으로 표시한다.
- 하수처리시설 검색 목록 조회 시 선택된 레이어 기준에 맞춰 `fclt_cpct >= 500` 또는 `fclt_cpct < 500` 조건을 함께 적용한다.
- WFS 목록 조회의 `cql_filter`는 `URLSearchParams`로 인코딩해 500톤 기준 조건이 실제 요청에 반영되도록 보정했다.
- 시설 선택 시 현재 지도 source만 검색하지 않고, feature id 기준 GeoServer 재조회 후 검색 레이어를 선택하도록 보정했다.
- 두 레이어가 모두 꺼진 경우 하수처리시설 목록은 선택 불가 placeholder만 표시한다.
- `tb_sn_lyr_stng`의 `lyr_sn=112/113` 레이어명을 `500톤 이상`, `500톤 미만`으로 정리하는 DML 초안을 `DB작업.sql`에 추가했다.
## 4. 검증
- TSX 파서 검사 통과
- `pnpm --filter @common/business_components run build` 통과
## 5. 영향 범위와 리스크
- 프론트 지도 화면의 레이어명 표시와 상단 시설물 검색 목록에 한정된다.
- API 계약과 DB 스키마 변경은 없다.
- DB 기준데이터 레이어명 변경 DML은 실제 대상 DB 반영 후 지도 레이어 트리에서 확인해야 한다.
- 레이어 메타데이터의 `flterSql/filtrSql` 또는 레이어명이 변경되면 500톤 기준 판별 조건을 함께 확인해야 한다.