fix(B06): 등록표에 extraspan 이 빠져 B06 화면이 통째로 안 뜨던 회귀

캐시·세션 일원화에서 조정창 값 8종을 옮기며 다단 구간값(`extraspan`)을 등록표에
빠뜨렸다. 표에 없는 이름을 꺼내다 예외가 나 페이지 렌더가 통째로 실패했고 화면에는
「준비 중인 페이지입니다」만 떴다(화면 확인으로 발견).

- `extraspan` 을 등록표에 넣음.
- 표에 없는 이름은 이제 **예외 대신 저장 건너뛰기** — 그 값만 안 남고 화면은 선다.
  콘솔에 한 번 알려 표에 줄을 넣게 한다.
- B06 이 키를 만들 때 쓰던 형변환을 없앰 — 앞으로 등록표에 없는 이름은 컴파일에서
  걸린다. 같은 사고가 화면까지 가지 않는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-06 12:21:32 +09:00
co-authored by Claude Opus 5
parent 2b18c1b535
commit 1bfc9ee12c
2 changed files with 23 additions and 6 deletions
+19 -4
View File
@@ -124,6 +124,7 @@ export const STATE_REGISTRY = {
extrawall: { bucket: "draft", scope: "route", legacy: (p, r) => `b06:extrawall:${p}:${r}` },
fordadjust: { bucket: "draft", scope: "route", legacy: (p, r) => `b06:fordadjust:${p}:${r}` },
boxadjust: { bucket: "draft", scope: "route", legacy: (p, r) => `b06:boxadjust:${p}:${r}` },
extraspan: { bucket: "draft", scope: "route", legacy: (p, r) => `b06:extraspan:${p}:${r}` },
/* ── ④ 계산 결과 ─────────────────────────────────────────────────────── */
/** 노선·종단 최신 응답. 페이지를 오갈 때 이 값으로 먼저 그린다. */
@@ -146,8 +147,21 @@ export const STATE_REGISTRY = {
export type StateName = keyof typeof STATE_REGISTRY;
function entryOf(name: StateName): StateEntry {
return STATE_REGISTRY[name] as StateEntry;
const warnedNames = new Set<string>();
/**
* 등록표에서 한 줄을 꺼낸다. 표에 없는 이름이면 **화면을 죽이지 않고** null 을 돌려준다 —
* 그 값만 저장이 안 될 뿐 페이지는 그대로 선다(2026-09-06: `extraspan` 이 빠져 B06 이
* 통째로 안 뜬 적이 있다). 대신 콘솔에 한 번 알려 표에 줄을 넣게 한다.
*/
function entryOf(name: StateName): StateEntry | null {
const entry = STATE_REGISTRY[name] as StateEntry | undefined;
if (entry) return entry;
if (!warnedNames.has(name)) {
warnedNames.add(name);
console.warn(`[page-state] 등록표에 없는 이름입니다 — 저장하지 않습니다: ${name}`);
}
return null;
}
/**
@@ -160,6 +174,7 @@ export function stateKey(
routeId?: number | string | null,
): string | null {
const entry = entryOf(name);
if (!entry) return null;
const suffix = entry.version && entry.version > 1 ? `-v${entry.version}` : "";
const head = `aislo:${entry.bucket}:${name}${suffix}`;
if (entry.scope === "global") return head;
@@ -194,7 +209,7 @@ function migrate(
projectId?: string | null,
routeId?: number | string | null,
): void {
const legacy = entryOf(name).legacy?.(projectId ?? undefined, routeId ?? undefined);
const legacy = entryOf(name)?.legacy?.(projectId ?? undefined, routeId ?? undefined);
if (!legacy || legacy === key) return;
const old = readRaw(legacy);
if (old !== null && readRaw(key) === null) writeRaw(key, old);
@@ -258,7 +273,7 @@ export function clearState(
/** 등록표에서 한 통에 속한 이름만 고른다. */
export function namesInBucket(bucket: StateBucket): StateName[] {
return (Object.keys(STATE_REGISTRY) as StateName[]).filter(
(name) => entryOf(name).bucket === bucket,
(name) => entryOf(name)?.bucket === bucket,
);
}
+4 -2
View File
@@ -1,6 +1,6 @@
import { CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend";
import { leaveForDashboard } from "../A00_Common/b_missing_data_guard";
import { stateKey, type StateName } from "../A00_Common/b_page_state";
import { stateKey } from "../A00_Common/b_page_state";
import { navigateTo } from "../A00_Common/router";
import { createButton, createInputField, showToast } from "@ui/ui_template_elements";
import { createWorkflowLayout } from "@ui/ui_template_workflow_layout";
@@ -401,7 +401,9 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise<void> {
const stationControls = createStationControls({
// 키는 등록표(`b_page_state`)가 만든다 — 이름만 넘기면 통·범위·옛 키 이관이 따라온다.
sessionKey: (kind) => stateKey(kind as StateName, projectId, currentRouteId),
// 형변환을 두지 않는다: 등록표에 없는 이름을 쓰면 **컴파일에서** 걸린다
// (2026-09-06 `extraspan` 누락으로 B06 이 안 뜬 뒤 막음).
sessionKey: (kind) => stateKey(kind, projectId, currentRouteId),
refreshCard: (chainageM) => sectionView.refreshCard(chainageM),
detail: () => sectionDetail,
crossHalfWidth,