JavaScript 스크롤링을 제거하여 Interaction to Next Paint 개선하기
Core Web Vitals에 영향을 주지 않는 아름답고 부드러운 스크롤링 경험 만들기

오늘 당장 JavaScript 스크롤링을 CSS로 대체하세요
이 블로그에는 약간의 역사가 있습니다. 2017년까지 일관되게 부드러운 스크롤링 동작을 추가하는 유일한 방법은 JavaScript 라이브러리를 사용하는 것뿐이었습니다. 그리고 많은 개발자들이 그렇게 했습니다. 당시에는 Core Web Vitals라는 개념이 없었고, 모두가 jQuery를 메인 JavaScript 라이브러리로 사용하고 있었습니다.
이제 현재로 돌아와 보겠습니다. JavaScript 기반 앵커 스크롤링을 대체할 훨씬 더 낫고, 빠르며, 쉬운 대안들이 존재합니다. 하지만 Core Web Vitals 컨설턴트로서 저는 여전히 매일 이러한 스크롤링 방식을 목격합니다.
Arjen Karel의 최종 검토일: 2026년 2월
JavaScript 기반 스크롤링이 '나쁜' 이유

JavaScript 기반 스크롤링이 나쁜 이유는 두 가지입니다. 복잡하고 느리기 때문입니다.
복잡성: JavaScript 기반 스크롤링은 불필요하게 복잡합니다. 읽기 어려운 코드를 작성하고 유지보수해야 하며, Window.scrollTo() 메서드가 이 코드를 훨씬 쉽게 만들었음에도 불구하고 여전히 한 줄의 CSS보다 유지보수하기가 훨씬 어렵습니다.
느린 속도: JavaScript는 메인 스레드를 차단하는 경향이 있으며, CSS 대응 방식보다 항상 느립니다. 스크롤 기능이 어떻게 구현되었는지에 따라 메인 스레드를 꽤 오랫동안 차단할 수 있으며, 이는 Interaction to Next Paint 지표에 큰 지연을 유발합니다! 2025 Web Almanac에 따르면, 모바일의 중간(median) Total Blocking Time은 1,916 밀리초에 달합니다. 메인 스레드를 놓고 경쟁하는 불필요한 JavaScript 함수는 이 상황을 더욱 악화시킵니다.
CSS 부드러운 스크롤링(Smooth Scrolling)이 '좋은' 이유
앞서 JavaScript 기반 스크롤링이 왜 나쁜지 이야기했습니다. CSS 기반의 부드러운 스크롤링은 그 정반대입니다. 스타일시트에 다음 코드를 추가하는 것만큼 정말 간단합니다:
html{ scroll-behavior: smooth;}
간단하고 효과적입니다
CSS 한 줄인 html { scroll-behavior: smooth; }는 구현하기가 매우 간단하며 JavaScript 기반 솔루션보다 더 효율적입니다. 이 단 한 줄의 코드로 유지보수하기 어려운 JavaScript 함수 없이 전체 페이지에 부드러운 스크롤링을 활성화할 수 있습니다. 한 걸음 더 나아가 불필요한 JavaScript를 완전히 지연(defer)하거나 제거한다면, INP processing time이 크게 개선될 것입니다.
더 나은 성능
CSS 기반의 부드러운 스크롤링은 브라우저에서 네이티브로 처리되므로 모든 경우에서 JavaScript 구현보다 뛰어난 성능을 발휘합니다. 이는 브라우저의 메인 스레드에 가해지는 부담을 줄여주기 때문에 Interaction to Next Paint (INP) 지표를 최적화하는 데 특히 중요합니다. JavaScript 스크롤 핸들러는 메인 스레드에서 실행되어야 하며, 이곳에서 소비되는 1밀리초는 다음 사용자 상호작용(user interaction)을 지연시키는 1밀리초가 됩니다. CSS 스크롤 동작은 메인 스레드를 전혀 사용하지 않고 실행됩니다. 만약 상호작용 중에 실행해야 하는 JavaScript가 여전히 남아있다면, 메인 스레드에 yield하는 방법에 대해 읽어보세요.
부드러운 앵커 스크롤링은 시작에 불과합니다. CSS는 과거 JavaScript를 필요로 했던 더 많은 스크롤 기능들을 대신하고 있습니다. Scroll-driven animations(Chrome 115+, Safari 26)를 사용하면 애니메이션 진행률을 스크롤 위치에 연결할 수 있어 requestAnimationFrame 스크롤 핸들러를 완전히 대체합니다. Scroll-triggered animations(Chrome 145)는 스크롤 오프셋을 지날 때 시간 기반 애니메이션을 실행하여 등장(reveal) 효과를 위한 IntersectionObserver를 대체합니다. 그리고 scroll-state container queries(Chrome 133+)를 사용하면 요소가 붙어(stuck) 있는지, 스냅(snapped)되었는지, 스크롤 가능한지에 따라 JavaScript 없이 요소를 스타일링할 수 있습니다. 트렌드는 명확합니다: 가능한 한 스크롤 로직을 JavaScript에서 CSS로 이동시키세요.
브라우저 호환성
CanIUse에 따르면 CSS 부드러운 스크롤링은 전 세계적으로 95%의 브라우저 지원율을 보이고 있습니다. 이는 2022년 3월 이후 모든 주요 브라우저에서 기본적인 기능으로 자리 잡았습니다. 부드러운 스크롤링이 지원되지 않는 구형 브라우저에서는 앵커로 직접 점프하는 기본 동작으로 fallback됩니다.
쉬운 커스터마이징
CSS를 사용하면 스크롤 동작을 쉽게 커스터마이징할 수 있습니다. 예를 들어 scroll-margin-top 속성을 사용하면 스크롤된 요소의 상단 간격을 설정할 수 있습니다:
h1, h2, h3 {scroll-margin-top: 5rem;}
또는 'target' 가상 클래스를 사용하면 더욱 쉽습니다.
:target {scroll-margin-top: 5rem;}
모션 환경설정 존중
일부 사용자는 스크롤 애니메이션으로 인해 멀미를 경험할 수 있습니다. 부드러운 스크롤링을 prefers-reduced-motion 미디어 쿼리로 감싸면, 이러한 사용자들에게는 즉각적인 점프가 제공되도록 보장할 수 있습니다:
@media (prefers-reduced-motion: no-preference) {
html { scroll-behavior: smooth; }
}
이는 모션 감소를 요청하지 않은 사용자에게만 부드러운 스크롤링을 적용합니다. 전정 기관 장애나 모션 민감성을 가진 사용자는 기본 설정인 즉각적인 스크롤을 보게 됩니다.
전체 예제
이 코드가 실제로 어떻게 작동하는지 보려면 이 페이지 상단의 목차를 클릭해 보세요. 클릭한 앵커로 멋지고 부드럽게 스크롤되는 것을 확인할 수 있습니다.
다음은 여러분이 복사하여 재사용할 수 있는 단순화된 예제입니다.
<html>
<head>
<title>Smooth scroll to target</title>
<style>
html {
scroll-behavior: smooth;
}
:target {
scroll-margin-top: 5rem;
}
.largedivider {
height: 1000px;
}
</style>
</head>
<body>
<nav>
<a href="#target">scroll to target</a>
</nav>
<div class="largedivider"> -- </div>
<h2 id="target">scroll to me</h2>
<div class="largedivider"> -- </div>
</body>
</html>

