지반침하 지도 UX 및 목록 표시 개선
This commit is contained in:
@@ -38,16 +38,16 @@ type SinkholeTableRow = SinkholeListItem & {
|
||||
};
|
||||
|
||||
const HEADLIST = [
|
||||
{ id: "rowNo", title: "NO", widthPercent: 6 },
|
||||
{ id: "ocrnYmd", title: "발생일자", widthPercent: 11 },
|
||||
{ id: "fullAddress", title: "주소", widthPercent: 22 },
|
||||
{ id: "sizeWidthDisplay", title: "발생규모폭", widthPercent: 10 },
|
||||
{ id: "sizeLengthDisplay", title: "발생규모연장", widthPercent: 10 },
|
||||
{ id: "sizeDepthDisplay", title: "발생규모깊이", widthPercent: 10 },
|
||||
{ id: "detailCauseDisplay", title: "상세발생원인", widthPercent: 12 },
|
||||
{ id: "restoreStatus", title: "복구상태", widthPercent: 9 },
|
||||
{ id: "coordStatusDisplay", title: "좌표보정상태", widthPercent: 10 },
|
||||
{ id: "restoreCompletedYmdDisplay", title: "복구완료일자", widthPercent: 10 },
|
||||
{ id: "acdntNo", title: "사고번호", widthPercent: 17 },
|
||||
{ id: "ocrnYmd", title: "발생일자", widthPercent: 10 },
|
||||
{ id: "fullAddress", title: "주소", widthPercent: 20 },
|
||||
{ id: "sizeWidthDisplay", title: "발생규모폭", widthPercent: 8 },
|
||||
{ id: "sizeLengthDisplay", title: "발생규모연장", widthPercent: 8 },
|
||||
{ id: "sizeDepthDisplay", title: "발생규모깊이", widthPercent: 8 },
|
||||
{ id: "detailCauseDisplay", title: "상세발생원인", widthPercent: 11 },
|
||||
{ id: "restoreStatus", title: "복구상태", widthPercent: 8 },
|
||||
{ id: "coordStatusDisplay", title: "좌표보정상태", widthPercent: 9 },
|
||||
{ id: "restoreCompletedYmdDisplay", title: "복구완료일자", widthPercent: 11 },
|
||||
] as const;
|
||||
|
||||
const toDisplay = (value: unknown) => {
|
||||
|
||||
@@ -144,6 +144,8 @@ const AGED_PIPE_ROW_SELECT_MESSAGE = "SN_AGED_PIPE_ROW_SELECT";
|
||||
const AGED_PIPE_POPUP_MINIMIZE_MESSAGE = "SN_AGED_PIPE_POPUP_MINIMIZE";
|
||||
const AGED_PIPE_FILTER_SYNC_MESSAGE = "SN_AGED_PIPE_FILTER_SYNC";
|
||||
const SINKHOLE_DEFAULT_POINT_LIMIT = 2000;
|
||||
// 지반침하 지도 포인트와 같은 파란 계열의 기존 레이어 트리 심볼 자산을 재사용한다.
|
||||
const SINKHOLE_TREE_ICON_NAME = "SN_SINKHOLE_SPOT_BLUE.png";
|
||||
const SOUTH_KOREA_VIEW_EXTENT: [number, number, number, number] = [
|
||||
600000,
|
||||
1300000,
|
||||
@@ -1197,6 +1199,26 @@ const createSinkholeRadiusStyle = (variant: SinkholePointVariant = "blue") =>
|
||||
}),
|
||||
zIndex: 9993,
|
||||
});
|
||||
// 반경 원 상단에 실제 적용 거리값을 표시하는 선택 전용 지도 라벨 스타일이다.
|
||||
const createSinkholeRadiusLabelStyle = (
|
||||
radiusMeters: number,
|
||||
variant: SinkholePointVariant = "blue",
|
||||
) =>
|
||||
new Style({
|
||||
text: new Text({
|
||||
text: `반경 ${radiusMeters.toLocaleString("ko-KR")}m`,
|
||||
font: "600 13px Pretendard, sans-serif",
|
||||
textAlign: "center",
|
||||
offsetY: -14,
|
||||
padding: [4, 7, 4, 7],
|
||||
fill: new Fill({ color: SINKHOLE_POINT_PALETTE[variant].strokeSelected }),
|
||||
stroke: new Stroke({ color: "rgba(255, 255, 255, 0.98)", width: 3 }),
|
||||
backgroundFill: new Fill({ color: "rgba(255, 255, 255, 0.9)" }),
|
||||
backgroundStroke: new Stroke({ color: SINKHOLE_POINT_PALETTE[variant].radiusStroke, width: 1.5 }),
|
||||
}),
|
||||
zIndex: 9998,
|
||||
});
|
||||
|
||||
|
||||
const createSinkholeFocusedPipeStyle = () => [
|
||||
new Style({
|
||||
@@ -2426,7 +2448,7 @@ const normalizeTreeRowSpacing = (data: any[]) => {
|
||||
|
||||
return {
|
||||
...item,
|
||||
iconNm: null,
|
||||
iconNm: item.iconNm || SINKHOLE_TREE_ICON_NAME,
|
||||
gobjTy: "",
|
||||
};
|
||||
});
|
||||
@@ -2463,7 +2485,7 @@ const appendSinkholeControlNode = (data: any[]) => {
|
||||
lyrSn: existingRootId,
|
||||
id: existingRootId,
|
||||
gobjTy: "",
|
||||
iconNm: null,
|
||||
iconNm: existingRoot.iconNm || SINKHOLE_TREE_ICON_NAME,
|
||||
clickPsblYn: "N",
|
||||
actlLyrYn: "N",
|
||||
children: null,
|
||||
@@ -2481,7 +2503,7 @@ const appendSinkholeControlNode = (data: any[]) => {
|
||||
lyrTyNm: SINKHOLE_LAYER_TYPE,
|
||||
useYn: "Y",
|
||||
minViewSize: null,
|
||||
iconNm: null,
|
||||
iconNm: SINKHOLE_TREE_ICON_NAME,
|
||||
iconSize: null,
|
||||
lblAtrbNm: null,
|
||||
fontNm: null,
|
||||
@@ -4198,7 +4220,14 @@ const Map = () => {
|
||||
});
|
||||
radiusFeature.setStyle(createSinkholeRadiusStyle(pointVariant));
|
||||
|
||||
source.addFeatures([radiusFeature, pointFeature]);
|
||||
// 원의 북쪽 경계에 라벨을 두고 같은 source에서 관리해 선택 해제 시 함께 정리한다.
|
||||
const radiusLabelFeature = new Feature({
|
||||
geometry: new Point([detail.x, detail.y + radiusMeters]),
|
||||
});
|
||||
radiusLabelFeature.set("sinkholeRadiusLabel", true);
|
||||
radiusLabelFeature.setStyle(createSinkholeRadiusLabelStyle(radiusMeters, pointVariant));
|
||||
|
||||
source.addFeatures([radiusFeature, radiusLabelFeature, pointFeature]);
|
||||
},
|
||||
[clearSinkholeSelectionLayer, ensureSinkholeSelectionLayer],
|
||||
);
|
||||
@@ -4642,11 +4671,13 @@ const Map = () => {
|
||||
|
||||
try {
|
||||
const layerTypeName = String(pipe?.lyrTypeNm ?? "tb_ms_swage_ppl");
|
||||
const storageNm = String(GisApp.Module.layer?.storageNm ?? "").trim();
|
||||
if (!storageNm) throw new Error("GeoServer 저장소 이름을 확인할 수 없습니다.");
|
||||
const params = new URLSearchParams({
|
||||
SERVICE: "WFS",
|
||||
VERSION: "1.1.0",
|
||||
REQUEST: "GetFeature",
|
||||
TYPENAME: `${GisApp.Module.layer.storageNm}${layerTypeName}`,
|
||||
TYPENAME: `${storageNm}${layerTypeName}`,
|
||||
SRSNAME: GisApp.TO_PROJ,
|
||||
OUTPUTFORMAT: "application/json",
|
||||
FEATUREID: fid,
|
||||
@@ -4845,6 +4876,48 @@ const Map = () => {
|
||||
[activateRightPanelTab, clearSinkholeFocusedPipeLayer, clearSinkholeSelectionLayer, rightPanelTabs],
|
||||
);
|
||||
|
||||
// 선택 피처가 없는 지도 본체 클릭을 기존 지반침하 상세 닫기 흐름과 연결한다.
|
||||
const handleSinkholeMapBlankClick = useCallback(() => {
|
||||
if (gisEdit.isEditMode || !selectedSinkholeDetail?.acdntNo) return;
|
||||
|
||||
const sinkholeTabId = `sinkhole:${selectedSinkholeDetail.acdntNo}`;
|
||||
const hasSinkholeTab = rightPanelTabs.some((tab) => tab.id === sinkholeTabId);
|
||||
|
||||
if (hasSinkholeTab && activeRightPanelTabId === sinkholeTabId) {
|
||||
closeRightPanelTab(sinkholeTabId);
|
||||
} else {
|
||||
// 비활성 지반침하 탭은 현재 활성 탭을 바꾸지 않고 제거하며 종속 관로의 부모 연결만 해제한다.
|
||||
setRightPanelTabs((currentTabs) =>
|
||||
currentTabs
|
||||
.filter((tab) => tab.id !== sinkholeTabId)
|
||||
.map((tab) => (tab.parentTabId === sinkholeTabId ? { ...tab, parentTabId: null } : tab)),
|
||||
);
|
||||
setSelectedSinkholeDetail(null);
|
||||
setSelectedSinkholeNearbyPipes([]);
|
||||
clearSinkholeSelectionLayer();
|
||||
if (pipeParentTabIdRef.current === sinkholeTabId) {
|
||||
pipeParentTabIdRef.current = null;
|
||||
}
|
||||
if (!hasSinkholeTab && getSelectedFeatureLayerTypeName(featureData) === SINKHOLE_LAYER_TYPE) {
|
||||
setFeatureData(null);
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
const select = GisApp.Module.select as any;
|
||||
select?.resetSelectedFeature?.();
|
||||
clearSinkholeFocusedPipeLayer();
|
||||
}, [
|
||||
activeRightPanelTabId,
|
||||
clearSinkholeFocusedPipeLayer,
|
||||
clearSinkholeSelectionLayer,
|
||||
closeRightPanelTab,
|
||||
featureData,
|
||||
gisEdit.isEditMode,
|
||||
rightPanelTabs,
|
||||
selectedSinkholeDetail,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !featureData) return;
|
||||
const nextTab = buildRightPanelTab(featureData);
|
||||
@@ -7260,6 +7333,8 @@ const Map = () => {
|
||||
detailView={GisEditAwareDetailView}
|
||||
canSelectFeature={canSelectFeatureDuringGisEdit}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
onMapBlankClick={handleSinkholeMapBlankClick}
|
||||
sinkholeLegendIconVisible={true}
|
||||
featureData={featureData}
|
||||
setFeatureData={setFeatureData}
|
||||
isOpen={isOpen}
|
||||
|
||||
@@ -104,3 +104,27 @@
|
||||
- 처리시설/처리구역 탭에 실제 시설명/구역명과 유형별 색상이 표시되는지 확인한다.
|
||||
- 부모 지반침하를 닫으면 관로의 부모 링크가 사라지고, 최대 5개 자동 정리에서는 활성 관로의 부모가 보호되는지 확인한다.
|
||||
- 자동 검증은 `diff --check`, TypeScript, Next build로 수행하고, 화면 상호작용은 로그인 가능한 `/map` 통합 환경에서 수동 수행한다.
|
||||
|
||||
### 3.6 2026-07-14 지도 UX 현행 유지보수 검증 계획
|
||||
|
||||
- 반경이 다른 사고를 각각 선택해 지도 라벨 `반경 {값}m`와 상세 패널 값이 일치하는지 확인한다.
|
||||
- 새 사고 선택, 탭 닫기, 빈 지도 클릭, 레이어 해제 시 반경 원·라벨·중심점이 함께 제거되는지 확인한다.
|
||||
- 지반침하 루트에 기존 파란 심볼이 보이고 다른 앱/서비스의 공통 트리 노출은 바뀌지 않는지 확인한다.
|
||||
- 빈 지도 클릭은 현재 선택 지반침하와 종속 관로 강조만 해제하고 다른 지반침하·처리시설·처리구역·GIS 편집 탭은 유지하는지 확인한다.
|
||||
- 다른 피처 클릭과 GIS 편집 핸들/중간점/미리보기 클릭이 빈 영역 해제로 오인되지 않는지 확인한다.
|
||||
- 비활성 지반침하를 정리할 때 현재 활성 탭이 바뀌지 않는지 확인한다.
|
||||
- 지반침하발생현황 그리드 첫 열에 사고번호가 표시되고 화면 NO 열은 없으며 행 선택·페이징·정렬이 회귀하지 않는지 확인한다.
|
||||
- 정적 검사는 변경 파일 구문/타입, `git diff --check`, Next build로 수행하고 상호작용은 로그인 가능한 `/map`에서 수동 확인한다.
|
||||
- BE는 기존 목록·상세·반경 응답의 `acdntNo`, `radiusMeters` 유지 여부만 회귀 확인한다.
|
||||
|
||||
### 3.7 2026-07-14 빈 지도 클릭 선택 해제 수동 검증 절차
|
||||
|
||||
1. 로그인 후 `/map`에서 지반침하정보 레이어를 켠다.
|
||||
2. 목록이 아니라 지도 위 지반침하 포인트를 직접 선택하고 우측 상세 탭, 반경 원, `반경 {값}m` 라벨이 표시되는지 확인한다.
|
||||
3. 다른 피처·지도 컨트롤·GIS 편집 핸들이 없는 빈 지도 영역을 클릭한다.
|
||||
4. 선택한 지반침하 상세 탭 또는 현재 선택 상태, 반경 원·라벨·중심점, 종속 관로 강조가 함께 해제되는지 확인한다. 기본 지반침하 레이어의 포인트는 남아 있어야 한다.
|
||||
5. 다른 업무 탭을 열어 둔 상태에서 반복해 다른 탭은 유지되고 활성 탭이 정상적으로 복귀하는지 확인한다.
|
||||
6. GIS 편집 모드에서는 빈 영역을 클릭해도 지반침하 선택 해제가 오동작하지 않는지 확인한다.
|
||||
7. 해제 후 다른 지반침하 포인트를 다시 선택해 상세와 반경 표시가 정상적으로 열리는지 확인한다.
|
||||
|
||||
- 이 절차는 지도 포인트 선택 후 해제를 검증한다. 목록 행 선택만 한 상태는 지도 선택 모듈의 빈 선택 이벤트 대상이 아니므로 별도 시나리오로 구분한다.
|
||||
|
||||
@@ -293,3 +293,31 @@
|
||||
- 백엔드 `compileJava`는 성공했다.
|
||||
- 사용자 요청에 따라 서버와 브라우저는 실행하지 않았다.
|
||||
- 남은 수동 검증은 실행 중 통합 환경에서 관로→부모 지반침하 복귀 후 화살표 숨김, 처리구역 클릭 200 응답과 분석 표 표시를 확인하는 것이다.
|
||||
|
||||
### 2.27 2026-07-14 지도 UX 유지보수 구현 확인 및 사용자 검증 대기
|
||||
|
||||
- 구현 파일의 변경 diff에서 반경 라벨 피처, 트리 심볼 fallback/선택 prop, 빈 선택 결과 콜백, 목록 사고번호 첫 열을 정적으로 확인했다.
|
||||
- 공통 빈 선택 결과는 `GisSelect` 한 곳에서만 소비하며 콜백 미전달 화면은 기존처럼 아무 동작도 하지 않는 구조를 확인했다.
|
||||
- `pnpm --filter sn exec tsc --noEmit --pretty false --ignoreDeprecations 5.0`은 실행했으나 기존 common/floodHistory/html/chart 오류로 전체 성공하지 않았다.
|
||||
- 검사에서 함께 확인된 `page.tsx`의 GeoServer storage null 가능성은 같은 지반침하 관로 조회 흐름에서 방어 처리했다.
|
||||
- 개발 서버가 실행 중이어서 저장소 규칙에 따라 Next 및 `business_components` 빌드는 수행하지 않았다.
|
||||
- 사용자 요청에 따라 브라우저·API·화면 상호작용 검증은 수행하지 않았다.
|
||||
- 현재 판정은 `구현 완료 / 사용자 수동 검증 대기`다.
|
||||
- 사용자 확인 항목은 반경 라벨 값과 시인성, 트리 파란 심볼, 빈 지도 클릭 시 선택 해제와 다른 탭 보존, 목록 첫 열 사고번호다.
|
||||
|
||||
### 2.28 2026-07-14 목록 NO 열 제거 및 수동 검증 이관
|
||||
|
||||
- `SinkholePopupPage.tsx`의 `HEADLIST`에서 `acdntNo`가 첫 열이고 `rowNo/NO` 열이 없는 것을 정적으로 확인했다.
|
||||
- 내부 `rowNo` 생성과 다운로드 순번은 기존 호환을 위해 유지했다.
|
||||
- 지도 선택 해제의 구체적인 수동 절차와 기대 결과를 통합검증계획 3.7에 추가했다.
|
||||
- 브라우저·API·빌드는 사용자 요청에 따라 실행하지 않았으며 현재 판정은 `구현 완료 / 사용자 수동 검증 대기`다.
|
||||
|
||||
### 2.29 2026-07-14 사용자 통합 검증 결과
|
||||
|
||||
- 검증 주체: 사용자
|
||||
- 검증 환경: 실행 중인 통합 화면
|
||||
- 반경 표시 영역에 실제 반경 값이 정상 표시됨을 확인했다.
|
||||
- 지반침하 레이어 트리에 심볼이 정상 표시됨을 확인했다.
|
||||
- 지도에서 선택한 지반침하가 빈 지도 영역 클릭으로 정상 해제되고 관련 반경·강조 표시도 함께 정리됨을 확인했다.
|
||||
- 지반침하발생현황 목록 첫 열에 사고번호가 표시되고 화면 `NO` 열이 제거된 것을 확인했다.
|
||||
- 이번 유지보수 요구사항 전체가 성공했으므로 최종 판정은 `완료`다.
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
## 1. 작업 개요
|
||||
|
||||
- 작업 ID: SFR-010-010-FE
|
||||
- 작업명: 하수관망도 지반침하정보 프론트엔드 구현 계획
|
||||
- 상태: 설계
|
||||
- 작업명: 하수관망도 지반침하정보 프론트엔드 유지보수
|
||||
- 상태: 완료
|
||||
- 작성일: 2026-06-02
|
||||
- 담당 역할: FE
|
||||
- 관련 역할: DE, BE, QA
|
||||
@@ -27,9 +27,10 @@
|
||||
|
||||
## 1-2. 최신 해석 메모
|
||||
|
||||
- 현재 `apps/sn`에는 `지반침하정보` 실제 기능 구현이 없고, HTML 샘플 수준 흔적만 확인됐다.
|
||||
- 목록 팝업은 `노후관로` popup 패턴을, 우측 상세 패널은 `FeatureClickEventPopup`의 세로 패널 패턴을 우선 재사용한다.
|
||||
- 좌표 보정과 반경 내 관로 탐색은 프론트에서 계산하지 않고 `sewerage-service` 응답 기준으로만 처리한다.
|
||||
- 현재 `apps/sn`에는 레이어 on/off, 목록 팝업, 지도 포인트 선택, 사고별 우측 상세 탭, 반경 원과 반경 내 관로 표시가 구현되어 있다.
|
||||
- 이번 작업은 신규 화면 구축이 아니라 반경 값 라벨, 트리 심볼, 빈 지도 클릭 선택 해제, 목록 사고번호 첫 열을 보완하는 현행 유지보수다.
|
||||
- 목록 팝업은 `SinkholePopupPage`, 우측 상세와 선택 오버레이는 `map/page.tsx`의 기존 상태 흐름을 유지해 확장한다.
|
||||
- 목록의 `acdntNo`와 상세/반경의 `radiusMeters`가 이미 제공되므로 백엔드 계약은 변경하지 않는다.
|
||||
|
||||
## 2. 작업 목적
|
||||
|
||||
@@ -92,10 +93,10 @@
|
||||
| 기능 | Method | URL | 요청 | 응답 | 확인 상태 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 지도 포인트 조회 | `GET` | `/api/v1/sinkholes/geometry` | 기간/행정구역/limit | GeoJSON `FeatureCollection` | 현재 BE 선반영 |
|
||||
| 목록 조회 | `GET` | `/api/v1/sinkholes` | 기간, 시도, 시군구, 복구상태, 좌표보정상태, page, size | `items[]`, `total` | BE 신규 필요 |
|
||||
| 상세 조회 | `GET` | `/api/v1/sinkholes/{acdntNo}` | 사고번호 | 상세 DTO | BE 신규 필요 |
|
||||
| 반경 내 관로 조회 | `GET` | `/api/v1/sinkholes/{acdntNo}/nearbyPipes` | 사고번호, 반경 | 관로 목록 | BE 신규 필요 |
|
||||
| 동기화 | `POST` | `/api/v1/sinkholes/sync` | 관리자 요청 body | 처리 결과 | BE 신규 필요 |
|
||||
| 목록 조회 | `GET` | `/api/v1/sinkholes` | 기간, 시도, 시군구, 복구상태, 좌표보정상태, page, size | `items[]`, `total` | 구현 확인 |
|
||||
| 상세 조회 | `GET` | `/api/v1/sinkholes/{acdntNo}` | 사고번호 | 상세 DTO | 구현 확인 |
|
||||
| 반경 내 관로 조회 | `GET` | `/api/v1/sinkholes/{acdntNo}/nearbyPipes` | 사고번호, 반경 | 관로 목록 | 구현 확인 |
|
||||
| 동기화 | `POST` | `/api/v1/sinkholes/sync` | 관리자 요청 body | 처리 결과 | 구현 확인 |
|
||||
| 다운로드 | `GET` | `/api/v1/sinkholes` | 현재 조회조건 + `includeCoordStatus=false` | FE xlsx 생성 | 목록 재조회 기반 |
|
||||
|
||||
- FE 메모
|
||||
@@ -221,3 +222,45 @@
|
||||
- 후속 결정 후보는 `현행 다중 탭 유지`와 `처리시설 1개 + 처리구역 1개 유형별 singleton`이다.
|
||||
- singleton을 선택하면 동일 유형의 다음 선택은 새 탭을 추가하지 않고 기존 탭의 표시명, feature payload, 활성 시각을 최신 대상으로 교체한다.
|
||||
- 이 정책은 아직 미확정이며 현재 코드 변경 대상이 아니다.
|
||||
|
||||
## 14. 2026-07-14 현행 유지보수 설계 범위
|
||||
|
||||
### 14.1 구현 원칙
|
||||
|
||||
- 현행 지도/우측 탭/API 흐름을 유지하고 네 요구사항에 필요한 최소 분기만 추가한다.
|
||||
- 지도 반경 라벨은 원 상단 Point 피처로 추가해 원·중심점과 같은 생명주기로 관리한다.
|
||||
- 트리 심볼은 기존 파란 지반침하 자산을 사용하고 공통 트리 영향은 기본 숨김인 선택 prop으로 격리한다.
|
||||
- 빈 지도 클릭은 현재 선택 지반침하와 종속 관로 강조만 정리하며 다른 탭과 GIS 편집 상태는 보존한다.
|
||||
- 목록 팝업은 사고번호를 첫 데이터 열로 두고 화면 NO 열은 표시하지 않는다.
|
||||
|
||||
### 14.2 예상 코드 대상
|
||||
|
||||
- `app/(map-layout)/map/page.tsx`
|
||||
- `app/(map-layout)/map/components/sinkhole/SinkholePopupPage.tsx`
|
||||
- `packages/common/business_components/src/ui/map/Gis.js`
|
||||
- `packages/common/business_components/src/ui/map/GisTree.js`
|
||||
- `packages/common/business_components/src/ui/map/GisSelect.js`
|
||||
- `packages/common/business_components/src/ui/map/lib/gis.select.js`
|
||||
|
||||
### 14.3 제외 범위
|
||||
|
||||
- BE API·DTO·Repository·DB 변경
|
||||
- 엑셀 다운로드 열 순서 변경
|
||||
- 기존 탭 최대 개수, 처리시설·처리구역 탭 정책 변경
|
||||
|
||||
### 14.4 완료 기준
|
||||
|
||||
- 구현·정적 검사·빌드 후 로그인 가능한 `/map`에서 반경 라벨, 트리 심볼, 빈 클릭 해제, 목록 첫 열을 수동 확인한다.
|
||||
- 구현 결과는 통합구현기록과 통합검증결과, 2026-07-14 공통 작업기록에 반영했다.
|
||||
- 사용자가 통합 화면에서 반경 라벨, 트리 심볼, 빈 지도 클릭 해제, 사고번호 첫 열 및 화면 NO 열 제거를 모두 확인했으며 최종 완료로 판정했다.
|
||||
|
||||
### 14.5 실제 구현 파일
|
||||
|
||||
- `app/(map-layout)/map/page.tsx`
|
||||
- `app/(map-layout)/map/components/sinkhole/SinkholePopupPage.tsx`
|
||||
- `packages/common/business_components/src/ui/map/Gis.js`
|
||||
- `packages/common/business_components/src/ui/map/GisTree.js`
|
||||
- `packages/common/business_components/src/ui/map/GisSelect.js`
|
||||
- `packages/common/business_components/src/ui/map/lib/gis.select.js`
|
||||
|
||||
- BE API·DTO·Repository·DB는 변경하지 않았다.
|
||||
|
||||
@@ -453,3 +453,40 @@
|
||||
- 처리시설과 처리구역은 현재 피처별 다중 탭으로 동작한다.
|
||||
- 후속 운영 결과에 따라 처리시설 1개와 처리구역 1개를 각각 singleton으로 바꾸는 안을 검토한다.
|
||||
- 정책 확정 전에는 현행 동작을 유지하며 이번 라운드에서 추가 코드 변경은 하지 않았다.
|
||||
|
||||
### 4.53 2026-07-14 반경 라벨·트리 심볼·지도 선택 해제·목록 사고번호 유지보수 설계
|
||||
|
||||
- 현행 구현을 기준으로 유지보수 변경 지점을 확정했다. 이번 단계에서는 FE 코드를 수정하지 않았다.
|
||||
- `map/page.tsx`에는 반경 원 상단 라벨 피처와 빈 지도 클릭 시 현재 지반침하/종속 관로 강조 정리 로직을 추가할 예정이다.
|
||||
- `SinkholePopupPage.tsx` 목록은 사고번호를 첫 데이터 열로 조정하고 화면 NO 열은 표시하지 않을 예정이다.
|
||||
- 공통 `Gis.js`, `GisTree.js`에는 기본값이 현행 숨김인 선택 prop을 추가하고 `sn` 지반침하 루트에서만 기존 파란 심볼을 노출할 예정이다.
|
||||
- 기존 `acdntNo`, `radiusMeters` 계약으로 구현 가능하므로 BE·DB 변경은 없다.
|
||||
- 실제 코드, 검사 및 브라우저 결과는 다음 구현 단계에서 같은 통합 기록의 후속 절로 추가한다.
|
||||
|
||||
### 4.54 2026-07-14 지도 반경 라벨·트리 심볼·빈 클릭 해제·목록 사고번호 구현
|
||||
|
||||
- `map/page.tsx`의 지반침하 선택 source에 반경 원 상단 Point 라벨을 추가했다. 문구는 `반경 {radiusMeters}m`이며 원·중심점과 함께 정리된다.
|
||||
- 지반침하 루트 `iconNm`은 기존 값 우선, 값이 없으면 `SN_SINKHOLE_SPOT_BLUE.png`를 사용하도록 변경했다.
|
||||
- 공통 `GisTree`의 `sinkholeLegendIconVisible` 기본값을 false로 두고 `sn` 지도에서만 true를 전달해 타 앱 노출을 격리했다.
|
||||
- 공통 선택 모듈은 선택 결과가 없을 때 빈 배열을 전달하고, `GisSelect`가 선택적 `onMapBlankClick` 콜백을 호출하도록 확장했다.
|
||||
- `sn` 지도는 GIS 편집 중 빈 클릭을 무시하고 현재 지반침하를 기존 닫기 흐름으로 정리한다. 비활성 지반침하 탭은 활성 탭을 바꾸지 않고 제거하며 종속 관로 부모 링크를 비운다.
|
||||
- `SinkholePopupPage.tsx` 그리드 첫 데이터 열에 `사고번호(acdntNo)`를 배치하고 화면 `NO` 열은 제거했다. 내부 `rowNo`와 다운로드 순번은 유지했다.
|
||||
- 반경 내 관로 GeoServer 조회의 기존 storage nullable 타입 경고는 저장소 이름 확인 후 요청하도록 방어 처리했다.
|
||||
- BE API·DTO·Repository·DB 변경은 없다.
|
||||
- 브라우저·API·빌드 검증은 사용자 요청에 따라 수행하지 않았으며 사용자 검증 대기 상태다.
|
||||
|
||||
### 4.55 2026-07-14 목록 NO 열 제거 후속 반영
|
||||
|
||||
- 사용자 후속 결정에 따라 `HEADLIST`에서 `rowNo/NO` 열을 제거했다.
|
||||
- 사고번호를 첫 화면 열로 유지하고 열 너비를 17로 조정했다.
|
||||
- `SinkholeTableRow.rowNo`, `toTableRows`, `buildDownloadRows`는 다운로드 순번 호환을 위해 유지했다.
|
||||
- BE API·DTO·Repository·DB 변경은 없다.
|
||||
- 브라우저·API·빌드 검증은 사용자 요청에 따라 수행하지 않았으며 사용자 수동 검증 대기 상태다.
|
||||
|
||||
### 4.56 2026-07-14 사용자 통합 검증 완료
|
||||
|
||||
- 사용자가 통합 화면에서 이번 유지보수 요구사항 전체를 직접 검증했고 모두 정상 동작함을 확인했다.
|
||||
- 반경 라벨은 실제 반경 값과 일치하고, 지반침하 레이어 트리 심볼은 의도한 위치에 정상 표시됐다.
|
||||
- 지도 지반침하 선택 후 빈 지도 영역 클릭 시 선택 상세, 반경 원·라벨, 종속 강조가 정상 해제되고 다른 탭과 기본 레이어는 유지됐다.
|
||||
- 지반침하발생현황 목록은 사고번호가 첫 열로 표시되고 화면 `NO` 열은 제거된 상태로 정상 동작했다.
|
||||
- BE API·DTO·Repository·DB 계약 변경 없이 최종 상태를 `완료`로 전환했다.
|
||||
|
||||
@@ -49,7 +49,9 @@ function Gis({
|
||||
searchView: SearchView,
|
||||
detailView: DetailView,
|
||||
onFeatureSelect,
|
||||
onMapBlankClick,
|
||||
canSelectFeature,
|
||||
sinkholeLegendIconVisible = false,
|
||||
setIsSearchModalOpen,
|
||||
onCheckedLayersChange,
|
||||
searchBar: SearchBar,
|
||||
@@ -311,6 +313,7 @@ function Gis({
|
||||
}}
|
||||
imageVisible={imgVisble}
|
||||
layerIconVisible={layerIconVisible}
|
||||
sinkholeLegendIconVisible={sinkholeLegendIconVisible}
|
||||
isPlaying={isPlaying}
|
||||
setIsPlaying={setIsPlaying}
|
||||
targetLayerTypes={targetLayerTypes}
|
||||
@@ -359,7 +362,11 @@ function Gis({
|
||||
<GisZoomLevel zoomGuide={zoomGuide} showZoomScaleInfo={showZoomScaleInfo} />
|
||||
)}
|
||||
<GisMapToggle />
|
||||
<GisSelect onFeatureSelect={handleFeatureSelect} canSelectFeature={canSelectFeature} />
|
||||
<GisSelect
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
onMapBlankClick={onMapBlankClick}
|
||||
canSelectFeature={canSelectFeature}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,15 +3,21 @@ import { GisContext } from "./Gis";
|
||||
import GisApp from "./lib/gis.core";
|
||||
import GisOlSelect from "./lib/gis.select";
|
||||
|
||||
export default React.memo(function GisSelect({ onFeatureSelect, canSelectFeature }) {
|
||||
export default React.memo(function GisSelect({ onFeatureSelect, onMapBlankClick, canSelectFeature }) {
|
||||
const { gisMap } = useContext(GisContext);
|
||||
const onFeatureSelectRef = useRef(onFeatureSelect);
|
||||
// 빈 지도 클릭 콜백도 interaction 재생성 없이 최신 화면 상태를 참조하도록 ref로 보관한다.
|
||||
const onMapBlankClickRef = useRef(onMapBlankClick);
|
||||
const canSelectFeatureRef = useRef(canSelectFeature);
|
||||
|
||||
useEffect(() => {
|
||||
onFeatureSelectRef.current = onFeatureSelect;
|
||||
}, [onFeatureSelect]);
|
||||
|
||||
useEffect(() => {
|
||||
onMapBlankClickRef.current = onMapBlankClick;
|
||||
}, [onMapBlankClick]);
|
||||
|
||||
useEffect(() => {
|
||||
canSelectFeatureRef.current = canSelectFeature;
|
||||
}, [canSelectFeature]);
|
||||
@@ -31,6 +37,8 @@ export default React.memo(function GisSelect({ onFeatureSelect, canSelectFeature
|
||||
return;
|
||||
}
|
||||
onFeatureSelectRef.current?.(features[0]);
|
||||
} else {
|
||||
onMapBlankClickRef.current?.();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ export default React.memo(function GisTree({
|
||||
onProgressChange = () => { },
|
||||
layerSettingView: LayerSettingView = null,
|
||||
layerIconVisible,
|
||||
sinkholeLegendIconVisible = false,
|
||||
inlineLayerSettingView,
|
||||
treeSectionClassName = "",
|
||||
toggleButtonPosition = "center",
|
||||
@@ -561,7 +562,8 @@ export default React.memo(function GisTree({
|
||||
nodeLabel === "지반침하정보";
|
||||
const isFloodHistoryRootTreeNode = nodeLabel === "침수흔적도";
|
||||
const hideTreeLegendIcon =
|
||||
isSinkholeRootTreeNode || isFloodHistoryRootTreeNode;
|
||||
(isSinkholeRootTreeNode && !sinkholeLegendIconVisible) ||
|
||||
isFloodHistoryRootTreeNode;
|
||||
const showLayerActionButton = layerIconVisible?.(node);
|
||||
const showLayerActionIcon = depth > 0 && imageVisible;
|
||||
|
||||
|
||||
@@ -388,7 +388,11 @@ GisOlSelect.prototype = {
|
||||
this.selectInteraction.on('select', function (e) {
|
||||
if (!e.auto) {
|
||||
let features = me.selectInteraction.getFeatures();
|
||||
if(features.getArray().length == 0) return;
|
||||
if(features.getArray().length == 0) {
|
||||
// 기존 소비자는 빈 배열을 무시하고, 필요한 화면만 빈 지도 클릭으로 해석할 수 있게 전달한다.
|
||||
callback([], []);
|
||||
return;
|
||||
}
|
||||
let layers = [];
|
||||
for (let i in features.array_) {
|
||||
let feature = features.array_[0];
|
||||
|
||||
Reference in New Issue
Block a user