자바스크립트 프레임워크에서 리소스 인라인 처리

JavaScript 생태계 전반에서 최대 콘텐츠 페인트를 개선합니다.

Google은 Aurora 프로젝트의 일환으로 인기 있는 웹 프레임워크가 Core Web Vitals에 따라 우수한 성능을 발휘할 수 있도록 노력하고 있습니다. Angular와 Next.js는 이미 글꼴 인라인을 지원하며 이는 이 도움말의 첫 번째 부분에 설명되어 있습니다. 두 번째로 다룰 최적화는 이제 Angular CLI에서 기본적으로 사용 설정되고 Nuxt.js에서 구현 중인 중요한 CSS 인라인 처리입니다.

글꼴 인라인 처리

Aurora팀은 수백 개의 애플리케이션을 분석한 결과, 개발자가 index.html<head> 요소에서 글꼴을 참조하여 애플리케이션에 글꼴을 포함하는 경우가 많다는 사실을 발견했습니다. Material 아이콘을 포함하는 경우의 예는 다음과 같습니다.

<!doctype html>
<html lang="en">
<head>
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  ...
</html>

이 패턴은 완전히 유효하고 작동하지만 애플리케이션의 렌더링을 차단하고 추가 요청을 도입합니다. 어떤 일이 일어나고 있는지 더 잘 이해하려면 위의 HTML에서 참조된 스타일시트의 소스 코드를 살펴보세요.

/* fallback */
@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}

.material-icons {
  /*...*/
}

font-face 정의가 fonts.gstatic.com에서 호스팅되는 외부 파일을 어떻게 참조하는지 확인합니다. 애플리케이션을 로드할 때 브라우저는 먼저 헤드에 참조된 원래 스타일시트를 다운로드해야 합니다.

웹사이트가 서버에 요청하고 외부 스타일시트를 다운로드하는 방법을 보여주는 이미지
먼저 웹사이트에서 글꼴 스타일시트를 로드합니다.

다음으로 브라우저에서 woff2 파일을 다운로드하고 마지막으로 애플리케이션 렌더링을 진행할 수 있습니다.

두 개의 요청, 즉 글꼴 스타일시트에 대한 요청과 글꼴 파일에 대한 두 번째 요청을 보여주는 이미지입니다.
다음으로 글꼴을 로드하도록 요청합니다.

최적화할 수 있는 기회는 빌드 시간에 초기 스타일시트를 다운로드하여 index.html에 인라인하는 것입니다. 이렇게 하면 런타임 시 CDN으로의 전체 왕복이 건너뛰어 차단 시간이 줄어듭니다.

애플리케이션을 빌드할 때 CDN에 요청이 전송되면 CDN에서 스타일시트를 가져와 HTML 파일에 인라인 처리하여 도메인에 <link rel=preconnect>를 추가합니다. 이 기법을 적용하면 다음과 같은 결과가 나옵니다.

<!doctype html>
<html lang="en">
<head>
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
  <style type="text/css">
  @font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
  ...
</html>

이제 Next.js 및 Angular에서 글꼴 인라인을 사용할 수 있습니다.

프레임워크 개발자가 기본 도구에서 최적화를 구현하면 기존 애플리케이션과 새 애플리케이션에서 더 쉽게 최적화를 사용 설정할 수 있어 전체 생태계가 개선됩니다.

이 개선사항은 Next.js v10.2 및 Angular v11부터 기본적으로 사용 설정됩니다. 둘 다 Google 및 Adobe 글꼴 인라인을 지원합니다. Angular는 v12.2에서 후자를 도입할 예정입니다.

GitHub의 Next.js에서 글꼴 인라인 처리 구현을 확인하고 Angular 컨텍스트에서 이 최적화를 설명하는 동영상을 확인하세요.

중요한 CSS 인라인 처리

