서드 파티 자바스크립트 최적화

타사 스크립트는 성능에 영향을 미치므로 타사 스크립트를 정기적으로 감사하고 효율적인 로드 기술을 사용하는 것이 중요합니다. 이 Codelab에서는 서드 파티 리소스의 로드를 최적화하는 방법을 보여줍니다. 여기에서 다루는 기법은 다음과 같습니다.

  • 스크립트 로드 지연

  • 중요하지 않은 리소스 지연 로드

  • 필수 출처에 사전 연결

포함된 샘플 앱에는 타사 소스에서 제공하는 세 가지 기능이 있는 간단한 웹페이지가 있습니다.

  • 동영상 삽입

  • 선 그래프를 렌더링하기 위한 데이터 시각화 라이브러리

  • 소셜 미디어 공유 위젯

타사 리소스가 강조 표시된 페이지의 스크린샷
샘플 앱의 서드 파티 리소스

먼저 앱 성능을 측정한 다음 각 기법을 적용하여 앱 성능의 여러 측면을 개선합니다.

성능 측정

먼저 전체 화면 뷰에서 샘플 앱을 엽니다.

  1. 리믹스하여 수정을 클릭하여 프로젝트를 수정할 수 있도록 합니다.
  2. 사이트를 미리 보려면 View App을 누른 다음 Fullscreen 전체 화면을 누릅니다.

페이지에서 Lighthouse 성능 감사를 실행하여 기준 성능을 설정합니다.

  1. `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
  2. Lighthouse 탭을 클릭합니다.
  3. 모바일을 클릭합니다.
  4. 실적 체크박스를 선택합니다. (감사 섹션의 나머지 체크박스는 선택 해제할 수 있습니다.)
  5. 시뮬레이션된 빠른 3G, 4배 CPU 속도 저하를 클릭합니다.
  6. 저장용량 비우기 체크박스를 선택합니다.
  7. 감사 실행을 클릭합니다.

시스템에서 감사를 실행할 때 정확한 결과는 다를 수 있지만 첫 콘텐츠 페인트 (FCP) 시간이 상당히 높으며 Lighthouse는 렌더링 차단 리소스 제거필수 출처에 사전 연결이라는 두 가지 조사 기회를 제안합니다. 측정항목이 모두 녹색인 경우에도 최적화하면 실적이 향상됩니다.

2.4초의 FCP와 2가지 기회(렌더링 차단 리소스 제거 및 필수 출처에 사전 연결)를 보여주는 Lighthouse 감사 스크린샷입니다.

서드 파티 자바스크립트 지연

렌더링 차단 리소스 제거 감사에서 d3js.org에서 제공하는 스크립트를 연기하면 시간을 절약할 수 있는 것으로 확인되었습니다.

d3.v3.min.js 스크립트가 강조 표시된 렌더링 차단 리소스 감사 제거 스크린샷

D3.js는 데이터 시각화를 만들기 위한 자바스크립트 라이브러리입니다. 샘플 앱의 script.js 파일은 D3 유틸리티 함수를 사용하여 SVG 선 차트를 만들어 페이지에 추가합니다. 여기서는 작업 순서가 중요합니다. script.js는 문서가 파싱되고 D3 라이브러리가 로드된 후 실행되어야 하므로 index.html에서 닫는 </body> 태그 바로 앞에 포함되어 있습니다.

그러나 D3 스크립트가 페이지의 <head>에 포함되어 있어 나머지 문서의 파싱을 차단합니다.

헤드에 스크립트 태그가 강조 표시된 index.html 스크린샷

다음 두 가지 매직 속성이 스크립트 태그에 추가될 때 파서의 차단을 해제할 수 있습니다.

  • async는 스크립트가 백그라운드에서 다운로드되고 다운로드가 완료된 후 첫 번째 기회에 실행되도록 합니다.

  • defer는 스크립트가 백그라운드에서 다운로드되고 파싱 후가 완전히 완료되도록 합니다.

이 차트는 전체 페이지에 크게 중요하지 않으며 스크롤해야 볼 수 있는 부분에 있을 가능성이 높으므로 defer를 사용하여 파서 차단이 없는지 확인합니다.

1단계: defer 속성을 사용하여 스크립트 비동기적으로 로드

index.html의 17번째 줄에서 defer 속성을 <script> 요소에 추가합니다.

<script src="https://d3js.org/d3.v3.min.js" defer></script>

2단계: 작업 순서 확인

이제 D3가 지연되므로 D3이 준비되기 전에 script.js가 실행되어 오류가 발생합니다.

defer 속성이 있는 스크립트는 지정된 순서대로 실행됩니다. D3가 준비된 후 script.js가 실행되도록 하려면 defer를 추가하고 문서의 <head>, D3 <script> 요소 바로 뒤에 위로 이동합니다. 이제 더 이상 파서를 차단하지 않고 다운로드가 더 빨리 시작됩니다.

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

서드 파티 리소스 지연 로드

스크롤해야 볼 수 있는 부분에 있는 모든 리소스는 지연 로드에 적합합니다.

샘플 앱에는 iframe에 삽입된 YouTube 동영상이 있습니다. 페이지에서 요청하는 수와 삽입된 YouTube iframe에서 발생하는 요청을 확인하려면 다음 단계를 따르세요.

  1. 사이트를 미리 보려면 View App을 누른 다음 Fullscreen 전체 화면을 누릅니다.
  2. `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
  3. 네트워크 탭을 클릭합니다.
  4. 캐시 사용 중지 체크박스를 선택합니다.
  5. 제한 드롭다운 메뉴에서 Fast 3G를 선택합니다.
  6. 페이지를 새로고침합니다.

