더욱 쉬워진 웹 성능 - Google I/O 2018 에디션

Google I/O 2018에서는 웹 성능을 더 쉽게 개선할 수 있는 도구, 라이브러리, 최적화 기법을 소개했습니다. 여기서는 The Oodles Theater 앱을 사용하여 이들에 대해 설명합니다. 또한 예측 로드를 사용한 실험과 새로운 Guess.js 이니셔티브에 대해서도 다룹니다.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

Google은 지난 1년 동안 웹을 더 빠르고 성능이 우수하게 만드는 방법을 찾기 위해 매우 바빴습니다. 이를 통해 새로운 도구, 접근 방식, 라이브러리가 탄생했으며 이 도움말에서는 이를 여러분과 공유하고자 합니다. 첫 번째 부분에서는 Oodles Theater 앱을 개발할 때 실제로 사용한 몇 가지 최적화 기법을 보여줍니다. 두 번째 부분에서는 예측 로드 실험과 새로운 Guess.js 이니셔티브에 대해 설명합니다.

성능의 필요성

인터넷은 해가 갈수록 점점 더 무거워지고 있습니다. 웹 상태를 확인하면 모바일 페이지의 중앙값이 약 1.5MB이며 대부분 자바스크립트와 이미지임을 알 수 있습니다.

웹사이트 규모가 커지면서 네트워크 지연 시간, CPU 제한, 렌더링 차단 패턴 또는 불필요한 서드 파티 코드와 같은 기타 요인과 함께 성능 문제가 복잡해지고 있습니다.

대부분의 사용자는 속도가 자신이 필요로 하는 UX 계층의 최상위에 있다고 평가합니다. 페이지 로드가 완료될 때까지는 별로 할 수 있는 일이 없으므로 놀랄 일은 아닙니다. 페이지에서 가치를 도출할 수 없으며 미학을 감상할 수 없습니다.

UX 계층 구조 피라미드
그림 1. 사용자에게 속도가 얼마나 중요한가요? (Speed Matters, 3권)

Google은 성능이 사용자에게 중요하다는 것을 알고 있지만 최적화의 시작점을 찾는 것이 비밀처럼 느껴질 수도 있습니다. 다행히 이 과정에서 도움이 되는 도구가 있습니다.

Lighthouse - 성능 워크플로의 기반

Lighthouse는 Chrome DevTools의 일부로, 웹사이트를 감사하고 웹사이트를 개선하는 방법에 관한 힌트를 제공합니다.

최근 일상적인 개발 워크플로에 유용한 새로운 성능 감사를 출시했습니다.

새로운 Lighthouse 감사
그림 2. 새로운 Lighthouse 감사

이를 활용하는 방법을 실용적인 예시인 Oodles Theater 앱을 통해 살펴보겠습니다. 이 앱은 좋아하는 양방향 Google Doodle을 사용해 보고 게임도 즐길 수 있는 작은 데모 웹 앱입니다.

우리는 앱을 빌드하는 동안 앱의 성능을 최대한으로 유지하고자 했습니다. 최적화의 출발점은 Lighthouse 보고서였습니다.

Oodles 앱의 Lighthouse 보고서
그림 3. Oodles 앱의 Lighthouse 보고서

Lighthouse 보고서에서 볼 수 있듯이 앱의 초기 성능은 꽤 형편없었습니다. 3G 네트워크에서 사용자는 첫 번째 의미 있는 페인트가 실행되거나 앱이 상호작용할 때까지 15초를 기다려야 했습니다. Lighthouse에서 사이트의 수많은 문제를 강조 표시했으며 전반적인 실적 점수 23이 이를 정확하게 반영했습니다.

페이지의 크기는 약 3.4MB였으므로 불필요한 부분을 줄여야 했습니다.

이때부터 첫 번째 성능 과제가 시작되었습니다. 전반적인 환경에 영향을 주지 않으면서 쉽게 삭제할 수 있는 항목을 찾는 것이었습니다.

성능 최적화 기회

불필요한 리소스 삭제

공백과 주석은 안전하게 삭제할 수 있는 명백한 항목입니다.

축소의 이점
그림 4. JavaScript 및 CSS 축소 및 압축

Lighthouse는 미최적화된 CSS 및 JavaScript 감사에서 이 기회를 강조 표시합니다. 빌드 프로세스에 webpack을 사용하고 있었으므로 축소를 위해 Uglify JS 플러그인을 사용했습니다.

축소는 일반적인 작업이므로 어떤 빌드 프로세스를 사용하든 미리 준비된 솔루션을 찾을 수 있어야 합니다.

