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

JavaScript 생태계에서 최대 콘텐츠 렌더링 시간 개선

Google은 Aurora 프로젝트의 일환으로 널리 사용되는 웹 프레임워크가 코어 웹 바이탈에 따라 원활하게 작동할 수 있도록 협력해 왔습니다. Angular와 Next.js는 이미 글꼴 인라인 처리를 도입했습니다. 이에 대해서는 이 문서의 첫 번째 부분에 설명되어 있습니다. 두 번째 최적화는 중요한 CSS 인라인 처리로, 이제 Angular CLI에서 기본적으로 사용 설정되고 Nuxt.js에서 구현 작업이 진행 중입니다.

글꼴 인라인 처리

수백 개의 애플리케이션을 분석한 결과, 개발자가 index.html<head> 요소에서 글꼴을 참조하여 애플리케이션에 글꼴을 포함하는 경우가 많다는 사실을 발견했습니다. 다음은 머티리얼 아이콘을 포함할 때 표시되는 방식의 예입니다.

<!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으로 요청이 전송되면 스타일시트를 가져와 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가 사용된다는 것을 발견합니다. 마지막으로 critter는 페이지의 <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에서 중요한 CSS 인라인 처리를 사용할 수 있으며 v12에서 기본적으로 사용 설정됩니다. v11을 사용 중이면 angular.json에서 inlineCritical 속성을 true로 설정하여 사용 설정합니다. Next.js에서 이 기능을 사용하려면 next.config.jsexperimental: { optimizeCss: true }를 추가하세요.

결론

이 게시물에서는 Chrome과 웹 프레임워크 간의 공동작업에 관해 다루었습니다. 프레임워크 작성자이고 Google이 기술에서 해결한 몇 가지 문제를 알고 있다면 이러한 결과를 통해 유사한 성능 최적화를 적용하는 데 도움이 되기를 바랍니다.

개선사항에 대해 자세히 알아보기 Aurora 소개 게시물에서 코어 웹 바이탈과 관련하여 Google에서 진행한 최적화 작업의 전체 목록을 확인할 수 있습니다.