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

서드 파티 스크립트는 성능에 영향을 미칩니다. 따라서 정기적으로 감사하고 효율적인 로드 기법을 사용하는 것이 중요합니다. 이 Codelab에서는 서드 파티 리소스 로드를 최적화하는 방법을 보여줍니다. 다음 기법을 다룹니다.

  • 스크립트 로드 지연

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

  • 필수 출처에 사전 연결

포함된 샘플 앱에는 서드 파티 소스에서 가져온 세 가지 기능이 있는 간단한 웹페이지가 있습니다.

  • 동영상 삽입

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

  • 소셜 미디어 공유 위젯

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

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

성능 측정

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

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

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

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

머신에서 감사를 실행할 때 정확한 결과는 다를 수 있지만 콘텐츠가 포함된 첫 번째 페인트 (FCP) 시간이 상당히 길고 Lighthouse에서 렌더링 차단 리소스 제거필요한 출처에 미리 연결이라는 두 가지 조사 기회를 제안합니다. 측정항목이 모두 녹색이더라도 최적화를 통해 실적이 개선됩니다.

2.4초의 FCP와 렌더링 차단 리소스 제거 및 필수 출처에 사전 연결이라는 두 가지 기회를 보여주는 Lighthouse 감사 스크린샷

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

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

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

D3.js는 데이터 시각화를 만드는 JavaScript 라이브러리입니다. 샘플 앱의 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를 추가하고 D3 <script> 요소 바로 뒤의 문서 <head>로 이동합니다. 이제 더 이상 파서를 차단하지 않으며 다운로드가 더 빨리 시작됩니다.

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

서드 파티 리소스 지연 로드

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

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

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

DevTools Network 패널의 스크린샷

Network 패널에는 페이지가 총 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);

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. 사이트를 미리 보려면 앱 보기를 누른 다음 전체 화면 전체 화면을 누릅니다.
  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 감사를 다시 실행합니다.

Lighthouse 감사에서 1초의 FCP와 99의 성능 점수를 보여줍니다.