이 영역에서 유용한 또 다른 감사는 텍스트 압축 사용입니다. 비압축 파일을 전송할 이유가 없으며 요즘 대부분의 CDN은 이를 기본적으로 지원합니다.

Firebase 호스팅을 사용하여 코드를 호스팅하고 있었는데 Firebase에서는 기본적으로 gzipping을 사용 설정합니다. 따라서 합리적인 CDN에 코드를 호스팅하기만 하면 무료로 이 기능을 사용할 수 있었습니다.

gzip은 매우 인기 있는 압축 방법이지만 ZopfliBrotli와 같은 다른 메커니즘도 주목을 받고 있습니다. Brotli는 대부분의 브라우저에서 지원하며 바이너리를 사용하여 애셋을 서버에 전송하기 전에 사전 압축할 수 있습니다.

효율적인 캐시 정책 사용

다음 단계는 불필요하면 리소스를 두 번 전송하지 않도록 하는 것이었습니다.

Lighthouse의 비효율적인 캐시 정책 감사를 통해 이를 달성하기 위해 캐싱 전략을 최적화할 수 있다는 사실을 알게 되었습니다. 서버에 max-age 만료 헤더를 설정하여 사용자가 재방문할 때 이전에 다운로드한 리소스를 재사용할 수 있도록 했습니다.

이상적으로는 최대한 많은 리소스를 최대한 안전하게 최대한 오래 캐시하고 업데이트된 리소스의 효율적인 유효성 재검사를 위해 유효성 검사 토큰을 제공해야 합니다.

사용하지 않는 코드 삭제하기

지금까지 불필요한 다운로드에서 눈에 띄는 부분은 삭제했습니다. 그러나 덜 명확한 부분은 어떻게 해야 할까요? 예를 들어 사용하지 않는 코드가 있습니다.

DevTools의 코드 적용 범위
그림 5. 코드 적용 범위 확인

앱 코드에 실제로는 필요하지 않은 코드가 포함되는 경우가 있습니다. 이는 특히 앱을 장기간 개발하거나 팀 또는 종속 항목이 변경되는 경우 발생하며, 이때 고아 라이브러리가 남겨지는 경우가 있습니다. 저희에게도 정확히 이런 일이 발생했습니다.

처음에는 머티리얼 구성요소 라이브러리를 사용하여 앱의 프로토타입을 신속하게 제작했습니다. 시간이 지남에 따라 좀 더 맞춤 디자인으로 전환했고 이 라이브러리에 관해서는 완전히 잊어버렸습니다. 다행히 코드 적용 범위 검사를 통해 번들에서 다시 찾을 수 있었습니다.

DevTools에서 애플리케이션의 런타임과 로드 시간 모두에 대한 코드 적용 범위 통계를 확인할 수 있습니다. 하단 스크린샷에서 큰 빨간색 줄무늬 두 개를 볼 수 있습니다. CSS의 95% 이상이 사용되지 않았고 JavaScript도 상당히 많았습니다.

Lighthouse에서도 사용되지 않는 CSS 규칙 감사에서 이 문제를 발견했습니다. 400kb 이상의 절감 효과가 있었습니다. 그래서 코드로 돌아가서 라이브러리의 JavaScript 및 CSS 부분을 모두 삭제했습니다.

MVC 어댑터를 삭제하면 스타일이 10KB로 줄어듭니다.
그림 6. MVC 어댑터를 삭제하면 스타일이 10KB로 줄어듭니다.

그 결과 CSS 번들이 20배 줄어들었습니다. 2줄 길이의 아주 작은 커밋에 아주 적합합니다.

물론 실적 점수가 올라가고 Time to Interactive도 크게 개선되었습니다.

하지만 이번과 같은 변경사항이 적용되면 측정항목과 점수만 확인해서는 충분하지 않습니다. 실제 코드를 삭제하는 것은 항상 위험이 있으므로 항상 잠재적인 회귀를 주의해야 합니다.

코드는 95%에서 사용되지 않았습니다. 어딘가에 아직 5%가 남아 있습니다. 구성요소 중 하나가 여전히 해당 라이브러리의 스타일(낙서 슬라이더의 작은 화살표)을 사용하고 있었던 것으로 보입니다. 하지만 크기가 너무 작기 때문에 그냥 수동으로 이 스타일을 버튼에 다시 통합하기만 하면 됩니다.

라이브러리 누락으로 인해 버튼이 손상됨
그림 7. 한 구성요소가 삭제된 라이브러리를 계속 사용하고 있었습니다.

따라서 코드를 삭제하는 경우 잠재적인 시각적 회귀를 방지하는 데 도움이 되는 적절한 테스트 워크플로가 마련되어 있는지 확인하세요.

