REPO DEEP DIVE · 2026-07-29 · GREENSOCK/GSAP · 웹 애니메이션의 사실상 표준 엔진

greensock/GSAP 딥다이브
"모든 것을 움직이는" 단일 틱 엔진, 이제 100% 무료

GSAP(GreenSock Animation Platform)는 CSS·SVG·캔버스·WebGL·일반 JS 객체까지 "숫자로 표현되는 모든 값"을 시간에 따라 부드럽게 바꿔주는 프레임워크 무관 애니메이션 엔진이다. React든 Vue든 순수 HTML이든 상관없이 붙는다.

2008년부터 개발된 이 라이브러리는 2025년 Webflow에 인수된 뒤 유료였던 "Club GSAP" 전용 플러그인(ScrollTrigger, SplitText, MorphSVG, Draggable 등)이 전부 무료로 풀리면서 다시 한 번 크게 주목받고 있다. 코드 몇 줄로 스크롤 연동 애니메이션·텍스트 분할·모핑까지 되는데 심지어 공짜라는 사실이 확산의 기폭제가 됐다.

(저장소 greensock/GSAP · 제작 Jack Doyle(GreenSock), 현 Webflow 소속 · 라이선스 Standard "no-charge" license(MIT 아님) · 버전 3.15.0 · 런타임 의존성 0개 · 코어 gsap-core.js 약 3,250줄 + 플러그인 20여 개 · 최신 커밋 13e2b79, 2026-04-13 · 12M+ 사이트에서 사용 중이라고 스스로 소개)
목차
  1. 한 줄 정체
  2. 왜 지금 뜨는가
  3. 무엇을 해결하나
  4. 전체 구조
  5. 핵심 설계·동작 원리
  6. 기술 스택·의존성
  7. 설치·요구사항
  8. 실제 사용 흐름
  9. 함정·오해 방지
  10. 대안 비교
  11. 정리 — 누가 쓰면 좋은가

1한 줄 정체

"타깃이 무엇이든, 숫자로 표현되는 값이면 시간에 따라 보간해주는 고속 엔진"

GSAP를 한 문장으로 요약하면 "고성능 프로퍼티 조작기(property manipulator)"다. gsap.to(target, {x: 100, duration: 1}) 처럼 "이 대상의 이 속성을 이 값까지, 이 시간 동안 바꿔라"라고 말하면, 매 프레임 그 값을 계산해 대상에 적용해준다. 대상이 DOM 엘리먼트든, SVG든, Canvas 컨텍스트든, {x:0} 같은 순수 JS 객체든 상관하지 않는다.

핵심 저장소인 greensock/GSAP에는 코어 엔진(gsap-core.js)과, 그 위에 얹어 쓰는 20여 개의 플러그인(스크롤 연동 ScrollTrigger, 텍스트 쪼개기 SplitText, 드래그 Draggable, SVG 모핑 MorphSVGPlugin, 레이아웃 전환 Flip 등)이 함께 들어 있다. 예전에는 이 중 다수가 Club GSAP라는 유료 멤버십 전용이었지만, 2025년 Webflow 인수 이후 전부 무료로 전환됐다.

한 줄 비유

"세상 모든 슬라이더 손잡이를 동시에, 정확한 타이밍으로 움직여주는 오케스트라 지휘자"

웹페이지 안의 색상·위치·크기·스크롤 위치는 결국 전부 숫자다. GSAP는 이 숫자 슬라이더들을 "몇 초 동안, 어떤 곡선(easing)으로, 어떤 순서로" 움직일지 지휘하는 지휘자다. 악기(대상)가 바이올린(CSS)이든 드럼(캔버스)이든, 지휘자는 박자(프레임)만 정확히 맞춰주면 된다.

그리고 이 지휘자는 무대(브라우저) 전체에 단 한 명만 존재한다 — 뒤에서 볼 "단일 틱 엔진"이 그 이유다.

2왜 지금 뜨는가

Webflow 인수 + "전부 무료" 선언 · 스크롤 스토리텔링 유행 · React 생태계 편입

GSAP는 신생 라이브러리가 아니다. 오히려 2008년부터 17년 넘게 웹 애니메이션 표준처럼 쓰여 온 "구관"이다. 그런데도 2026년 지금 다시 화제가 되는 이유는 셋으로 요약된다.

