From cd61f06ba5f2a916d59b94f4c134f7c9367f18fa Mon Sep 17 00:00:00 2001 From: AWESOMEDEV Date: Thu, 23 Jul 2026 12:35:31 +0900 Subject: [PATCH] =?UTF-8?q?refactor(docs):=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EB=B7=B0=EC=96=B4=20iframe=20=EC=A0=9C=EA=B1=B0,=20Shadow=20DO?= =?UTF-8?q?M=20=EB=84=A4=EC=9D=B4=ED=8B=B0=EB=B8=8C=20=EB=A0=8C=EB=8D=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iframe의 이중 스크롤 문제를 없애고, 문서 HTML/스타일을 Shadow DOM에 격리해 렌더한다. 문서 HTML(public/docs/*.html)은 한 글자도 안 고치고 재사용. --- frontend/src/pages/DocViewerPage.jsx | 132 +++++++++++++++++++-------- frontend/src/styles/global.css | 51 +++++++++++ 2 files changed, 144 insertions(+), 39 deletions(-) diff --git a/frontend/src/pages/DocViewerPage.jsx b/frontend/src/pages/DocViewerPage.jsx index ac50e55..291db63 100644 --- a/frontend/src/pages/DocViewerPage.jsx +++ b/frontend/src/pages/DocViewerPage.jsx @@ -1,32 +1,99 @@ -// 이 파일이 하는 일: 문서 하나를 iframe으로 크게 띄우는 뷰어. -// 주소의 :slug로 문서를 찾아 filePath의 HTML을 그대로 보여준다. -import { useEffect, useState } from 'react'; +// 이 파일이 하는 일: 학습 문서 하나를 iframe 없이 React 안에서 네이티브로 렌더한다. +// +// ⚠️ 왜 iframe을 걷어냈나 — iframe은 "페이지 속 또 다른 완성된 페이지"라, 스크롤이 이중으로 +// 생기고(바깥+안쪽), 모바일에선 문서가 좁은 창에 갇혔다. 앱과 완전히 분리된 세계라 로고도 +// 두 번 나왔다. 그렇다고 문서 HTML을 앱 화면에 그냥 부어 넣으면, 문서마다 다른 226줄짜리 +// 자체 스타일(.wrap, .callout …)이 앱 CSS와 뒤엉켜 서로를 망가뜨린다. +// +// 해법: Shadow DOM. 문서의 HTML과 자체
${bodyInner}
`); + }) + .catch(() => { + if (!cancelled) setError(true); + }); + return () => { + cancelled = true; + }; + }, [doc]); + + // 학습 포인트: 몰입 화면에선 ESC로 빠져나가는 게 관례다. 키보드만 쓰는 사용자를 위한 배려. + useEffect(() => { + const onKey = (e) => { + if (e.key === 'Escape') navigate('/docs'); + }; + window.addEventListener('keydown', onKey); + return () => window.removeEventListener('keydown', onKey); + }, [navigate]); + + if (error) { return (

문서를 찾을 수 없어요.

@@ -37,38 +104,25 @@ export default function DocViewerPage() { ); } - if (!doc) { + if (!doc || !shadowHtml) { return

불러오는 중...

; } return ( -
-
- {/* whiteSpace: nowrap — 모바일에서 버튼 글자가 "뒤로가/기"로 꺾이는 것 방지 */} - -

{doc.title}

+ {doc.title} +
+ {/* 문서 본문 — 남은 세로 공간을 채우고 여기'만' 스크롤한다(이중 스크롤 없음). */} +
+
- {/* 학습 포인트: 기존 HTML 자산 재사용 — - 학습 문서들은 이미 완성된 정적 HTML(public/docs/*.html)이다. - React로 다시 만들 필요 없이 iframe으로 "액자에 넣듯" 보여주면 - 문서의 자체 스타일도 그대로 살고 앱 CSS와 충돌하지도 않는다. - ⚠️ ?v= 캐시 버스터: 예전에 잘못된 보안헤더(X-Frame-Options:DENY)가 붙은 문서가 - 브라우저 캐시에 박혀 새로고침으로도 안 지워지는 문제가 있었다. 주소 뒤에 버전을 - 붙이면 브라우저가 "다른 파일"로 보고 새로 받아온다(캐시 키가 달라짐). 헤더가 또 - 바뀌면 이 숫자만 올리면 된다. */} -