본문 바로가기
Graphic/lib

GSAP 가이드

by curious week 2025. 9. 4.

Part 1: GSAP의 기초


Chapter 1: GreenSock Animation Platform 소개


1.1 GSAP란 무엇이며, 왜 업계 표준인가?

GSAP(GreenSock Animation Platform)는 프레임워크에 구애받지 않는 고성능 JavaScript 애니메이션 라이브러리로, 모든 주요 브라우저에서 안정적으로 작동하는 애니메이션을 구축하는 데 사용됩니다. GSAP의 핵심 역량은 JavaScript가 제어할 수 있는 거의 모든 것(CSS, SVG, React, WebGL 등)을 애니메이션화할 수 있는 능력에 있습니다. 현재 1,100만 개 이상의 웹사이트에서 사용되고 있으며, 이는 GSAP의 신뢰성과 성능을 입증합니다.  

GSAP이 업계 표준으로 자리 잡은 이유는 단순히 강력한 API 때문만이 아닙니다. 그 본질적인 가치는 개발자가 브라우저 간의 수많은 불일치를 직접 처리할 필요가 없도록 하는 추상화와 신뢰성에 있습니다. 웹 개발에서 애니메이션을 구현할 때 마주치는 벤더 프리픽스(vendor prefix), transform 속성 순서의 불일치, 특정 브라우저에서만 발생하는 버그 등은 상당한 시간과 노력을 요구하는 문제입니다. GSAP은 이러한 문제들을 내부적으로 해결하여 개발자가 애니메이션의 로직과 창의적인 표현에만 집중할 수 있도록 합니다. 이는 단순한 편의 기능을 넘어, 개발 생산성을 극적으로 향상시키고 예측 가능한 결과를 보장하는 핵심적인 설계 철학입니다. 결과적으로 개발자는 GSAP을 통해 '애니메이션이 그냥 작동할 것'이라는 확신을 얻게 되며, 이것이 GSAP이 전문가들 사이에서 압도적인 지지를 받는 근본적인 이유입니다.

성능 측면에서도 GSAP은 jQuery보다 최대 20배 빠른 속도를 자랑하며, 이는 고속의 속성 조작자(property manipulator)로서의 역할을 충실히 수행하기 때문입니다.  


1.2 GSAP 생태계 이해: 코어와 플러그인

GSAP은 모듈식 아키텍처를 채택하여 핵심 기능과 확장 기능을 명확히 분리합니다. GSAP의 코어(gsap.js)는 트윈(tween) 및 타임라인과 관련된 모든 필수 기능을 포함하면서도 파일 크기를 작고 빠르게 유지합니다. 스크롤 기반 애니메이션(ScrollTrigger), 드래그 앤 드롭(Draggable), SVG 모핑(MorphSVG)과 같은 추가적인 고급 기능들은 필요할 때만 선택적으로 로드할 수 있는 플러그인 형태로 제공됩니다.  

이러한 플러그인 아키텍처는 현대 웹 개발에서 중요하게 여겨지는 '번들 크기' 문제에 대한 GSAP의 전략적인 대응입니다. 모든 기능을 단일 파일에 포함하는 대신, 사용자가 필요한 기능만 선택적으로 포함하도록 함으로써 초기 로딩 성능에 대한 부담을 최소화합니다. 이는 개발자가 프로젝트의 요구사항에 맞춰 기능을 확장할 수 있는 유연성을 제공하며, 간단한 마이크로 인터랙션부터 복잡한 플러그인 기반의 애플리케이션에 이르기까지 모든 규모의 프로젝트에서 GSAP을 효율적으로 사용할 수 있게 만듭니다.  

주요 플러그인 카테고리는 다음과 같습니다 :  

  • Scroll Plugins: ScrollTrigger, ScrollTo, ScrollSmoother
  • Text Plugins: SplitText, ScrambleText, Text
  • SVG Plugins: DrawSVG, MorphSVG, MotionPath
  • UI Plugins: Flip, Draggable, Inertia

1.3 설치 및 설정: CDN, NPM/Yarn, 그리고 gsap.registerPlugin()의 중요성

GSAP을 프로젝트에 통합하는 방법은 크게 두 가지입니다.

CDN (Content Delivery Network) 가장 빠르고 간단한 방법으로, HTML 파일에 <script> 태그를 추가하여 GSAP을 로드합니다. cdnjs나 JSDelivr와 같은 주요 CDN을 통해 제공됩니다.  

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/ScrollTrigger.min.js"></script>

NPM / Yarn React, Vue, Svelte와 같은 현대적인 프레임워크 기반의 모듈 번들러 환경에서는 NPM이나 Yarn을 사용하는 것이 표준입니다.  

npm install gsap
# 또는
yarn add gsap

설치 후, 필요한 모듈을 프로젝트 파일로 가져옵니다.

import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

gsap.registerPlugin()의 중요성 모듈 번들러 환경에서 GSAP 플러그인을 사용할 때는 gsap.registerPlugin()을 호출하는 것이 매우 중요합니다. 이는 현대적인 빌드 도구의 '트리 쉐이킹(tree shaking)' 과정에서 발생하는 문제를 방지하기 위함입니다.  

트리 쉐이킹은 프로덕션 빌드 시 사용되지 않는 코드를 제거하여 최종 번들 크기를 최적화하는 기술입니다. 그러나 GSAP 플러그인은 scrollTrigger: {}와 같이 문자열 속성을 통해 활성화되는 경우가 많아, 정적 코드 분석기는 플러그인이 실제로 사용되고 있는지 판단하기 어렵습니다. 이로 인해 번들러가 플러그인 코드를 '사용되지 않는 코드'로 오인하여 제거해 버릴 수 있으며, 이는 프로덕션 환경에서만 발생하는 런타임 오류로 이어질 수 있습니다.  