① Webflow 인수 → "유료 플러그인 전부 무료" 선언

README에는 이렇게 적혀 있다: "Thanks to Webflow, GSAP is now 100% FREE including ALL of the bonus plugins like SplitText, MorphSVG, and all the others that were exclusively available to Club GSAP members." 예전에는 SplitText·MorphSVGPlugin·InertiaPlugin·ScrollSmoother 같은 강력한 도구를 쓰려면 연 구독료를 내는 Club GSAP 멤버십이 필요했다. 이 장벽이 통째로 사라졌다는 것이 이번 화제의 진앙이다.

② "스크롤이 곧 스토리텔링"이 되는 웹 디자인 트렌드

최근 랜딩페이지·포트폴리오·에이전시 사이트들은 스크롤에 맞춰 요소가 나타나고, 고정(pin)되고, 모양이 바뀌는 연출을 즐겨 쓴다. 이런 연출의 사실상 표준 도구가 바로 무료로 풀린 ScrollTrigger다. 유료 장벽이 없어지자 튜토리얼·템플릿·쇼케이스가 폭발적으로 늘었다.

③ React 생태계와의 자연스러운 결합

공식 보조 패키지 @gsap/react가 제공하는 useGSAP() 훅은 useEffect를 대체하며 정리(cleanup)를 자동화한다. "React는 선언적, GSAP는 명령적"이라는 이질감이 이 훅과 gsap.context() 덕분에 크게 줄어, React/Next.js 프로젝트에서도 부담 없이 쓰이게 됐다.

경쟁 지형
Motion(구 Framer Motion)·anime.js·CSS 애니메이션 사이에서

선언적 React 애니메이션은 Motion이, 가벼운 범용 트위닝은 anime.js가 맡는 영역이 있다. GSAP의 차별점은 타임라인 기반 정밀 시퀀싱 + 스크롤/드래그/모핑까지 아우르는 플러그인 생태계다. "간단한 트랜지션 하나"가 아니라 "정교하게 지휘된 다단계 연출"이 필요할 때 GSAP가 선택된다. 자세한 비교는 10번 섹션 참고.

3무엇을 해결하나

브라우저 애니메이션의 3대 고질병 — 크로스브라우저 불일치 · 시퀀싱 난이도 · 성능

순수 CSS transition/@keyframes나 Web Animations API만으로도 애니메이션은 가능하다. 그런데도 GSAP가 필요한 이유는 CSS·JS 애니메이션이 실제 제품에서 부딪히는 세 가지 벽 때문이다.

① 크로스브라우저 변환(transform) 불일치

transform: translateX()rotate()를 동시에 쓰거나, SVG 요소에 CSS 변환을 걸 때 브라우저마다 미묘하게 다르게 동작하던 시절이 있었다. GSAP의 CSSPlugin은 이런 불일치를 흡수해 "당신의 애니메이션은 그냥 동작한다(just works)"는 약속을 지킨다.

② 복잡한 시퀀싱을 코드로 표현하기 어려움

"A가 끝나고 0.2초 뒤 B와 C가 동시에 시작하고, 그 중간 지점에 D가 끼어든다" 같은 시나리오를 setTimeout이나 CSS 애니메이션 지연 값 계산으로 짜면 순식간에 스파게티가 된다. GSAP의 타임라인(Timeline)은 이런 시퀀스를 "타임코드"라는 직관적인 위치 파라미터("-=0.2", "<" 등)로 선언적으로 표현하게 해준다.

③ 대량 애니메이션의 성능

한 페이지에 수백 개의 트윈이 동시에 돌아가면, 트윈마다 개별 requestAnimationFrame 콜백을 등록하는 순진한 구현은 금방 버벅인다. GSAP는 페이지 전체의 모든 애니메이션을 단 하나의 rAF 루프로 묶어 처리한다(5번 섹션에서 자세히). README는 "jQuery보다 최대 20배 빠르다"고 주장하는데, 근거가 바로 이 단일 틱 구조와 프로퍼티별 최적화된 렌더러다.