과도한 네트워크 페이로드 방지

Google은 대용량 리소스가 웹페이지 로드 속도를 저하시킬 수 있다는 것을 알고 있습니다. 사용자에게 비용이 들 수 있고 데이터 요금제에 큰 영향을 미칠 수 있으므로 이 점을 염두에 두는 것이 매우 중요합니다.

Lighthouse에서 대용량 네트워크 페이로드 감사를 사용하여 일부 네트워크 페이로드에 문제가 있음을 감지했습니다.

방대한 네트워크 페이로드 감지
그림 8. 방대한 네트워크 페이로드 감지

여기에서 3MB가 넘는 코드가 다운로드되고 있는 것을 확인했습니다. 특히 모바일에서는 상당히 많은 양입니다.

이 목록의 맨 위에 있는 Lighthouse에서 압축되지 않은 코드가 2MB인 JavaScript 공급업체 번들이 있음을 강조 표시했습니다. 이는 webpack에서도 강조하는 문제입니다.

속담에 '요청하지 않는 것이 가장 빠른 요청'이라고 하죠.

이상적으로는 사용자에게 제공하는 모든 애셋의 가치를 측정하고, 이러한 애셋의 성능을 측정하고, 초기 환경과 함께 실제로 제공할 가치가 있는지 판단해야 합니다. 이러한 애셋은 유휴 시간 동안 지연되거나 지연 로드되거나 처리될 수 있기 때문입니다.

여기서는 많은 JavaScript 번들을 다루고 있기 때문에 다행히 JavaScript 커뮤니티에 다양한 JavaScript 번들 감사 도구 세트가 있기 때문입니다.

JavaScript 번들 감사
그림 9. JavaScript 번들 감사

Webpack 번들 분석기부터 시작했습니다. 유니코드라는 종속 항목이 포함되어 있다는 것을 알 수 있었습니다. 유니코드는 파싱된 JavaScript의 1.6MB로서 상당히 많은 것이었습니다.

그런 다음 편집기로 이동하여 Visual Code용 Import Cost 플러그인을 사용하여 가져오는 모든 모듈의 비용을 시각화할 수 있었습니다. 이를 통해 이 모듈을 참조하는 코드를 포함하는 구성요소를 확인할 수 있었습니다.

그런 다음 다른 도구인 BundlePhobia로 전환했습니다. 이 도구를 사용하면 NPM 패키지 이름을 입력하고 최소화 및 GZIP 처리된 크기의 예상값을 확인할 수 있습니다. 사용 중인 slug 모듈의 좋은 대안을 찾았는데 크기가 2.2kb에 불과하여 이를 전환했습니다.

이로 인해 실적이 크게 영향을 받았습니다. 이 변경사항과 JavaScript 번들 크기를 줄일 수 있는 다른 기회를 발견한 결과 2.1MB의 코드를 절약했습니다.

이러한 번들의 gzip 압축 및 축소 크기를 고려하면 전반적으로 65% 향상되었습니다. 이러한 절차를 진행하는 것이 정말로 가치가 있었습니다.

따라서 일반적으로 사이트와 앱에서 불필요한 다운로드를 없애는 것이 좋습니다. 애셋 인벤토리를 만들고 성능에 미치는 영향을 측정하면 큰 차이를 만들 수 있으므로 애셋을 공정하게 감사해야 합니다.

코드 분할로 JavaScript 부팅 시간 단축

대용량 네트워크 페이로드는 앱에 큰 영향을 미칠 수 있지만, JavaScript도 앱에 큰 영향을 미칠 수 있습니다.

JavaScript는 가장 비싼 애셋입니다. 모바일에서 대량의 JavaScript 번들을 전송하면 사용자가 사용자 인터페이스 구성요소와 상호작용할 수 있는 시간이 지연될 수 있습니다. 즉, 실제로 의미 있는 일이 일어나지 않아도 UI를 탭할 수 있습니다. 따라서 JavaScript가 그렇게 많은 비용이 드는 이유를 이해하는 것이 중요합니다.

브라우저가 JavaScript를 처리하는 방식입니다.

JavaScript 처리
그림 10. JavaScript 처리

먼저 스크립트를 다운로드해야 합니다. 그런 다음 JavaScript 엔진이 코드를 파싱하고 컴파일하여 실행해야 합니다.

이러한 단계는 데스크톱 컴퓨터나 노트북, 고급형 휴대전화와 같은 고급형 기기에서 시간이 많이 걸리지 않습니다. 하지만 중간 수준의 휴대전화에서는 이 프로세스가 5~10배 더 오래 걸릴 수 있습니다. 이로 인해 상호작용이 지연되므로 이를 줄이는 것이 중요합니다.

