From 27f3f085de0ab6ea42f31f66251e6f948840e29d Mon Sep 17 00:00:00 2001 From: seo Date: Sun, 12 Jul 2026 06:35:35 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B0=A8=EB=9F=89=20=EB=AA=A8=EB=8B=88?= =?UTF-8?q?=ED=84=B0=20=EB=B3=B5=EA=B7=80=20=EB=A7=81=ED=81=AC=20=EA=B0=99?= =?UTF-8?q?=EC=9D=80=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- monitor.php | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 40c1112..e1391b9 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저 헤더 우측 컨트롤은 WakeLock, 언어, 테마 순서입니다. WakeLock은 Screen Wake Lock API를 지원하는 브라우저에서 화면 꺼짐을 방지하며, 활성 상태는 초록색 아이콘 버튼으로 표시합니다. 사용자의 선택은 `localStorage`에 저장하고, 새로고침이나 탭 복귀 후에는 브라우저 정책에 맞춰 다시 획득을 시도합니다. -좌측 상단 복귀 링크는 이전 페이지가 `chaegeon.com` 계열이면 해당 URL을 사용합니다. `chaegeon.com`이면 채건닷컴, `/blog`이면 블로그처럼 알려진 경로명을 우선 표시하고, 같은 출처의 다른 이전 페이지는 문서 title을 읽어 표시합니다. 직접 접속이나 이전 페이지 확인이 어려운 경우에는 `https://chaegeon.com/`를 기본 목적지로 사용합니다. `seo.chaegeon.com`의 상대 경로로 이동하지 않도록 절대 URL을 기본값으로 둡니다. +좌측 상단 복귀 링크는 이전 페이지가 `chaegeon.com` 계열이면 해당 URL을 사용합니다. `chaegeon.com`이면 채건닷컴, `/blog`이면 블로그처럼 알려진 경로명을 우선 표시하고, 같은 출처의 다른 이전 페이지는 문서 title을 읽어 표시합니다. 쿼리만 다른 같은 차량 모니터 화면은 건너뛰고, 직접 접속이나 이전 페이지 확인이 어려운 경우에는 `https://chaegeon.com/`를 기본 목적지로 사용합니다. `seo.chaegeon.com`의 상대 경로로 이동하지 않도록 절대 URL을 기본값으로 둡니다. ## 구성 diff --git a/monitor.php b/monitor.php index 60e0f4b..7f7ab65 100644 --- a/monitor.php +++ b/monitor.php @@ -2338,6 +2338,7 @@ $carMonitorSession = car_monitor_launcher_session(); let calibrationModalOpen = false; let calibrationSaveInFlight = false; const FALLBACK_BACK_TARGET = 'https://chaegeon.com/'; + const BACK_HISTORY_KEY = 'chaegeon.backHistory.v1'; const BACK_LINK_PATH_TITLE = { '/': '채건닷컴', '/home': '홈', @@ -2405,6 +2406,38 @@ $carMonitorSession = car_monitor_launcher_session(); return BACK_LINK_PATH_TITLE[parsed.pathname.replace(/\/$/, '') || '/'] || cleanPageTitle(parsed.pathname.split('/').filter(Boolean).pop() || ''); } + function backPageKey(url) { + const parsed = sameSiteUrl(url); + if (!parsed) return ''; + const pathname = parsed.pathname.replace(/\/+$/, '') || '/'; + return `${parsed.hostname.replace(/^www\./, '')}${pathname}`; + } + + function rememberBackHistory() { + try { + const current = new URL(location.href); + const list = JSON.parse(sessionStorage.getItem(BACK_HISTORY_KEY) || '[]') + .filter(item => item && item.href && item.key); + const entry = { href: current.href, key: backPageKey(current.href), time: Date.now() }; + const last = list[list.length - 1]; + const next = last && last.href === entry.href ? list : [...list, entry]; + sessionStorage.setItem(BACK_HISTORY_KEY, JSON.stringify(next.slice(-20))); + } catch (e) {} + } + + function storedBackTarget(currentKey) { + try { + const list = JSON.parse(sessionStorage.getItem(BACK_HISTORY_KEY) || '[]'); + for (let i = list.length - 1; i >= 0; i--) { + const item = list[i]; + if (item?.href && item.key && item.key !== currentKey && sameSiteUrl(item.href)) { + return item.href; + } + } + } catch (e) {} + return ''; + } + function updateBackLinkLabel(label) { const link = document.getElementById('blogBackLink'); if (!link) return; @@ -2414,13 +2447,17 @@ $carMonitorSession = car_monitor_launcher_session(); async function resolveBackTarget() { const ref = sameSiteUrl(document.referrer || ''); const current = new URL(location.href); - const target = ref && ref.href !== current.href ? ref.href : FALLBACK_BACK_TARGET; + const currentKey = backPageKey(current.href); + rememberBackHistory(); + const refIsDifferentPage = ref && backPageKey(ref.href) !== currentKey; + const target = refIsDifferentPage ? ref.href : (storedBackTarget(currentKey) || FALLBACK_BACK_TARGET); const knownLabel = titleFromBackUrl(target); let label = knownLabel; + const targetUrl = sameSiteUrl(target); - if (!knownLabel && ref && ref.origin === location.origin) { + if (!knownLabel && targetUrl && targetUrl.origin === location.origin) { try { - const res = await fetch(ref.href, { credentials: 'same-origin', cache: 'force-cache' }); + const res = await fetch(targetUrl.href, { credentials: 'same-origin', cache: 'force-cache' }); if (res.ok) { const html = await res.text(); const doc = new DOMParser().parseFromString(html, 'text/html');