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+ 사이트에서 사용 중이라고 스스로 소개)
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)이든 드럼(캔버스)이든, 지휘자는 박자(프레임)만 정확히 맞춰주면 된다.
그리고 이 지휘자는 무대(브라우저) 전체에 단 한 명만 존재한다 — 뒤에서 볼 "단일 틱 엔진"이 그 이유다.
GSAP는 신생 라이브러리가 아니다. 오히려 2008년부터 17년 넘게 웹 애니메이션 표준처럼 쓰여 온 "구관"이다. 그런데도 2026년 지금 다시 화제가 되는 이유는 셋으로 요약된다.
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다. 유료 장벽이 없어지자 튜토리얼·템플릿·쇼케이스가 폭발적으로 늘었다.
공식 보조 패키지 @gsap/react가 제공하는 useGSAP() 훅은 useEffect를 대체하며 정리(cleanup)를 자동화한다. "React는 선언적, GSAP는 명령적"이라는 이질감이 이 훅과 gsap.context() 덕분에 크게 줄어, React/Next.js 프로젝트에서도 부담 없이 쓰이게 됐다.
선언적 React 애니메이션은 Motion이, 가벼운 범용 트위닝은 anime.js가 맡는 영역이 있다. GSAP의 차별점은 타임라인 기반 정밀 시퀀싱 + 스크롤/드래그/모핑까지 아우르는 플러그인 생태계다. "간단한 트랜지션 하나"가 아니라 "정교하게 지휘된 다단계 연출"이 필요할 때 GSAP가 선택된다. 자세한 비교는 10번 섹션 참고.
순수 CSS transition/@keyframes나 Web Animations API만으로도 애니메이션은 가능하다. 그런데도 GSAP가 필요한 이유는 CSS·JS 애니메이션이 실제 제품에서 부딪히는 세 가지 벽 때문이다.
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배 빠르다"고 주장하는데, 근거가 바로 이 단일 틱 구조와 프로퍼티별 최적화된 렌더러다.
gsap.to()가 하는 일이 바로 이것이다.
src/ 폴더를 열어보면 구조가 명확하다. 코어(gsap-core.js, 약 3,255줄) 하나가 엔진·타임라인·트윈·플러그인 등록 시스템을 전부 담당하고, 나머지는 전부 독립된 플러그인 파일이다. 코어만 있어도 순수 JS 객체 애니메이션은 되지만, DOM을 움직이려면 CSSPlugin부터 필요하다.
구조가 주는 메시지는 명확하다. "코어는 아무것도 모른다". 코어는 CSS도, 스크롤도, SVG도 모른다. 오직 "속성을 시간에 따라 보간하고, 그 결과를 렌더러에게 넘긴다"는 일반적인 메커니즘만 안다. DOM을 움직이는 지식(transform 파싱, 단위 처리 등)은 전부 CSSPlugin에, 스크롤 위치 감지는 ScrollTrigger에 위임돼 있다. 이 관심사 분리 덕분에 필요한 기능만 골라 번들 크기를 최소화할 수 있다.
package.json의 main/module이 가리키는 index.js는 코어 + CSSPlugin뿐이다. ScrollTrigger·Draggable·SplitText 등은 전부 import ScrollTrigger from "gsap/ScrollTrigger" 식으로 따로 import하고 registerPlugin해야 쓸 수 있다. "gsap 하나만 설치하면 다 된다"는 오해가 흔하다.
이 섹션이 이 문서에서 가장 중요하다. GSAP가 왜 빠르고, 왜 "타임라인"이라는 개념이 강력한지는 전부 gsap-core.js 소스 안에 있다.
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가 대량의 애니메이션에서도 버벅이지 않는 이유다.
gsap-core.js에는 class Animation(공통 기반), 그 아래 class Tween extends Animation과 class 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이라 타임라인끼리 중첩 가능 |
트윈이 실제로 값을 바꾸는 최소 단위는 class PropTween이다. 대상(target)·속성명(p)·렌더 함수(r)·시작값(s)·변화량(c)·setter를 들고 있는 가벼운 객체이며, 트윈 하나가 여러 속성(x, y, opacity...)을 가지면 그만큼의 PropTween이 연결 리스트(_pt → _next)로 이어진다. 매 틱마다 이 리스트를 한 번 순회하며 각 노드의 렌더 함수를 호출하는 것만으로 모든 속성이 갱신된다 — 배열 생성이나 객체 순회 오버헤드를 최소화한 설계다.
코어에는 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번 섹션의 대표적 함정).
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()를 호출해서 생긴다.
틱마다 루트 타임라인은 render(totalTime)을 호출해 현재 로컬 시간을 계산하고, 각 자식의 PropTween 리스트를 순회해 값을 적용한다. 흥미로운 디테일은 자동 절전(autoSleep)이다. 코드 상에는 이렇게 돼 있다:
if (_ticker.frame >= _nextGCFrame) {
_nextGCFrame += _config.autoSleep || 120;
...
child || _ticker.sleep(); // 돌아가는 애니메이션이 없으면 rAF 루프 자체를 정지
}
애니메이션이 하나도 없는 순간에는 _ticker.sleep()으로 rAF 요청 자체를 멈춰 배터리·CPU를 아낀다. 새 트윈이 만들어지면 _wake()가 다시 깨운다.
CustomEase.js는 SVG cubic-bezier 패스 문자열을 파싱해 내부 좌표 배열로 정규화(_normalize)한 뒤, gsap.registerEase()로 이름 붙은 커스텀 이징 함수를 등록한다. 디자이너가 그래픽 에디터에서 그린 곡선을 그대로 이징으로 쓸 수 있게 해주는 다리 역할이다.
| 항목 | 내용 |
|---|---|
| 런타임 의존성 | 0개. package.json에 dependencies 필드 자체가 없다. README도 "Zero dependencies"를 명시적으로 강조 |
| 빌드 산출물 | dist/(UMD — 전역 <script>·CommonJS require 겸용) · esm/(ES 모듈, 번들러용) · types/(TypeScript .d.ts) |
| 모듈 진입점 | package.json의 exports 맵: 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.json에 scripts·devDependencies 필드가 없다 — 빌드/압축(min)·소스맵 생성 파이프라인은 이 공개 저장소 밖에서 비공개로 돈다. 즉 이 레포는 "소스 + 완성된 빌드 산출물"의 읽기용 미러에 가깝다 |
| 테스트/CI | 레포 안에 .github/workflows·테스트 파일이 보이지 않는다. GreenSock 내부 QA는 비공개로 진행되는 것으로 보이며, 공개 레포만 보고 "테스트가 없다"고 단정하면 오해다 |
| 보조 패키지 | @gsap/react(별도 npm 패키지, 이 레포에는 없음) — useGSAP() 훅으로 React cleanup 자동화 |
| 타입스크립트 | types/에 전체 .d.ts 제공. 다만 SplitText는 src/에 .js(333줄)와 .ts(517줄) 두 버전이 공존 — TS로 전면 재작성 중인 과도기 흔적 |
require)·AMD·전역 <script> 세 가지 모듈 방식을 하나의 파일이 전부 지원하도록 감싸는 패턴. GSAP의 dist/gsap.js 최상단이 이 패턴으로 시작한다.LICENSE 파일조차 없고, package.json과 README가 gsap.com/standard-license 링크만 가리킨다.GSAP는 서버가 아니라 브라우저(또는 브라우저와 유사한 렌더링 컨텍스트)에서 도는 클라이언트 라이브러리다. "설치"는 사실상 스크립트 참조 또는 npm 패키지 추가가 전부다.
<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을 찾아 자동 등록해준다.
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:false가 package.json에 명시돼 있음 |
| 과거 유료 플러그인 설치 | 이전엔 GreenSock 전용 npm 레지스트리 로그인(.npmrc 토큰)이 필요했지만, 2025년 무료 전환 이후로는 공개 npm에서 바로 설치 가능 |
| SSR (Next.js 등) | 코어는 _windowExists() 체크로 서버에서 안전하지만, 스크롤·드래그처럼 DOM에 의존하는 플러그인 등록·실행은 클라이언트 전용 컴포넌트/훅 안에서 해야 함 |
| 패키지 규모 | 런타임 의존성 0개이므로 node_modules 설치 시 부수 패키지가 딸려오지 않음(gsap 자체만 추가됨) |
패키지 설치는 모든 플러그인 파일을 node_modules에 내려받지만, 실제로 쓰려는 플러그인은 개별 import + registerPlugin이 필요하다. "gsap/all"에서 한 번에 가져올 수도 있지만 그만큼 번들 크기가 커진다.
기본적인 사용 흐름을 단계별로 살펴본다.
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 }, "<"); // 바로 앞 트윈과 동시 시작
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 // 구간 동안 요소를 화면에 고정
}
});
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>;
}
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가 대신 해준다는 것이다.
아니다. package.json의 license 필드는 "Standard 'no charge' license"라고 명시돼 있고, 저장소에는 LICENSE 파일 자체가 없다. "돈을 받지 않는다"는 것과 "OSI 기준 오픈소스"는 다른 개념이다. 상업적 재배포·경쟁 제품화 등 세부 조건은 반드시 공식 라이선스 페이지에서 직접 확인해야 한다.
전역 <script> 빌드에서는 각 플러그인 파일 끝의 _getGSAP() && gsap.registerPlugin(X) 코드가 window.gsap를 찾아 자동 등록해준다. 하지만 ESM/번들러 환경에서는 이 자동 등록 조건이 성립하지 않는 경우가 많아, gsap.registerPlugin(ScrollTrigger)를 빠뜨리면 "Invalid property... Missing plugin?" 경고만 뜨고 애니메이션이 조용히 무시된다.
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뿐인 매우 얇은 파일이라는 점도 헷갈리지 않아야 한다.
공개 GitHub 레포에 .github/workflows나 테스트 파일이 보이지 않는 것은 사실이지만, 이는 GreenSock의 빌드·QA 파이프라인이 비공개로 운영된다는 뜻이지 "검증 안 된 코드"라는 뜻이 아니다. 이 레포는 소스 + 완성된 dist/esm/types 산출물을 함께 배포하는 형태에 가깝다.
useEffect 안에서 gsap.to()를 직접 호출하고 정리(cleanup)를 빼먹으면, 컴포넌트가 unmount돼도 트윈이 루트 타임라인에 계속 남아 존재하지 않는 DOM을 참조하려 시도한다. gsap.context() 또는 useGSAP()로 스코프를 감싸고 revert()하는 습관이 필수다.
모든 애니메이션이 하나의 rAF 콜백을 공유한다는 것은 장점이자 위험이다. onUpdate 콜백 안에서 무거운 동기 연산(예: 레이아웃을 강제로 읽는 getBoundingClientRect 남발)을 하면, 그 프레임에서 페이지의 모든 GSAP 애니메이션이 함께 지연된다. 무거운 로직은 쓰로틀링하거나 별도 최적화가 필요하다.
| 도구 | 강점 | 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보다 개발자 경험이 떨어짐 |
| Lottie | After Effects로 만든 벡터 애니메이션을 그대로 재생 | "디자이너가 만든 고정 애니메이션 재생"에 특화. GSAP처럼 코드로 동적·인터랙티브하게 값을 바꾸는 용도는 아님 |
· React 컴포넌트 자체의 mount/unmount/레이아웃 애니메이션이 중심이면 Motion이 더 자연스러울 수 있다.
· 가볍고 단순한 트윈 몇 개면 anime.js나 CSS로 충분하다.
· 스크롤 스토리텔링, 정밀한 다단계 시퀀싱, SVG 모핑, 드래그, 프레임워크 무관 재사용이 필요하면 GSAP가 사실상 표준이며, 이제 비용 장벽도 없다.
GSAP는 "움직임을 정밀하게 지휘해야 하는 모든 웹 프로젝트"에 잘 맞는다. 특히 다음과 같은 사람·상황에 추천한다.
ScrollTrigger로 짧은 코드에 구현gsap.context()/useGSAP()로 프레임워크 생명주기와 충돌 없이 명령형 애니메이션을 안전하게 섞어 쓰고 싶은 경우Flip으로 레이아웃 전환을, MorphSVGPlugin으로 아이콘·일러스트 모핑을 무료로 구현하고 싶은 경우반대로 버튼 hover 색상 전환처럼 아주 단순한 트랜지션 하나만 필요하다면 굳이 GSAP를 끌어올 필요는 없다 — CSS만으로 충분하고, 번들 크기도 아낄 수 있다. "얼마나 복잡한 타임라인을 지휘해야 하는가"가 GSAP 도입 여부를 가르는 가장 실용적인 기준이다.