Next.js · GSAP Flip · SplitType

클릭 시 확장되는 갤러리


Overview

썸네일 갤러리에서 클릭하면 GSAP Flip으로 이미지가 풀스크린으로 확장되는 인터랙티브 갤러리입니다. 단순한 모달이 아니라 — Flip 레이아웃 전환, SplitType 텍스트 리빌, SVG 동심원 모션, 커스텀 커서까지 프리미엄 인터랙션을 하나의 훅에서 오케스트레이션합니다.

Feature-Sliced Design으로 갤러리 전체 코드를 하나의 feature에 응집하고, 15개 UI 컴포넌트가 역할별로 분리되어 있습니다. 300줄 규모의 커스텀 훅이 Flip · SplitType · 캐러셀 네비게이션을 전담하고, UI 컴포넌트는 순수한 렌더링만 담당합니다.

Anatomy

UI 해부도

갤러리는 8개의 독립 UI 컴포넌트로 구성됩니다. 데스크탑에서는 absolute 레이어로 자유롭게 배치되고, 모바일에서는 flex 세로 레이아웃으로 전환됩니다.

Nav
로고 · 메뉴 · 검색
CircleSvg
SVG 동심원 배경 모션
BigTitle
SplitType 타이틀 애니메이션
Carousel
썸네일 트랙 + 이미지 카드
Counter
현재 / 전체 인덱스
MetaInfo
Established · Based in
SocialLinks
좌측 소셜 아이콘
BottomLeft
Play 버튼 · 설명 텍스트

Structure

프로젝트 아키텍처

Feature-Sliced Design 기반으로 갤러리 관련 모든 코드를 하나의 feature 폴더에 응집합니다. 15개 UI 컴포넌트가 역할별로 분리되어 있고, 하나의 커스텀 훅이 전체 애니메이션을 전담합니다.

features/galleryFSD
data/3
gallery.json
6개 갤러리 아이템 (이미지 · 제목 · 컬러)
meta.json
Established · Based in 메타 정보
social.json
소셜 링크 아이콘 매핑
hooks/1
useGalleryAnimation.ts
300+ lines · Flip 확장 + 캐러셀 오케스트레이션
ui/15
SectionGallery.tsx
루트 컨테이너 · 훅 연결
Gallery.tsx
3-레이어 레이아웃 (top · carousel · bottom)
Carousel.tsx
썸네일 트랙
GalleryItem.tsx
이미지 카드 + 그림자
ContentDetail.tsx
Flip 확장 상세 뷰
BigTitle.tsx
SplitType 대형 타이틀
Counter.tsx
슬라이드 번호 카운터
CarouselNav.tsx
이전/다음 버튼
CustomCursor.tsx
커스텀 크로스 커서
Nav.tsx
상단 네비게이션
CircleSvg.tsx
SVG 동심원 배경
MetaInfo.tsx
메타 정보 DL
SocialLinks.tsx
소셜 아이콘
DescRight.tsx
우측 설명 텍스트
BottomLeft.tsx
Play 버튼 · 하단 텍스트
styles/1
gallery.css
CSS 변수 · 캐러셀 사이징 · 커서 스타일

Data Flow

컴포넌트 데이터 플로우

데이터는 단방향으로 흐릅니다. JSON에서 시작해 커스텀 훅이 Flip · SplitType 로직을 처리하고, UI 컴포넌트는 순수하게 렌더링만 담당합니다.

DATA
gallery.json6개 갤러리 객체image · title · colors
HOOK
useGalleryAnimationgsap.context()Flip.getState()SplitType
UI
SectionGalleryGalleryCarousel ×6ContentDetail ×6

ANIMATION OUTPUT

Carousel
썸네일 슬라이드
track translateX + scale
Flip
이미지 확장
thumbnail → fullscreen 1.25s
SplitType
제목 리빌
chars clipPath stagger
ContentDetail
상세 진입
lines yPercent + swatch scale

Timeline

애니메이션 타임라인

상세 뷰 열기는 약 2.0초 동안 9개의 애니메이션 레이어가 정밀하게 오케스트레이션됩니다. GSAP Flip이 이미지 전환을 담당하고, SplitType이 텍스트 리빌을 처리합니다.

0.0s0.3s0.6s0.9s1.2s1.5s1.8s2.0s
Shadow Out
UI Fade
Flip Image
Clip Others
Vertical Text
Title Chars
Divider Line
Body Lines
Color Swatch
클릭된 카드 그림자 페이드아웃
상단 · 하단 레이어 투명도 0
GSAP Flip — 썸네일 → 풀스크린 이미지 (1.25s)
나머지 카드 clipPath inset 퇴장
좌측 세로 레이블 xPercent 진입
SplitType chars clipPath 리빌 (stagger 15ms)
구분선 scaleX 0 → 1
본문 lines yPercent 순차 페이드인 (stagger 25ms)
컬러 칩 scale + back easing 팝인