DevTools Network 패널의 스크린샷

네트워크 패널을 통해 페이지에서 총 28개의 요청을 수행하고 약 1MB의 압축된 리소스를 전송한 것을 확인할 수 있습니다.

YouTube iframe의 요청을 확인하려면 Initiator 열에서 동영상 ID 6lfaiXM6waw을 찾습니다. 도메인별로 모든 요청을 그룹화하려면 다음 안내를 따르세요.

  • 네트워크 패널에서 열 제목을 마우스 오른쪽 버튼으로 클릭합니다.

  • 드롭다운 메뉴에서 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);

callbackIntersectionObserverEntry 객체 목록과 IntersectionObserver 객체 자체를 수신합니다. 각 항목에는 크기, 위치, 표시 영역에 진입한 시간 등을 설명하는 target 요소와 속성이 포함됩니다. IntersectionObserverEntry의 속성 중 하나는 isIntersecting로, 요소가 표시 영역에 들어올 때 true와 같은 불리언 값입니다.

이 예에서 targetiframe입니다. target가 표시 영역에 진입하면 isIntersectingtrue와 같습니다. 실제 동작을 보려면 callback를 다음 함수로 바꿉니다.

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. 사이트를 미리 보려면 View App을 누른 다음 Fullscreen 전체 화면을 누릅니다.
  2. `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
  3. 콘솔 탭을 클릭합니다.

위아래로 스크롤해 보세요. 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 Network 패널을 열고 페이지를 다시 새로고침합니다. 네트워크 패널에는 페이지에서 14개의 요청을 보냈지만 260KB에 불과한 것으로 표시됩니다. 상당한 개선이 있었군요!

이제 페이지를 아래로 스크롤하고 네트워크 패널을 지켜봅니다. 동영상이 표시되면 페이지에서 추가 요청을 트리거하는 것을 확인할 수 있습니다.

필수 출처에 미리 연결

중요하지 않은 JavaScript를 연기하고 YouTube 요청을 지연 로드했습니다. 이제 나머지 서드 파티 콘텐츠를 최적화해야 합니다.

링크에 rel=preconnect 속성을 추가하면 리소스에 대한 요청이 이루어지기 전에 브라우저에 도메인 연결을 설정하라고 지시합니다. 이 속성은 페이지에 확실하게 필요한 리소스를 제공하는 출처에 사용하는 것이 가장 좋습니다.

필수 출처에 사전 연결에서 제안된 첫 번째 단계에서 실행한 Lighthouse 감사는 staticxx.facebook.com 및 youtube.com에 대한 조기 연결을 설정하여 약 400ms를 절약할 수 있습니다.

staticxx.facebook.com 도메인이 강조 표시된 필수 출처 감사에 미리 연결합니다.

이제 YouTube 동영상이 지연 로드되므로 소셜 미디어 공유 위젯의 소스인 staticxx.facebook.com만 남습니다. 이 도메인에 대한 조기 연결은 문서의 <head><link> 태그를 추가하는 것만큼 간단합니다.

  <link rel="preconnect" href="https://staticxx.facebook.com">

성능 재평가

최적화 후 페이지의 상태입니다. Codelab의 성능 측정 섹션에 나온 단계에 따라 다른 Lighthouse 감사를 실행합니다.

1초 FCP와 성능 점수 99를 보여주는 Lighthouse 감사