용어
트위닝 (tweening)
"in-betweening"의 줄임말. 시작 값과 끝 값 사이의 중간(between) 프레임들을 자동으로 계산해 채워주는 것. GSAP의 gsap.to()가 하는 일이 바로 이것이다.
용어
타임라인 (Timeline)
여러 개의 트윈(또는 다른 타임라인)을 순서·겹침·간격까지 지정해 하나의 시퀀스로 묶는 컨테이너. 영상 편집 소프트웨어의 타임라인과 개념이 같다.

4전체 구조

코어 1개 + 플러그인 20여 개, "필요한 것만 골라 등록"하는 구조

src/ 폴더를 열어보면 구조가 명확하다. 코어(gsap-core.js, 약 3,255줄) 하나가 엔진·타임라인·트윈·플러그인 등록 시스템을 전부 담당하고, 나머지는 전부 독립된 플러그인 파일이다. 코어만 있어도 순수 JS 객체 애니메이션은 되지만, DOM을 움직이려면 CSSPlugin부터 필요하다.

┌─────────────────────────────────────────────────────────────────┐ │ GSAP 저장소 구조 (src/) │ └─────────────────────────────────────────────────────────────────┘ gsap-core.js # ★ 심장 — Animation/Tween/Timeline, 틱 엔진, 플러그인 레지스트리 index.js # npm "gsap" 메인 진입점 — 코어 + CSSPlugin 만 (얇음!) all.js # "gsap/all" — 사실상 모든 플러그인을 한 번에 export ── DOM/시각 기본 ── CSSPlugin.js CSSRulePlugin.js EasePack.js ── 스크롤 계열 ── ScrollTrigger.js (1,790줄) ScrollSmoother.js Observer.js ── 인터랙션 ── Draggable.js (1,950줄) InertiaPlugin.js Physics2DPlugin.js PhysicsPropsPlugin.js ── SVG/모핑/텍스트 ── MorphSVGPlugin.js (859줄) DrawSVGPlugin.js MotionPathPlugin.js MotionPathHelper.js SplitText.js/.ts (333/517줄) TextPlugin.js ScrambleTextPlugin.js ── 레이아웃 전환 ── Flip.js (1,058줄) ── 이징 ── CustomEase.js CustomBounce.js CustomWiggle.js ── 렌더러 통합 ── PixiPlugin.js EaselPlugin.js ── 개발 도구 ── GSDevTools.js ── 빌드 산출물 ── dist/ # UMD(전역 <script>/CommonJS), *.min.js + .map esm/ # ES 모듈 (번들러용) types/ # TypeScript .d.ts

구조가 주는 메시지는 명확하다. "코어는 아무것도 모른다". 코어는 CSS도, 스크롤도, SVG도 모른다. 오직 "속성을 시간에 따라 보간하고, 그 결과를 렌더러에게 넘긴다"는 일반적인 메커니즘만 안다. DOM을 움직이는 지식(transform 파싱, 단위 처리 등)은 전부 CSSPlugin에, 스크롤 위치 감지는 ScrollTrigger에 위임돼 있다. 이 관심사 분리 덕분에 필요한 기능만 골라 번들 크기를 최소화할 수 있다.

구조상 주의
"gsap" 메인 패키지 ≠ "모든 기능 포함"

package.jsonmain/module이 가리키는 index.js코어 + CSSPlugin뿐이다. ScrollTrigger·Draggable·SplitText 등은 전부 import ScrollTrigger from "gsap/ScrollTrigger" 식으로 따로 import하고 registerPlugin해야 쓸 수 있다. "gsap 하나만 설치하면 다 된다"는 오해가 흔하다.

5핵심 설계·동작 원리

단일 틱 엔진 · Animation→Tween/Timeline 계층 · PropTween 렌더 파이프라인 · 플러그인 자가등록 · Context/matchMedia

이 섹션이 이 문서에서 가장 중요하다. GSAP가 왜 빠르고, 왜 "타임라인"이라는 개념이 강력한지는 전부 gsap-core.js 소스 안에 있다.

① 단일 틱 엔진 — 페이지 전체에 rAF 콜백은 단 하나

gsap-core.js 안의 _ticker는 즉시실행함수로 만들어진 독립 객체다. 실제 코드를 보면:

