필요할 때까지 스크립트 지연하기
IntersectionObserver 및 사용자 상호작용 트리거를 사용하여 필요에 따라 JavaScript 로드

필요할 때까지 스크립트 지연하기
2025 Web Almanac에 따르면 모바일 페이지의 중앙값은 251KB의 사용하지 않는 JavaScript를 전송합니다. 이는 방문자가 필요로 하기도 전에 브라우저가 다운로드, 파싱, 컴파일하는 JavaScript입니다. 아무도 클릭하지 않은 폼. 아무도 열지 않은 채팅 위젯. 스크롤 아래에 있는 지도 통합. 이 모든 것이 페이지 로드의 가장 중요한 단계에서 대역폭과 CPU 시간을 놓고 경쟁합니다.
이를 처리하는 가장 효과적인 방법은 스크립트가 실제로 필요할 때까지 로드하지 않는 것입니다. 이는 스크립트 태그에 async 또는 defer 속성을 사용하는 것과는 다릅니다. 해당 속성들은 여전히 페이지 로드 중에 스크립트를 다운로드하며, 단지 실행 시점만 변경할 뿐입니다. 온디맨드 로딩은 트리거가 발생할 때까지 스크립트를 전혀 다운로드하지 않습니다.
최종 검토: Arjen Karel, 2026년 3월
우리는 오랫동안 이미지에 이 방식을 사용해 왔습니다. 이를 지연 로딩(lazy loading)이라고 합니다. 지연 로딩을 사용하면 스크롤 아래의 이미지는 뷰포트에 나타나기 직전에 로드됩니다. 브라우저는 실제로 필요한 항목을 다운로드, 파싱, 페인팅하는 데 리소스를 집중할 수 있습니다. 동일한 원칙이 JavaScript에도 적용되며, 이는 Lighthouse 경고 "reduce unused JavaScript"를 해결하고 Interaction to Next Paint (INP)와 같은 반응성 지표를 개선할 것입니다.
안타깝게도 이미지에 loading="lazy"를 추가하는 것만큼 간단하지는 않지만, 작은 헬퍼 함수와 트리거를 사용하면 작동하게 만들 수 있습니다.
스크립트 삽입 헬퍼
페이지 로드 후 페이지에 스크립트를 추가하려면 스크립트 요소를 생성하고 이를 문서의 head에 추가하는 작은 함수가 필요합니다.
function injectScript(scriptUrl, callback) {
const script = document.createElement('script');
script.src = scriptUrl;
if (typeof callback === 'function') {
script.onload = callback;
}
document.head.appendChild(script);
}
scriptUrl 매개변수는 로드할 스크립트의 URL입니다. 선택적인 callback 함수는 스크립트 로딩이 완료된 후 실행됩니다. 이는 Google Maps API를 로드한 후 initMap()을 호출하는 것과 같이 초기화가 필요한 스크립트에 중요합니다.
온디맨드 로드 트리거하기
삽입 헬퍼가 준비되었으므로, 이제 트리거가 필요합니다. 신뢰할 수 있는 두 가지 방법이 있습니다: 요소가 뷰포트로 스크롤될 때 로드하는 방법과 사용자가 요소와 상호작용할 때 로드하는 방법입니다.
IntersectionObserver: 화면에 보일 때 로드하기
IntersectionObserver는 요소가 뷰포트에 들어올 때 발생합니다. 이는 지도 컨테이너, 댓글 섹션, 스크롤 아래에 임베드된 위젯 등 페이지의 특정 섹션에 연결된 스크립트에 적합한 트리거입니다.
function injectScriptOnIntersection(scriptUrl, elementSelector, callback) {
const element = document.querySelector(elementSelector);
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
injectScript(scriptUrl, callback);
obs.unobserve(entry.target);
}
});
});
observer.observe(element);
}
이 함수는 스크립트 URL, 로드를 트리거해야 하는 요소의 CSS 선택자, 그리고 선택적인 초기화 콜백을 받습니다. 요소가 뷰포트로 스크롤되면 스크립트가 삽입되고 옵저버의 연결이 해제됩니다.
// 지도 컨테이너가 뷰포트로 스크롤될 때 Google Maps API 로드
injectScriptOnIntersection(
'https://maps.googleapis.com/maps/api/js?key=YOUR_KEY',
'#map-container',
() => initMap()
);
IntersectionObserver는 모든 최신 브라우저에서 지원됩니다(Can I Use에 따르면 전 세계 적용 범위 95.76%). 폴리필이 필요하지 않습니다.
상호작용 시: 사용자가 참여할 때 로드하기
가장 효과적인 방법은 방문자가 스크립트를 필요로 하는 요소와 실제로 상호작용할 때만 스크립트를 로드하는 것입니다. 채팅 위젯은 누군가 채팅 버튼을 클릭할 때까지 로드할 필요가 없습니다. 폼 유효성 검사 라이브러리는 사용자가 폼 필드에 포커스를 맞출 때까지 로드할 필요가 없습니다.
function injectScriptOnInteraction(scriptUrl, elementSelector, eventTypes, callback) {
const element = document.querySelector(elementSelector);
const handler = () => {
eventTypes.forEach(type => element.removeEventListener(type, handler));
injectScript(scriptUrl, callback);
};
eventTypes.forEach(type => {
element.addEventListener(type, handler);
});
}
이 함수는 대상 요소에서 지정된 이벤트를 수신합니다. 첫 번째 이벤트 발생 시 모든 리스너를 제거하고 스크립트를 삽입합니다. 장점: 방문자가 요소와 상호작용하지 않으면 스크립트는 전혀 로드되지 않습니다.
// 채팅 버튼을 클릭하거나 마우스를 올릴 때 채팅 위젯 스크립트 로드
injectScriptOnInteraction(
'chat-widget.js',
'#chat-button',
['click', 'mouseover', 'touchstart'],
() => initChat()
);
실제 영향
이 패턴은 초기 페이지 로드 중에 필요하지 않은 모든 스크립트에 적용됩니다. 몇 가지 일반적인 사용 사례는 다음과 같습니다:
- 채팅 위젯: 일반적인 채팅 위젯은 200~400KB의 JavaScript를 로드합니다. Postmark가 Intercom 위젯을 즉시 로드하는 대신 클릭 시 로드되도록 지연시켰을 때, Time to Interactive가 7.7초에서 3.7초로 떨어졌습니다.
- 동영상 임베드: YouTube 임베드는 1MB 이상의 데이터를 로드합니다. 재생 버튼이 있는 썸네일을 표시하고 클릭 시 임베드를 로드하세요.
- 지도 통합: Google Maps는 수백 킬로바이트의 JavaScript를 로드합니다. IntersectionObserver를 사용하여 지도 컨테이너가 뷰포트로 스크롤될 때 로드하세요.
- 분석 및 추적: 분석 스크립트는 첫 번째 사용자 상호작용 이후까지 기다릴 수 있습니다. 페이지 로드 3초 후에 히트맵 도구가 기록을 시작한다고 해서 실망한 사람은 아무도 없습니다.
- 폼 라이브러리: 유효성 검사 라이브러리, 날짜 선택기(date picker), 리치 텍스트 편집기는 사용자가 폼에 포커스를 맞출 때 로드할 수 있습니다.
지연시키지 않아야 할 때
모든 스크립트를 지연시켜야 하는 것은 아닙니다. 스크립트가 스크롤 위 콘텐츠 렌더링을 담당하는 경우, 이를 지연시키면 Largest Contentful Paint가 개선되는 것이 아니라 더 악화됩니다. 헤더 내비게이션을 초기화하거나, 히어로 섹션을 렌더링하거나, 중요한 A/B 테스트 변형을 설정하는 스크립트는 일찍 실행해야 합니다.
규칙은 간단합니다: 스크립트가 생성하는 내용을 방문자가 첫 번째 뷰포트 내에서 보거나 상호작용하게 된다면 정상적으로 로드하세요. 스크립트가 스크롤 아래에 있거나 사용자 작업 뒤에 숨겨진 기능을 구동한다면, 위의 패턴 중 하나를 사용하여 지연시키세요.
Tip: 모든 JavaScript 로딩 전략에 대한 전체 개요는 JavaScript를 지연하거나 예약하는 16가지 방법을 참조하세요.
개선 사항 측정하기
2025 Web Almanac은 모바일 Total Blocking Time의 중앙값이 1,916ms로 2024년 대비 58% 증가했다고 보고합니다. 이러한 차단의 상당 부분은 페이지 로드 중에 실행할 필요가 없었던 JavaScript에서 비롯됩니다. 중요하지 않은 스크립트를 지연시킴으로써 중요한 렌더링 경로에서 이를 완전히 제거할 수 있습니다.
온디맨드 로딩을 구현한 후 Real User Monitoring으로 개선 사항을 확인하세요. Lighthouse에만 의존하지 말고 필드 데이터에서 INP 점수와 Total Blocking Time을 확인하세요. 실험실 테스트는 캐시가 비어 있는 빠른 기기에서 실행됩니다. 여러분의 방문자는 15개의 브라우저 탭이 열려 있는 모바일 네트워크 환경에 있습니다. 차이는 바로 거기에서 나타납니다.