gsap.registerPlugin()은 번들러에게 "이 플러그인은 프로젝트에서 명시적으로 사용되고 있으니, 절대 제거하지 마시오"라는 신호를 보내는 역할을 합니다. 따라서 플러그인을 사용하기 전에 반드시 등록해야 합니다.

import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { TextPlugin } from "gsap/TextPlugin";

// 사용하려는 모든 플러그인을 등록합니다.
gsap.registerPlugin(ScrollTrigger, TextPlugin);

이 과정은 GSAP의 동적인 플러그인 시스템과 최신 빌드 도구의 정적 분석 사이의 간극을 메우는 필수적인 절차이며, 안정적인 프로덕션 빌드를 위한 핵심적인 관행입니다.


Chapter 2: 첫 번째 애니메이션: 트윈(Tween) 마스터하기


2.1 트윈의 구조: Target, Vars, 그리고 Duration

GSAP 애니메이션의 가장 기본적인 단위는 '트윈(Tween)'입니다. 트윈은 시간이 지남에 따라 속성 값을 변경하는 단일 애니메이션을 의미합니다. 모든 트윈은 세 가지 핵심 요소로 구성됩니다 :  

  1. Target (대상): 애니메이션을 적용할 대상을 지정합니다. CSS 선택자 문자열(.box), DOM 요소 변수, 또는 요소들의 배열이 될 수 있습니다.  
  2. Vars (변수 객체): 애니메이션의 동작을 정의하는 객체입니다. 변경하려는 속성(예: x, opacity)과 그 값, 그리고 애니메이션의 세부 설정(duration, ease 등)을 포함합니다.
  3. Duration (지속 시간): 애니메이션이 완료되는 데 걸리는 시간(초)입니다. vars 객체 내에 duration 속성으로 정의됩니다.
//.box 클래스를 가진 요소를 200px 오른쪽으로 1초 동안 이동시킵니다.
gsap.to(".box", {
  x: 200,
  duration: 1
});

2.2 핵심 메서드: gsap.to(), gsap.from(), gsap.fromTo()

GSAP은 세 가지 주요 트윈 생성 메서드를 제공하며, 각각의 메서드는 애니메이션의 시작과 끝 상태를 다르게 정의합니다.

  • gsap.to(): 가장 일반적으로 사용되는 메서드입니다. 대상 요소의 현재 상태에서 vars 객체에 정의된 목표 상태로 애니메이션을 실행합니다.  
  • gsap.from(): gsap.to()와 반대로 작동합니다. vars 객체에 정의된 시작 상태에서 대상 요소의 현재 상태로 애니메이션을 실행합니다. 마치 역재생되는 트윈과 같습니다. 주로 요소를 화면에 나타나게 하는 '등장' 애니메이션에 유용합니다.  
  • gsap.fromTo(): 애니메이션의 시작 상태끝 상태를 모두 명시적으로 정의하여 완전한 제어권을 제공합니다.  

이 세 가지 메서드 중 어떤 것을 선택하는가는 단순히 편의의 문제가 아니라, 애니메이션의 상태 관리와 예측 가능성에 대한 전략적인 결정입니다. gsap.to()는 간단하지만, 애니메이션 시작 시점의 '현재 상태'에 의존하기 때문에 다른 애니메이션이나 CSS 규칙의 영향을 받아 예측 불가능한 결과를 낳을 수 있습니다. gsap.from() 역시 CSS에 정의된 최종 상태에 의존한다는 점에서 비슷한 한계를 가집니다.

반면, gsap.fromTo()는 애니메이션을 요소의 현재 상태나 스타일시트로부터 완전히 분리시킵니다. 시작과 끝을 명확히 정의하기 때문에 외부 요인의 영향을 받지 않아 가장 견고하고 예측 가능한 방법입니다. 복잡하고 중요한 애니메이션의 경우, gsap.fromTo()는 예측 불가능한 시작 조건이라는 주요 버그 원인을 제거하므로 전문가들이 선호하는 방식입니다. 이는 특히 여러 애니메이션이 동일한 요소에 적용되는 ScrollTrigger 시나리오에서 논리적 충돌을 해결하는 데 중요한 역할을 합니다.  

//.box를 현재 위치에서 x: 200, opacity: 0으로 애니메이션
gsap.to(".box", { x: 200, opacity: 0, duration: 1 });

//.box를 x: 200, opacity: 0 상태에서 현재 상태로 애니메이션
gsap.from(".box", { x: 200, opacity: 0, duration: 1 });

//.box를 x: -200, opacity: 0 상태에서 x: 200, opacity: 1 상태로 애니메이션
gsap.fromTo(".box", { x: -200, opacity: 0 }, { x: 200, opacity: 1, duration: 1 });

2.3 CSS 속성 애니메이션: Transform 단축 속성의 힘

GSAP은 복잡한 CSS transform 속성을 매우 직관적이고 간결하게 제어할 수 있도록 단축 속성(shorthand)을 제공합니다. transform: "translateX(100px) rotate(90deg)"와 같은 긴 문자열 대신, x: 100, rotation: 90, scale: 2, xPercent: -50과 같은 속성을 사용할 수 있습니다.  

이러한 단축 속성을 사용하는 것은 단순히 코드를 간결하게 만드는 것 이상의 이점을 가집니다. GSAP은 이 값들을 직접 처리할 수 있기 때문에, 브라우저로부터 CSS matrix() 값을 다시 읽어와 파싱하는 추가적인 계산 과정을 생략할 수 있어 성능적으로 더 효율적입니다.  

다음 표는 주요 GSAP 단축 속성과 그에 해당하는 CSS 속성을 정리한 것입니다.

GSAP 속성 CSS 속성 설명
x transform: translateX() x축으로 이동 (기본 단위: px)
y transform: translateY() y축으로 이동 (기본 단위: px)
xPercent transform: translateX() 요소 너비의 백분율만큼 x축으로 이동
yPercent transform: translateY() 요소 높이의 백분율만큼 y축으로 이동
scale transform: scale() x축과 y축으로 동시에 크기 조절
scaleX transform: scaleX() x축으로 크기 조절
scaleY transform: scaleY() y축으로 크기 조절
rotation transform: rotate() 회전 (기본 단위: deg)
skewX transform: skewX() x축으로 기울이기 (기본 단위: deg)
skewY transform: skewY() y축으로 기울이기 (기본 단위: deg)
transformOrigin transform-origin 변형의 기준점 설정

