왜 스크롤 연동 애니메이션이 필요한가
랜딩 페이지의 히어로 영역이나 세로 갤러리에서 스크롤에 따라 텍스트가 나타나고 이미지가 확대되면 시선이 자연스럽게 이어집니다. CSS Scroll-driven Animations는 자바스크립트 없이 스크롤 위치를 애니메이션 타이밍으로 직접 연결해 이런 상호작용을 간단히 만듭니다.
기존에는 스크롤 이벤트를 듣고 수치를 계산해 스타일을 바꾸는 방식이 흔했지만, 성능 튜닝과 보정 코드가 늘어나는 문제가 있었습니다. 이 글은 view-timeline/animation-timeline 속성을 사용해 히어로와 갤러리를 구현하는 최소한의 패턴을 단계적으로 설명합니다.
실무에서는 유지보수 비용도 중요합니다. CSS만으로 타이밍을 선언하면 “디자인 변경 → 타이밍 조정”이 스타일시트 안에서 끝나 코드 의존성이 줄어듭니다.
> 호환성은 브라우저에 따라 다를 수 있습니다. 운영 전 대상 브라우저를 확인하고 폴백(단순 페이드·고정 이미지)을 준비하는 편이 안전합니다.
핵심 개념을 예제로 이해하기
스크롤 연동의 출발점은 요소가 화면에 들어오고 나가는 범위를 시간축으로 바꾸는 것입니다. view-timeline은 “요소의 보임 정도”를 0%→100%로 환산해 애니메이션의 진행률로 제공합니다.
히어로 예시로, 헤더 문단(.hero-copy)에 view-timeline을 만들고 그 타임라인을 텍스트 페이드 인에 연결한다고 생각해 보세요. 사용자가 섹션 상단에 닿을 때 0%, 중앙을 지날 때 50%, 섹션이 사라질 때 100%가 되어 불투명도와 이동이 자연스럽게 이어집니다.
갤러리에서는 각 카드가 자신의 view-timeline을 갖고, 카드가 뷰포트에 보이는 동안만 살짝 확대·밝기 변화를 줍니다. 별도 계산 없이 “보임 상태 -> 스타일 변화”가 이어지기 때문에 카드 개수가 늘어나도 선언만 추가하면 됩니다.
> 안티패턴: 하나의 긴 타임라인에 모든 효과를 몰아넣으면 구간 조정이 어렵습니다. 섹션/요소 단위로 타임라인을 나눠 짧은 효과를 겹치는 편이 유지보수에 유리합니다.
바로 적용 요약
히어로와 갤러리에 함께 쓰는 최소 구성이면 됩니다. 작게 시작해 요소별 타임라인을 나누면 조정이 쉽습니다.
- 섹션 컨테이너에 position·패딩을 잡아 뷰포트(보이는 화면) 진입·이탈 구간을 만듭니다.
- 움직일 대상에 view-timeline(요소가 보이는 구간을 시간 축처럼 쓰는 속성) 이름을 줍니다.
- keyframes를 만들고 animation-timeline으로 연결합니다.
- animation-range로 시작·끝을 비율로 자릅니다.
- 스크롤 스냅으로 갤러리 카드 체류 시간을 일정하게 맞춥니다.
아래 예시는 히어로 텍스트는 보일 때 페이드 + 상승, 갤러리 카드는 보이는 동안만 살짝 확대됩니다.
/* 1) 레이아웃 베이스 */
.hero { position: relative; padding-block: 20vh; }
.gallery { display: grid; gap: 24px; }
.card { view-timeline-name: --card; view-timeline-axis: block; }
/* 2) 히어로 타임라인 */
.hero-copy { view-timeline-name: --hero; view-timeline-axis: block; }
/* 3) 애니메이션 연결 */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pop {
from { transform: scale(0.96); filter: brightness(0.95); }
to { transform: scale(1); filter: brightness(1); }
}
.hero-copy {
animation: fadeUp 1s linear both;
animation-timeline: --hero;
animation-range: entry 10% cover 60%;
}
.card {
animation: pop 1s linear both;
animation-timeline: --card;
animation-range: entry 0% exit 100%;
}
/* 4) 갤러리 스냅(선택) */
.gallery { scroll-snap-type: y proximity; }
.card { scroll-snap-align: start; }
요점은 요소마다 별도 view-timeline을 두는 것입니다. animation-range 비율만 바꾸면 픽셀 계산 없이 시작·종료를 다듬을 수 있습니다.
> 체크포인트: overflow가 걸린 부모가 있으면 타임라인 기준이 달라질 수 있습니다. 스크롤 컨테이너를 먼저 확인하고, 불필요한 3D transform은 레이어 분리로 모션이 튈 수 있어 피하세요.
흔한 실수와 선택 기준 한눈에 정리
CSS만으로도 충분하지만, 타임라인 범위를 과하게 겹치면 예상과 다른 타이밍이 나옵니다. 요소마다 view-timeline을 분리하고 animation-range로 구간을 짧게 자르는 편이 조정이 쉽습니다.
히어로처럼 한 번만 등장할 효과와 갤러리처럼 반복 요소에 붙는 효과는 설정이 다릅니다. 히어로는 섹션 단위 타임라인, 갤러리는 카드 단위 타임라인이 유지보수에 유리합니다.
- 타임라인 범위: start/end 오프셋을 크게 겹치지 말고 20~60% 같은 중간 구간만 사용
- 고정/상대 좌표: sticky·transform이 섞이면 보임 기준이 달라지므로 레이아웃을 먼저 확정
- 접근성: 이동·확대 효과에는 prefers-reduced-motion 매체 쿼리로 대체 스타일 제공
- 폴백: 미지원 브라우저에는 단순 페이드·정지 이미지를 기본값으로 지정
> 안티패턴: 하나의 긴 타임라인에 여러 keyframes를 억지로 배치하면 디버깅이 급격히 어려워집니다. 섹션 -> 요소 순서로 타임라인을 잘게 나누세요.
JS와 비교해 선택 기준을 간단히 잡으면 다음과 같습니다. 스크롤 위치에 비례한 단순 전환은 CSS, 사용자 입력·상태와 복합적으로 연동되는 로직은 JS가 낫습니다.
- CSS 적합: 히어로 페이드/패럴랙스, 카드 인뷰 확대, 헤더 색 전환 같은 비례형 효과
- JS 적합: 진행바 합산, 조건부 분기(예: 일부 섹션 건너뛰기), 서버 상태에 따른 타이밍 변경
- 트레이드오프: CSS는 렌더러가 최적화해 부하가 낮지만, 동적 분기에는 한계가 있습니다.
요약과 바로 실행 체크리스트
이 글의 핵심은 요소가 보이는 정도를 시간축으로 바꿔 애니메이션에 연결하는 것입니다. 히어로는 섹션 단위, 갤러리는 카드 단위로 타임라인을 나눠 짧게 조정하면 유지보수가 수월합니다.
바로 적용하려면 아래 순서로 최소 패턴부터 시작하세요. 한 단계씩 시각 변화를 확인하며 범위를 다듬는 방식이 안전합니다.
- 섹션 레이아웃 확정: 패딩/높이, sticky 사용 여부를 먼저 결정
- 타겟 요소 지정: view-timeline 이름 부여, 각 요소는 고유 타임라인
- 키프레임 작성: 불투명도·변형 등 1개 효과로 시작
- 연결: animation-timeline과 animation-range로 구간 자르기
- 접근성/폴백: prefers-reduced-motion, 미지원 브라우저 기본 스타일
> 실무 팁: 타임라인을 길게 잡아 겹치면 디자이너 변경에 약해집니다. 구간을 20~60%처럼 중간 범위로 짧게 설정하고 필요 시 섹션을 분리하세요.
히어로 텍스트: view-timeline + animation-range로 자연스러운 등장
히어로는 “섹션이 화면에 머무르는 중간 구간”만 써서 과한 효과를 피하는 것이 좋습니다. 요소가 뷰포트 중앙 근처에 있을 때만 페이드·상승을 실행하면 시선이 깨지지 않습니다.
아래 코드는 히어로 문구가 화면 20%~60% 구간에 있을 때만 서서히 나타나도록 자릅니다. 스크롤 속도에 상관없이 타이밍이 안정적입니다.
.hero {
position: relative;
padding-block: 24vh;
}
.hero-copy {
view-timeline-name: --hero;
view-timeline-axis: block;
animation-name: fadeUp;
animation-timeline: --hero;
animation-range: 20% 60%;
animation-fill-mode: both;
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
핵심은 타임라인 이름을 요소에 부여하고 animation-range로 “실행 구간”을 짧게 자르는 점입니다. 시작·끝 오프셋을 바꾸면 디자이너 요구에 맞춰 미세 조정이 쉽습니다.
> 실무 팁: 섹션에 sticky 요소가 있으면 보임 기준이 달라질 수 있습니다. 먼저 레이아웃을 확정한 뒤 타임라인 구간을 조정하세요.
'프로그래밍' 카테고리의 다른 글
| HTML autocomplete 토큰 완성 가이드: 로그인·주소·결제 폼 자동완성 성공률 높이기 (0) | 2026.07.21 |
|---|---|
| Navigation API + View Transitions로 프레임워크 없이 SPA 라우팅과 전환 애니메이션 구현 가이드 (0) | 2026.07.21 |
| Next.js App Router .env 완전 정복: NEXT_PUBLIC, 로드 순서, 빌드·런타임 구분 가이드 (0) | 2026.07.15 |
| Playwright + browser-use로 웹 태스크 LLM 자동화: 셀프힐링 로케이터와 검증 루프 패턴 시작 가이드 (0) | 2026.07.14 |
| CSS Anchor Positioning + Popover로 셀렉트·드롭다운·툴팁 무자바스크립트 구현 가이드 (0) | 2026.07.10 |