From 5df79d2dff6ddd89a223ec07df21e20d8c722e1c Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 4 Sep 2026 12:50:17 +0900 Subject: [PATCH] =?UTF-8?q?fix(B05):=20=EC=A0=80=EC=9E=A5=20=EC=A0=84=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EB=AC=BC=20[=EC=82=AD=EC=A0=9C]=20=EB=AC=B4?= =?UTF-8?q?=EB=8F=99=EC=9E=91=20=E2=80=94=20=EC=A1=B0=EA=B0=81=EC=9D=B4=20?= =?UTF-8?q?=EC=98=9B=20=EB=B0=B0=EC=97=B4=EC=9D=84=20=EA=B3=A0=EC=B9=98?= =?UTF-8?q?=EB=8D=98=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 증상: 방금 [추가]한(아직 저장 전) 항목을 목록에서 고른 뒤 [삭제]를 눌러도 목록에 그대로 남음. 저장 뒤에는 정상. 원인: 700줄 분리(2026-09-04)로 `_Commit`·`_Events` 조각이 `structures` 배열을 **참조로** 받아 두는데, `setStructures()` 가 `structures = next.map(...)` 로 배열을 갈아끼움. 화면 목록은 새 배열로 그려지고 조각은 옛 배열을 splice 해 사라지지 않았음. 같은 이유로 `renderOptionFields()` 의 `optionInputs = []` 도 조각이 옛 칸 목록을 읽을 수 있어 저장값이 어긋날 자리였음. 조치: 두 배열 모두 **자체를 바꾸지 않고 내용만 갈아끼움**(`splice` · `length = 0`)하고 `const` 로 못박아 다시 갈아끼우지 못하게 함. 데이터 흐름은 그대로 — 삭제는 캐시만 바꾸고, 영구저장은 [저장]에서만 나감(5장 데이터 3층). 검증(시험 프로젝트 8cd2e635, 공용 브라우저 실조작): - 저장 전: [추가] → 목록 12건 → 고르고 [삭제] → **11건으로 사라짐**, 서버는 revision 4·0건 그대로(쓰기 없음), 세션 캐시도 빈 목록 - 저장 경로: [추가] → [저장] revision 5·1건 → [삭제](서버 5·1건 유지) → [저장] revision 6·0건 - 저장값: 옹벽 옵션 `height_m 2.5 · length_m 10 · before_m 5 · after_m 5`, 기준 5+0(100m) → 구간 95~105m 정상 - 시험 프로젝트는 구조물 0건으로 되돌려 둠. `tsc --noEmit` 통과, tmp/tests 378 passed Co-Authored-By: Claude Opus 5 (1M context) --- B05_Profile/B05_Profile_UI_Structures_Panel.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel.ts b/B05_Profile/B05_Profile_UI_Structures_Panel.ts index fabeafe0..3516d813 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel.ts @@ -158,7 +158,8 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu } let types: StructureType[] = []; - let structures: StructureInstance[] = []; + // 배열 자체를 바꾸지 않는다(조각들이 참조로 들고 있음) — 내용만 갈아끼운다. + const structures: StructureInstance[] = []; let pipeFacilities: PipeFacilityItem[] = []; let editingId: string | null = null; /** 목록에서 고른 계곡 통과 시설(누가거리 키). 삭제 버튼이 이쪽으로 동작한다. */ @@ -171,7 +172,7 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu * 순간(또는 폼에 위치를 실어 연 순간)에만 true. 옵션 활성화 시점도 임시 배치와 * 같은 이 순간이다(2026-08-18 사용자 지시). */ let positionConfirmed = false; - let optionInputs: Array<{ + const optionInputs: Array<{ key: string; required: boolean; input: HTMLInputElement | HTMLSelectElement; @@ -290,7 +291,9 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu * 몫이라 그리지 않는다(B05 = 유무·종류·위치 단계, 2026-08-17 사용자 확정). */ function renderOptionFields(values: Record = {}): void { const type = currentType(); - optionInputs = []; + // 여기도 배열을 갈아끼우지 않는다 — 조각들이 참조로 받아 두므로 새 배열로 + // 바꾸면 옛 칸 목록을 읽어 저장값이 어긋난다(2026-09-04). + optionInputs.length = 0; optionRow.replaceChildren(); // 서브폼이 담당하는 타입(계곡 통과 시설·독립 기슭막이)은 여기서 그리지 않는다. const visible = type && !facilityFormKind(type) ? type.options.filter(isB05Option) : []; @@ -537,7 +540,10 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu setStructures(next) { // 서버 정본 주입 — onChange를 울리지 않는다. 울리면 Page가 다시 저장을 걸어 // 저장→재조회→주입→저장의 무한 고리가 된다(변경 알림은 사용자 조작에서만). - structures = next.map((entry) => ({ ...entry })); + // 배열을 **갈아끼우지 않고 안을 채운다** — 저장·배선 조각(`_Commit`·`_Events`)이 + // 이 배열을 참조로 받아 두므로, 새 배열로 바꾸면 그쪽이 옛 배열을 고쳐 + // 목록에 반영되지 않는다(2026-09-04 실측: 저장 전 항목 [삭제]가 안 먹던 원인). + structures.splice(0, structures.length, ...next.map((entry) => ({ ...entry }))); // 실시간 반영으로 되돌아온 목록이면 폼을 닫지 않는다 — 고치던 칸이 사라진다. if (applyingLive) renderList(); else {