앱에서 이러한 문제를 발견할 수 있도록 Lighthouse에 새로운 JavaScript 부팅 시간 감사를 도입했습니다.

JavaScript 부팅 시간
그림 11. JavaScript 부팅 시간 감사

Oodle 앱의 경우 JavaScript 부팅에 1.8초가 소요되었다고 표시되었습니다. 모든 경로와 구성요소를 하나의 모놀리식 JavaScript 번들로 정적으로 가져오고 있었습니다.

이 문제를 해결하는 한 가지 방법은 코드 분할을 사용하는 것입니다.

코드 분할은 피자와 같습니다.

코드 분할은 사용자에게 피자 한 판의 자바스크립트를 한꺼번에 제공하는 대신 필요한 대로 한 번에 한 조각씩만 제공하는 개념입니다.

코드 분할은 경로 수준 또는 구성요소 수준에서 적용할 수 있습니다. React 및 React Loadable, Vue.js, Angular, Polymer, Preact, 기타 여러 라이브러리와 함께 잘 작동합니다.

코드 분할을 애플리케이션에 통합하고 정적 가져오기에서 동적 가져오기로 전환하여 필요에 따라 코드를 비동기식으로 지연 로드할 수 있도록 했습니다.

동적 가져오기를 사용한 코드 분할
그림 13. 동적 가져오기를 사용한 코드 분할

이로 인해 번들 크기가 줄어들고 JavaScript 부팅 시간이 단축되었습니다. 0.78초로 줄여 앱 속도를 56% 높였습니다.

일반적으로 JavaScript가 많이 사용되는 환경을 빌드하는 경우 사용자에게 필요한 코드만 전송해야 합니다.

코드 분할과 같은 개념을 활용하고, 트리 셰이킹과 같은 아이디어를 살펴보고, webpack을 사용하는 경우 라이브러리 크기를 줄이는 방법에 관한 몇 가지 아이디어를 확인하려면 webpack-libs-optimizations 저장소를 확인하세요.

이미지 최적화

이미지 로드 성능 농담

Oodle 앱에서는 많은 이미지를 사용합니다. 안타깝게도 Lighthouse는 우리보다 훨씬 덜 열정적이었습니다. 사실 세 가지 이미지 관련 감사에서 모두 실패했습니다.

이미지를 최적화하는 것을 잊고 이미지 크기를 올바르게 조정하지 못했으며 다른 이미지 형식을 사용하여 얻을 수 있는 이점도 있었습니다.

이미지 감사
그림 14. Lighthouse 이미지 감사

먼저 이미지를 최적화했습니다.

일회성 최적화 라운드의 경우 ImageOptim 또는 XNConvert와 같은 시각적 도구를 사용할 수 있습니다.

더 자동화된 접근 방식은 imagemin과 같은 라이브러리를 사용하여 빌드 프로세스에 이미지 최적화 단계를 추가하는 것입니다.

이렇게 하면 향후 추가된 이미지가 자동으로 최적화됩니다. Akamai와 같은 일부 CDN 또는 Cloudinary, Fastly 또는 Uploadcare와 같은 타사 솔루션은 포괄적인 이미지 최적화 솔루션을 제공합니다. 따라서 이러한 서비스에 이미지를 호스팅하기만 하면 됩니다.

비용이나 지연 시간 문제로 인해 그렇게 하고 싶지 않다면 Thumbor 또는 Imageflow와 같은 프로젝트에서 자체 호스팅 대안을 제공합니다.

최적화 전후
그림 15. 최적화 전과 후

백그라운드 PNG가 webpack에서 크기가 너무 크다고 신고되었습니다. 표시 영역에 맞게 크기를 조정하고 ImageOptim을 통해 실행한 후 100kb로 줄였는데, 이는 허용 가능한 크기입니다.

사이트의 여러 이미지에 대해 이 작업을 반복하여 전반적인 페이지 크기를 크게 줄일 수 있었습니다.

애니메이션 콘텐츠에 적합한 형식 사용

GIF는 가격이 매우 비쌀 수 있습니다. 놀랍게도 GIF 형식은 애초에 애니메이션 플랫폼으로 제작된 것이 아닙니다. 따라서 더 적합한 동영상 형식으로 전환하면 파일 크기 면에서 큰 절감 효과를 얻을 수 있습니다.

