From 5efe19b5a3febedb12c549c2307cdb5936b4217c Mon Sep 17 00:00:00 2001 From: hyungi Date: Wed, 17 Jun 2026 15:19:35 +0900 Subject: [PATCH] =?UTF-8?q?fix(docpage):=20=EC=A0=88=EB=B7=B0=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=20=EC=8B=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20'?= =?UTF-8?q?=EB=82=98=EC=99=94=EB=8B=A4=20=EC=82=AC=EB=9D=BC=EC=A7=90'=20?= =?UTF-8?q?=ED=94=8C=EB=9E=98=EC=8B=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 절 보유 문서(예 5180)에서 이미지가 살짝 보였다 빈 절로 바뀌는 2단 플래시 수정: ① sections 로딩 전 useSectionView=false → fallback 풀-문서 뷰어(전체 md_content=이미지)가 잠깐 뜨고 곧 절뷰로 교체 → sectionsLoaded 플래그로 로딩 중엔 skeleton(풀-문서 미표시). ② 절뷰 진입 시 selectedSectionId=null 이면 selectedItem 이 outline[0](표지/front-matter, 이미지 가능)로 잠깐 렌더됐다 effect 가 defaultSelId(첫 본문 Part)로 점프 → selectedItem 조회 키를 (selectedSectionId ?? defaultSelId)로 바꿔 첫 프레임부터 본문 Part 직행. 데이터는 정상(5180 이미지 207개 DB row+파일 실존+key 일치) — 순수 렌더 전환 플래시였음. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/src/routes/documents/[id]/+page.svelte | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/documents/[id]/+page.svelte b/frontend/src/routes/documents/[id]/+page.svelte index e124be1..8410728 100644 --- a/frontend/src/routes/documents/[id]/+page.svelte +++ b/frontend/src/routes/documents/[id]/+page.svelte @@ -77,10 +77,14 @@ let treeGroupIndex = $derived(treeGroups ? groupKeyByChunkId(treeGroups) : null); let treeExpanded = $state({}); // key 없으면 접힘(기본 전부 접힘). Svelte5 deep-proxy 반응형. function toggleTreeGroup(key) { treeExpanded[key] = !treeExpanded[key]; } + // sections 로딩 완료 플래그 — 미완 동안 fallback 풀-문서 뷰어를 띄우면, 곧 절뷰로 교체되며 + // 풀-문서 이미지가 '살짝 보였다 사라지는' 플래시가 난다(절 보유 문서). 로딩 중엔 skeleton. + let sectionsLoaded = $state(false); async function loadSections() { const reqId = docId; try { const r = await api(`/documents/${reqId}/sections`); if (reqId === docId) sections = r?.sections ?? []; } catch { if (reqId === docId) sections = []; } + finally { if (reqId === docId) sectionsLoaded = true; } } onMount(async () => { @@ -149,7 +153,9 @@ const gk = idx.get(sel); if (gk) untrack(() => { treeExpanded[gk] = true; }); }); - let selectedItem = $derived(outline.find((it) => it.section.chunk_id === selectedSectionId) ?? outline[0] ?? null); + // selectedSectionId 미설정(초기) 시 defaultSelId(첫 본문 Part)로 바로 해석 — outline[0](표지/front-matter) + // 를 잠깐 렌더했다 effect 가 defaultSelId 로 바꾸는 절뷰 내부 플래시 차단. + let selectedItem = $derived(outline.find((it) => it.section.chunk_id === (selectedSectionId ?? defaultSelId)) ?? outline[0] ?? null); let selectedSection = $derived(selectedItem?.section ?? null); let selIdx = $derived(outline.findIndex((it) => it.section.chunk_id === selectedItem?.section?.chunk_id)); // 절 본문 = 청크 원문(it.bodyText, window 조각 합본) 직접 렌더. 과거 char_start 로 md_content 를 @@ -435,7 +441,10 @@ - {#if useSectionView} + {#if !sectionsLoaded} + + + {:else if useSectionView}