Hero 섹션 결과물 시연
개요
이번 시간에는 저의 GTA 카피 프로젝트에 사용된 기술들을 분석해볼 텐데요. 먼저 Hero섹션에 사용된 기술들을 먼저 알아보겠습니다. 혹시나 따라 해보고 싶으신 분들은 아래 코드를 다운 받아서 하나씩 만들어가며 설명해보도록 하게요.
자료
코드
깃허브
gta-examples
chugue • Updated Sep 29, 2025
구성
위에 시연된 gif를 보시면 Hero섹션에는 GSAP기술과 CSS Mask옵션으로 쉽게 구현이 가능해요. 그래서 위에 제공된 기본 코드에서 같이 구현해 나갈텐데, 이 블로그에서는 초점을 맞추기위해서 tailwind 설명은 최대한 자제 하도록 할게요. 이번 블로그 구성을 간단히 알려드리자면,
- 마스킹 이미지 설정
- GSAP set 함수를 활용한 초기 상태 설정
- GSAP timeline과 scrollTrigger를 활용한 애니메이션 오케스트레이션
- 원형으로 확장되며 나타나는 ComingSoon 연출
들로 구성되어 있으니 참고해주세요!
1. 마스킹 이미지 설정
일단 기본코드를 받으시고
npm i 로 패키지들을 설치하고 npm run dev 로 프로젝트를 띄워보면 아래와 같은 화면을 만나실 수 있을거에요. 애니메이션 작업을 할 때도 마찬가지이겠지만, 기본 HTML의 구성은 모두 끝내고 그 다음 부가효과를 입힌다는 생각으로 작업하시면 좋아요. 그래서 화면처럼 기본적으로
HeroSection에 완성된 화면은 Navbar와 1,2,3번 이미지로 보시는 바와 같이 구성되어있어요. 그럼 현재 이 기본 코드에서 어떻게 아래와 같이 자연스럽게 배경이 사라지며 글자가 들어오는 효과를 줄 수가 있을까요?
그럼 기본 배경을 덮을 수 있는
SVG파일과 CSS의 maskImage 속성을 사용하면 되요. maskImage를 속성은 이미지를 마스크 레이어로 사용해서 이미지 모양대로 표시할 수 있게 해줘요. 그럼 프로젝트 폴더로 가셔서 public/images 폴더를 확인해보면 big-hero-text.svg파일이 있을거에요. public/images/big-hero-text.svg
이 이미지를 엄청 확대한 값을 초기상태로 설정해서, 하얀부분 사이로 그림이 보이게 해줄거에요. 그리고 점점 작아지면서 글자만 남아 강조되는 느낌을 주면 되겠죠? 그럼 여기서 GSAP을 사용해서 초기상태를 설정해볼게요. GSAP설치 방법을 모르시면 이 곳을 클릭해서 설치하시고 다시 이어가면 좋을 것 같아요.
프로젝트 폴더의
components/Hero.tsx 파일 상단에 gsap과 useGSAP훅을 import 해주면 사용준비는 완료에요. 그리고 gsap은 클라이언트 컴포넌트에서만 사용되니 use client 지시어도 추가해주시구요.2. gsap.set 함수를 활용한 초기 상태 설정
지금 우리가
gsap.set함수가 필요한 이유는 마스킹 이미지를 크게 확대한 상태에서 글자 사이로 이미지가 보이게 하고, 점점 줄어들면서 글자만 남게되는 효과를 주기 위한 것이에요. 그래서 첫 상태와 나중 상태가 필요하죠. 그리고 이 첫 상태를 설정하기 위해 gsap.set 함수를 사용해보도록 할게요. components/Hero.tsx
TypeScript
"use client";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import Image from "next/image";
const Hero = () => {
useGSAP(() => {
gsap.set(".mask-wrapper", {
maskPosition: "center 20%",
maskSize: "3600% 3600%",
maskRepeat: "no-repeat",
maskImage: 'url("/images/big-hero-text.svg")',
});
});
return (
<section className="hero-section">
<div className="size-full mask-wrapper">
{/* 기존 코드 */}여기서
useGSAP훅을 사용하는 이유는 리액트 전용으로 메모리 누수나 스코프 관리가 용이하기 떄문에 리액트에서는 useGSAP을 사용하는 것이 깔끔해요. 여기서 gsap.set 함수를 사용하면서 두 가지 매개변수를 넣어줘야 되는데요. 첫 번째는 애니메이션을 적용할 대상이고, 두 번째는 CSS 스타일 객체에요. - 첫 번쨰 매개변수 :
‘.mask-wrapper’
첫 번째 매개변수에
. 을 붙이면 이는 mask-wrapper라는 클래스를 선택하게 되요. 만약 붙이지 않으면 HTML 태그 이름으로 인식하게 되요. 즉 <mask-wrapper> 라는 태그이름을 찾으려고 할 것이기 때문에 꼭 클래스를 가진 요소를 선택할 때는 .을 붙여주세요. 아이디를 가진 요소를 선택하려면 앞에 #을 붙여주면 되요.- 두 번째 매개변수
maskPosition: “center 20%”maskSize: "3600% 3600%"maskRepeat: “no-repeat”maskImage: 'url("/images/big-hero-text.svg")’
CSS속성을 여기서 정의해주면 되는데,
카멜 표기법을 사용한다는 거 인지해주시면 좋을 것 같아요. 여기서 속성을 하나씩 설명해볼게요.마스크 이미지의 위치를 설정하는 속성이에요. x와 y위치를 지정해줘야 되기 때문에 두 값이 들어가있는 것을 확인할 수 가 있죠. 즉
x는 center로 잡고 y는 상단 20%에 위치시키겠다는 뜻이에요. 마스크 이미지의 사이즈의 가로와 세로 사이지를 지정할 수 있죠. 글자 사이로 이미지가 보이게 끔 해야되기 떄문에 확대해서 마스크 이미지가 안보일 때까지 확대해야 했어요.
3600%가 적당했기 때문에 이렇게 설정한거에요. 만약 백그라운드 이미지 요소보다 마스킹이미지가 작다면 이미지가 반복하게 되는데, 이미지가 작아졌을 때 반복하지 않도록 하기 위해서 필요해요.
maskImage 속성으로 public 폴더 경로를 제외한 에셋의 위치를 넣어주면 되요.
자 이제 초기 상태를 설정했으니 다음은
timeline 함수와 scrollTrigger를 사용해서 애니메이션을 만들면 되겠네요.3. GSAP timeline과 scrollTrigger를 활용한 애니메이션 오케스트레이션
오케스트레이션이라는 단어를 쓰는게 너무 장황하단 생각을 하지만, 여러 애니메이션을 지휘하듯 다루어야 하기 떄문에 적절한 단어라고 생각이 드네요. 그리고 여기서
timeline 함수를 쓰는 이유는 여러가지 애니메이션을 언제 어디서 어떻게 나타나게 할 지 손쉽게 컨트롤할 수 있기 때문이에요! 즉, 우리가 원하는 것은 스크롤을 내리긴 하지만 그렇다고 Hero 섹션이 스크롤이 되면 안되죠. Hero섹션이 고정된 채로 우리가 원하는 애니메이션이 실행이 되어야 해요. 그럼 아래와 같이 작성해볼까요?
트리거 세팅
그럼 아까 만들었던
gsap.set 아래에 gsap.timeline을 선언을 해주고 scrollTrigger객체를 사용할 거에요. 물론 트리거가 타임라인을 사용하는 데 필수적인 것은 아니지만 저희가 만들고 싶은 효과는 HeroSection에서 여러 효과를 주고 싶기 때문에 필요해요. components/Hero.tsx
TypeScript
//import { ScrollTrigger } from "gsap/ScrollTrigger";
// 중간 코드 생략
//gsap.registerPlugin(ScrollTrigger);
const Hero = () => {
useGSAP(() => {
gsap.set(".mask-wrapper", {
// 코드 생략
});
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".hero-section",
start: "top top",
end: "+=200%"
scrub: 2.5,
pin: true,
},
});
});
return ( // 기존 코드먼저 스크롤 트리거를 사용하기 위해서는 scrollTrigger 플러그인을 선언해줘야 해요 하지만 애니메이션이 준비가 되어있지 않으면 어떻게 작동하는 지 확인할 수 없으니 일단 주석처리를 하도록 할게요!
그리고 위 코드처럼
gsap.timeline 함수에는 scrollTrigger 객체를 설정할 수 있는데요. 설정 하나하나씩 알아가볼게요.- 저희가 고정하고 싶은 타겟은
.hero-section이고 이 요소의 top 부분이 viewport의 top에 도달하면 트리거를 시키므로‘top top'을 start로 설정해요.
- 그리고 고정한다는 말은 언제까지 고정할 건데란 질문이 따라오겠죠? 그래서 end를 설정해야되는데 현재 타겟 높이의 2배를 스크롤하는 동안 고정시키겠다 라는 의미로
“+=200%”를 설정했어요.
- scrub이라는 옵션은 true또는 숫자를 넣을 수 있는데, 우리가 마우스를 스크롤하면 바로 화면이 연동되는 것은 가끔 딱딱하게 느껴져요. 그래서 2.5초의 지연시간을 추가하면 부드럽게 스크롤 된다는 느낌이 들기 때문에
2.5초로 설정했어요.
- 당연히 고정 효과를 사용하기 위해서는 pin이라는 옵션을
true로 설정해줘야 완성이 되요.
자 이렇게 시작 부분은 설정이 되었네요. 이제 애니메이션을 하나하나씩 추가해보도록 할게요.
타임라인에 애니메이션 추가하기
이제
timeline을 tl 이라는 변수로 정의했다면, 해당 tl에 메서드 체이닝 방식으로 애니메이션 효과를 붙일 수 있어요. 그럼 마스킹이미지가 작아지는 효과를 바로 붙여보도록 할게요. components/Hero.tsx
TypeScript
// 윗 코드 생략
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".hero-section",
start: "top top",
end: "+=200%",
scrub: 2.5,
pin: true,
},
});
tl.to(".mask-wrapper", {
maskPosition: "center 20%",
maskSize: "20% 20%",
});
});
// 아래 코드 생략위 코드 처럼
tl. 변수에 to라는 메서드를 붙일 수 있어요. to는 기본문법중에 하나로 간단히 설명드리자면 이런 상태로 완성이 되어야 해! 라고 명시해주는 함수라고 생각하면 좋겠네요. 문법은 이곳을 클릭해서 좀 더 자세히 알아보시면 좋을 것 같아요. 그래서 maskPosition을 그대로 유지하고 maskSize를 가로세로 ‘20% 20%’로 바꿔볼게요. 그리고 애니메이션이 지속되는 시간으로 duration: 2를 설정해볼게요. 그럼 저장하고 페이지를 새로고침하면 아래와 같이 애니메이션이 작동할거에요.
![[GSAP] GTA-Hero 섹션 기술 설명](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fattachment%253Aa12af999-06bd-4feb-98f6-085cfb2e1bed%253Aopen-image.webp%3Ftable%3Dblock%26id%3D2759c76c-6cb4-807e-9646-d6a2f4292f4f%26spaceId%3Db4216657-966f-4c29-ae8c-42f6c4adb66d&w=3840&q=75)