Oodle 앱에서는 GIF를 홈페이지의 인트로 시퀀스로 사용하고 있었습니다. Lighthouse에 따르면 더 효율적인 동영상 형식으로 전환하면 7MB가 넘게 절약될 수 있습니다. 클립의 크기는 약 7.3MB로, 일반적인 웹사이트에 표시하기에는 너무 컸습니다. 대신 더 광범위한 브라우저 지원을 위해 mp4와 WebM이라는 두 가지 소스 파일이 있는 동영상 요소로 변환했습니다.

애니메이션 GIF를 동영상으로 대체
그림 16. 애니메이션 GIF를 동영상으로 바꾸기

FFmpeg 도구를 사용하여 애니메이션 GIF를 mp4 파일로 변환했습니다. WebM 형식은 훨씬 더 큰 절감 효과를 제공합니다. ImageOptim API가 이러한 변환을 자동으로 처리할 수 있습니다.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

이러한 전환 덕분에 전체 체중의 80% 이상을 줄일 수 있었습니다. 이렇게 해서 약 1MB로 줄었습니다.

하지만 1mb는 특히 제한된 대역폭의 사용자에게 유선을 밀어내는 데 큰 리소스입니다. 다행히 Effective Type API를 사용하여 느린 대역폭을 사용하고 있음을 인식하고 대신 훨씬 더 작은 JPEG를 제공할 수 있습니다.

이 인터페이스는 유효한 왕복 시간 및 중단 값을 사용하여 사용자가 사용 중인 네트워크 유형을 추정합니다. 느린 2G, 2G, 3G 또는 4G인 문자열을 반환합니다. 따라서 이 값에 따라 사용자가 4G 미만을 사용하는 경우 동영상 요소를 이미지로 대체할 수 있습니다.

if (navigator.connection.effectiveType) { ... }

환경이 약간 저하되지만 느린 연결에서도 사이트를 사용할 수 있습니다.

화면을 벗어난 이미지 지연 로드

캐러셀, 슬라이더 또는 매우 긴 페이지에서는 사용자가 페이지에서 바로 이미지를 볼 수 없더라도 이미지가 로드되는 경우가 많습니다.

Lighthouse는 오프스크린 이미지 감사에서 이 동작을 신고하며 DevTools의 네트워크 패널에서도 직접 확인할 수 있습니다. 이미지가 많이 수신되지만 페이지에 표시되는 이미지가 몇 개 되지 않는다면 대신 지연 로드를 고려해 볼 수 있습니다.

지연 로드는 아직 브라우저에서 기본적으로 지원되지 않으므로 이 기능을 추가하려면 JavaScript를 사용해야 합니다. Lazysizes 라이브러리를 사용하여 Oodle 커버에 지연 로드 동작을 추가했습니다.

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

Lazysizes는 요소의 가시성 변경사항을 추적할 뿐만 아니라 최적의 사용자 환경을 위해 뷰 근처에 있는 요소를 사전에 미리 가져오므로 스마트합니다. 또한 IntersectionObserver 통합 옵션도 제공하므로 매우 효율적인 공개 상태 조회가 가능합니다.

이 변경 후에는 주문형 이미지를 가져옵니다. 이 주제에 대해 더 자세히 알아보려면 매우 편리하고 포괄적인 리소스인 images.guide를 참조하세요.

브라우저가 중요한 리소스를 조기에 전송하도록 지원

인터넷을 통해 브라우저에 전송되는 모든 바이트가 똑같이 중요한 것은 아니며, 브라우저도 이것을 알고 있습니다. 많은 브라우저에는 먼저 가져올 항목을 결정하기 위한 휴리스틱이 있습니다. 따라서 이미지나 스크립트보다 먼저 CSS를 가져오는 경우도 있습니다.

페이지 작성자로서 브라우저에 실제로 중요한 정보를 알려주는 것이 유용할 수 있습니다. 다행히 지난 몇 년 동안 브라우저 공급업체는 이를 돕기 위해 link rel=preconnect, preload 또는 prefetch와 같은 리소스 힌트를 비롯한 여러 기능을 추가해 왔습니다.

웹 플랫폼에 도입된 이러한 기능은 브라우저가 적절한 시점에 적절한 항목을 가져오는 데 도움이 되며, 대신 스크립트를 사용하여 실행되는 일부 맞춤 로드, 로직 기반 접근 방식보다 약간 더 효율적일 수 있습니다.

Lighthouse가 실제로 이러한 기능 중 일부를 효과적으로 사용하기 위해 실제로 어떻게 안내하는지 살펴보겠습니다.

Lighthouse에서 가장 먼저 알려주는 사항은 비용이 많이 드는 출처 왕복을 여러 번 피하는 것입니다.

