diff --git a/A00_Common/b_page_state.ts b/A00_Common/b_page_state.ts index 683e89ab..4184ce8c 100644 --- a/A00_Common/b_page_state.ts +++ b/A00_Common/b_page_state.ts @@ -130,6 +130,10 @@ export const STATE_REGISTRY = { /* ── ④ 계산 결과 ─────────────────────────────────────────────────────── */ /** 노선·종단 최신 응답. 페이지를 오갈 때 이 값으로 먼저 그린다. */ latest: { bucket: "result", scope: "project", legacy: (p) => `b05:latest:${p}` }, + /** 종횡단 설정값(config 상수 + 프로젝트 임도 종류 + 노선 id). B05·B06 이 같은 값을 + * 받아 쓰므로 화면을 오갈 때마다 다시 묻지 않는다(2026-09-06 호출 정리). + * 노선이 바뀌면 `latest` 와 함께 버린다. */ + "section-context": { bucket: "result", scope: "project" }, /** 횡단 상세 응답 — 예전에는 메모리에만 있어 페이지를 떠나면 사라졌다. */ "section-detail": { bucket: "result", scope: "route" }, /** 서버가 준 규격 횡단 기본값 — 사용자가 만든 값이 아니라 **기억해 둔 서버 값**이다. */ diff --git a/B05_Profile/B05_Profile_Api_Fetch.ts b/B05_Profile/B05_Profile_Api_Fetch.ts index 6666c1d6..bf89201a 100644 --- a/B05_Profile/B05_Profile_Api_Fetch.ts +++ b/B05_Profile/B05_Profile_Api_Fetch.ts @@ -323,4 +323,6 @@ export function writeRouteLatestCache(projectId: string, value: RouteLatestRespo export function clearRouteLatestCache(projectId: string): void { clearState("latest", projectId); + // 설정값에 노선 id 가 실려 있어 함께 버려야 옛 노선을 가리키지 않는다(2026-09-06). + clearState("section-context", projectId); } diff --git a/B05_Profile/B05_Profile_UI_Page.ts b/B05_Profile/B05_Profile_UI_Page.ts index e324fe6b..aeced756 100644 --- a/B05_Profile/B05_Profile_UI_Page.ts +++ b/B05_Profile/B05_Profile_UI_Page.ts @@ -13,8 +13,7 @@ import { } from "../A00_Common/b_workflow_nav"; import { fetchConfirmedSurface, - listSurfaceModels, - type SurfaceModelSummary, + type SurfaceConfirmedResponse, } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; import { fetchLatestRoute, @@ -169,7 +168,10 @@ export async function renderB05Route(root: HTMLElement): Promise { * 선택은 3D·그래프·사이드바가 서로를 갱신하므로, `selectionSyncing` 가드로 재진입을 막아 * 무한 재귀(스택 오버플로우·프리즈)를 방지한다. */ - let confirmedSurface: SurfaceModelSummary | null = null; + /** 확정 지표면 — 모델 id 와 3D 로딩에 필요한 범위만 들고 있는다(2026-09-06 호출 정리). + * 예전에는 목록(`listSurfaceModels`)으로 id 를 찾고 3D 단계에서 `surface/confirmed` + * 를 또 불러 요청이 두 번 나갔다. 확정 응답 하나에 둘 다 들어 있다. */ + let confirmedSurface: SurfaceConfirmedResponse | null = null; let latest: RouteLatestResponse | null = null; let roadWidths = DEFAULT_ROAD_WIDTHS; let currentSectionDetail: SectionDetailResponse | null = null; @@ -685,9 +687,9 @@ export async function renderB05Route(root: HTMLElement): Promise { latest = latestResponse; advanceLoading("확정 지표면 모델을 확인하는 중…"); - // ③ 확정 지표면 모델 목록. - const models = await listSurfaceModels(activeProjectId); - confirmedSurface = models.models.find((model) => model.status === "CONFIRMED") ?? null; + // ③ 확정 지표면 — 모델 id·범위를 한 번에 받는다. + const confirmed = await fetchConfirmedSurface(activeProjectId); + confirmedSurface = confirmed.model_id === null ? null : confirmed; if (!confirmedSurface) { // 새 자료가 올라와 옛 결과가 지워진 상태 — 여기서 보여 줄 게 없다. leaveForDashboard(); @@ -716,16 +718,15 @@ export async function renderB05Route(root: HTMLElement): Promise { const [surface, current] = [confirmedSurface, latest]; if (!surface || !current) return; try { - // 가장자리만 받는다 — 포인트클라우드 전체(수십 MB)는 안 받는다. - const confirmed = await fetchConfirmedSurface(activeProjectId); - if (!confirmed.bounds) throw new Error("지표면 범위 정보를 찾을 수 없습니다."); + // 범위는 ③에서 받아 둔 확정 응답에 이미 들어 있다 — 다시 부르지 않는다. + if (!surface.bounds) throw new Error("지표면 범위 정보를 찾을 수 없습니다."); await viewer.loadSurface( activeProjectId, - surface.id, + surface.model_id as number, current.surface_params.method, current.surface_params.smooth, current.surface_params.contour_interval_m, - toBounds(confirmed.bounds), + toBounds(surface.bounds), ); renderLatest(current); // 지형이 올라온 뒤에야 마커·측점선이 지형 위에 놓인다. if (currentSectionDetail) renderStationLines(currentSectionDetail); diff --git a/B05_Profile/B05_Profile_UI_Page_Actions.ts b/B05_Profile/B05_Profile_UI_Page_Actions.ts index 88ebe46e..f5aac8a3 100644 --- a/B05_Profile/B05_Profile_UI_Page_Actions.ts +++ b/B05_Profile/B05_Profile_UI_Page_Actions.ts @@ -16,7 +16,6 @@ import { showToast, } from "@ui/ui_template_elements"; import { navigateTo } from "../A00_Common/router"; -import type { SurfaceModelSummary } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; import { clearRouteLatestCache, confirmRoute, @@ -46,7 +45,8 @@ function L(key: keyof typeof ui_locales): string { export interface PageActionContext { projectId: string; latest: () => RouteLatestResponse | null; - confirmedSurface: () => SurfaceModelSummary | null; + /** 확정 지표면 응답 — 모델 id 는 `model_id` 다(2026-09-06 호출 정리). */ + confirmedSurface: () => { model_id: number | null } | null; routeReady: () => boolean; viewer: () => ReturnType; panel: () => ReturnType; @@ -77,7 +77,7 @@ export async function solveRouteAction(ctx: PageActionContext): Promise { filter_key: latest.surface_params.source_filter, method: latest.surface_params.method, smooth: latest.surface_params.smooth, - surface_model_id: confirmedSurface.id, + surface_model_id: confirmedSurface.model_id ?? undefined, algorithm: values.algorithm, bp: routePoint(points.bp), ep: routePoint(points.ep), @@ -149,7 +149,7 @@ export async function tempSaveAction(ctx: PageActionContext): Promise { filter_key: latest?.surface_params.source_filter, method: latest?.surface_params.method, smooth: latest?.surface_params.smooth, - surface_model_id: ctx.confirmedSurface()?.id, + surface_model_id: ctx.confirmedSurface()?.model_id ?? undefined, irregular_stations: ctx .bridge() .irregularStations() diff --git a/B06_Section/B06_Section_Api_Fetch.ts b/B06_Section/B06_Section_Api_Fetch.ts index ab2ff622..59d21970 100644 --- a/B06_Section/B06_Section_Api_Fetch.ts +++ b/B06_Section/B06_Section_Api_Fetch.ts @@ -16,6 +16,7 @@ * 기존 호출 코드가 깨지지 않도록 여기서 그대로 다시 내보낸다. * ========================================================================== */ +import { clearState, readState, writeState } from "../A00_Common/b_page_state"; import { API_BASE_URL, API_TIMEOUT_MS } from "@config/config_frontend"; import type { CrossDesign, @@ -58,10 +59,21 @@ async function requestJson(path: string, init: RequestInit): Promise { } /** 최신 확정 경로와 B04 확정값, config 기반 생성 기본값을 조회한다. */ +/** 종횡단 설정값 — ④ 계산 결과라 세션에 담아 두고 화면을 오갈 때마다 다시 묻지 않는다 + * (2026-09-06 호출 정리). 노선이 바뀌면 `clearSectionContextCache` 로 버린다. */ export async function fetchSectionContext(projectId: string): Promise { - return requestJson(`/projects/${projectId}/sections/context`, { - method: "GET", - }); + const cached = readState("section-context", projectId); + if (cached) return cached; + const fresh = await requestJson( + `/projects/${projectId}/sections/context`, + { method: "GET" }, + ); + writeState("section-context", fresh, projectId); + return fresh; +} + +export function clearSectionContextCache(projectId: string): void { + clearState("section-context", projectId); } /** 경로의 종단면 요약을 조회한다. */