diff --git a/docs/map.md b/docs/map.md index bb998f6..2ded824 100644 --- a/docs/map.md +++ b/docs/map.md @@ -10,7 +10,7 @@ | layouts/post.vue | 개별 게시물 — `default`와 동일, 오른쪽 컬럼 전체 높이 구분선 | | layouts/admin.vue | 관리자 전체, 320px 밝은 Ghost형 사이드바(대시보드 `/admin` 활성 링크·사이트 보기 새 창·콘텐츠 구간 여백), 우측 전체 높이 캔버스, 멤버 수 표시, 하단 사용자 드롭다운·설정, `내 프로필` 멤버 편집 이동, 게시글 `+` 새 글 진입, 글 작성/수정 화면의 전체 화면 편집 모드와 문서 스크롤 잠금, **`/admin/settings`에서는 사이드바 숨김·본문 전체 화면·설정용 문서 스크롤 잠금** | | layouts/page.vue | 고정 페이지 전체 화면 | -| app.vue | 공개 사이트 설정 기반 파비콘 head 링크, 페이지 제목이 있으면 제목만 쓰고 없으면 사이트 이름을 쓰는 title template, 브랜드 컬러 `--site-accent` 적용, 새 화면 렌더 후 해제되는 불투명 전역 이동 로딩 오버레이 | +| app.vue | 공개 사이트 설정 기반 파비콘 head 링크, 페이지 제목이 있으면 제목만 쓰고 없으면 사이트 이름을 쓰는 title template, 브랜드 컬러 `--site-accent` 적용, 새 화면 렌더·BFCache 복귀 후 해제되는 불투명 전역 이동 로딩 오버레이 | | error.vue | 공개 404/서버 오류 화면, 홈 이동 버튼 | ## Composables diff --git a/docs/spec.md b/docs/spec.md index e38d2c6..696302c 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -177,6 +177,7 @@ - `/posts` - 게시물 전체 목록. 사이트 설정 POST 설정의 `postListPageSize` 값 기준으로 서버 페이지네이션을 사용하며, 페이지 번호가 바뀌면 화면을 상단으로 이동한다. 하단 페이지네이션은 이전·다음 화살표와 페이지 번호 직접 이동을 함께 제공하며, 전체 페이지가 많으면 첫 페이지·끝 페이지·현재 페이지 주변 번호를 유지하고 중간 구간은 `...`으로 접는다. 현재 페이지는 브랜드 포인트 배경색으로 표시한다. 같은 path에서 `page` query만 바뀌는 이동은 Nuxt 페이지 컴포넌트가 재사용될 수 있으므로 전역 전환 베일을 켜지 않는다. - `/post/:slug` - 개별 게시물 상세. 직접 접속·SSR과 클라이언트 내부 이동 모두 본문을 먼저 조회한 뒤 완성된 상세 화면으로 진입한다. 이동 시작 시 불투명 전환 베일을 즉시 표시하고, `page:finish` 이후 베일 뒤에서 스크롤을 최상단으로 맞춘 뒤 베일을 해제한다. 뒤로가기는 저장 스크롤 위치를 베일 뒤에서 복원한다. `history.scrollRestoration`은 `manual`로 두어 브라우저 자동 복원과 충돌하지 않게 한다. 이전·다음 글은 상세 API 응답의 `previous`·`next`로 표시하며, `previous`는 더 과거 글, `next`는 더 최신 글을 뜻한다. - `/` - 홈. 클라이언트 복귀 시에도 주요 데이터를 블로킹 조회한 뒤 전환한다. +- 공개 전환 베일은 Nuxt `page:finish`뿐 아니라 브라우저 `pagehide`/`pageshow` BFCache 이벤트에서도 해제해, HTML 문서 페이지 이동 뒤 뒤로가기로 복원된 화면에 로딩 오버레이가 남지 않게 한다. - `/sitemap.xml` - 공개 sitemap XML. 홈, 게시물 목록, 태그 목록, 공개 발행글, 공개 고정 페이지, 글이 있는 태그 URL을 요청 시점 DB 기준으로 자동 생성한다. 멤버십·비공개·초안·예약 대기 글과 `noindex` 글은 포함하지 않는다. - `/robots.txt` - 크롤러 안내 파일. 관리자·API·회원 설정·로그인 경로를 제외하고 `Sitemap: /sitemap.xml` 절대 URL을 제공한다. - `/rss.xml`, `/feed.xml`, `/rss` - 최근 공개 발행글 RSS 2.0 피드. 멤버십·비공개·초안·예약 대기 글은 포함하지 않으며, 최신순 최대 50개를 XML로 응답한다. 게시물에 대표 이미지 또는 OG 이미지가 있으면 절대 URL로 변환해 `media:thumbnail`과 `media:content medium="image"`를 함께 포함한다. diff --git a/docs/update.md b/docs/update.md index ac2bb7f..a1d6a5b 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,10 @@ # 업데이트 이력 +## v1.11.60 + +- 브라우저 BFCache에 들어가거나 뒤로가기로 복원되는 공개 문서에서 전환 베일 상태를 즉시 정리하도록 수정. +- HTML 문서 페이지 이동 후 뒤로가기로 블로그 화면에 돌아올 때 로딩 오버레이가 남는 회귀를 방지. + ## v1.11.59 - 사용자 iframe 렌더링에서 `allowfullscreen`을 DOM 속성으로 중복 출력하지 않고 `allow`의 `fullscreen` 권한으로 정규화해 브라우저 경고를 줄임. diff --git a/package-lock.json b/package-lock.json index a2c0cec..c5fbd98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.11.59", + "version": "1.11.60", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.11.59", + "version": "1.11.60", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index 334fa46..e9b59ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.11.59", + "version": "1.11.60", "private": true, "type": "module", "imports": { diff --git a/plugins/site-page-transition.client.js b/plugins/site-page-transition.client.js index b9ad5c3..d6d5daa 100644 --- a/plugins/site-page-transition.client.js +++ b/plugins/site-page-transition.client.js @@ -42,6 +42,19 @@ export default defineNuxtPlugin((nuxtApp) => { document.documentElement.classList.remove(SITE_ROUTE_VEIL_CLASS) } + /** + * BFCache에 들어가거나 BFCache에서 돌아온 문서의 전환 베일을 정리한다. + * @param {PageTransitionEvent} event - 페이지 전환 이벤트 + * @returns {void} + */ + const handlePageCacheTransition = (event) => { + if (!event.persisted) { + return + } + + hideRouteVeil() + } + /** * 내부 링크 클릭이 라우터보다 먼저 베일을 덮도록 한다. * @param {MouseEvent} event - 클릭 이벤트 @@ -131,6 +144,8 @@ export default defineNuxtPlugin((nuxtApp) => { } document.addEventListener('click', handleInternalLinkCapture, true) + window.addEventListener('pagehide', handlePageCacheTransition) + window.addEventListener('pageshow', handlePageCacheTransition) router.beforeEach((to, from) => { if (!shouldStartSiteRouteVeilNavigation(to, from)) {