From 96196229f4a18816f2496746ad1edcec7fe9cbb5 Mon Sep 17 00:00:00 2001 From: mind-one001 Date: Wed, 16 Sep 2026 11:15:18 +0900 Subject: [PATCH 1/2] fix(sn): align business form limits with standardized database --- .../ctyFldngPrvtnBiz/ui/CtyFldngPrvtnBizFormPage.tsx | 5 +++++ apps/sn/src/features/obsltPpl/ui/ObsltPplFormPage.tsx | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/sn/src/features/ctyFldngPrvtnBiz/ui/CtyFldngPrvtnBizFormPage.tsx b/apps/sn/src/features/ctyFldngPrvtnBiz/ui/CtyFldngPrvtnBizFormPage.tsx index b4db9d898..c49c8c673 100644 --- a/apps/sn/src/features/ctyFldngPrvtnBiz/ui/CtyFldngPrvtnBizFormPage.tsx +++ b/apps/sn/src/features/ctyFldngPrvtnBiz/ui/CtyFldngPrvtnBizFormPage.tsx @@ -379,6 +379,10 @@ export function CtyFldngPrvtnBizFormPage({ id }: { id?: string }) { type, ]: (typeof BASIC_FIELDS)[number]) => { const fieldId = `cty-fldng-basic-${key}`; + // SN 표준 DB 길이와 입력 한계를 맞추되 API 필드명은 유지한다. + const standardMaxLengths: Partial> = { + bizCd: 10, bizNm: 200, drareaNm: 100, drndstNm: 100, sggNm: 40, picNm: 100, + }; const suggestionOptions = key === "dsgnType" ? cityDesignationTypeOptions @@ -401,6 +405,7 @@ export function CtyFldngPrvtnBizFormPage({ id }: { id?: string }) { 10) nextErrors.bizCd = "사업코드는 10자 이하여야 합니다."; + if (form.bizNm.length > 200) nextErrors.bizNm = "사업명은 200자 이하여야 합니다."; if (!form.ctpvNm.trim() || ctpvCd === "0") nextErrors.region = "시도는 필수입니다."; [...NUMBER_KEYS].forEach((key) => { @@ -364,7 +367,7 @@ export function ObsltPplFormPage({ id }: { id?: string }) { className="w-full !text-[1.3rem]" type={field.type === "number" ? "number" : "text"} step={field.type === "number" ? "any" : undefined} - maxLength={DATE_KEYS.has(field.key) ? 10 : undefined} + maxLength={DATE_KEYS.has(field.key) || field.key === "bizCd" ? 10 : field.key === "bizNm" ? 200 : undefined} list={suggestionListId} value={form[field.key]} placeholder={field.placeholder} From a8f2c8f41a014e83a7dafda89a31ef8edea3ac0d Mon Sep 17 00:00:00 2001 From: kiminchan Date: Wed, 16 Sep 2026 17:32:02 +0900 Subject: [PATCH 2/2] =?UTF-8?q?GIS=20=ED=8E=B8=EC=A7=91=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C=20=EA=B4=80=EB=A1=9C=20=EB=AF=B8=EB=A6=AC=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=ED=91=9C=EC=8B=9C=20=EB=B3=B4=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/(map-layout)/map/page.tsx | 48 +++++++++++++++++- .../001-qa-SFR-012-003-하수관로편집-통합검증계획.md | 17 +++++++ .../002-qa-SFR-012-003-하수관로편집-통합검증결과.md | 43 ++++++++++++++++ .../001-fe-SFR-012-003-하수관로편집-작업카드.md | 32 ++++++++++-- .../001-fe-SFR-012-003-하수관로편집-통합구현기록.md | 24 +++++++++ .../sn/tests/sn-structure-extraction.test.cjs | 50 +++++++++++++++++++ 6 files changed, 210 insertions(+), 4 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/page.tsx b/apps/sn/app/(map-layout)/map/page.tsx index c1afa2348..4d22405ba 100644 --- a/apps/sn/app/(map-layout)/map/page.tsx +++ b/apps/sn/app/(map-layout)/map/page.tsx @@ -1444,6 +1444,8 @@ const Map = () => { const flowDirectionFailureAlertedRef = useRef(false); const gisEditPreviewLayerRef = useRef(null); const gisEditPreviewSourceRef = useRef(null); + // 신규 관로마다 한 번만 우측 상세 패널을 피한 가시 영역으로 이동하기 위한 change 식별자다. + const gisEditAutoFocusedAddChangeIdRef = useRef(""); const gisEditPipeSnapSourceRef = useRef(null); const gisEditPipeSnapAbortControllerRef = useRef( null, @@ -2247,12 +2249,18 @@ const Map = () => { if (!map) return null; if (waterRiskAreaLayerRef.current && waterRiskAreaSourceRef.current) { + const layers = map.getLayers?.().getArray?.() ?? []; + if (!layers.includes(waterRiskAreaLayerRef.current)) { + map.addLayer(waterRiskAreaLayerRef.current); + } + waterRiskAreaLayerRef.current.setZIndex?.(9996); return waterRiskAreaSourceRef.current; } + // 통수위험 overlay 초기화는 자신의 stale layer만 정리해 GIS 편집 preview를 보존한다. const layers = map.getLayers?.().getArray?.() ?? []; layers.forEach((existingLayer: any) => { - if (existingLayer?.get?.("id") === GIS_EDIT_PREVIEW_LAYER_ID) { + if (existingLayer?.get?.("id") === WATER_RISK_AREA_LAYER_ID) { map.removeLayer(existingLayer); } }); @@ -5363,6 +5371,7 @@ const Map = () => { if (!layers.includes(gisEditPreviewLayerRef.current)) { map.addLayer(gisEditPreviewLayerRef.current); } + gisEditPreviewLayerRef.current.setVisible?.(true); gisEditPreviewLayerRef.current.setZIndex?.(10001); return gisEditPreviewSourceRef.current; } @@ -6627,6 +6636,43 @@ const Map = () => { mapReady, ]); + // ADD 완료 직후 열린 상세 패널 아래에 신규 선형이 가려지지 않도록 최초 1회 지도 가시 영역에 맞춘다. + useEffect(() => { + if (!mapReady || !gisEdit.isEditMode || !gisEdit.focusedChangeId) return; + + const focusedChange = gisEdit.changes.find( + (change) => change.changeId === gisEdit.focusedChangeId, + ); + if ( + focusedChange?.editType !== "ADD" || + !focusedChange.afterGeometry || + gisEditAutoFocusedAddChangeIdRef.current === focusedChange.changeId + ) { + return; + } + + gisEditAutoFocusedAddChangeIdRef.current = focusedChange.changeId; + const focusTimer = window.setTimeout(() => { + const map = gisMaps.current; + if (!map) return; + ensureGisEditPreviewLayer(); + map.updateSize?.(); + focusGisEditChangeOnMap(focusedChange.changeId); + gisEditPreviewSourceRef.current?.changed?.(); + gisEditPreviewLayerRef.current?.changed?.(); + map.renderSync?.(); + }, 120); + + return () => window.clearTimeout(focusTimer); + }, [ + ensureGisEditPreviewLayer, + focusGisEditChangeOnMap, + gisEdit.changes, + gisEdit.focusedChangeId, + gisEdit.isEditMode, + mapReady, + ]); + const gisEditMaskedPipeFidKey = useMemo(() => { const fids = gisEdit.changes .filter((change) => change.layerId === "tb_ms_swage_ppl") diff --git a/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/001-qa-SFR-012-003-하수관로편집-통합검증계획.md b/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/001-qa-SFR-012-003-하수관로편집-통합검증계획.md index 2fa3712c3..0795d34f1 100644 --- a/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/001-qa-SFR-012-003-하수관로편집-통합검증계획.md +++ b/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/001-qa-SFR-012-003-하수관로편집-통합검증계획.md @@ -239,3 +239,20 @@ 2. 하단 GIS 편집 바와 인라인 편집 세션이 함께 사라지는지 확인한다. 3. 팝업 뷰 상태에서 GIS 편집을 끄면 팝업도 닫히는지 확인한다. 4. GIS 편집을 다시 켜면 기존 변경분이 인라인 세션에 복원되는지 확인한다. + +## 12. 2026-09-16 `ADD` preview 유지 회귀 검증 계획 + +- 정적 검증 + - 통수위험 영역 레이어 초기화가 `SN_GIS_EDIT_PREVIEW`를 제거하지 않는지 확인한다. + - GIS 편집 preview ref/source와 실제 map layer collection의 일치 여부를 확인한다. +- 브라우저 검증 + - 신규 관로 추가 완료 직후 흰색 외곽선, 진한 본선, `신규 추가` 라벨이 보이는지 확인한다. + - 편집 세션에서 `NEW-*`를 선택했을 때 동일 선형이 강조되는지 확인한다. + - 통수위험 선택 상태 생성·변경·해제 전후에 신규 preview가 유지되는지 확인한다. + - 우측 상세정보 패널 개폐와 입력 중에도 preview가 유지되는지 확인한다. + - 저장 성공 후에는 preview가 제거되고 응답 `fid`의 최종본 레이어가 refresh되는지 확인한다. +- 회귀 범위 + - `MOVE`, `VERTEX_EDIT`, `DELETE` preview + - 통수위험 영역·관로·맨홀 overlay + - GIS 편집 종료와 세션 초기화 + - 기존 하수관로 WMS/WFS 레이어 diff --git a/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/002-qa-SFR-012-003-하수관로편집-통합검증결과.md b/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/002-qa-SFR-012-003-하수관로편집-통합검증결과.md index 299a253fc..00b0fe33d 100644 --- a/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/002-qa-SFR-012-003-하수관로편집-통합검증결과.md +++ b/apps/sn/docs/작업검증/SFR-012-003-하수관로편집/002-qa-SFR-012-003-하수관로편집-통합검증결과.md @@ -776,3 +776,46 @@ - TypeScript 또는 이번 변경 코드의 컴파일 오류는 아니다. - `git diff --check`는 줄바꿈 변환 경고 외 whitespace 오류 없이 통과했다. - 실제 지도 화면에서 툴바와 인라인·팝업 세션 라벨이 CMS 응답으로 표시되는지는 로컬 배포 또는 개발 서버 기동 후 수동 확인이 남아 있다. + +### 2.33 2026-09-16 사용자 화면 기반 `ADD` preview 소실 분석 결과 + +- 사용자 화면에서 `추가 / NEW-2` 세션 카드와 우측 하수관로 기본정보 패널은 확인됐으나, 신규 관로의 고대비 preview는 확인되지 않았다. +- 정적 추적으로 `Draw -> EPSG:5179 GeoJSON -> MultiLineString 정규화 -> changes[] -> preview source` 흐름과 `ADD` 스타일 생성은 존재함을 확인했다. +- `ensureWaterRiskAreaLayer()`가 `GIS_EDIT_PREVIEW_LAYER_ID` 레이어를 제거하는 교차 overlay 결함을 확인했다. +- 이 결함은 세션 state를 삭제하지 않으므로 사용자 증상인 “편집 세션에는 표시되지만 지도에는 표시되지 않음”과 구조적으로 일치한다. +- 백엔드 저장 호출 전 발생하는 현상이므로 BE API, DB geometry, GeoServer/GWC는 직접 원인에서 제외했다. +- 판정: 원인 확인, 구현 및 브라우저 회귀검증 대기. + +### 2.34 2026-09-16 overlay 수명주기 보정 자동검증 결과 + +- `node --test tests/sn-structure-extraction.test.cjs` + - 13건 전체 통과 + - 신규 `통수위험 영역 초기화는 GIS 편집 preview를 제거하지 않음` 통과 +- `node scripts/run-refactor-tests.cjs` + - SN 전체 리팩터 회귀 테스트 72건 전체 통과 +- `node apps/sn/node_modules/typescript/bin/tsc -p apps/sn/tsconfig.json --noEmit --incremental false --pretty false --ignoreDeprecations 5.0` + - 오류 없이 통과 +- `node node_modules/next/dist/bin/next build` + - 최적화 컴파일 성공 + - page data 수집 단계에서 기존 `.next/server/pages/_document.js`가 `../chunks/ssr/[turbopack]_runtime.js`를 찾지 못해 종료 + - 이번 변경의 TypeScript 또는 컴파일 오류는 아님 +- 백엔드 `./gradlew.bat test --tests iipbs.sewerage.service.map.SwagePplEditServiceTest` + - `BUILD SUCCESSFUL` +- 실제 브라우저에서 `ADD` 후 preview, 통수위험 상태 전환, 우측 상세 패널 개폐, 저장 성공 흐름은 수동 검증 대기 +- 판정: 코드·타입·전체 리팩터 회귀 통과, 브라우저 통합검증 대기. + +### 2.35 2026-09-16 사용자 재검증 및 2차 가시성 보강 결과 + +- 첫 수정 후 사용자 재검증 화면에서도 `NEW-1` 세션과 상세 패널은 보이지만 신규 관로를 확인하기 어렵다는 결과를 받았다. +- 재분석 결과 + - 첫 교차 레이어 제거 결함은 유효했지만 단독 원인은 아니었다. + - 우측 상세 패널 오픈 후 신규 geometry 자동 focus가 없었다. + - preview layer 재사용 경로에서 visible 복구가 누락돼 있었다. +- 2차 반영 + - preview 재사용 시 `setVisible(true)` + - 신규 `ADD` change별 최초 1회 자동 focus + - map size 갱신, source/layer changed, `renderSync()` 호출 +- `node --test tests/sn-structure-extraction.test.cjs`: 14건 전체 통과 +- TypeScript 검사: 통과 +- 실행 중인 `localhost:3002` 제공 chunk에서 2차 보강 코드 포함 확인 +- 판정: 2차 구현·자동검증 완료, 사용자 동일 화면 재검증 대기. diff --git a/apps/sn/docs/작업계획/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-작업카드.md b/apps/sn/docs/작업계획/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-작업카드.md index 23d1465f3..cb9f05ab7 100644 --- a/apps/sn/docs/작업계획/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-작업카드.md +++ b/apps/sn/docs/작업계획/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-작업카드.md @@ -69,7 +69,7 @@ - 기존 하수관망도 지도 화면에 `GIS편집` 기능을 추가해 사용자가 편집 대상 레이어를 고른 뒤 객체 추가, 이동, 꼭지점편집, 삭제, 저장까지 한 흐름에서 수행할 수 있게 한다. - 프론트는 저장 전까지 편집 변경분을 로컬 세션으로 관리하고, 저장 시 BE가 제공할 편집 저장 API에 맞춰 payload를 전달한다. - 백엔드 개발자가 바로 이어받을 수 있도록 필요한 API, 화면 상태, 예외 흐름을 FE 기준으로 구체화한다. -- 실제 API 구현은 범위 밖이며, 백엔드 공유용 요청 사항은 `작업내용` 문서로 분리해 관리한다. +- 편집 저장 API는 현재 구현되어 있으며 FE가 `POST /api/v1/map/gisEditSessions/save`를 호출한다. 과거 API 요청 문서는 구현 이력으로 유지한다. - 추가 운영 제약: - `GIS편집`, `GIS관리`는 하수관로만 대상으로 제한한다. - 다른 GIS 테이블은 타회사 소유 범위로 보고 FE에서 선택/업로드 대상으로 노출하지 않는다. @@ -129,9 +129,9 @@ | 기능 | Method | URL | 요청 | 응답 | 확인 상태 | | --- | --- | --- | --- | --- | --- | | 기존 레이어 트리 조회 | `GET` | `/api/v1/map/lyrStngs/SN_NETWORK` | 기존 지도 레이어 그룹 | 레이어 트리 데이터 | 기존 사용 중 | -| 편집 가능 레이어 메타 조회 | `GET` | `/api/v1/map/gisEditLayers` | `lyrGrp`, `screenId` | 편집 가능 레이어, geometry 타입, 허용 도구 | BE 신규 필요 | +| 편집 가능 레이어 메타 조회 | `GET` | `/api/v1/map/gisEdit/swagePpl/meta` | `lyrGrp` | 하수관로 편집 대상, 좌표계, 허용 도구 | BE 구현 완료, FE는 현재 레이어 트리 기반 정책 사용 | | 관로 기본정보 조회 | `GET` | `/api/v1/fcltPpl/{fcltMngNo}/{mngInstCd}` | 관로 식별자 | `PplDto` 기본정보 7개 필드 | 기존 사용 중 | -| 편집 저장 | `POST` | `/api/v1/map/gisEditSessions/save` | 레이어 정보, view, `changes[]` | `sessionId`, `editFileId`, `versionNo`, `savedCount` | BE 신규 필요 | +| 편집 저장 | `POST` | `/api/v1/map/gisEditSessions/save` | 레이어 정보, view, `changes[]` | 저장 건수, 결과별 `fid/editSn`, `refreshLayerTypeNames` | FE/BE 구현 완료 | - FE 메모: - 저장 전까지 변경분은 FE 로컬 상태로 보관한다. @@ -292,3 +292,29 @@ - GIS 편집 도구바는 하단 표시 영역 중앙에 고정된다. - 팝업 전환 시 인라인 숨김과 `화면에서 보기` 복귀는 유지된다. - 공통 `Gis.js`는 수정하지 않는다. + +## 14. 2026-09-16 `ADD` preview 소실 유지보수 분석 + +- 작업 모드: `maintenance` +- 사용자 증적: 편집 세션과 우측 상세 패널에는 `NEW-2`가 존재하지만 지도에 신규 선형 preview가 보이지 않는다. +- 코드 확인 결과 `Draw -> registerAddGeometry -> changes[] -> preview 동기화 effect` 흐름은 정상적으로 연결돼 있다. +- 직접 원인은 `ensureWaterRiskAreaLayer()`가 통수위험 영역 레이어 초기화 중 `GIS_EDIT_PREVIEW_LAYER_ID`를 제거하는 잘못된 교차 레이어 정리다. +- preview layer ref/source는 남고 지도 layer collection에서만 빠질 수 있으므로 세션 카드와 상세정보는 유지되면서 지도 선형만 사라지는 현상과 일치한다. +- 다음 구현은 `map/page.tsx`의 레이어 정리 대상 수정과 preview 재부착 방어로 제한한다. +- 백엔드 저장 API, DTO, DB, GeoServer refresh 계약은 변경하지 않는다. + +### 14.1 구현 완료 + +- `ensureWaterRiskAreaLayer()`는 이제 자신의 `WATER_RISK_AREA_LAYER_ID` 중복만 제거한다. +- 기존 통수위험 영역 ref가 map에서 빠졌으면 동일 레이어를 재부착해 overlay 수명주기를 복구한다. +- `SN_GIS_EDIT_PREVIEW`는 통수위험 초기화의 제거 대상에서 제외했다. +- 프론트 전체 리팩터 회귀 테스트 72건과 타입검사, 백엔드 저장 서비스 테스트를 통과했다. +- 실제 브라우저에서 사용자 재현 절차를 수행하는 검증은 남아 있다. + +### 14.2 사용자 재검증 후 2차 보강 + +- 첫 수정 후에도 신규 관로가 보이지 않는 사용자 화면을 확인했다. +- preview 레이어 재사용 경로에 `setVisible(true)`를 추가했다. +- 신규 `ADD` change별 최초 1회 자동 focus를 추가해 우측 상세 패널의 420px 영역을 피한 지도 영역에 geometry를 맞춘다. +- 자동 focus는 기본정보 입력으로 change가 갱신될 때 반복되지 않도록 change ID ref로 제한한다. +- 실행 중인 3002 dev 서버의 제공 chunk에 2차 보강 코드가 반영된 것을 확인했다. diff --git a/apps/sn/docs/작업내용/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-통합구현기록.md b/apps/sn/docs/작업내용/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-통합구현기록.md index e5d422428..dfbb346f5 100644 --- a/apps/sn/docs/작업내용/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-통합구현기록.md +++ b/apps/sn/docs/작업내용/SFR-012-003-하수관로편집/001-fe-SFR-012-003-하수관로편집-통합구현기록.md @@ -496,6 +496,30 @@ - 기존 CMS 호출 구조인 `getCmsData('/api/v1/code/cache/{upCd}')`를 유지한 `getCommonCodes(upCd)` 타입 안전 조회 함수를 추가했다. - 기존 `searchCmmnCd()`도 같은 조회 함수를 사용하도록 정리해 CMS의 배열 응답을 `getData()`의 공통 응답 wrapper로 잘못 해제하던 경로를 제거했다. - `useGisEditOperationLabels()`는 `GISEDITOPRT`를 브라우저 세션당 한 번 조회하고 툴바와 인라인·윈도우 편집 세션이 같은 코드명을 공유한다. + +### 4.33 2026-09-16 신규 관로 preview 소실 현행 분석 + +- 이번 라운드는 구현 전 유지보수 분석이며 코드 변경은 수행하지 않았다. +- 신규 관로는 `Draw` 완료 시 preview source에 들어가고 `changes[]`에는 `MultiLineString`, `EPSG:5179` 기준으로 정상 보관된다. +- `createGisEditPreviewStyle()`은 `ADD` 선형에 흰색 외곽선, 진한 본선, `신규 추가` 라벨을 반환하므로 스타일 부재가 직접 원인은 아니다. +- `ensureWaterRiskAreaLayer()`에 들어간 교차 레이어 정리 코드가 `SN_GIS_EDIT_PREVIEW`를 map에서 제거한다. 세션 state와 layer ref는 유지되므로 변경 카드와 우측 상세는 남고 지도 선형만 사라질 수 있다. +- 다음 구현에서는 통수위험 overlay와 GIS 편집 overlay가 서로의 레이어를 제거하지 않게 수명주기를 분리한다. +- 저장 전 현상이므로 BE API와 DB는 직접 원인이 아니다. + +### 4.34 2026-09-16 기능별 overlay 수명주기 분리 구현 + +- `ensureWaterRiskAreaLayer()`에서 잘못 참조하던 `GIS_EDIT_PREVIEW_LAYER_ID`를 `WATER_RISK_AREA_LAYER_ID`로 교정했다. +- 통수위험 영역 ref/source가 이미 존재할 때 실제 map layer collection 포함 여부를 확인하고, 누락됐으면 기존 레이어를 다시 추가한다. +- 신규 별도 레이어나 상태를 만들지 않고 기존 GIS 편집 preview와 통수위험 overlay의 소유 경계만 바로잡았다. +- `sn-structure-extraction.test.cjs`에 통수위험 초기화 블록이 GIS 편집 preview ID를 참조하지 않고 자체 ID와 재부착 경로를 유지하는 회귀 검사를 추가했다. + +### 4.35 2026-09-16 신규 관로 가시영역 자동 focus 보강 + +- 사용자 재검증에서 첫 수정 후에도 신규 관로가 보이지 않는 현상을 확인해 지도 표시 흐름을 다시 추적했다. +- 기존 preview layer 재사용 시 `visible=true`를 복구한다. +- `gisEditAutoFocusedAddChangeIdRef`로 신규 change별 자동 focus 수행 여부를 기억한다. +- `ADD` change가 처음 생성되면 preview layer를 재확인하고 map size를 갱신한 뒤 우측 상세 패널 여백을 적용해 geometry extent로 이동한다. +- source/layer change와 `renderSync()`를 함께 호출해 상세 패널 레이아웃 변경 뒤 preview를 다시 렌더링한다. - `ADD`, `MOVE`, `VERTEX_EDIT`, `DELETE` 타입과 상태 분기는 유지하고 표시명만 공통코드의 `lwpoCdNm`으로 교체했다. - CMS 조회 실패 시에는 기존 `추가/이동/꼭지점편집/삭제` 문구를 fallback으로 사용해 편집 기능 자체가 중단되지 않게 했다. - `GISEDITTYPE(C/U/D)`는 실제 CMS 응답까지 확인했지만 현재 SN GIS 편집 화면에 직접 표시되는 소비처가 없어 불필요한 조회는 추가하지 않았다. diff --git a/apps/sn/tests/sn-structure-extraction.test.cjs b/apps/sn/tests/sn-structure-extraction.test.cjs index a4f6aa785..bb13c446f 100644 --- a/apps/sn/tests/sn-structure-extraction.test.cjs +++ b/apps/sn/tests/sn-structure-extraction.test.cjs @@ -572,3 +572,53 @@ test("불필요한 HTML 실행 파일은 없고 의도적 공개 barrel은 유 .includes("AuthorityButton"), ); }); + +test("통수위험 영역 초기화는 GIS 편집 preview를 제거하지 않음", () => { + const pageSource = fs.readFileSync( + path.resolve(__dirname, "../app/(map-layout)/map/page.tsx"), + "utf8", + ); + const areaLayerStart = pageSource.indexOf( + "const ensureWaterRiskAreaLayer = useCallback", + ); + const pipeLayerStart = pageSource.indexOf( + "const ensureWaterRiskPipeLayer = useCallback", + areaLayerStart, + ); + assert.ok(areaLayerStart >= 0 && pipeLayerStart > areaLayerStart); + + const areaLayerBlock = pageSource.slice(areaLayerStart, pipeLayerStart); + assert.match(areaLayerBlock, /WATER_RISK_AREA_LAYER_ID/); + assert.doesNotMatch(areaLayerBlock, /GIS_EDIT_PREVIEW_LAYER_ID/); + assert.match( + areaLayerBlock, + /map\.addLayer\(waterRiskAreaLayerRef\.current\)/, + ); +}); + +test("신규 관로 preview는 재표시되고 상세 패널 밖으로 최초 1회 focus", () => { + const pageSource = fs.readFileSync( + path.resolve(__dirname, "../app/(map-layout)/map/page.tsx"), + "utf8", + ); + const previewLayerStart = pageSource.indexOf( + "const ensureGisEditPreviewLayer = useCallback", + ); + const previewLayerEnd = pageSource.indexOf( + "const clearGisEditPreviewLayer = useCallback", + previewLayerStart, + ); + const previewLayerBlock = pageSource.slice( + previewLayerStart, + previewLayerEnd, + ); + assert.match(previewLayerBlock, /setVisible\?\.\(true\)/); + assert.match( + pageSource, + /gisEditAutoFocusedAddChangeIdRef\.current === focusedChange\.changeId/, + ); + assert.match( + pageSource, + /focusGisEditChangeOnMap\(focusedChange\.changeId\)/, + ); +});