Back to list

Astro View Transitions: SPA 전환의 함정과 올바른 JS 생명주기

Astro에 <ViewTransitions />를 추가하는 건 한 줄짜리 작업입니다. 하지만 그 한 줄이 기존에 작성한 모든 클라이언트 사이드 JavaScript를 조용히 망가뜨릴 수 있습니다.

왜 DOMContentLoaded가 안 되는가?

<ViewTransitions />를 켜면 Astro는 페이지 전환 시 전체 HTML을 새로 내려받는 대신, fetch로 새 페이지의 콘텐츠만 가져와서 현재 DOM에 패치합니다. 진짜 SPA처럼요.

이 과정에서 DOMContentLoaded최초 페이지 로드 때 한 번만 발생합니다. 이후 View Transitions로 전환된 페이지에서는 절대 발생하지 않습니다. 그래서 DOMContentLoaded 안에 작성한 Intersection Observer, 이벤트 리스너 등이 두 번째 페이지부터 동작하지 않는 거죠.

해결책: astro:page-load

Astro는 이를 위해 astro:page-load 이벤트를 제공합니다. 이 이벤트는:

  1. 최초 페이지 로드 시 발생
  2. View Transitions로 새 페이지가 마운트될 때마다 발생
// ❌ 이렇게 하면 첫 페이지에서만 동작
document.addEventListener('DOMContentLoaded', () => {
  initScrollObserver();
});

// ✅ 이렇게 해야 모든 페이지에서 동작
document.addEventListener('astro:page-load', () => {
  initScrollObserver();
});

실전 패턴: 다크모드 토글

document.addEventListener('astro:page-load', () => {
  const toggle = document.getElementById('theme-toggle');

  toggle?.addEventListener('click', () => {
    const isDark = document.documentElement.classList.toggle('dark');
    localStorage.setItem('theme', isDark ? 'dark' : 'light');
  });
});

추가 훅: astro:before-swap

페이지 전환이 시작되기 직전에 실행할 코드는 astro:before-swap을 사용하세요. Observer 해제, 타이머 정리 등 클린업 작업에 적합합니다.

document.addEventListener('astro:before-swap', (e) => {
  observer?.disconnect();
});

이 두 가지 훅만 제대로 이해하면 View Transitions와 바닐라 JS를 완벽하게 공존시킬 수 있습니다.

링크가 복사되었습니다