이 표는 CSS 애니메이션에 익숙한 개발자들이 GSAP 구문으로 빠르게 전환할 수 있도록 돕는 실용적인 참조 자료 역할을 합니다.


Part 2: 애니메이션 제어 및 시퀀싱 마스터하기

이 파트에서는 단일 애니메이션을 넘어, 타이밍의 미묘한 차이를 제어하고 여러 트윈을 조화롭게 엮어 풍부하고 표현력 있는 움직임을 만드는 방법을 다룹니다.


Chapter 3: 타이밍의 예술: 필수 트윈 속성


3.1 지속 시간, 지연, 반복 제어 (duration, delay, repeat, yoyo)

트윈의 생명주기를 제어하는 핵심 속성들은 다음과 같습니다.

  • duration: 애니메이션의 지속 시간(초)입니다. 기본값은 0.5초입니다.  
  • delay: 애니메이션이 시작되기 전 대기 시간(초)입니다.  
  • repeat: 애니메이션을 반복할 횟수입니다. -1로 설정하면 무한 반복됩니다.  
  • yoyo: true로 설정하면, 반복될 때마다 애니메이션이 정방향과 역방향을 오가며 재생됩니다. 마치 요요처럼 움직입니다.  
gsap.to(".box", {
  rotation: 360,
  duration: 2,
  delay: 1,    // 1초 후 시작
  repeat: 3,   // 3번 추가 반복 (총 4회 재생)
  yoyo: true   // 정방향 -> 역방향 -> 정방향 -> 역방향으로 반복
});

3.2 움직임에 생명 불어넣기: 이징(ease) 심층 분석

이징(ease)은 애니메이션에 개성과 생명력을 불어넣는 가장 중요한 요소입니다. 이징은 트윈이 진행되는 동안의 속도 변화율을 제어하는 함수입니다. 이징을 사용하지 않으면(  ease: "none") 움직임은 기계처럼 등속 운동을 하지만, 적절한 이징을 적용하면 가속, 감속, 탄성 등 자연스럽고 역동적인 느낌을 연출할 수 있습니다.

https://gsap.com/docs/v3/Eases/

GSAP의 이징은 크게 세 가지 타입으로 나뉩니다 :  

  • in: 느리게 시작해서 빠르게 끝납니다. (예: power2.in)
  • out: 빠르게 시작해서 느리게 끝납니다. (예: power2.out)
  • inOut: 느리게 시작해서 중간에 가속했다가 다시 느리게 끝납니다. (예: power2.inOut)

이징의 선택은 단순히 미적인 측면을 넘어 사용자 경험(UX) 원칙과도 깊은 관련이 있습니다. 예를 들어, UI 전환 애니메이션에는 ease: "power1.out"이 가장 적합한 것으로 알려져 있습니다. 이는 애니메이션이 사용자 입력에 즉각적으로 반응하는 것처럼 빠르게 시작하여 '반응성'이 좋다고 느끼게 하고, 끝으로 갈수록 부드럽게 감속하여 자연스러운 마찰력을 느끼게 하기 때문입니다. 이처럼 이징의 선택은 브랜드의 톤앤매너와 애니메이션의 목적에 부합하도록 신중하게 이루어져야 합니다.   

elastic 이징은 유쾌하고 재미있는 느낌을, power2.inOut은 부드럽고 전문적인 느낌을 줍니다.

GSAP 공식 문서의 Ease Visualizer는 다양한 이징 곡선을 시각적으로 확인하고 코드를 생성할 수 있는 강력한 도구이므로, 이를 적극적으로 활용하는 것이 좋습니다.  

gsap.to(".box", {
  x: 500,
  duration: 2,
  ease: "bounce.out" // 통통 튀는 효과
});

3.3 콜백으로 코드 실행하기 (onComplete, onStart, onUpdate)

콜백 함수를 사용하면 트윈의 특정 생명주기 시점에 원하는 JavaScript 코드를 실행할 수 있습니다.  

  • onStart: 트윈이 시작될 때 호출됩니다.
  • onUpdate: 트윈이 업데이트될 때마다(매 프레임마다) 호출됩니다.
  • onComplete: 트윈이 완료되었을 때 호출됩니다.
  • onRepeat: 트윈이 반복될 때마다 호출됩니다.

콜백 함수에 파라미터를 전달해야 할 경우, onCompleteParams: ["param1", "param2"]와 같이 Params 접미사가 붙은 속성을 사용합니다. 또한, 애니메이션과 관련된 시간 지연이 필요할 때는  setTimeout보다 GSAP의 gsap.delayedCall()을 사용하는 것이 프레임 동기화 측면에서 더 안정적이고 권장됩니다.  

function onAnimationComplete(element) {
  console.log(element + " 애니메이션 완료!");
}

gsap.to(".box", {
  x: 500,
  duration: 2,
  onComplete: onAnimationComplete,
  onCompleteParams: [".box"] // onComplete 함수에 전달할 파라미터 배열
});

Chapter 4: 타임라인으로 복잡한 시퀀스 조율하기


4.1 gsap.timeline() 소개: 단순한 지연을 넘어서

여러 애니메이션을 순차적으로 또는 복잡한 시간 관계에 따라 실행해야 할 때, 각 트윈에 delay를 개별적으로 설정하는 것은 매우 번거롭고 유지보수가 어렵습니다. 첫 번째 애니메이션의 duration이 변경되면 그 이후 모든 트윈의 delay 값을 수동으로 재조정해야 하기 때문입니다.

