diff --git a/apps/sn/docs/작업내용/FE-MAP-레이어트리스크롤유지/001-fe-FE-MAP-레이어트리스크롤유지-구현기록.md b/apps/sn/docs/작업내용/FE-MAP-레이어트리스크롤유지/001-fe-FE-MAP-레이어트리스크롤유지-구현기록.md new file mode 100644 index 000000000..3183a96e7 --- /dev/null +++ b/apps/sn/docs/작업내용/FE-MAP-레이어트리스크롤유지/001-fe-FE-MAP-레이어트리스크롤유지-구현기록.md @@ -0,0 +1,32 @@ +# 시설물 레이어 트리 스크롤 유지 구현기록 + +## 작업 정보 + +- 작업일: 2026-07-20 +- 모드: `quick_vibe` +- 범위: 프론트엔드 + +## 요구사항 및 원인 + +- 시설물 레이어 트리 하단의 체크박스를 변경해도 세로 스크롤이 현재 위치를 유지해야 한다. +- 기존 `GisTree`는 체크 변경 후 부모 지도 화면과 하단 레이어 설정영역이 다시 렌더되지만, 스크롤 요소의 `scrollTop`을 별도로 보존하지 않아 위치가 초기화될 수 있었다. + +## 변경 내용 + +- `GisTree`의 실제 세로 스크롤 요소를 ref로 추적한다. +- 스크롤 이벤트에서 현재 `scrollTop`을 보관하고, 렌더 완료 직후 유효 범위 안에서 같은 위치를 복원한다. +- API, DB, 백엔드 계약은 변경하지 않았다. + +## 수정 파일 + +- `packages/common/business_components/src/ui/map/GisTree.js` + +## 검증 + +- 공통 비즈니스 컴포넌트 로컬 `tsup` ESM/DTS 빌드 성공 +- `git diff --check` 성공 +- 로컬 `/map`은 인증되지 않은 브라우저 세션에서 로그인 화면으로 전환되어 실제 레이어 체크 검증은 후속 수동 확인으로 남겼다. + +## 롤백 + +- `GisTree.js`의 스크롤 ref, 스크롤 이벤트 핸들러, `useLayoutEffect` 복원 로직을 제거한다. diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index de04cb91b..e58d66cbf 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -5,7 +5,7 @@ import { DndProvider, } from "@minoru/react-dnd-treeview"; import { ChevronLeft, ChevronRight } from "lucide-react"; -import React, { useEffect, useState, useRef, useCallback } from "react"; +import React, { useEffect, useLayoutEffect, useState, useRef, useCallback } from "react"; import { useGisContext } from "./Gis"; import GisApp from "./lib/gis.core"; @@ -66,6 +66,29 @@ export default React.memo(function GisTree({ const targetNodesRef = useRef([]); const isPausedRef = useRef(false); const timeoutIdRef = useRef(null); + // 체크 후 부모 설정영역까지 다시 렌더되어도 사용자가 보던 트리 위치를 유지한다. + const layerPanelScrollRef = useRef(null); + const layerPanelScrollTopRef = useRef(0); + + const handleLayerPanelScroll = useCallback((event) => { + layerPanelScrollTopRef.current = event.currentTarget.scrollTop; + }, []); + + useLayoutEffect(() => { + const scrollContainer = layerPanelScrollRef.current; + if (!scrollContainer) return; + + const maxScrollTop = Math.max( + 0, + scrollContainer.scrollHeight - scrollContainer.clientHeight + ); + const nextScrollTop = Math.min(layerPanelScrollTopRef.current, maxScrollTop); + + if (scrollContainer.scrollTop !== nextScrollTop) { + scrollContainer.scrollTop = nextScrollTop; + } + layerPanelScrollTopRef.current = nextScrollTop; + }); // ===================== // 유틸리티: 트리 탐색 관련 함수들 @@ -533,6 +556,8 @@ export default React.memo(function GisTree({ }`} >