_ticker = (function() {
  let _getTime = Date.now,
    _gap = 1000 / 240,           // 내부 정밀도 240fps 기준
    _tick = v => {
      let elapsed = _getTime() - _lastUpdate, ...
      if (overlap > 0 || manual) {
        frame = ++_self.frame;
        _self.time = time = time / 1000;
        dispatch = 1;
      }
      manual || (_id = _req(_tick));      // 다음 rAF 예약
      if (dispatch) {
        for (_i = 0; _i < _listeners.length; _i++) {
          _listeners[_i](time, _delta, frame, v);  // 리스너 전원 호출
        }
      }
    };
  ...
})();

// 코어 파일 맨 끝부분:
_ticker.add(Timeline.updateRoot);   // 전체 애니메이션 트리의 유일한 진입점

즉 브라우저의 requestAnimationFrame은 딱 하나만 걸려 있고, 그 콜백이 _listeners 배열을 순회한다. 그런데 이 배열에 등록된 리스너는 사실상 Timeline.updateRoot 단 하나뿐이다 — 이것이 루트 타임라인(_globalTimeline)을 갱신하고, 루트가 자신의 모든 자식(하위 타임라인·트윈)을 재귀적으로 갱신한다. 페이지에 트윈이 1개든 1만 개든 브라우저 입장에서는 rAF 콜백이 딱 하나인 셈이다.

비유

학교에서 "종을 치는 사람"이 한 명뿐이라고 생각해보자. 종이 울리면(rAF 틱) 교장선생님(루트 타임라인)이 그 신호를 받아 각 학년 부장(하위 타임라인)에게 전달하고, 부장은 다시 각 반 담임(개별 트윈)에게 전달한다. 학생(속성값) 수가 아무리 많아도 종은 하나다 — 이게 GSAP가 대량의 애니메이션에서도 버벅이지 않는 이유다.

② Animation → Tween / Timeline 계층 구조

gsap-core.js에는 class Animation(공통 기반), 그 아래 class Tween extends Animationclass Timeline extends Animation이 정의돼 있다. Timeline은 자식으로 Tween이나 다른 Timeline을 가질 수 있는 합성(Composite) 구조다. 실제로 페이지의 전체 애니메이션은 _globalTimeline = new Timeline({id:"root", ...})라는 단 하나의 루트를 정점으로 한 트리(연결 리스트, _first/_next로 순회)를 이룬다. gsap.timeline()으로 만든 타임라인도 결국 이 루트의 자식으로 편입된다.

클래스역할
Animation재생/일시정지/시간 탐색(seek)/진행률(progress) 등 모든 애니메이션 공통 기능의 기반 클래스
Tween실제 속성값을 바꾸는 최소 단위. _ptLookup(대상별 PropTween 목록)을 들고 있음
Timeline여러 Tween/Timeline을 순서·겹침으로 배치하는 컨테이너. 자신도 Animation이라 타임라인끼리 중첩 가능

③ PropTween — 속성 하나하나가 연결 리스트 노드가 된다

트윈이 실제로 값을 바꾸는 최소 단위는 class PropTween이다. 대상(target)·속성명(p)·렌더 함수(r)·시작값(s)·변화량(c)·setter를 들고 있는 가벼운 객체이며, 트윈 하나가 여러 속성(x, y, opacity...)을 가지면 그만큼의 PropTween이 연결 리스트(_pt_next)로 이어진다. 매 틱마다 이 리스트를 한 번 순회하며 각 노드의 렌더 함수를 호출하는 것만으로 모든 속성이 갱신된다 — 배열 생성이나 객체 순회 오버헤드를 최소화한 설계다.

용어
PropTween
"Property Tween"의 줄임. 하나의 속성(예: x좌표)에 대한 시작값·끝값·렌더 방법을 담은 최소 단위 노드. GSAP 내부 성능의 비밀은 이 가벼운 구조체를 연결 리스트로 이어 순회 비용을 최소화한 데 있다.

④ 플러그인 시스템 — 등록(register) 기반의 확장

코어에는 let _plugins = {}라는 빈 레지스트리가 있다. gsap.registerPlugin(ScrollTrigger, Flip, ...)을 호출하면 내부 _createPlugin()이 각 플러그인 설정을 받아 init/render/kill 생명주기를 가진 Plugin 클래스를 만들고 _plugins에 등록한다. 이후 gsap.to(el, {scrollTrigger: {...}})처럼 등록된 이름의 속성을 쓰면 해당 플러그인의 렌더 로직이 PropTween 파이프라인에 끼워 넣어진다.