gsap.timeline()은 이러한 문제를 해결하는 강력한 시퀀싱 도구입니다. 타임라인은 여러 트윈과 다른 타임라인을 담을 수 있는 컨테이너 역할을 하며, 전체 시퀀스를 하나의 단위처럼 제어(재생, 일시정지, 역재생, 속도 조절 등)할 수 있게 해줍니다.  

// 타임라인 생성
const tl = gsap.timeline();

// 타임라인에 순차적으로 트윈 추가
tl.to(".box1", { x: 200, duration: 1 });
tl.to(".box2", { y: 100, duration: 0.5 }); //.box1 애니메이션이 끝난 후 시작
tl.to(".box3", { rotation: 360, duration: 1.5 }); //.box2 애니메이션이 끝난 후 시작

4.2 시퀀스 구축: 메서드 체이닝과 기본 속성

타임라인을 생성한 후, 메서드 체이닝(method chaining)을 사용하여 코드를 간결하게 작성하는 것이 일반적인 패턴입니다.  

const tl = gsap.timeline();

tl.to(".box1", { x: 200, duration: 1 })
 .to(".box2", { y: 100, duration: 0.5 })
 .to(".box3", { rotation: 360, duration: 1.5 });

또한, 타임라인에 포함될 모든 트윈에 공통적으로 적용될 속성이 있다면, 타임라인 생성 시 defaults 객체를 설정하여 코드 중복을 피할 수 있습니다.  

const tl = gsap.timeline({
  defaults: {
    duration: 1,
    ease: 'power2.inOut'
  }
});

tl.to(".box1", { x: 200 }) // duration: 1, ease: 'power2.inOut'이 자동으로 적용됨
 .to(".box2", { y: 100 }) // duration: 1, ease: 'power2.inOut'이 자동으로 적용됨
 .to(".box3", { rotation: 360 }); // duration: 1, ease: 'power2.inOut'이 자동으로 적용됨

4.3 정밀한 타이밍: position 파라미터 해부

타임라인의 진정한 강력함은 position 파라미터를 통해 드러납니다. 이 파라미터는 각 트윈이 타임라인의 어느 시점에 삽입될지를 매우 정밀하게 제어할 수 있게 해줍니다.  

다음 표는 position 파라미터의 다양한 사용법과 실제 활용 사례를 정리한 것입니다. 이 표는 단순한 문법 나열을 넘어, 각 문법이 어떤 문제를 해결하고 어떤 창의적인 표현을 가능하게 하는지 구체적인 맥락을 제공함으로써, 개발자가 애니메이션을 전문가 수준으로 설계할 수 있도록 돕습니다.

파라미터 문법 설명 실용적인 활용 사례
3 (절대 시간) 타임라인 시작 후 정확히 3초 지점에 트윈을 삽입합니다. 오디오 트랙의 특정 비트나 영상의 특정 장면에 맞춰 애니메이션을 동기화할 때 사용합니다.
"+=1" (끝 기준 상대 시간) 마지막에 추가된 애니메이션이 끝나는 지점으로부터 1초 뒤에 트윈을 삽입하여 간격을 만듭니다. 애니메이션 장면과 장면 사이에 의도적인 멈춤이나 여백을 만들어 호흡을 조절할 때 사용합니다.
"-=1" (끝 기준 상대 시간) 마지막에 추가된 애니메이션이 끝나기 1초 전에 트윈을 삽입하여 겹치게 만듭니다. 한 요소가 사라지면서 다른 요소가 나타나는 것과 같은 부드러운 오버랩 전환 효과를 만들 때 사용합니다.
"myLabel" (레이블) myLabel이라는 이름표가 붙은 위치에 트윈을 삽입합니다. (tl.addLabel("myLabel")로 생성)  
 

복잡한 시퀀스에서 '인트로 끝', '메인 시작'과 같은 주요 지점을 표시하고, 해당 지점으로 쉽게 이동(tl.seek("myLabel"))하거나 애니메이션을 그룹화할 때 사용합니다.
"myLabel+=0.5" (레이블 기준 상대 시간) myLabel 위치로부터 0.5초 뒤에 트윈을 삽입합니다.  
 

특정 이벤트(레이블) 발생 후 약간의 시간차를 두고 후속 애니메이션을 정밀하게 실행할 때 사용합니다.
"<" (이전 트윈 시작점) 바로 이전에 추가된 애니메이션의 시작점과 동일한 시간에 트윈을 삽입합니다.  
 

한 객체의 위치와 색상을 동시에 변경하고 싶을 때, 코드를 명확하게 분리하기 위해 두 개의 트윈으로 작성하되 동일한 시간에 시작하도록 할 때 유용합니다.
">" (이전 트윈 종료점) 바로 이전에 추가된 애니메이션이 끝나는 지점에 트윈을 삽입합니다. (기본 동작)  
 

코드를 읽는 사람이 애니메이션이 정확히 순차적으로 실행됨을 명확히 인지하도록 의도를 드러낼 때 사용합니다.
"<0.5" (이전 트윈 시작점 기준 상대 시간) 바로 이전에 추가된 애니메이션이 시작된 후 0.5초 뒤에 트윈을 삽입합니다.  
 

여러 요소가 거의 동시에 나타나지만 미세한 시간차를 두어 깊이감이나 계층적인 느낌을 주는 레이어드 효과를 만들 때 사용합니다.

Part 3: 고급 애니메이션 기법

이 파트에서는 다수의 요소를 리드미컬하게 애니메이션하거나, 특정 효과를 위해 특화된 플러그인을 활용하는 등 더 복잡한 시나리오를 다루는 기법을 탐구합니다.


Chapter 5: 스태거(Stagger)로 그룹 애니메이션하기


5.1 간단한 스태거: 리드미컬한 시퀀스 만들기

stagger 속성을 사용하면 여러 대상 요소에 애니메이션을 적용할 때 각 요소의 애니메이션 시작 시간 사이에 일정한 간격을 둘 수 있습니다. 이를 통해 순차적으로 나타나거나 움직이는 리드미컬한 효과를 쉽게 만들 수 있습니다.  

