히어로 전환

공유 요소의 연속성으로 컨텍스트를 유지하는 전환

히어로 전환

히어로 전환은 두 화면 간 공유되는 요소가 자연스럽게 이동하고 변형되는 **연속성 전환(Continuity Transition)**입니다. 요소가 한 화면에서 다른 화면으로 "여행"하며, 사용자에게 강력한 시각적 연결성과 컨텍스트를 제공합니다.

데모

Loading demo...

UX 원칙

언제 사용하나요?

히어로 전환은 연속성과 컨텍스트 유지가 중요할 때 사용됩니다.

콘텐츠 관계별 적합성

콘텐츠 관계적합성설명
관련 없는 콘텐츠공유 요소가 없는 경우 부적합
형제 관계⚠️갤러리 등 특정 상황에서 효과적
계층 관계목록→상세처럼 같은 객체의 다른 뷰에 최적

주요 사용 케이스

  • 갤러리 → 풀스크린: 이미지가 확대되며 상세 보기로 전환
  • 제품 목록 → 상세: 제품 카드가 상세 페이지로 확장
  • 프로필 → 편집: 프로필 사진이 유지되며 편집 모드로 전환
  • 미디어 플레이어: 미니 플레이어에서 전체 플레이어로 확장

왜 이렇게 동작하나요?

  1. 시각적 연속성: 요소의 이동을 추적하여 방향감각 유지
  2. 공간적 관계 표현: 작은 것에서 큰 것으로, 요약에서 상세로
  3. 인지 부담 최소화: 갑작스러운 변화 대신 자연스러운 변형
  4. 흥미와 즐거움: 역동적인 모션으로 사용자 참여도 증가

모션 디자인

히어로 요소의 여정:
1. 출발점 → 위치, 크기, 모양 기록
2. 변형 중 → 부드러운 보간 애니메이션
3. 도착점 → 새로운 위치와 크기로 정착

다른 요소들:
- 나가는 페이지: 페이드 아웃
- 들어오는 페이지: 히어로 요소 정착 후 페이드 인

기본 사용법

1. 전환 설정

import { Ssgoi } from '@ssgoi/react';
import { hero } from '@ssgoi/react/view-transitions';

const config = {
  transitions: [
    {
      from: '/gallery',
      to: '/gallery/*',
      transition: hero(),
      symmetric: true
    }
  ]
};

export default function App() {
  return (
    <Ssgoi config={config}>
      {/* 앱 내용 */}
    </Ssgoi>
  );
}

2. 요소 마킹

공유될 요소에 data-hero-key 속성 추가:

// 목록 페이지
function ProductList() {
  return (
    <div className="grid">
      {products.map(product => (
        <Link key={product.id} to={`/product/${product.id}`}>
          <img 
            data-hero-key={`product-${product.id}`}
            src={product.image}
            alt={product.name}
          />
          <h3>{product.name}</h3>
        </Link>
      ))}
    </div>
  );
}

// 상세 페이지
function ProductDetail({ product }) {
  return (
    <div>
      <img 
        data-hero-key={`product-${product.id}`}
        src={product.image}
        alt={product.name}
        className="w-full"
      />
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </div>
  );
}

실전 활용 예시

1. 이미지 갤러리

썸네일에서 풀스크린으로 확장:

// 갤러리 그리드
function Gallery() {
  return (
    <div className="grid grid-cols-3 gap-4">
      {images.map(img => (
        <Link to={`/photo/${img.id}`} key={img.id}>
          <div 
            data-hero-key={`photo-${img.id}`}
            className="aspect-square overflow-hidden"
          >
            <img src={img.thumb} className="object-cover w-full h-full" />
          </div>
        </Link>
      ))}
    </div>
  );
}

// 풀스크린 뷰
function PhotoView({ id }) {
  return (
    <div 
      data-hero-key={`photo-${id}`}
      className="fixed inset-0 bg-black"
    >
      <img src={image.full} className="w-full h-full object-contain" />
    </div>
  );
}

2. 카드 확장

작은 카드에서 전체 화면 상세로:

// 카드 목록
function CardGrid() {
  return (
    <div className="grid gap-6">
      {items.map(item => (
        <article 
          key={item.id}
          data-hero-key={`card-${item.id}`}
          onClick={() => navigate(`/item/${item.id}`)}
          className="bg-white rounded-lg shadow-lg p-4 cursor-pointer"
        >
          <h3>{item.title}</h3>
          <p>{item.summary}</p>
        </article>
      ))}
    </div>
  );
}

// 확장된 상세
function ItemDetail({ item }) {
  return (
    <article 
      data-hero-key={`card-${item.id}`}
      className="min-h-screen bg-white p-8"
    >
      <h1 className="text-4xl">{item.title}</h1>
      <p className="text-lg">{item.fullContent}</p>
    </article>
  );
}

3. 다중 히어로 요소

여러 요소가 동시에 전환:

// 프로필 카드
function ProfileCard({ user }) {
  return (
    <div onClick={() => navigate(`/profile/${user.id}`)}>
      <img 
        data-hero-key={`avatar-${user.id}`}
        src={user.avatar}
        className="w-16 h-16 rounded-full"
      />
      <h3 data-hero-key={`name-${user.id}`}>
        {user.name}
      </h3>
    </div>
  );
}

// 프로필 상세
function ProfileDetail({ user }) {
  return (
    <div>
      <img 
        data-hero-key={`avatar-${user.id}`}
        src={user.avatar}
        className="w-32 h-32 rounded-full"
      />
      <h1 data-hero-key={`name-${user.id}`}>
        {user.name}
      </h1>
    </div>
  );
}

주의사항

히어로 키 규칙

  • 각 페이지에서 data-hero-key고유해야
  • 키는 문자열이며, 동적으로 생성 가능
  • 같은 키를 가진 요소끼리만 연결됨

성능 고려사항

  • 히어로 요소는 DOM에서 복제되어 애니메이션됨
  • 너무 많은 히어로 요소는 성능에 영향
  • 이미지는 미리 로드하여 깜빡임 방지

접근성

  • 모션 감소 설정 시 즉시 전환
  • 포커스 관리로 키보드 네비게이션 지원
  • 스크린 리더는 일반 페이지 전환으로 인식

모범 사례

✅ DO

  • 시각적으로 중요한 요소에 사용
  • 동일한 객체의 다른 표현에 적용
  • 자연스러운 크기/위치 변화 유지
  • 사용자의 시선을 이끌 때 활용

❌ DON'T

  • 너무 많은 요소에 동시 적용하지 마세요
  • 관련 없는 요소끼리 연결하지 마세요
  • 극단적인 크기 변화는 피하세요 (10배 이상)
  • 텍스트 콘텐츠에는 신중하게 사용