최근 프로젝트에서 Next.js에서 GSAP ScrollTrigger를 사용하려고 했는데, 여러 문제가 발생했다.
SSR(서버 사이드 렌더링)과 GSAP가 충돌하면서 예상대로 동작하지 않았다.
이 과정에서 발생한 오류들과 해결 방법을 정리해본다.
1. ScrollTrigger가 정의되지 않는 오류
❌ 문제
Next.js에서 ScrollTrigger를 사용하려고 하면 ScrollTrigger is not defined라는 오류가 발생했다.
이유를 찾아보니 Next.js는 서버에서 먼저 HTML을 렌더링하는데, GSAP의 ScrollTrigger는 브라우저에서만 실행되기 때문이었다.
✅ 해결 방법
useEffect 내부에서 import("gsap/ScrollTrigger")를 사용해 ScrollTrigger를 동적으로 로드하면 서버에서는 실행되지 않도록 할 수 있다.
import React, { useEffect, useState, useRef } from "react";
import gsap from "gsap";
export default function MyComponent() {
const sectionRef = useRef(null);
const [scrollTriggerLoaded, setScrollTriggerLoaded] = useState(false);
useEffect(() => {
if (typeof window !== "undefined") {
import("gsap/ScrollTrigger").then(({ ScrollTrigger }) => {
gsap.registerPlugin(ScrollTrigger);
setScrollTriggerLoaded(true);
});
}
}, []);
useEffect(() => {
if (!scrollTriggerLoaded || !sectionRef.current) return;
import("gsap/ScrollTrigger").then(({ ScrollTrigger }) => {
ScrollTrigger.create({
trigger: sectionRef.current,
start: "top center",
end: "bottom center",
scrub: true,
pin: true,
});
ScrollTrigger.refresh();
});
return () => {
import("gsap/ScrollTrigger").then(({ ScrollTrigger }) => {
ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
});
};
}, [scrollTriggerLoaded]);
return <section ref={sectionRef}>Scroll Animation Section</section>;
}
이렇게 하면 서버에서 실행되지 않고, 브라우저에서만 GSAP가 적용된다.
2. ScrollTrigger가 적용되지 않는 문제
❌ 문제
ScrollTrigger가 오류 없이 실행되는데도 스크롤을 내려도 애니메이션이 적용되지 않았다.
콘솔을 확인해보니 GSAP가 .about-line-{i} 클래스를 제대로 찾지 못하는 문제였다.
Next.js의 SSR에서 요소가 아직 렌더링되지 않았거나, querySelectorAll()을 사용해야 할 수도 있었다.
✅ 해결 방법
- .about-line-{i} 대신 querySelectorAll(".about-line")을 사용하여 모든 요소를 한 번에 가져오도록 수정
- useEffect 실행 전에 aboutRef.current가 null이 아닌지 확인
import React, { useEffect } from "react";
import gsap from "gsap";
export default function AboutSection({ aboutRef }) {
useEffect(() => {
if (typeof window === "undefined" || !aboutRef?.current) return;
import("gsap/ScrollTrigger").then(({ ScrollTrigger }) => {
gsap.registerPlugin(ScrollTrigger);
const aboutLines = aboutRef.current.querySelectorAll(".about-line");
if (aboutLines.length === 0) {
console.warn("No elements found with class `.about-line`");
return;
}
gsap.timeline({
scrollTrigger: {
trigger: aboutRef.current,
start: "top center",
end: "bottom center",
scrub: true,
pin: true,
},
}).to(aboutLines, { color: "#fff", duration: 1.5, stagger: 0.3 });
ScrollTrigger.refresh();
});
return () => {
import("gsap/ScrollTrigger").then(({ ScrollTrigger }) => {
ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
});
};
}, [aboutRef]);
return (
<section ref={aboutRef}>
<p className="about-line">Hello, this is a GSAP ScrollTrigger example.</p>
<p className="about-line">Scroll down to see the color change.</p>
</section>
);
}
이렇게 수정하고 나니 스크롤을 내리면 글씨 색상이 정상적으로 변경되었다.
3. ScrollTrigger가 특정 섹션에서만 동작하지 않는 문제
❌ 문제
Next.js에서 dynamic()을 사용해 컴포넌트를 동적으로 로드했더니 ScrollTrigger가 특정 섹션에서만 동작하지 않는 문제가 발생했다.
이는 useEffect 실행 시점에서 ref.current가 null일 가능성이 있었기 때문이다.
✅ 해결 방법
- Home.js에서 useRef()를 생성하고, 자식 컴포넌트에 props로 전달
- useEffect 실행 전에 ref.current가 null이 아닌지 체크
import React, { useEffect, useRef, useState } from "react";
import dynamic from "next/dynamic";
import gsap from "gsap";
// 동적 로드 (SSR 비활성화)
const AboutSection = dynamic(() => import("../components/AboutSection"), { ssr: false });
export default function Home() {
const aboutRef = useRef(null);
const [scrollTriggerLoaded, setScrollTriggerLoaded] = useState(false);
useEffect(() => {
if (typeof window !== "undefined") {
import("gsap/ScrollTrigger").then(({ ScrollTrigger }) => {
gsap.registerPlugin(ScrollTrigger);
setScrollTriggerLoaded(true);
});
}
}, []);
return (
<>
<div ref={aboutRef}>
<AboutSection aboutRef={aboutRef} />
</div>
</>
);
}
이렇게 수정하고 나니 모든 섹션에서 ScrollTrigger가 정상적으로 동작했다.
4. 정리
ScrollTrigger 적용할 때 주의할 점
✅ GSAP ScrollTrigger는 SSR에서 실행되지 않도록 import("gsap/ScrollTrigger")를 useEffect에서 실행
✅ querySelectorAll(".about-line")을 사용하여 Next.js의 SSR 문제 없이 요소를 찾도록 변경
✅ useRef()를 부모에서 생성하고 자식 컴포넌트에 props로 전달
✅ ScrollTrigger.getAll().forEach((trigger) => trigger.kill());을 사용해 메모리 누수 방지
이렇게 수정하니까 Next.js에서도 GSAP ScrollTrigger가 정상적으로 작동했다.
Next.js에서 GSAP를 사용하다가 ScrollTrigger가 적용되지 않는다면, 위 방법을 시도해보면 해결될 것이다.
'Next.js' 카테고리의 다른 글
캐시 활용으로 누락된 서버데이터 처리 (0) | 2025.03.01 |
---|---|
React : useMemo로 성능 최적화하기 (0) | 2025.02.26 |
Next.js에서 GSAP 최적화하기: 중앙집중화로 성능 개선 (0) | 2025.01.22 |
Next.js 15에서 발생한 Hydration 오류와 해결 과정 (0) | 2025.01.20 |
Next.js 14: Pages Router에서 App Router로 마이그레이션하기 (0) | 2025.01.20 |