Next.js · GSAP · FSD

풀스크린 이미지 슬라이더


Overview

풀스크린 비디오 슬라이더를 Next.js + GSAP으로 구현한 프로젝트입니다. 단순히 움직이는 UI가 아니라 — SVG 클립 패스 리빌, 포인터 드래그 실시간 프리뷰, 캐릭터 단위 텍스트 스플릿까지 프리미엄 인터랙션을 프로덕션 수준의 아키텍처 위에 구현했습니다.

Feature-Sliced Design으로 슬라이더 전체 코드를 하나의 feature에 응집하고, 450줄 규모의 커스텀 훅이 애니메이션 오케스트레이션을 전담합니다. UI 컴포넌트는 순수한 렌더링만 담당하여 관심사 분리가 명확합니다.

Anatomy

UI 해부도

풀스크린 슬라이더는 7개의 독립 UI 컴포넌트로 구성됩니다. 각 영역은 자체 애니메이션 로직을 가지며, 하나의 커스텀 훅이 전체 오케스트레이션을 담당합니다.

NavBar
로고 · 메뉴 · 햄버거
BigText
글자 분할 애니메이션
InfoBox
카테고리 · 제목 · 설명
StripClip
6-strip SVG 마스크
SlideCounter
현재 / 전체 슬라이드
BottomBar
SNS 링크 · 카피라이트

Structure

프로젝트 아키텍처

Feature-Sliced Design 기반으로 슬라이더 관련 모든 코드를 하나의 feature 폴더에 응집합니다. UI · 로직 · 데이터 · 스타일이 명확히 분리되어 있어 유지보수와 팀 협업에 유리합니다.

features/sliderFSD
data/1
slides.json
5개 슬라이드 데이터
hooks/2
useSliderAnimation.ts
450+ lines · 전체 애니메이션 오케스트레이션
useMobileMenu.ts
모바일 메뉴 토글 · GSAP 연동
ui/9
FullSlider.tsx
루트 컨테이너
SlideItem.tsx
비디오 슬라이드
BigText.tsx
대형 타이포그래피
InfoBox.tsx
정보 패널
StripClip.tsx
SVG 클립 패스
NavBar.tsx
네비게이션
SlideCounter.tsx
번호 카운터
BottomBar.tsx
하단 바
MobileMenu.tsx
모바일 메뉴
styles/1
slider.css
CSS 변수 · clamp 반응형

Data Flow

컴포넌트 데이터 플로우

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

DATA
slides.json5개 슬라이드 객체video · text · meta
HOOK
useSliderAnimationgsap.context()goToSlide()drag handlers
UI
FullSliderSlideItem ×5BigText ×5InfoBox ×5

ANIMATION OUTPUT

SlideItem
비디오 전환
scale 1.3 → 1.0
StripClip
마스크 리빌
6-strip 교차 슬라이드
BigText
글자 분할
char stagger 60ms
InfoBox
정보 전환
라인별 fade 40ms

Timeline

애니메이션 타임라인

슬라이드 전환은 약 2.1초 동안 6개의 애니메이션 레이어가 정밀하게 오케스트레이션됩니다. GSAP Timeline의 절대 시간 오프셋으로 각 레이어의 시작 · 종료를 제어합니다.

0.0s0.3s0.6s0.9s1.2s1.5s1.8s2.1s
Text Exit
Strip Reveal
Video Scale
Border Enter
Text Enter
Info Fade
이전 BigText · InfoBox 퇴장
6개 스트립 교차 슬라이드 (stagger 60ms)
scale 1.3 → 1.0 줌인 효과
4면 보더라인 scaleX/Y 진입
새 BigText chars yPercent 애니메이션
InfoBox 라인별 순차 페이드인

Interaction

인터랙션 플로우

사용자 입력은 Wheel과 Drag 두 갈래로 분기됩니다. Drag는 실시간 프리뷰 → 60% 임계값 판정 → 완료/취소 3단계를 거치며, 모든 경로에서 isAnimating 가드가 중복 실행을 차단합니다.

USER INTERACTION
Wheel ScrollSIMPLE
  1. 1

    deltaY 방향 감지

    e.deltaY > 0 ? next : prev
  2. 2

    modulo 순환 인덱스 계산

  3. 3

    goToSlide() 즉시 실행

    → Timeline 2.1s
Pointer DragADVANCED
  1. 1

    pointerdown → setPointerCapture

    8px 최소 이동 후 활성화
  2. 2

    setupDragPreview → 다음 슬라이드 준비

  3. 3

    pointermove → 실시간 스트립 + 엘리먼트 업데이트

    smoothstep easing: p²(3-2p)
  4. 4

    pointerup → 진행률 판정

dragProgress ≥ 60% ?
completeDragTransition스트립 완주 → 슬라이드 교체 (0.9s)
cancelDragTransition전체 되감기 → 원상 복구 (0.4s)

Patterns

핵심 기술 패턴

단순히 모션이 예쁜 것이 아니라, 프로덕션에서 유지보수 가능한 구조로 설계했습니다. 메모리 누수 방지 · DOM 쿼리 최적화 · 상태 충돌 가드까지 — 실무에서 바로 적용할 수 있는 패턴입니다.

GSAP Context
라이프사이클 안전성
gsap.context()로 모든 트윈을 스코프화 — 언마운트 시 ctx.revert() 한 줄로 전체 클린업
ctx = gsap.context(() => { ... }, container)
Selector Cache
DOM 쿼리 최적화
gsap.utils.selector()로 초기화 시 한 번만 DOM을 탐색 — 매 전환마다 재탐색 제거
q = gsap.utils.selector(container) q("[data-slider='slide']")
Timeline Composition
정밀한 시간 제어
절대 시간 오프셋으로 6개 레이어의 시작점을 지정 — 병렬 + 순차 혼합 오케스트레이션
tl.to(el, {...}, 0.3) // 0.3s에 시작
Data Attributes
JS-HTML 디커플링
data-slider 속성으로 GSAP 셀렉터 바인딩 — 클래스 충돌 없이 구조 독립적 쿼리
<figure data-slider="slide" />
Animation Guard
상태 충돌 방지
useRef 기반 isAnimating 플래그로 wheel + drag 중복 실행 원천 차단 — 리렌더 없이 상태 관리
if (isAnimating.current) return
Video Lifecycle
리소스 효율화
활성 슬라이드의 비디오만 재생 — 전환 완료 시 이전 비디오 정지, 새 비디오 0초부터 시작
v.currentTime = 0; v.play().catch(() => {})

Responsive

반응형 전략

데스크탑 퍼스트로 설계하고 max-xl · max-md 2단계 브레이크포인트만 사용합니다. CSS 변수 clamp()로 박스 사이즈가 뷰포트에 맞게 자연스럽게 스케일됩니다.

Desktop1280px+
  • BigText 좌측 고정
  • InfoBox 중앙 배치
  • SlideCounter 우하단
  • BottomBar 풀 너비
  • Drag 제스처 활성
Tabletmax-xl~1280px
  • BigText 좌측 축소
  • InfoBox 중앙 유지
  • 간격 · 패딩 축소
  • clamp 반응형 적용
  • 폰트 사이즈 조정
Mobilemax-md~768px
  • BigText 상단으로 이동
  • InfoBox 하단으로 이동
  • 햄버거 메뉴 전환
  • Counter · SNS 숨김
  • 터치 스와이프 전용