출발지로의 여러 번의 왕복 이동으로 인한 비용 절감
그림 17. 출발지로 왕복하는 비용이 많이 드는 경로를 여러 번 사용하지 않도록 합니다.

Oodle 앱의 경우 실제로 Google Fonts를 많이 사용하고 있습니다. Google Font 스타일시트를 페이지에 드롭할 때마다 최대 2개의 하위 도메인과 연결됩니다. Lighthouse는 이 연결을 준비할 수 있다면 초기 연결 시간에서 최대 300밀리초를 절약할 수 있다고 알려줍니다.

link rel preconnect를 활용하면 이러한 연결 지연 시간을 효과적으로 마스킹할 수 있습니다.

특히 글꼴 페이스 CSS가 googleapis.com에 호스팅되고 글꼴 리소스가 Gstatic에 호스팅되는 Google Fonts와 같은 경우 이는 매우 큰 영향을 미칠 수 있습니다. 이 최적화를 적용한 결과 수백 밀리초가 단축되었습니다.

Lighthouse에서 다음으로 제안하는 것은 주요 요청을 미리 로드하는 것입니다.

키 요청 미리 로드
그림 18. 주요 요청 미리 로드하기

<link rel=preload>는 매우 강력합니다. 현재 탐색의 일부로 리소스가 필요하다고 브라우저에 알리고 브라우저가 최대한 빨리 가져오도록 시도합니다.

여기에서 Lighthouse는 두 개의 웹 글꼴을 로드하고 있으므로 주요 웹 글꼴 리소스를 미리 로드해야 한다고 알려줍니다.

웹 글꼴로 미리 로드하는 방법은 다음과 같습니다. rel=preload를 지정하고 글꼴 유형과 함께 as를 전달한 다음 로드하려는 글꼴 유형(예: woff2)을 지정합니다.

이로 인해 페이지에 미치는 영향은 상당히 큽니다.

리소스 미리 로드의 영향
그림 19. 리소스 미리 로드의 영향

일반적으로 링크 상대 미리 로드를 사용하지 않고 웹 글꼴이 페이지에 중요한 경우 브라우저는 먼저 HTML을 가져오고, CSS를 파싱해야 하며, 한참 후에 웹 글꼴을 가져옵니다.

링크 상대 미리 로드를 사용하면 브라우저가 HTML을 파싱하는 즉시 이러한 웹 글꼴을 훨씬 일찍 가져오기 시작할 수 있습니다. 앱의 경우 웹 글꼴을 사용하여 텍스트를 렌더링하는 데 걸리는 시간을 1초 단축할 수 있었습니다.

하지만 Google Fonts를 사용하여 글꼴을 미리 로드하려면 한 가지 주의할 점이 있습니다.

스타일시트의 글꼴 면에 지정된 Google 글꼴 URL은 글꼴팀에서 상당히 정기적으로 업데이트하는 항목이었습니다. 이러한 URL은 만료되거나 정기적으로 업데이트될 수 있으므로 글꼴 로드 환경을 완전히 제어하려면 웹 글꼴을 자체 호스팅하는 것이 좋습니다. 이렇게 하면 link rel preload와 같은 항목에 액세스할 수 있으므로 유용할 수 있습니다.

여기서는 Google Web Fonts Helper 도구가 일부 웹 글꼴을 오프라인으로 설정하고 로컬에서 설정하는 데 매우 유용하므로 이 도구를 확인해 보세요.

웹 글꼴을 중요한 리소스의 일부로 사용하든 JavaScript를 사용하든 브라우저에서 중요한 리소스를 최대한 빨리 제공할 수 있도록 도와주세요.

실험용: 우선순위 힌트

오늘은 특별한 소식을 전해드립니다. 미리 로드뿐만 아니라 리소스 힌트와 같은 기능 외에도 Google은 우선순위 힌트라고 부르는 새로운 실험용 브라우저 기능을 개발해 왔습니다.

처음에 표시되는 콘텐츠의 우선순위 설정
그림 20. 우선순위 힌트

이는 브라우저에 리소스가 얼마나 중요한지 알려줄 수 있는 새로운 기능입니다. 낮음, 높음 또는 자동이라는 새로운 속성(중요도)을 노출합니다.

이를 통해 중요하지 않은 스타일, 이미지 또는 가져오기 API 호출과 같이 덜 중요한 리소스의 우선순위를 낮춰 경합을 줄일 수 있습니다. 대표 이미지와 같이 더 중요한 항목의 우선순위를 높일 수도 있습니다.

Oodle 앱의 경우 실제로 최적화할 수 있는 실용적인 위치를 찾을 수 있었습니다.

