Files
2nd/10_Wiki/Topics/AI_and_ML/Soft Navigation.md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

6.0 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-soft-navigation Soft Navigation 10_Wiki/Topics verified self
SPA Navigation
Client-side Routing
Soft Nav
none A 0.9 applied
web-performance
web-vitals
spa
navigation
history-api
2026-05-10 pending
language framework
JavaScript React/Next.js/Remix

Soft Navigation

매 한 줄

"매 page 의 reload 없이 매 URL + DOM 의 swap". Soft navigation 매 SPA pattern, History API (pushState) + view transitions 매 modern impl. 매 2025+ 의 Web Vitals 매 soft-nav LCP/INP 의 attribute (Soft Navigations API), 매 Chrome 138+ stable.

매 핵심

매 hard vs soft 의 line

  • Hard nav: 매 full document load, 매 unload/load, fresh JS context.
  • Soft nav: 매 client-side router. URL 매 history.pushState, DOM 매 partial swap. 매 same JS context.
  • CWV impact: 매 v1 vitals 매 hard 만 measure. 매 2025 Soft Navigations API 매 soft 도 LCP/INP/CLS 의 attribute.

매 detection criteria (Chrome)

  1. User-initiated (click, keypress).
  2. URL change via History API.
  3. DOM change (significant) post-event.
  4. 매 셋 의 모두 conjunction → soft-nav event.

매 view transitions API

  • same-doc: document.startViewTransition(() => updateDOM()). 매 2024 stable.
  • cross-doc (MPA mode): 매 2025 Chrome 126+. @view-transition { navigation: auto }.
  • 매 Web Animations API 의 wrap, 매 CSS ::view-transition-* 의 customize.

매 응용

  1. Next.js App Router (RSC streaming + soft nav).
  2. Remix nested routes.
  3. Astro view-transitions.
  4. SvelteKit goto().

💻 패턴

Native History API

// router.js
class SoftRouter {
  constructor() {
    window.addEventListener('popstate', this.render.bind(this));
    document.addEventListener('click', this.intercept.bind(this));
  }

  intercept(e) {
    const a = e.target.closest('a[data-soft]');
    if (!a) return;
    e.preventDefault();
    this.navigate(a.href);
  }

  async navigate(url) {
    history.pushState({}, '', url);
    await this.render();
  }

  async render() {
    const path = location.pathname;
    if (document.startViewTransition) {
      document.startViewTransition(() => this.update(path));
    } else {
      this.update(path);
    }
  }

  update(path) {
    document.querySelector('main').innerHTML = renderRoute(path);
  }
}
new SoftRouter();

Next.js 15 App Router

// app/products/[id]/page.tsx
import { Suspense } from 'react';

export default async function Product({ params }) {
  const data = await fetch(`/api/p/${params.id}`).then(r => r.json());
  return (
    <Suspense fallback={<Skeleton />}>
      <ProductView data={data} />
    </Suspense>
  );
}

// link triggers soft nav by default
import Link from 'next/link';
<Link href="/products/42" prefetch>View</Link>

Cross-doc view transition (Astro / vanilla)

<!-- src/layouts/Base.astro -->
<head>
  <style>
    @view-transition { navigation: auto; }
    ::view-transition-old(root) { animation: fade-out 0.2s; }
    ::view-transition-new(root) { animation: fade-in 0.2s; }
  </style>
</head>

Soft Navigations API (measurement)

// observer.js
new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.log('soft nav', {
      from: entry.url,
      startTime: entry.startTime,
      navigationId: entry.navigationId,
    });
  }
}).observe({ type: 'soft-navigation', buffered: true });

// Attribute LCP per soft-nav
new PerformanceObserver(list => {
  for (const e of list.getEntries()) {
    if (e.navigationId !== 'auto') {
      console.log('soft LCP', e.startTime, e.element);
    }
  }
}).observe({ type: 'largest-contentful-paint', buffered: true });

Prefetch on hover/intent

document.addEventListener('mouseover', e => {
  const a = e.target.closest('a[data-soft]');
  if (a && !a.dataset.prefetched) {
    fetch(a.href, { priority: 'low' });
    a.dataset.prefetched = '1';
  }
});

web-vitals attribution (soft nav)

import { onLCP, onINP, onCLS } from 'web-vitals/attribution';

onLCP(metric => sendBeacon('/v', { ...metric, navType: metric.navigationType }), {
  reportSoftNavs: true,
});
onINP(m => sendBeacon('/v', m), { reportSoftNavs: true });

매 결정 기준

상황 Approach
Static-mostly site 매 MPA + cross-doc view transition
Heavy interactivity 매 SPA soft nav (Next.js / Remix)
Mixed 매 islands (Astro) + selective soft nav
Performance-critical 매 prefetch on intent + Suspense streaming

기본값: 매 framework 의 router (Next.js / Remix / SvelteKit) 의 use; 매 view transitions 의 progressive-enhance.

🔗 Graph

🤖 LLM 활용

언제: 매 router code scaffolding, 매 RUM analytics dashboard query gen, 매 view-transition CSS draft. 언제 X: 매 perf measurement (real RUM data needed), 매 a11y validation (manual + axe).

안티패턴

  • No focus management: 매 soft nav 후 focus 매 lost. 매 a11y violation. route.focus() on <main>.
  • No scroll restoration: 매 back-button 매 wrong scroll. history.scrollRestoration='manual' + manual restore.
  • CLS spike: 매 layout shift 의 unmeasured. 매 view-transitions 의 use.
  • Blocking JS during nav: 매 INP 매 800ms+. 매 Suspense / streaming.
  • No prefetch budget: 매 hover 매 모든 link 의 fetch — 매 bandwidth waste.

🧪 검증 / 중복

  • Verified (web.dev Soft Navigations 2025; Chrome Status #5085744967254016; web-vitals.js v4 docs).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — full content (History API + view transitions + Soft Nav API patterns)