재미있는 점은 각 플러그인 파일이 스스로 등록을 시도한다는 것이다. 예를 들어 ScrollTrigger.js 맨 끝 줄은 이렇다:

// ScrollTrigger.js 맨 끝
_getGSAP() && gsap.registerPlugin(ScrollTrigger);

// _getGSAP()의 정의:
_getGSAP = () => gsap || (_windowExists() && (gsap = window.gsap) && gsap.registerPlugin && gsap)

<script> 태그로 전역 window.gsap가 이미 있는 환경(브라우저 전역 빌드)에서는 import만 해도 자동으로 등록된다. 하지만 번들러(webpack/Vite) + ESM 환경에서는 전역 window.gsap가 없을 수 있어 자동 등록이 실패하고, 개발자가 직접 gsap.registerPlugin(ScrollTrigger)를 호출해야 한다(9번 섹션의 대표적 함정).

⑤ gsap.context() / matchMedia — 프레임워크 친화적 정리(cleanup)

class Context는 특정 함수 스코프 안에서 생성된 모든 애니메이션(과 셀렉터)을 this.data 배열에 수집한다. context.revert()를 호출하면 수집된 모든 트윈·타임라인을 역순으로 되돌리고 제거한다 — React의 useEffect cleanup이나 컴포넌트 unmount에서 "이 컴포넌트가 만든 애니메이션만 깔끔히 지우기"에 정확히 맞는 도구다. class MatchMedia는 이 Context를 브레이크포인트별로 감싸, 네이티브 window.matchMedia()change 이벤트에 맞춰 조건에 맞는 애니메이션만 자동으로 되돌리고 다시 만든다.

실전 연결고리

공식 React 훅 useGSAP()(별도 패키지 @gsap/react)는 내부적으로 바로 이 gsap.context()를 감싼 것이다. "React에서 GSAP 쓰다가 메모리 누수 나는" 문제 대부분은 이 context를 안 쓰고 직접 gsap.to()를 호출해서 생긴다.

⑥ 렌더 파이프라인과 자동 절전(autoSleep)

틱마다 루트 타임라인은 render(totalTime)을 호출해 현재 로컬 시간을 계산하고, 각 자식의 PropTween 리스트를 순회해 값을 적용한다. 흥미로운 디테일은 자동 절전(autoSleep)이다. 코드 상에는 이렇게 돼 있다:

if (_ticker.frame >= _nextGCFrame) {
  _nextGCFrame += _config.autoSleep || 120;
  ...
  child || _ticker.sleep();   // 돌아가는 애니메이션이 없으면 rAF 루프 자체를 정지
}

애니메이션이 하나도 없는 순간에는 _ticker.sleep()으로 rAF 요청 자체를 멈춰 배터리·CPU를 아낀다. 새 트윈이 만들어지면 _wake()가 다시 깨운다.

⑦ CustomEase — SVG 패스를 이징 함수로 압축

CustomEase.js는 SVG cubic-bezier 패스 문자열을 파싱해 내부 좌표 배열로 정규화(_normalize)한 뒤, gsap.registerEase()로 이름 붙은 커스텀 이징 함수를 등록한다. 디자이너가 그래픽 에디터에서 그린 곡선을 그대로 이징으로 쓸 수 있게 해주는 다리 역할이다.

6기술 스택·의존성

