원형 마스크 회전 슬라이더
Overview
conic-gradient 마스크를 활용한 원형 슬라이더를 Next.js + GSAP으로 구현한 프로젝트입니다. 3중 동심원 이미지 레이어가 시차를 두고 스윕하며, SVG 오버레이 위에서 도트 · 틱 · 진행 원이 끊임없이 회전하는 고급 인터랙션을 프로덕션 수준의 아키텍처 위에 구현했습니다.
Feature-Sliced Design으로 슬라이더 전체 코드를 하나의 feature에 응집하고, 1,150줄 규모의 커스텀 훅이 conic-gradient 스윕 · RAF 연속 모션 · 자동 재생 · 입력 핸들링을 전담합니다. UI 컴포넌트는 순수한 렌더링만 담당하여 관심사 분리가 명확합니다.
Anatomy
UI 해부도
원형 슬라이더는 6개의 독립 UI 레이어로 구성됩니다. 3중 동심원 이미지 마스크 위에 SVG 오버레이가 겹치고, 하단 Info 영역과 원형 텍스트가 인터랙티브한 경험을 만들어냅니다.
- SliderHeader
- 로고 · 네비게이션 링크
- ImageLayers
- 3중 원형 마스크 이미지 레이어
- SVG Overlay
- 동심원 · 진행 원 · 회전 도트 · 틱
- SliderInfo
- 제목 · PREV/NEXT · 네온 라인
- RadialText
- 원형 배치 슬라이드 이름 9개
- SliderCounter
- 현재 / 전체 카운터 뱃지
Structure
프로젝트 아키텍처
Feature-Sliced Design 기반으로 원형 슬라이더의 모든 코드를 하나의 feature에 응집합니다. 1,150줄 규모의 애니메이션 훅이 전체 오케스트레이션을 전담하고, UI 컴포넌트는 순수한 렌더링만 담당합니다.
- slides.json
- 10개 슬라이드 이미지 · 이름 데이터
- useCircularSliderAnimation.ts
- 1150+ lines · 전체 애니메이션 엔진
- CircularSlider.tsx
- 3중 레이어 + SVG 오버레이 루트
- SliderHeader.tsx
- 로고 + 네비게이션 헤더
- SliderInfo.tsx
- 제목 + PREV/NEXT + 라인
- SliderCounter.tsx
- 현재/전체 카운터 뱃지
- SliderRadialText.tsx
- 원형 배치 슬라이드 이름
- circular-slider.css
- CSS 변수 · radial text · progress
Data Flow
컴포넌트 데이터 플로우
데이터는 단방향으로 흐릅니다. JSON에서 시작해 1,150줄 규모의 커스텀 훅이 conic-gradient 마스킹과 연속 회전 애니메이션을 처리하고, UI 컴포넌트는 순수하게 렌더링만 담당합니다.
ANIMATION OUTPUT
- ImageLayers
- 원형 마스크 전환
- conic-gradient sweep 3중
- SVG Overlay
- 진행 원 + 도트 회전
- stroke-dashoffset + RAF
- SliderInfo
- 제목 · 라인 전환
- fade + scaleX + neon flash
- RadialText
- 원형 텍스트 회전
- DOM insert/remove + CSS
Timeline
애니메이션 타임라인
슬라이드 전환은 약 0.82초 동안 7개의 애니메이션 레이어가 정밀하게 오케스트레이션됩니다. 3중 conic-gradient 마스크가 시차를 두고 스윕하며, 도트 가속과 네온 플래시가 전환감을 강화합니다.
- 이전 슬라이드 제목 퇴장 (0.3s fade)
- 외부 레이어 conic-gradient 스윕 + 1.3× 줌
- 중간 레이어 스윕 (70ms 딜레이) + 2× 줌
- 내부 레이어 스윕 (120ms 딜레이) + 2.2× 줌
- 회전 도트 가속 (BOOST 배수 적용)
- 네온 라인 scaleX 축소 → 확장 + 색상 플래시
- 새 슬라이드 제목 진입 (0.4s fade + offset)
Interaction
인터랙션 플로우
사용자 입력은 Wheel · Keyboard · Touch · Click 네 갈래로 분기됩니다. 모든 경로가 동일한 goToSlide()로 수렴하며, isAnimating 가드가 중복 실행을 차단합니다.
- 1
deltaY 누적
threshold: 50px - 2
방향 감지 → next / prev
- 1
방향키 감지
→ ↓ next / ← ↑ prev - 2
즉시 goToSlide 호출
- 1
touchStart 좌표 저장
startY 기록 - 2
touchEnd 스와이프 판정
threshold: 50px
- 1
PREV / NEXT 버튼
handlePrev · handleNext
Patterns
핵심 기술 패턴
단순히 원형 전환이 아니라, 프로덕션에서 유지보수 가능한 구조로 설계했습니다. 3중 마스크 레이어 · RAF 연속 모션 · 커스텀 커서까지 — 고급 인터랙션의 실무 패턴을 담았습니다.
- 3중 원형 마스크 전환
- conic-gradient로 각 레이어를 독립적으로 스윕 — 반지름별 radial-gradient 마스크와 조합하여 깊이감 있는 전환 구현
- conic-gradient(from -90deg, black 0deg, black ${sweep}deg, transparent ${sweep}deg)
- SVG 자동 재생 인디케이터
- stroke-dashoffset 애니메이션으로 5초 주기 progress 표시 — 완료 시 자동으로 다음 슬라이드 전환
- gsap.to(circle, { strokeDashoffset: 0, duration: 5 })
- 끊김 없는 연속 회전
- requestAnimationFrame 루프에서 3개 도트 + 틱 마크를 독립된 속도로 연속 회전 — 슬라이드 전환과 무관하게 항상 동작
- innerDotAngle += 0.005 midDotAngle += 0.003 outerDotAngle += 0.002
- GSAP quickSetter 커서
- gsap.quickSetter()로 dot + ring 위치를 서브밀리초 단위로 업데이트 — ring은 lerp 추적으로 부드러운 체이스 연출
- gsap.quickSetter(dot, 'css') ticker.add(() => lerp(ring, mouse))
- 레이어별 시차 스윕
- 외부 0ms → 중간 70ms → 내부 120ms 딜레이로 3중 레이어가 시차를 두고 스윕 — 줌 배율도 1.3× → 2× → 2.2× 차등 적용
- outer: 0ms, 0.6s, 1.3× mid: 70ms, 0.75s, 2× inner: 120ms, 0.6s, 2.2×
- CSS 기반 원형 텍스트
- 9개 텍스트 슬롯을 nth-child CSS로 회전 · 투명도 · 크기 제어 — 슬라이드 전환 시 DOM insert/remove로 순환
- :nth-child(5) { rotate: 0deg; opacity: 1; font-size: 1.4rem; }
Responsive
반응형 전략
데스크탑 퍼스트로 설계하고 max-xl · max-md 2단계 브레이크포인트만 사용합니다. 모바일에서는 Radial Text와 커스텀 커서를 비활성화하여 터치 중심 경험을 제공합니다.
- Radial Text 원형 배치
- 커스텀 커서 (dot + ring)
- 풀 네비게이션 표시
- 카운터 우하단 고정
- Wheel + 키보드 입력
- Radial Text 유지
- 간격 · 패딩 축소
- 폰트 사이즈 조정
- 네비게이션 유지
- 터치 스와이프 추가
- Radial Text 숨김
- 네비게이션 숨김
- 커스텀 커서 비활성
- 카운터 축소 · 위치 조정
- 터치 스와이프 전용