서드 파티 스크립트는 성능에 영향을 미칩니다. 따라서 정기적으로 감사하고 효율적인 로드 기법을 사용하는 것이 중요합니다. 이 Codelab에서는 서드 파티 리소스 로드를 최적화하는 방법을 보여줍니다. 다음 기법을 다룹니다.
스크립트 로드 지연
중요하지 않은 리소스 지연 로드
필수 출처에 미리 연결
포함된 샘플 앱에는 서드 파티 소스에서 가져온 세 가지 기능이 있는 간단한 웹페이지가 있습니다.
동영상 삽입
선 그래프 렌더링을 위한 데이터 시각화 라이브러리
소셜 미디어 공유 위젯
먼저 앱의 성능을 측정한 다음 각 기법을 적용하여 앱 성능의 다양한 측면을 개선합니다.
성능 측정
먼저 전체 화면 보기에서 샘플 앱을 엽니다.
- 리믹스하여 수정을 클릭하여 프로젝트를 수정할 수 있도록 합니다.
- 사이트를 미리 보려면 앱 보기를 누른 다음 전체 화면 을 누릅니다.
페이지에서 Lighthouse 성능 감사를 실행하여 기준 성능을 설정합니다.
- `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
- Lighthouse 탭을 클릭합니다.
- 모바일을 클릭합니다.
- 성능 체크박스를 선택합니다. 감사 섹션에서 나머지 체크박스는 선택 해제할 수 있습니다.
- 고속 3G 시뮬레이션, CPU 4배 감속을 클릭합니다.
- 저장용량 지우기 체크박스를 선택합니다.
- 감사 실행을 클릭합니다.
머신에서 감사를 실행할 때 정확한 결과는 다를 수 있지만 콘텐츠가 포함된 첫 번째 페인트 (FCP) 시간이 상당히 길고 Lighthouse에서 렌더링 차단 리소스 제거 및 필요한 출처에 미리 연결이라는 두 가지 조사 기회를 제안합니다. 측정항목이 모두 녹색이더라도 최적화를 통해 실적이 개선됩니다.
서드 파티 JavaScript 지연
렌더링 차단 리소스 제거 감사 결과 d3js.org에서 가져오는 스크립트를 지연하면 시간을 절약할 수 있는 것으로 확인되었습니다.
D3.js는 데이터 시각화를 만드는 JavaScript 라이브러리입니다. 샘플 앱의 script.js
파일은 D3 유틸리티 함수를 사용하여 SVG 선 차트를 만들고 페이지에 추가합니다. 여기서 작업 순서가 중요합니다. script.js
는 문서가 파싱되고 D3 라이브러리가 로드된 후에 실행되어야 하므로 index.html
의 닫는 </body>
태그 바로 앞에 포함되어 있습니다.
하지만 D3 스크립트가 페이지의 <head>
에 포함되어 나머지 문서의 파싱을 차단합니다.
두 가지 매직 속성을 스크립트 태그에 추가하면 파서 차단을 해제할 수 있습니다.
async
는 스크립트가 백그라운드에서 다운로드되고 다운로드가 완료된 후 최초 기회에 실행되도록 합니다.defer
: 스크립트가 백그라운드에서 다운로드되고 구문 분석이 완전히 완료된 후에 실행되도록 합니다.
이 차트는 전체 페이지에 중요하지 않으며 대부분 접히는 영역에 표시되므로 defer
를 사용하여 파서 차단이 없는지 확인합니다.
1단계: defer
속성으로 스크립트를 비동기식으로 로드합니다.
index.html
의 17번째 줄에서 <script>
요소에 defer
속성을 추가합니다.
<script src="https://d3js.org/d3.v3.min.js" defer></script>
2단계: 올바른 연산 순서 확인
이제 D3가 지연되므로 D3가 준비되기 전에 script.js
가 실행되어 오류가 발생합니다.
defer
속성이 있는 스크립트는 지정된 순서대로 실행됩니다. D3가 준비된 후에 script.js
가 실행되도록 하려면 defer
를 추가하고 D3 <script>
요소 바로 뒤의 문서 <head>
로 이동합니다. 이제 더 이상 파서를 차단하지 않으며 다운로드가 더 빨리 시작됩니다.
<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>
서드 파티 리소스 지연 로드
스크롤해야 볼 수 있는 부분에 있는 모든 리소스는 지연 로드에 적합합니다.
샘플 앱에는 iframe에 삽입된 YouTube 동영상이 있습니다. 페이지에서 실행하는 요청 수와 삽입된 YouTube iframe에서 발생하는 요청 수를 확인하려면 다음 단계를 따르세요.
- 사이트를 미리 보려면 앱 보기를 누른 다음 전체 화면 을 누릅니다.
- `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
- 네트워크 탭을 클릭합니다.
- 캐시 사용 중지 체크박스를 선택합니다.
- 제한 드롭다운 메뉴에서 빠른 3G를 선택합니다.
- 페이지를 새로고침합니다.
네트워크 패널을 보면 페이지에서 총 28회의 요청을 실행하고 거의 1MB의 압축된 리소스를 전송한 것으로 나타납니다.
YouTube iframe
에서 보낸 요청을 식별하려면 이니시에이터 열에서 동영상 ID 6lfaiXM6waw
를 찾습니다. 도메인별로 모든 요청을 그룹화하려면 다음 단계를 따르세요.
네트워크 패널에서 열 제목을 마우스 오른쪽 버튼으로 클릭합니다.
드롭다운 메뉴에서 Domains(도메인) 열을 선택합니다.
도메인별로 요청을 정렬하려면 Domains 열 제목을 클릭합니다.
새 정렬을 통해 Google 도메인에 대한 추가 요청이 있음을 알 수 있습니다. YouTube iframe은 스크립트, 스타일시트, 이미지, 글꼴을 총 14번 요청합니다. 하지만 사용자가 실제로 아래로 스크롤하여 동영상을 재생하지 않는 한 이러한 모든 애셋이 실제로는 필요하지 않습니다.
사용자가 페이지의 해당 섹션으로 아래로 스크롤할 때까지 동영상을 지연 로드하여 페이지에서 처음에 실행하는 요청 수를 줄입니다. 이 접근 방식은 사용자 데이터를 저장하고 초기 로드 속도를 높입니다.
지연 로드를 구현하는 한 가지 방법은 요소가 브라우저의 표시 영역에 진입하거나 종료될 때 알리는 브라우저 API인 Intersection Observer를 사용하는 것입니다.
1단계: 동영상이 처음에 로드되지 않도록 방지
동영상 iframe을 지연 로드하려면 먼저 일반적인 방식으로 로드되지 않도록 해야 합니다. src
속성을 data-src
속성으로 바꾸어 동영상 URL을 지정합니다.
<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
data-src
는 표준 HTML 요소에 추가 정보를 저장할 수 있는 데이터 속성입니다. 데이터 속성의 이름은 'data-'로 시작하는 한 무엇이든 지정할 수 있습니다.
src
가 없는 iframe은 로드되지 않습니다.
2단계: Intersection Observer를 사용하여 동영상을 지연 로드
사용자가 동영상으로 스크롤할 때 동영상을 로드하려면 스크롤이 발생하는 시점을 알아야 합니다. 이때 Intersection Observer API가 사용됩니다. Intersection Observer API를 사용하면 추적하려는 요소가 뷰포인트에 진입하거나 종료할 때마다 실행되는 콜백 함수를 등록할 수 있습니다.
시작하려면 새 파일을 만들어 이름을 lazy-load.js
로 지정합니다.
- 새 파일을 클릭하고 이름을 지정합니다.
- 이 파일 추가를 클릭합니다.
문서 헤더에 스크립트 태그를 추가합니다.
<script src="/lazy-load.js" defer></script>
lazy-load.js
에서 새 IntersectionObserver
를 만들고 실행할 콜백 함수를 전달합니다.
// create a new Intersection Observer
let observer = new IntersectionObserver(callback);
이제 observe
메서드에서 인수로 전달하여 observer
에 시청할 타겟 요소 (이 경우 동영상 iframe)를 제공합니다.
// the element that you want to watch
const element = document.querySelector('iframe');
// register the element with the observe method
observer.observe(element);
callback
는 IntersectionObserverEntry
객체 목록과 IntersectionObserver
객체 자체를 수신합니다. 각 항목에는 target
요소와 크기, 위치, 뷰포트에 진입한 시간 등을 설명하는 속성이 포함됩니다. IntersectionObserverEntry
의 속성 중 하나는 isIntersecting
입니다. 이 속성은 요소가 표시 영역에 들어갈 때 true
과 같은 불리언 값입니다.
이 예에서 target
은 iframe
입니다. target
가 뷰포트에 들어올 때 isIntersecting
는 true
와 같습니다. 실제 동작을 확인하려면 callback
를 다음 함수로 바꿉니다.
let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(entry => {
console.log(entry.target);
console.log(entry.isIntersecting);
});
});
- 사이트를 미리 보려면 앱 보기를 누른 다음 전체 화면 을 누릅니다.
- `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
- 콘솔 탭을 클릭합니다.
위아래로 스크롤해 보세요. isIntersecting
값이 변경되고 타겟 요소가 콘솔에 로깅됩니다.
사용자가 해당 위치로 스크롤할 때 동영상을 로드하려면 isIntersecting
를 조건으로 사용하여 loadElement
함수를 실행합니다. 이 함수는 iframe
요소의 data-src
에서 값을 가져와 iframe
요소의 src
속성으로 설정합니다. 이 교체로 인해 동영상 로드가 트리거됩니다. 그런 다음 동영상이 로드되면 observer
에서 unobserve
메서드를 호출하여 타겟 요소의 시청을 중지합니다.
let observer = new IntersectionObserver(function (entries, observer) {
entries.forEach(entry => {
console.log(entry.target);
console.log(entry.isIntersecting);
});
});
if (entry.isIntersecting) {
// do this when the element enters the viewport
loadElement(entry.target);
// stop watching
observer.unobserve(entry.target);
}
});
});
function loadElement(element) {
const src = element.getAttribute('data-src');
element.src = src;
}
3단계: 실적 재평가
리소스의 크기와 수가 어떻게 변경되었는지 확인하려면 DevTools 네트워크 패널을 열고 페이지를 다시 새로고침합니다. 네트워크 패널에는 페이지에서 14번 요청했으며 260KB만 사용했다고 표시됩니다. 의미 있는 개선입니다.
이제 페이지를 아래로 스크롤하고 네트워크 패널을 주시합니다. 동영상으로 이동하면 페이지에서 추가 요청이 트리거되는 것을 볼 수 있습니다.
필수 기점에 미리 연결
중요하지 않은 JavaScript를 지연하고 YouTube 요청을 지연 로드했으므로 이제 나머지 서드 파티 콘텐츠를 최적화할 차례입니다.
링크에 rel=preconnect
속성을 추가하면 브라우저에 해당 리소스에 대한 요청이 이루어지기 전에 도메인에 연결하라는 신호를 보냅니다. 이 속성은 페이지에 필요한 리소스를 제공하는 출처에 가장 적합합니다.
첫 번째 단계에서 실행한 Lighthouse 감사에서 필요한 출처에 미리 연결하여 staticxx.facebook.com 및 youtube.com에 조기 연결을 설정하면 약 400ms를 절약할 수 있다고 제안했습니다.
이제 YouTube 동영상이 지연 로드되므로 소셜 미디어 공유 위젯의 소스인 staticxx.facebook.com만 남게 됩니다. 이 도메인에 대한 초기 연결을 설정하는 것은 문서의 <head>
에 <link>
태그를 추가하는 것만큼 간단합니다.
<link rel="preconnect" href="https://staticxx.facebook.com">
실적 재평가
다음은 최적화 후 페이지의 상태입니다. Codelab의 성능 측정 섹션에 나온 단계에 따라 Lighthouse 감사를 다시 실행합니다.