//.box 클래스를 가진 모든 요소들이 0.1초 간격으로 y: 100으로 이동합니다.
gsap.to('.box', {
  y: 100,
  stagger: 0.1
});

5.2 고급 스태거 객체

stagger 속성에 숫자 대신 설정 객체를 전달하면 훨씬 더 정교하고 다채로운 스태거 효과를 구현할 수 있습니다.

  • each vs. amount: each는 각 요소 사이의 시간 간격을 직접 지정하는 반면, amount는 전체 요소에 걸쳐 스태거 효과가 완료될 총 시간을 지정합니다. GSAP은 이 총 시간을 요소의 개수만큼 나누어 각 간격을 자동으로 계산합니다.  
  • from: 스태거 효과가 시작될 기준점을 설정합니다. "start", "center", "edges", "random", "end"와 같은 문자열 키워드나 특정 요소의 인덱스 번호를 사용할 수 있습니다. 예를 들어   from: "center"는 중앙에 있는 요소부터 애니메이션이 시작되어 양쪽 가장자리로 퍼져나가는 효과를 만듭니다.
  • ease: 스태거의 시간 간격 자체에 이징을 적용합니다. 이를 통해 애니메이션 시작 간격이 일정하지 않고, 가속되거나 감속되는 등 유기적인 리듬을 만들 수 있습니다.  

여기서 stagger 객체 내부의 ease와 외부의 ease는 명확히 구분되어야 합니다. vars 객체 최상위에 있는 ease는 각 개별 요소의 움직임에 적용되는 반면, stagger 객체 내부의 ease는 애니메이션 시작 시간 사이의 *간격(리듬)*에 적용됩니다. 이는 마치 시간에 이징 곡선을 적용하는 것과 같습니다.

예를 들어  stagger: { each: 0.1, ease: "power1.out" }을 적용하면, 처음 몇 개의 요소는 매우 빠르게 연속적으로 애니메이션이 시작되고, 뒤로 갈수록 시작 간격이 점점 길어지는 독특한 리듬감을 연출할 수 있습니다.

gsap.to('.box', {
  scale: 0.5,
  duration: 1,
  stagger: {
    each: 0.1,
    from: "center",
    ease: "power2.inOut"
  }
});

5.3 그리드 기반 스태거링: grid와 axis

요소들이 2차원 그리드 형태로 배열되어 있을 때, grid 속성을 사용하면 위치에 기반한 스태거 효과를 쉽게 적용할 수 있습니다. grid: "auto"로 설정하면 GSAP이 요소들의 위치를 분석하여 그리드 구조를 자동으로 감지합니다. axis: "x" 또는 axis: "y"를 사용하면 특정 축을 기준으로 한 근접성에 따라 스태거 효과를 적용할 수도 있습니다. 이는 반응형 레이아웃에서 그리드 차원이 동적으로 변경될 때 특히 유용합니다.  

gsap.to('.grid-item', {
  opacity: 0,
  stagger: {
    grid: "auto",
    from: "center",
    amount: 1.5
  }
});

Chapter 6: 주요 GSAP 플러그인 탐험


6.1 TextPlugin으로 타이핑 효과 만들기

TextPlugin은 DOM 요소의 텍스트 콘텐츠를 타이핑하듯 한 글자씩 변경하는 효과를 쉽게 구현하게 해줍니다. 기본적으로 text: "새로운 텍스트" 구문을 사용하며, delimiter: " " 옵션을 추가하면 글자 단위가 아닌 단어 단위로 텍스트가 변경되는 효과를 만들 수 있습니다.  

gsap.to(".headline", {
  duration: 2,
  text: "이것이 새로운 텍스트입니다.",
  ease: "none"
});

6.2 MotionPathPlugin으로 경로 따라 움직이기

MotionPathPlugin을 사용하면 어떤 DOM 요소나 SVG 요소든 미리 정의된 SVG 경로를 따라 움직이게 할 수 있습니다. path 속성에 SVG 경로의 선택자나 경로 데이터를 직접 전달하고, autoRotate: true를 설정하면 요소가 경로의 방향에 맞춰 자동으로 회전합니다. align 속성을 사용하면 요소의 기준점을 경로에 정렬할 수 있습니다. 또한, MotionPathHelper라는 개발 도구를 사용하면 브라우저 상에서 직접 경로를 시각적으로 편집하고 생성할 수 있어 작업 효율을 크게 높일 수 있습니다.  

gsap.to("#ball", {
  motionPath: {
    path: "#path-curve",
    align: "#path-curve",
    autoRotate: true,
    alignOrigin: [0.5, 0.5] // 공의 중앙을 경로에 정렬
  },
  duration: 5,
  ease: "power1.inOut"
});

6.3 SplitText로 복잡한 텍스트 효과 만들기

전문가 수준의 텍스트 애니메이션은 대부분 SplitText 플러그인을 기반으로 합니다. SplitText는 텍스트를 글자(chars), 단어(words), 줄(lines) 단위로 분리하고, 각각을 <div> 태그로 감싸 개별적으로 애니메이션할 수 있는 상태로 만들어 줍니다.  

가장 일반적인 패턴은 텍스트를 분리한 후, 생성된 split.chars 또는 split.words 배열에 스태거 애니메이션을 적용하는 것입니다. 이를 통해 글자들이 하나씩 날아오거나, 단어들이 순차적으로 나타나는 등 매우 다채롭고 정교한 텍스트 효과를 구현할 수 있습니다.  

// 텍스트를 글자와 단어 단위로 분리
let split = new SplitText(".quote", { type: "words, chars" });
let chars = split.chars; // 분리된 글자들의 배열

// 글자들이 아래에서 위로 순차적으로 나타나는 애니메이션
gsap.from(chars, {
  duration: 0.8,
  opacity: 0,
  y: 100,
  ease: "circ.out",
  stagger: 0.02
});