또 다른 개선사항은 중요한 CSS를 인라인 처리하여 콘텐츠가 포함된 첫 페인트 (FCP)콘텐츠가 포함된 최대 페인트 (LCP) 측정항목을 개선하는 것입니다. 페이지의 중요 CSS에는 초기 렌더링에 사용된 모든 스타일이 포함됩니다. 이 주제에 관한 자세한 내용은 중요하지 않은 CSS 연기를 참고하세요.

많은 애플리케이션이 스타일을 동기식으로 로드하여 애플리케이션 렌더링을 차단하는 것으로 확인되었습니다. 스타일을 비동기식으로 로드하면 이 문제를 빠르게 해결할 수 있습니다. media="all"로 스크립트를 로드하는 대신 media 속성 값을 print로 설정하고 로드가 완료되면 속성 값을 all로 바꿉니다.

<link rel="stylesheet" href="..." media="print" onload="this.media='all'">

하지만 이렇게 하면 스타일이 지정되지 않은 콘텐츠가 깜박일 수 있습니다.

스타일이 로드될 때 페이지가 깜박입니다.

위 동영상은 페이지의 스타일을 비동기식으로 로드하는 페이지 렌더링을 보여줍니다. 브라우저가 먼저 스타일 다운로드를 시작한 다음 다음 HTML을 렌더링하기 때문에 플리커링이 발생합니다. 브라우저가 스타일을 다운로드하면 링크 요소의 onload 이벤트를 트리거하여 media 속성을 all로 업데이트하고 DOM에 스타일을 적용합니다.

HTML 렌더링과 스타일을 적용하는 사이에는 페이지의 스타일이 부분적으로 해제됩니다. 브라우저에서 스타일을 사용하면 깜박임이 발생하여 사용자 환경이 저하되고 누적 레이아웃 전환 (CLS)이 회귀합니다.

비동기 스타일 로드와 함께 중요한 CSS 인라인 처리를 사용하면 로드 동작을 개선할 수 있습니다. critters 도구는 스타일시트의 선택자를 보고 HTML과 일치시켜 페이지에서 사용되는 스타일을 찾습니다. 일치하는 항목을 찾으면 해당 스타일을 중요한 CSS의 일부로 간주하고 인라인 처리합니다.

예를 살펴보겠습니다.

금지사항
<head>
   <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>
/* styles.css */
section button.primary {
  /* ... */
}
.list {
  /* ... */
}

인라인 처리 전의 예

위 예시에서 크리터는 styles.css의 콘텐츠를 읽고 파싱한 후 두 선택기를 HTML과 일치시켜 section button.primary를 사용한다는 것을 발견합니다. 마지막으로 동물은 페이지의 <head>에 상응하는 스타일을 인라인하여 다음과 같은 결과를 가져옵니다.

권장사항
<head>
  <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">
  <style>
  section button.primary {
    /* ... */
  }
  </style>
</head>
<body>
  <section>
    <button class="primary"></button>
  </section>
</body>

인라인 처리 후의 예

HTML에 중요한 CSS를 인라인 처리하면 페이지의 깜박임이 사라집니다.

CSS 인라인 처리 후 페이지가 로드됩니다.

이제 Angular에서 Critical CSS 인라인을 사용할 수 있으며 v12에서는 기본적으로 사용 설정됩니다. v11을 사용하는 경우 angular.json에서 inlineCritical 속성을 true로 설정하여 사용 설정합니다. Next.js에서 이 기능을 사용하려면 next.config.jsexperimental: { optimizeCss: true }를 추가합니다.

결론

이 게시물에서는 Chrome과 웹 프레임워크 간의 공동작업에 대해 살펴봤습니다. 프레임워크 작성자이고 기술에서 해결한 문제 중 일부를 알고 있다면 Google의 발견사항을 바탕으로 유사한 성능 최적화를 적용해 보시기 바랍니다.

개선사항에 대해 자세히 알아보기 Aurora 소개 게시물에서 Core Web Vitals를 위해 진행한 최적화 작업의 포괄적인 목록을 확인할 수 있습니다.