처음 표시되는 콘텐츠의 우선순위 설정
그림 21. 처음에 표시되는 콘텐츠의 우선순위를 설정합니다.

이미지에 지연 로드를 추가하기 전에 브라우저는 모든 낙서가 포함된 이미지 캐러셀을 보유하고 있었으며, 브라우저는 캐러셀의 시작 부분에서 우선순위가 높은 모든 이미지를 초기에 가져왔습니다. 안타깝게도 캐러셀 중앙에 있는 이미지가 사용자에게 가장 중요했습니다. 그래서 저희는 이러한 배경 이미지의 중요성을 매우 낮게, 전경 이미지의 중요성을 매우 높게 설정했습니다. 이렇게 하면 느린 3G에서 2초 동안 이러한 이미지를 가져오고 렌더링하는 속도에 영향을 미칠 수 있었습니다. 긍정적인 경험을 했습니다.

몇 주 내에 Canary에서 이 기능을 제공할 예정이므로 계속 지켜봐 주세요.

웹 글꼴 로드 전략 수립

서체는 우수한 디자인의 기본 요소이며 웹 글꼴을 사용하는 경우 텍스트 렌더링을 차단하지 않는 것이 좋으며 보이지 않는 텍스트를 표시해서는 안 됩니다.

이제 Lighthouse에서 웹폰트 로드 중 보이지 않는 텍스트 방지 감사를 통해 이를 강조 표시합니다.

웹폰트가 로드되는 동안 텍스트가 보이지 않도록 하지 않음
그림 22. 웹폰트가 로드되는 동안 보이지 않는 텍스트를 피하세요.

글꼴 페이스 블록을 사용하여 웹 글꼴을 로드하면 웹 글꼴을 가져오는 데 시간이 오래 걸리는 경우 브라우저에서 어떻게 할지 결정하게 됩니다. 일부 브라우저는 시스템 글꼴로 대체하기 전에 최대 3초 동안 기다린 후 다운로드된 글꼴로 교체합니다.

Google에서는 이러한 보이지 않는 텍스트를 피하기 위해 노력하고 있습니다. 따라서 이 경우 웹 글꼴을 가져오는 데 시간이 너무 오래 걸렸다면 이번 주 기존 두들을 볼 수 없었을 것입니다. 다행히 font-display라는 새로운 기능을 사용하면 이 프로세스를 훨씬 더 세부적으로 제어할 수 있습니다.

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

글꼴 표시를 사용하면 전환하는 데 걸리는 시간에 따라 웹 글꼴이 렌더링되거나 대체되는 방식을 결정할 수 있습니다.

여기서는 글꼴 디스플레이 바꾸기를 사용합니다. 바꾸기는 글꼴에 0초의 차단 기간과 무한 스왑 기간을 제공합니다. 즉, 글꼴을 로드하는 데 시간이 걸리는 경우 브라우저는 폴백 글꼴로 텍스트를 거의 즉시 그립니다. 그리고 글꼴이 사용 가능해지면 글꼴을 전환합니다.

앱의 경우 이 방법을 사용하면 의미 있는 텍스트를 아주 일찍 표시하고 준비가 되면 웹 글꼴로 전환할 수 있었습니다.

글꼴 표시 결과
그림 23. 글꼴 표시 결과

일반적으로 웹의 상당 부분에서 웹 글꼴을 사용하는 경우 적절한 웹 글꼴 로드 전략을 마련해야 합니다.

글꼴 로드 환경을 최적화하는 데 사용할 수 있는 웹 플랫폼 기능이 많이 있지만, Zach Leatherman의 웹 글꼴 레시피 저장소도 확인해 보세요. 정말 유용합니다.

렌더링을 차단하는 스크립트 줄이기

다운로드 체인에서 더 일찍 푸시하여 기본적인 사용자 환경을 조금 더 일찍 제공할 수 있는 애플리케이션의 다른 부분도 있습니다.

Lighthouse 타임라인 스트립에서 모든 리소스가 로드되는 처음 몇 초 동안 사용자에게 콘텐츠가 표시되지 않는 것을 확인할 수 있습니다.

렌더링을 차단하는 스타일시트 기회 줄이기
그림 24. 렌더링을 차단하는 스타일시트 기회 줄이기

외부 스타일시트를 다운로드하고 처리하면 렌더링 프로세스가 진행되지 않습니다.

일부 스타일을 조금 더 일찍 제공하여 주요 렌더링 경로를 최적화해 볼 수 있습니다.

이 초기 렌더링을 담당하는 스타일을 추출하여 HTML에 인라인 처리하면 브라우저가 외부 스타일시트가 도착할 때까지 기다리지 않고 즉시 렌더링할 수 있습니다.