Part 4: ScrollTrigger를 활용한 스크롤 기반 스토리텔링의 기술

이 파트는 GSAP에서 가장 인기 있고 강력한 플러그인인 ScrollTrigger에 대해 심층적으로 다룹니다. ScrollTrigger를 통해 사용자의 스크롤 인터랙션에 반응하는 몰입감 넘치는 웹 경험을 만드는 방법을 배웁니다.


Chapter 7: ScrollTrigger 기초


7.1 첫 스크롤 트리거 애니메이션 설정하기

ScrollTrigger를 사용하는 가장 기본적인 방법은 트윈의 vars 객체에 scrollTrigger 속성을 추가하는 것입니다. 이렇게 하면 해당 애니메이션은 기본적으로 trigger 요소가 뷰포트(viewport)에 들어올 때 실행됩니다.  

gsap.from(".box", {
  opacity: 0,
  x: -100,
  duration: 1,
  scrollTrigger: ".box" //.box 요소가 뷰포트에 들어오면 애니메이션 시작
});

7.2 핵심 속성: trigger, start, end, toggleActions

ScrollTrigger의 동작을 정밀하게 제어하기 위해 다음 핵심 속성들을 이해해야 합니다.

  • trigger: ScrollTrigger를 활성화시키는 기준이 되는 요소입니다.  
  • start / end: 애니메이션이 시작되고 끝나는 스크롤 위치를 정의합니다. 값은 "top center"와 같은 문자열 형식으로 지정되며, 이는 "트리거 요소의 상단(top)이 뷰포트의 중앙(center)에 닿을 때"를 의미합니다. 첫 번째 값은 트리거 요소의 기준점, 두 번째 값은 뷰포트의 기준점을 나타냅니다.  
  • toggleActions: 스크롤 위치가 start와 end 지점을 통과할 때 애니메이션을 어떻게 처리할지 결정합니다. 값은 "onEnter onLeave onEnterBack onLeaveBack" 순서에 따라 4개의 키워드로 구성됩니다. (예: "play pause resume reset").  

7.3 markers로 디버깅하기

개발 과정에서 markers: true 옵션을 사용하는 것은 매우 중요합니다. 이 옵션은 start와 end 지점을 시각적으로 화면에 표시해주어, 트리거 위치를 직관적으로 확인하고 미세 조정할 수 있게 해줍니다. 이는 복잡한 스크롤 애니메이션을 구현할 때 디버깅 시간을 획기적으로 단축시켜 줍니다.  

gsap.to(".box", {
  rotation: 360,
  scrollTrigger: {
    trigger: ".box",
    start: "top center", // 트리거의 상단이 뷰포트 중앙에 닿을 때 시작
    end: "bottom top",   // 트리거의 하단이 뷰포트 상단에 닿을 때 종료
    toggleActions: "play reverse play reverse",
    markers: true      // 개발 중에만 사용
  }
});

Chapter 8: 고급 ScrollTrigger 기법


8.1 스크롤 위치와 애니메이션 연동: scrub 속성

scrub: true를 설정하면 애니메이션의 진행 상태가 스크롤바의 위치에 직접 연결됩니다. 사용자가 스크롤을 위아래로 움직이면 애니메이션이 정방향 또는 역방향으로 '문지르듯(scrubbing)' 재생됩니다.  

scrub: 1과 같이 숫자 값을 주면, 스크롤이 멈춘 후 애니메이션이 부드럽게 '따라잡는' 효과를 1초 동안 연출할 수 있습니다.  

gsap.to(".progress-bar", {
  scaleX: 1,
  scrollTrigger: {
    trigger: ".content",
    start: "top top",
    end: "bottom bottom",
    scrub: 0.5 // 0.5초의 부드러운 스크럽 효과
  }
});

8.2 '고정' 요소 만들기: pin 마스터하기

pin: true는 ScrollTrigger가 활성화된 동안 trigger 요소를 뷰포트의 특정 위치에 고정시키는 강력한 기능입니다. 다른 콘텐츠는 스크롤되어 지나가지만 고정된 요소는 제자리에 머물러, 몰입감 높은 섹션을 만드는 데 자주 사용됩니다.  

pinSpacing 속성은 고정된 요소가 차지하던 공간만큼 아래쪽에 자동으로 패딩을 추가하여, pin이 해제될 때 콘텐츠가 튀는 현상을 방지합니다.

ScrollTrigger.create({
  trigger: ".pinned-section",
  start: "top top",
  end: "+=300%", // 섹션 높이의 3배만큼 스크롤될 동안 고정
  pin: true
});

8.3 일반적인 패턴과 함정 피하기

ScrollTrigger를 전문적으로 사용하기 위해서는 몇 가지 흔한 실수와 올바른 패턴을 이해해야 합니다.

  • 함정 1: 중첩된 ScrollTrigger: ScrollTrigger가 적용된 타임라인 내부에 있는 애니메이션에 또 다른 ScrollTrigger를 적용해서는 안 됩니다. 이는 "애니메이션을 타임라인의 재생 헤드(playhead)가 제어해야 하는가, 아니면 스크롤바가 제어해야 하는가?"라는 논리적 충돌을 야기하여 예기치 않은 동작을 발생시킵니다. 해결책은 부모 타임라인에 단 하나의 ScrollTrigger를 적용하여 전체 시퀀스를 제어하는 것입니다.  
  • 함정 2: 여러 to() 트윈의 논리 문제: 동일한 요소의 동일한 속성을 여러 ScrollTrigger를 가진 gsap.to() 트윈으로 애니메이션하면, 두 번째 애니메이션이 시작될 때 요소가 초기 상태로 '점프'하는 현상이 발생할 수 있습니다. 이는 GSAP이 다음 애니메이션의 시작 값을 이전 상태가 아닌, 스타일시트에 정의된 초기 값으로 계산하기 때문입니다. 이 문제는 immediateRender: false를 사용하거나, gsap.fromTo()로 시작과 끝을 명확히 정의하거나, 모든 트윈을 단일 타임라인에 넣어 관리함으로써 해결할 수 있습니다.  
  • 패턴: 여러 섹션에 동일한 애니메이션 적용: 여러 요소에 동일한 스크롤 기반 애니메이션을 적용할 때는, forEach 루프를 사용하여 각 요소에 대해 개별적인 ScrollTrigger 인스턴스를 생성해야 합니다. 이렇게 하면 각 요소가 자신만의 트리거를 갖게 되어 독립적으로 작동합니다.  
 