런타임 의존성 0개 · UMD/ESM/TypeScript 삼중 빌드 · 버전의 진실원은 package.json
항목내용
런타임 의존성0개. package.jsondependencies 필드 자체가 없다. README도 "Zero dependencies"를 명시적으로 강조
빌드 산출물dist/(UMD — 전역 <script>·CommonJS require 겸용) · esm/(ES 모듈, 번들러용) · types/(TypeScript .d.ts)
모듈 진입점package.jsonexports 맵: import → esm/index.js, require → dist/gsap.js, types → types/index.d.ts. 하위 경로도 gsap/ScrollTrigger 식으로 동일 매핑
버전 진실원package.json"version": "3.15.0". 각 플러그인 소스 파일 상단 주석(/*! ScrollTrigger 3.15.0 */)에도 버전이 중복 기재돼 있어, 배포 시 사람이 함께 맞춰야 하는 구조(수동 동기화 지점)
개발 도구 체인package.jsonscripts·devDependencies 필드가 없다 — 빌드/압축(min)·소스맵 생성 파이프라인은 이 공개 저장소 밖에서 비공개로 돈다. 즉 이 레포는 "소스 + 완성된 빌드 산출물"의 읽기용 미러에 가깝다
테스트/CI레포 안에 .github/workflows·테스트 파일이 보이지 않는다. GreenSock 내부 QA는 비공개로 진행되는 것으로 보이며, 공개 레포만 보고 "테스트가 없다"고 단정하면 오해다
보조 패키지@gsap/react(별도 npm 패키지, 이 레포에는 없음) — useGSAP() 훅으로 React cleanup 자동화
타입스크립트types/에 전체 .d.ts 제공. 다만 SplitTextsrc/.js(333줄)와 .ts(517줄) 두 버전이 공존 — TS로 전면 재작성 중인 과도기 흔적
용어
UMD (Universal Module Definition)
CommonJS(require)·AMD·전역 <script> 세 가지 모듈 방식을 하나의 파일이 전부 지원하도록 감싸는 패턴. GSAP의 dist/gsap.js 최상단이 이 패턴으로 시작한다.
용어
라이선스 "Standard no-charge license"
MIT·Apache처럼 OSI가 인증한 표준 오픈소스 라이선스가 아니다. GreenSock(Webflow)이 만든 자체 약관으로, "돈을 받지 않는다"는 의미의 무료이지 소스 재배포·재판매 등에는 별도 제약이 있을 수 있는 독점(proprietary) 라이선스다. 실제 GitHub 저장소에는 LICENSE 파일조차 없고, package.json과 README가 gsap.com/standard-license 링크만 가리킨다.

7설치·요구사항

CDN 한 줄 또는 npm install gsap · 브라우저 전용, Node 서버 실행형 아님

GSAP는 서버가 아니라 브라우저(또는 브라우저와 유사한 렌더링 컨텍스트)에서 도는 클라이언트 라이브러리다. "설치"는 사실상 스크립트 참조 또는 npm 패키지 추가가 전부다.

방법 1 — CDN (가장 빠른 시작)

<script src="https://cdn.jsdelivr.net/npm/gsap@3.15/dist/gsap.min.js"></script>
<script>
  gsap.to(".box", { x: 300, duration: 1 });
</script>

전역 빌드이므로 플러그인 스크립트도 순서대로 추가하면 _getGSAP()window.gsap을 찾아 자동 등록해준다.

방법 2 — npm (번들러 환경)

npm install gsap
import gsap from "gsap";
import ScrollTrigger from "gsap/ScrollTrigger";
import Flip from "gsap/Flip";

gsap.registerPlugin(ScrollTrigger, Flip);  // 번들러 환경에선 자동등록이 안 되므로 명시 필수
항목요구사항 / 참고
대상 환경모던 에버그린 브라우저(Chrome/Firefox/Safari/Edge). v3부터 IE11 등 구형 지원 부담이 크게 줄었음
번들러ESM(esm/) 인식 가능한 아무 번들러(Webpack/Vite/Rollup/esbuild)나 사용 가능. 트리쉐이킹을 위해 sideEffects:falsepackage.json에 명시돼 있음
과거 유료 플러그인 설치이전엔 GreenSock 전용 npm 레지스트리 로그인(.npmrc 토큰)이 필요했지만, 2025년 무료 전환 이후로는 공개 npm에서 바로 설치 가능
SSR (Next.js 등)코어는 _windowExists() 체크로 서버에서 안전하지만, 스크롤·드래그처럼 DOM에 의존하는 플러그인 등록·실행은 클라이언트 전용 컴포넌트/훅 안에서 해야 함
패키지 규모런타임 의존성 0개이므로 node_modules 설치 시 부수 패키지가 딸려오지 않음(gsap 자체만 추가됨)
주의
"npm install gsap"만으로 모든 플러그인이 자동 등록되진 않는다

패키지 설치는 모든 플러그인 파일을 node_modules에 내려받지만, 실제로 쓰려는 플러그인은 개별 import + registerPlugin이 필요하다. "gsap/all"에서 한 번에 가져올 수도 있지만 그만큼 번들 크기가 커진다.

8실제 사용 흐름

타임라인 작성 → 플러그인 등록 → 스크롤 연동 → React에서 안전하게 정리