Interaction

인터랙션 플로우

사용자 인터랙션은 Carousel Nav, Gallery Click, Detail Click 세 갈래로 분기됩니다. Gallery Click은 GSAP Flip으로 이미지를 확장하고, Detail Click은 역순으로 복원합니다.

USER INTERACTION
Carousel NavSIMPLE
  1. 1

    Prev / Next 버튼 클릭

  2. 2

    트랙 translateX 이동

    track x: -step (0.6s)
  3. 3

    BigTitle SplitType 교체

    chars yPercent exit → enter
Gallery ClickADVANCED
  1. 1

    썸네일 이미지 클릭

  2. 2

    Flip.getState → 이미지 DOM 이동

    Flip.from(state, 1.25s)
  3. 3

    나머지 카드 clipPath 퇴장

  4. 4

    상세 텍스트 SplitType 리빌

    chars · lines · swatches
Detail CloseREVERSE
  1. 1

    오버레이 또는 Back 버튼 클릭

  2. 2

    텍스트 · 컬러칩 역순 퇴장

  3. 3

    Flip 역전환 → 썸네일 복원

    Flip.from(state, 1.25s)
isAnimating 체크
전환 완료isAnimating = false → 새 입력 수신 가능
입력 차단isAnimating = true → 모든 클릭 무시

Patterns

핵심 기술 패턴

단순히 모션이 예쁜 것이 아니라, 프로덕션에서 유지보수 가능한 구조로 설계했습니다. Flip 레이아웃 전환 · SplitType 텍스트 리빌 · AbortController 클린업까지 — 실무에서 바로 적용할 수 있는 패턴입니다.

GSAP Flip
레이아웃 전환 애니메이션
Flip.getState()로 현재 위치 캡처 → DOM 이동 후 Flip.from()으로 자연스러운 위치 · 크기 전환
const state = Flip.getState(img) target.appendChild(img) Flip.from(state, { duration: 1.25 })
SplitType
텍스트 분할 애니메이션
chars · lines 단위로 텍스트를 분할하고, clipPath · yPercent로 글자 단위 리빌 — 래퍼 요소로 오버플로 제어
new SplitType(el, { types: 'chars' }) chars → clipPath: inset(0 100% 0 0)
GSAP Context
라이프사이클 안전성
gsap.context()로 모든 트윈을 스코프화 — 언마운트 시 ctx.revert() 한 줄로 전체 클린업
ctx = gsap.context(() => { ... }, container)
Data Attributes
JS-HTML 디커플링
data-gallery 속성으로 GSAP 셀렉터 바인딩 — 클래스 충돌 없이 구조 독립적 쿼리
<figure data-gallery="image-wrapper" />
AbortController
이벤트 리스너 관리
signal 기반으로 모든 이벤트 리스너 등록 — abort() 한 번으로 전체 정리, 메모리 누수 원천 차단
el.addEventListener('click', fn, { signal }) // cleanup: abort.abort()
Custom Cursor
인터랙티브 커서 UX
rAF 기반 lerp 추적으로 부드러운 커서 — 상세 뷰에서 is-open 클래스로 회전 변환하여 닫기 시그널 제공
cursorX += (targetX - cursorX) * 0.15 cursor.style.transform = translate3d(...)

Responsive

반응형 전략

데스크탑 퍼스트로 설계하고, CSS 변수로 캐러셀 사이징을 제어합니다. 데스크탑은 absolute 레이어 배치, 태블릿 이하는 flex 세로 레이아웃으로 자연스럽게 전환됩니다.

Desktop1024px+
  • absolute 레이어 자유 배치
  • 캐러셀 우측 50vw 영역
  • BigTitle 좌측 고정
  • 커스텀 커서 활성
  • DescRight · SocialLinks 표시
Tabletmax-xl~1024px
  • flex 세로 레이아웃 전환
  • 캐러셀 전체 너비 96vw
  • BigTitle 상단 배치
  • DescRight 숨김
  • MetaInfo 가로 배치
Mobilemax-md~768px
  • 캐러셀 2.2장 노출
  • BigTitle 대형 12vw
  • 메뉴 링크 숨김
  • MetaInfo · SocialLinks 숨김
  • 터치 기반 인터랙션