// 올바른 패턴: forEach를 사용한 개별 트리거 생성
gsap.utils.toArray(".section").forEach(section => {
  gsap.from(section, {
    opacity: 0,
    y: 100,
    scrollTrigger: {
      trigger: section,
      start: "top 80%",
      toggleActions: "play none none reverse"
    }
  });
});

Part 5: 전문가 워크플로우 및 모범 사례

이 마지막 파트는 독자를 유능한 GSAP 사용자에서 성능, 프레임워크 통합, 접근성을 이해하는 전문가로 끌어올리는 데 초점을 맞춥니다.


Chapter 9: 고성능 애니메이션


9.1 브라우저 렌더링 파이프라인: transform과 opacity가 최고인 이유

부드러운 애니메이션을 구현하려면 브라우저의 렌더링 과정을 이해해야 합니다. 브라우저는 일반적으로 **Layout(레이아웃) → Paint(페인트) → Composite(합성)**의 단계를 거쳐 화면을 그립니다.  

width, height, top, margin과 같은 레이아웃 속성을 애니메이션하면, 브라우저는 해당 요소뿐만 아니라 페이지 전체의 레이아웃을 다시 계산해야 합니다. 이를 '리플로우(reflow)'라고 하며, 이는 CPU에 큰 부담을 주는 매우 비싼 작업입니다.  

반면, transform과 opacity 속성은 레이아웃과 페인트 단계를 건너뛰고 합성(Composite) 단계에서 GPU에 의해 직접 처리될 수 있습니다. 이는 해당 요소가 별도의 그래픽 레이어로 분리되어 처리되기 때문이며, 이 과정을 'GPU 가속'이라고 합니다. GPU 가속을 통해 애니메이션은 메인 스레드의 다른 작업(JavaScript 실행 등)에 영향을 받지 않고 초당 60프레임(60fps)의 부드러운 움직임을 유지할 수 있습니다.  


9.2 GPU 가속 강제화와 will-change 사용법

브라우저는 특정 조건(예: 3D transform 적용)에서 요소를 자동으로 별도의 '합성 레이어(compositor layer)'로 승격시켜 GPU 가속을 활성화합니다. 과거에는   

transform: translateZ(0)와 같은 '핵(hack)'을 사용하여 이를 강제했지만, 현대적인 접근 방식은 will-change CSS 속성을 사용하는 것입니다. will-change: transform, opacity;는 브라우저에게 "이 요소의 transform과 opacity 속성이 곧 변경될 것이니 미리 최적화 준비를 하라"는 힌트를 제공합니다.  

그러나 GPU 가속은 렌더링 속도와 메모리 사용량 사이의 트레이드오프 관계에 있습니다. 너무 많은 요소를 합성 레이어로 만들면 각 레이어의 텍스처를 GPU 메모리에 업로드해야 하므로, 특히 모바일 기기에서 메모리 부족 문제를 야기할 수 있습니다. 따라서 GPU 가속은 맹목적으로 적용할 '만능 해결책'이 아니라, 성능 병목 현상이 발생하는 특정 요소에 대해 전략적으로 사용해야 하는 최적화 기법입니다. 개발자는 브라우저 개발자 도구의 'Layers' 패널을 통해 어떤 요소가 레이어로 승격되었는지 확인하고 과도한 사용을 피해야 합니다.  


9.3 실용적인 최적화 팁

다음은 고성능 GSAP 애니메이션을 위한 실용적인 팁 목록입니다.

  • 화면에서 픽셀이 변경되는 영역을 최소화하세요.  
  • CSS filter나 box-shadow와 같이 렌더링 비용이 비싼 속성의 애니메이션은 신중하게 사용하세요.  
  • blur와 같은 비싼 효과는 포토샵 등에서 미리 렌더링된 이미지로 대체하는 것을 고려하세요.  
  • SVG 내부의 개별 요소를 애니메이션하는 것은 전체 SVG를 통째로 움직이는 것보다 훨씬 비용이 많이 듭니다.  
  • 스크롤 애니메이션에 포함될 이미지에 loading="lazy"를 사용하면 스크롤 시 이미지 로딩으로 인한 버벅임(jank)이 발생할 수 있으므로 피하는 것이 좋습니다.

Chapter 10: 현대 프레임워크에서의 GSAP: React 심층 분석


10.1 공식 useGSAP 훅: 현대적인 표준

React 환경에서 GSAP을 사용하는 공식적이고 권장되는 방법은 @gsap/react 패키지에서 제공하는 useGSAP 훅을 사용하는 것입니다. 이 훅은 React의   

useEffect나 useLayoutEffect를 대체하며, GSAP 애니메이션의 생성과 정리를 자동화합니다.  


10.2 범위가 지정된 선택자: 충돌 방지와 깔끔한 코드

useGSAP 훅의 핵심 기능 중 하나는 scope 속성입니다. 컨테이너 ref를 scope에 전달하면, 훅 내부에서 사용되는 모든 선택자(예: ".box")는 해당 컨테이너의 자손 요소 내에서만 검색됩니다. 이는 다른 컴포넌트의 요소를 실수로 타겟팅하는 것을 방지하고, 애니메이션을 적용할 모든 요소에 ref를 일일이 생성해야 하는 번거로움을 없애줍니다.  

const container = useRef();

useGSAP(() => {
  // 이 선택자는 'container' ref 내부의.box만 찾습니다.
  gsap.to(".box", { x: 100 });
}, { scope: container });