기본적인 사용 흐름을 단계별로 살펴본다.

1단계 — 단순 트윈과 타임라인

import gsap from "gsap";

// 단발성 트윈: .box를 1초간 x=300으로
gsap.to(".box", { x: 300, duration: 1, ease: "power2.out" });

// 타임라인: 순서·겹침을 타임코드로 지정
const tl = gsap.timeline();
tl.to(".title", { opacity: 1, y: 0, duration: 0.6 })
  .to(".subtitle", { opacity: 1, duration: 0.4 }, "-=0.2")  // 앞 트윈 끝나기 0.2초 전 시작
  .to(".cta", { scale: 1 }, "<");                            // 바로 앞 트윈과 동시 시작

2단계 — ScrollTrigger로 스크롤 연동

import ScrollTrigger from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);

gsap.to(".panel", {
  x: -500,
  scrollTrigger: {
    trigger: ".panel",
    start: "top top",
    end: "+=1000",
    scrub: true,   // 스크롤 위치에 애니메이션 진행률을 직접 연결
    pin: true      // 구간 동안 요소를 화면에 고정
  }
});

3단계 — React에서 gsap.context()로 안전하게 정리

import { useGSAP } from "@gsap/react";
import gsap from "gsap";

function Banner() {
  const container = useRef(null);

  useGSAP(() => {
    gsap.to(".hero", { opacity: 1, y: 0, duration: 0.8 });
    // 컴포넌트가 unmount되면 이 스코프 안 애니메이션이 자동으로 revert됨
  }, { scope: container });

  return <div ref={container}><div className="hero">Hello</div></div>;
}

4단계 — Flip으로 레이아웃 전환 애니메이션

import Flip from "gsap/Flip";
gsap.registerPlugin(Flip);

const state = Flip.getState(".card");  // 변경 "전" 위치·크기 스냅샷
container.appendChild(card);            // DOM 구조를 실제로 바꿈
Flip.from(state, { duration: 0.5, ease: "power1.inOut" }); // "전"에서 "후"로 자연스럽게 보간

이 네 단계는 실제로 GSAP를 쓰는 프로젝트에서 가장 자주 반복되는 패턴이다. 공통점은 "값을 직접 계산해 매 프레임 대입하는" 저수준 작업을 GSAP가 대신 해준다는 것이다.

9함정·오해 방지

"무료 = 오픈소스"는 아니다 · registerPlugin 누락 · README와 실제 코드의 괴리
오해 ①
"무료로 풀렸으니 MIT 오픈소스겠지"

아니다. package.jsonlicense 필드는 "Standard 'no charge' license"라고 명시돼 있고, 저장소에는 LICENSE 파일 자체가 없다. "돈을 받지 않는다"는 것과 "OSI 기준 오픈소스"는 다른 개념이다. 상업적 재배포·경쟁 제품화 등 세부 조건은 반드시 공식 라이선스 페이지에서 직접 확인해야 한다.

오해 ②
"import만 하면 플러그인이 알아서 등록된다"

전역 <script> 빌드에서는 각 플러그인 파일 끝의 _getGSAP() && gsap.registerPlugin(X) 코드가 window.gsap를 찾아 자동 등록해준다. 하지만 ESM/번들러 환경에서는 이 자동 등록 조건이 성립하지 않는 경우가 많아, gsap.registerPlugin(ScrollTrigger)를 빠뜨리면 "Invalid property... Missing plugin?" 경고만 뜨고 애니메이션이 조용히 무시된다.

오해 ③
README의 "gsap/all excludes members-only plugins"는 낡은 문구

README는 "all tools are exported from the 'all' file (excluding members-only plugins)"라고 쓰여 있지만, 실제 src/all.js를 열어보면 MorphSVGPlugin·SplitText·InertiaPlugin·ScrollSmoother·CustomBounce·GSDevTools 등 예전 Club 전용 플러그인이 이미 다 포함돼 있다. 무료화 이전에 쓰인 문구가 그대로 남아 있는 문서-코드 불일치 사례다. 반대로 gsap 메인 진입점(index.js)은 코어+CSSPlugin뿐인 매우 얇은 파일이라는 점도 헷갈리지 않아야 한다.

오해 ④
"레포에 테스트/CI가 없으니 검증이 부실하다"

