본문 바로가기
Frontend2026년 4월 24일8분 읽기

View Transitions API 실전 — Next.js 16 페이지 전환 애니메이션

YS
김영삼
조회 1
View Transitions API 실전 — Next.js 16 페이지 전환 애니메이션

핵심 요약

View Transitions API는 페이지 전환 애니메이션을 CSS 한 줄로. Chrome 111+, Safari 18+, Firefox 124+ 모두 지원. Next.js 16에서 공식 통합.

  • Cross-document (MPA·페이지 간): Chrome·Safari·Firefox
  • Same-document (SPA): 모든 메이저 브라우저
  • JavaScript 거의 없이 페이지 간 부드러운 전환

1. 가장 단순한 사용

/* CSS만으로 — 모든 페이지 전환 fade */
@view-transition {
  navigation: auto;
}

이 한 줄로 모든 내부 링크 클릭 시 fade 전환.

2. 커스텀 애니메이션

::view-transition-old(root) {
  animation: 0.3s slide-out;
}
::view-transition-new(root) {
  animation: 0.3s slide-in;
}

@keyframes slide-out {
  to { transform: translateX(-30px); opacity: 0; }
}
@keyframes slide-in {
  from { transform: translateX(30px); opacity: 0; }
}

3. Hero 이미지 트랜지션

/* 목록 페이지의 썸네일이 상세 페이지의 hero로 변하는 효과 */

/* 목록 페이지 */
.thumbnail.featured {
  view-transition-name: hero-image;
}

/* 상세 페이지 */
.hero-image {
  view-transition-name: hero-image;
}

/* 같은 name이면 자동으로 morph */

두 페이지에 같은 view-transition-name을 부여하면 클릭 시 부드럽게 morph.

4. Next.js 16 통합

// app/layout.tsx
import { ViewTransitions } from 'next-view-transitions'

export default function Layout({ children }) {
  return (
    <html>
      <ViewTransitions>
        <body>{children}</body>
      </ViewTransitions>
    </html>
  )
}

// 페이지 컴포넌트에서 그냥 Link 사용
import { Link } from 'next-view-transitions'

<Link href="/post/123">게시글</Link>

Next.js Link를 next-view-transitions Link로 교체. 나머지는 CSS만.

5. JavaScript API (수동 제어)

// SPA에서 직접 호출
async function navigate(url) {
  if (!document.startViewTransition) {
    location.href = url
    return
  }
  
  const transition = document.startViewTransition(async () => {
    await loadContent(url)
    history.pushState(null, '', url)
  })
  
  // 진행 단계별 콜백
  await transition.ready  // animation 시작 직전
  await transition.finished  // 애니메이션 끝
}

6. 동시 전환 — 여러 요소

.product-card { view-transition-name: card-1; }
.product-card:nth-child(2) { view-transition-name: card-2; }
/* 또는 동적 */

/* JS */
for (const [i, el] of document.querySelectorAll('.product-card').entries()) {
  el.style.viewTransitionName = `card-${i}`
}

7. Fallback 처리

@supports not (view-transition-name: a) {
  /* 폴백 — 기본 즉시 전환 */
  .page { transition: opacity 0.2s; }
}

/* JS */
if (!document.startViewTransition) {
  // 기존 방식
}

8. 성능 고려사항

  • 전환 중 paint·composite 비용. 큰 페이지는 0.4초 이상 권장
  • view-transition-name은 페이지당 unique 필요 — 중복 시 첫 번째만 적용
  • position: fixed·sticky 요소는 전환 시 동작 미묘
  • iOS Safari의 일부 버전에서 backdrop-filter 충돌

9. 실전 예시 — 블로그 목록 → 상세

/* 목록 카드 */
.post-card {
  view-transition-name: var(--post-id);
}

/* 상세 페이지 */
.post-header {
  view-transition-name: var(--post-id);
}
// Next.js 컴포넌트
function PostCard({ post }) {
  return (
    <Link href={`/post/${post.id}`} className="post-card" style={{ '--post-id': `post-${post.id}` }}>
      <h2>{post.title}</h2>
      <p>{post.excerpt}</p>
    </Link>
  )
}

function PostDetail({ post }) {
  return (
    <article>
      <header className="post-header" style={{ '--post-id': `post-${post.id}` }}>
        <h1>{post.title}</h1>
      </header>
      ...
    </article>
  )
}

10. 디버깅

/* Animation 느려지게 (개발용) */
::view-transition-group(*) {
  animation-duration: 5s !important;
}

/* 또는 DevTools — Animations 패널 */

Chrome DevTools의 Animations 패널에서 view-transition 단계별 추적 가능.

11. 모바일 — 좌우 swipe + transition

/* 모바일 좌우 swipe로 뒤로/앞으로 */
@view-transition {
  navigation: auto;
  types: slide;
}

[data-direction="back"]::view-transition-old(root) {
  animation: 0.25s slide-out-right;
}
[data-direction="forward"]::view-transition-new(root) {
  animation: 0.25s slide-in-left;
}

실측 효과

지표beforeafter
체감 속도baseline훨씬 빠름 느낌 (실제 동일)
이탈률baseline-12%
구현 코드 (페이지 전환)~150줄 JS~10줄 CSS

자주 묻는 질문

모든 페이지에 적용?

너무 강한 효과는 산만. 마케팅·콘텐츠 사이트 좋음, 대시보드는 신중.

Safari 호환?Safari 18+ (2024-09) 이후 풀 지원. iOS 18+가 70% 이상.

Next.js Pages Router?일부 지원. App Router + next-view-transitions 권장.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록