return (
  <div ref={container}>
    <div className="box"></div>
  </div>
);

10.3 정리(Cleanup) 마스터하기: React의 StrictMode에서 중요한 이유

useGSAP 훅의 가장 중요한 이점은 자동 정리(cleanup) 기능입니다. 훅 내에서 생성된 모든 GSAP 애니메이션, ScrollTrigger 등은 gsap.context()를 통해 내부적으로 추적되며, 컴포넌트가 언마운트(unmount)될 때 자동으로 원래 상태로 되돌려지고(reverted) 정리됩니다.  

이 기능은 React의 StrictMode에서 특히 중요합니다. StrictMode는 개발 모드에서 잠재적인 문제를 찾기 위해 effect를 두 번 호출하는데, 적절한 정리가 이루어지지 않으면 애니메이션이 중복 생성되어 충돌을 일으킬 수 있습니다. useGSAP는 이러한 문제를 자동으로 우아하게 해결합니다.  

이것은 단순히 편의 기능을 제공하는 것을 넘어, 근본적인 아키텍처 문제를 해결합니다. React는 선언적으로 UI 상태를 기술하는 반면, GSAP은 gsap.to(...)와 같이 직접적인 명령을 내리는 명령형 라이브러리입니다. 컴포넌트가 언마운트되어도 그 안에서 생성된 GSAP 트윈은 명시적으로 제거하지 않는 한 메모리에 계속 남아있게 됩니다. useGSAP의 context 시스템은 이 두 패러다임 사이의 '임피던스 불일치'를 해결하는 샌드박스 역할을 합니다. 즉, 명령형 GSAP 객체의 생명주기를 선언형 React 컴포넌트의 생명주기에 동기화시켜, 컴포넌트가 사라질 때 그 안의 모든 애니메이션도 함께 안전하게 제거되도록 보장합니다.


10.4 contextSafe로 사용자 상호작용 처리하기

클릭 이벤트와 같이 사용자의 상호작용에 의해 트리거되는 애니메이션은 useGSAP 훅이 실행된 이후에 생성되므로 자동 정리 대상에 포함되지 않습니다. 이 경우, useGSAP 훅이 반환하는 contextSafe 함수를 사용하여 이벤트 핸들러를 감싸주어야 합니다. 이렇게 하면 이벤트 핸들러 내에서 생성된 애니메이션도 context에 등록되어 컴포넌트가 언마운트될 때 함께 정리됩니다.  

const container = useRef();
const { contextSafe } = useGSAP({ scope: container });

const onBoxClick = contextSafe(() => {
  gsap.to(".box", { rotation: "+=360" });
});

return <div className="box" onClick={onBoxClick}>Click me</div>;

Chapter 11: 접근성 높은 애니메이션 만들기


11.1 prefers-reduced-motion 이해하기

일부 사용자, 특히 전정 장애가 있는 사람들은 과도한 움직임으로 인해 어지러움, 두통, 메스꺼움과 같은 신체적 불편함을 겪을 수 있습니다. 

prefers-reduced-motion은 사용자가 운영체제 수준에서 애니메이션을 최소화하도록 설정했는지 감지하는 CSS 미디어 쿼리입니다. 우리는 이 설정을 존중하여 모든 사용자가 쾌적하게 웹을 경험할 수 있도록 해야 합니다.  


11.2 gsap.matchMedia()로 사용자 선호도 존중하기

gsap.matchMedia()는 반응형 및 접근성을 고려한 애니메이션을 쉽게 만들 수 있는 GSAP의 도구입니다. 이를 사용하여  prefers-reduced-motion 설정에 따라 다른 애니메이션을 적용할 수 있습니다. 일반적인 패턴은 (prefers-reduced-motion: no-preference)(선호도 없음)와 (prefers-reduced-motion: reduce)(동작 줄이기) 두 가지 경우에 대해 별도의 애니메이션 로직을 작성하는 것입니다.  

let mm = gsap.matchMedia();

// 사용자가 '동작 줄이기'를 설정하지 않은 경우 (기본 애니메이션)
mm.add("(prefers-reduced-motion: no-preference)", () => {
  gsap.to(".box", {
    rotation: 360,
    duration: 2
  });
});

// 사용자가 '동작 줄이기'를 설정한 경우 (단순화된 애니메이션)
mm.add("(prefers-reduced-motion: reduce)", () => {
  gsap.from(".box", {
    opacity: 0,
    duration: 1
  });
});

11.3 동작 줄이기 vs. 제거하기 전략

접근성을 위한 애니메이션 조정이 항상 모든 애니메이션을 제거하는 것을 의미하지는 않습니다. UI의 기능성을 유지하면서 잠재적인 신체적 유발 요인을 제거하는 것이 목표입니다.

  • 동작 줄이기(Reduce): 큰 움직임이나 화면을 가로지르는 애니메이션을 단순한 페이드(fade) 효과로 대체하는 것처럼, 애니메이션의 본질적인 정보 전달 기능은 유지하되 자극적인 움직임을 줄이는 전략입니다.  
  • 동작 제거하기(Remove): 화면 전체가 흔들리거나, 빠르게 깜빡이는 등 특히 자극이 심한 애니메이션은 완전히 제거하는 것이 좋습니다.  

가장 좋은 방법은 사용자가 직접 제어할 수 있도록 UI 내에 애니메이션 토글 버튼을 제공하는 것입니다. 이를 통해 모든 사용자가 자신의 필요에 맞게 경험을 맞춤 설정할 수 있습니다.  

 

'Graphic > lib' 카테고리의 다른 글

glTF 성능 최적화(Draco 메시 압축 + KTX2 텍스처 + Three.js 로더 설정)  (0) 2025.11.18
motion(framer-motion)  (1) 2025.06.09
maath  (0) 2025.06.09
React Three Rapier  (3) 2025.05.27
GSAP (GreenSock Animation Platform)  (0) 2025.03.07