공개 GitHub 레포에 .github/workflows나 테스트 파일이 보이지 않는 것은 사실이지만, 이는 GreenSock의 빌드·QA 파이프라인이 비공개로 운영된다는 뜻이지 "검증 안 된 코드"라는 뜻이 아니다. 이 레포는 소스 + 완성된 dist/esm/types 산출물을 함께 배포하는 형태에 가깝다.

오해 ⑤
React에서 gsap.context() 없이 쓰다 생기는 메모리 누수

useEffect 안에서 gsap.to()를 직접 호출하고 정리(cleanup)를 빼먹으면, 컴포넌트가 unmount돼도 트윈이 루트 타임라인에 계속 남아 존재하지 않는 DOM을 참조하려 시도한다. gsap.context() 또는 useGSAP()로 스코프를 감싸고 revert()하는 습관이 필수다.

참고
단일 틱 엔진의 이면 — 무거운 콜백 하나가 전체를 버벅이게 할 수 있다

모든 애니메이션이 하나의 rAF 콜백을 공유한다는 것은 장점이자 위험이다. onUpdate 콜백 안에서 무거운 동기 연산(예: 레이아웃을 강제로 읽는 getBoundingClientRect 남발)을 하면, 그 프레임에서 페이지의 모든 GSAP 애니메이션이 함께 지연된다. 무거운 로직은 쓰로틀링하거나 별도 최적화가 필요하다.

10대안 비교

Motion · anime.js · 순수 CSS/Web Animations API와 무엇이 다른가
도구강점GSAP과의 차이
Motion
(구 Framer Motion)
React에 최적화된 선언적 API, 레이아웃 애니메이션(layout prop) 자동화React 바깥에서는 활용도가 낮음. GSAP는 프레임워크 무관하며 타임라인 기반 정밀 시퀀싱·스크롤/드래그/모핑 플러그인 생태계가 훨씬 넓음
anime.js가볍고(용량 작음) API가 간결. 범용 트위닝에 충분ScrollTrigger급 스크롤 연동, SplitText급 텍스트 분할, MorphSVG급 모핑 같은 전문 플러그인 생태계가 없음. "간단한 트윈 몇 개"엔 충분하지만 복합 연출엔 부족
CSS @keyframes/transition추가 JS 없이 브라우저 네이티브, 성능 좋음런타임 제어(일시정지·되감기·동적 값 계산)가 어렵고, 복잡한 시퀀싱을 코드로 표현하기 힘듦. GSAP는 이걸 JS API로 완전히 통제 가능하게 함
Web Animations API (WAAPI)브라우저 네이티브 표준, 별도 라이브러리 불필요API가 저수준이라 타임라인 시퀀싱·이징 커스터마이징·플러그인 확장성 면에서 GSAP보다 개발자 경험이 떨어짐
LottieAfter Effects로 만든 벡터 애니메이션을 그대로 재생"디자이너가 만든 고정 애니메이션 재생"에 특화. GSAP처럼 코드로 동적·인터랙티브하게 값을 바꾸는 용도는 아님
선택 기준 요약

· React 컴포넌트 자체의 mount/unmount/레이아웃 애니메이션이 중심이면 Motion이 더 자연스러울 수 있다.
· 가볍고 단순한 트윈 몇 개면 anime.js나 CSS로 충분하다.
· 스크롤 스토리텔링, 정밀한 다단계 시퀀싱, SVG 모핑, 드래그, 프레임워크 무관 재사용이 필요하면 GSAP가 사실상 표준이며, 이제 비용 장벽도 없다.

11정리 — 누가 쓰면 좋은가

"타임라인이 필요한 순간" 쓰는 도구 — 마케팅 사이트부터 인터랙티브 대시보드까지

GSAP는 "움직임을 정밀하게 지휘해야 하는 모든 웹 프로젝트"에 잘 맞는다. 특히 다음과 같은 사람·상황에 추천한다.

반대로 버튼 hover 색상 전환처럼 아주 단순한 트랜지션 하나만 필요하다면 굳이 GSAP를 끌어올 필요는 없다 — CSS만으로 충분하고, 번들 크기도 아낄 수 있다. "얼마나 복잡한 타임라인을 지휘해야 하는가"가 GSAP 도입 여부를 가르는 가장 실용적인 기준이다.