이 경우 빌드 단계에서 Critical라는 NPM 모듈을 사용하여 index.html에 중요한 콘텐츠를 인라인으로 삽입했습니다.

이 모듈이 대부분의 작업을 대신해 주었지만, 여러 경로에서 원활하게 작동하도록 하려면 약간 까다로웠습니다.

주의하지 않거나 사이트 구조가 매우 복잡한 경우 처음부터 앱 셸 아키텍처를 계획하지 않았다면 이러한 유형의 패턴을 도입하기가 매우 어려울 수 있습니다.

그렇기 때문에 초기에 성능을 고려하는 것이 중요합니다. 처음부터 성능을 고려하여 설계하지 않으면 나중에 문제가 발생할 가능성이 높습니다.

결국 위험을 감수한 결과, 앱이 콘텐츠를 훨씬 더 일찍 전송하기 시작하여 유의미한 첫 페인트 시간이 크게 개선되었습니다.

결과

이는 사이트에 적용된 성능 최적화의 긴 목록입니다. 결과를 살펴보겠습니다. 다음은 최적화 전후에 3G 네트워크의 중형 휴대기기에 앱이 로드되는 방식입니다.

Lighthouse 성능 점수가 23에서 91로 상승했습니다. 속도 면에서 상당히 뛰어난 발전입니다. 모든 변경사항은 Lighthouse 보고서를 지속적으로 확인하고 따르기 때문에 이루어졌습니다. Google에서 모든 개선사항을 기술적으로 구현한 방법을 확인하려면 언제든지 저장소, 특히 저장소에 반영된 PR을 살펴보세요.

예측 실적 - 데이터 기반 사용자 환경

Google은 머신러닝이 다양한 분야에서 미래를 위한 흥미로운 기회를 제시한다고 생각합니다. 향후 더 많은 실험이 활발히 이루어지기를 바라는 아이디어 중 하나는 실제 데이터가 우리가 만들고 있는 사용자 경험을 실제로 가이드할 수 있다는 것입니다.

현재 Google은 사용자가 원하거나 필요로 할 수 있는 항목과 따라서 미리 가져오거나 미리 로드하거나 미리 캐시할 가치가 있는 항목에 대해 많은 임의의 결정을 내립니다. 추측이 맞으면 소량의 리소스에 우선순위를 둘 수 있지만 웹사이트 전체로 확장하는 것은 매우 어렵습니다.

현재 최적화를 개선하는 데 도움이 되는 데이터가 있습니다. Google 애널리틱스 Reporting API를 사용하면 사이트의 다음 인기 페이지와 URL의 이탈률을 확인하여 우선순위를 지정해야 하는 리소스에 관한 결론을 도출할 수 있습니다.

이를 우수한 확률 모델과 결합하면 콘텐츠를 적극적으로 과도하게 미리 로드하여 사용자 데이터를 낭비하지 않을 수 있습니다. Google 애널리틱스 데이터를 활용하고, 이러한 모델을 구현하기 위해 머신러닝과 마르코프 체인 또는 신경망과 같은 모델을 사용할 수 있습니다.

웹 앱용 데이터 기반 번들
그림 25. 웹 앱용 데이터 기반 번들

이 실험을 원활하게 진행하기 위해 Guess.js라는 새로운 이니셔티브를 발표합니다.

Guess.js
그림 26. Guess.js

Guess.js는 웹용 데이터 기반 사용자 경험에 중점을 둔 프로젝트입니다. 데이터를 사용하여 웹 성능을 개선하고 그 이상으로 나아가는 데 도움이 되기를 바랍니다. 모두 오픈소스이며 현재 GitHub에서 사용할 수 있습니다. 이 도구는 민코 게체프, 개츠비의 카일 매튜스, 케이티 헴페니우스 등의 오픈소스 커뮤니티와 협력하여 빌드했습니다.

Guess.js를 사용해 보고 의견을 알려주세요.

요약

점수와 측정항목은 웹 속도를 개선하는 데 도움이 되지만 목표 자체가 아니라 단지 수단일 뿐입니다.

이동 중에 페이지 로드 속도가 느린 경우가 있었지만 이제는 사용자에게 매우 빠르게 로드되는 더 만족도 높은 환경을 제공할 수 있습니다.

실적 개선은 하나의 여정입니다. 작은 변화를 많이 하면 큰 효과를 얻을 수 있습니다. 적절한 최적화 도구를 사용하고 Lighthouse 보고서를 주시하면 사용자에게 더 나은 포용적인 환경을 제공할 수 있습니다.

Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse, Google Doodles에 감사의 인사를 전합니다.