웹 성능 손쉽게 개선하기 - Google I/O 2018 버전

Google I/O 2018에서 웹 성능을 쉽게 개선할 수 있는 도구, 라이브러리, 최적화 기법을 모아 발표했습니다. 여기에서는 The Oodles Theater 앱을 사용하여 설명합니다. 예측 로딩 및 새로운 Guess.js 이니셔티브에 관한 실험도 다룹니다.

Ewa Gasperowicz

지난 한 해 동안 Google은 웹을 더 빠르고 성능이 우수하게 만드는 방법을 알아내기 위해 매우 바쁘게 움직였습니다. 이로 인해 새로운 도구, 접근 방식, 라이브러리가 개발되었으며, 이 도움말에서 이를 공유하고자 합니다. 첫 번째 파트에서는 The Oodles Theater 앱을 개발할 때 실제로 사용한 최적화 기법을 보여줍니다. 두 번째 파트에서는 예측 로딩에 관한 실험과 새로운 Guess.js 이니셔티브에 대해 설명합니다.

성능의 필요성

인터넷은 매년 점점 더 무거워지고 있습니다. 웹 상태를 확인하면 모바일의 중간 페이지가 약 1.5MB이며 대부분이 JavaScript와 이미지로 구성되어 있습니다.

웹사이트의 크기가 커지고 네트워크 지연 시간, CPU 제한, 렌더링 차단 패턴, 불필요한 서드 파티 코드와 같은 다른 요인이 복잡한 성능 퍼즐에 기여합니다.

대부분의 사용자는 속도를 UX 요구사항의 최상위 계층으로 평가합니다. 페이지 로딩이 완료될 때까지는 할 수 있는 일이 많지 않으므로 이는 놀라운 일이 아닙니다. 페이지에서 가치를 도출할 수도 없고 미학을 감상할 수도 없습니다.

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

성능은 사용자에게 중요하지만 어디서부터 최적화를 시작해야 할지 알기 어려울 수 있습니다. 다행히도 이 과정에 도움이 되는 도구가 있습니다.

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. 코드 적용 범위 확인

때로는 앱에 실제로 필요하지 않은 코드가 포함되기도 합니다. 특히 앱을 장기간 작업하거나 팀 또는 종속 항목이 변경되는 경우, 때로는 고아 라이브러리가 남게 됩니다. Google도 마찬가지였습니다.

처음에는 Material Components 라이브러리를 사용하여 앱을 빠르게 프로토타입으로 만들었습니다. 시간이 지나면서 더 맞춤화된 디자인으로 전환했고 해당 라이브러리를 완전히 잊었습니다. 다행히 코드 커버리지 확인을 통해 번들에서 이 문제를 다시 발견할 수 있었습니다.

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

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

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

이로 인해 CSS 번들이 20배 줄어들었으며, 이는 두 줄 길이의 작은 커밋으로는 꽤 좋은 결과입니다.

물론 성능 점수가 올라갔고 상호작용 시간도 훨씬 개선되었습니다.

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

코드의 95%가 사용되지 않았습니다. 아직 5% 가 남아 있습니다. 이 라이브러리의 스타일을 사용하는 구성요소가 하나 있었던 것 같습니다. 바로 낙서 슬라이더의 작은 화살표입니다. 하지만 크기가 매우 작았기 때문에 해당 스타일을 버튼에 수동으로 다시 통합할 수 있었습니다.

라이브러리가 누락되어 버튼이 깨짐
그림 7. 한 구성요소에서 삭제된 라이브러리를 계속 사용하고 있었습니다.

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

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

대규모 리소스는 웹 페이지 로드 속도를 늦출 수 있습니다. 이러한 앱은 사용자에게 비용을 청구할 수 있고 데이터 요금제에 큰 영향을 미칠 수 있으므로 주의해야 합니다.

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

과도한 네트워크 페이로드 감지
그림 8. 과도한 네트워크 페이로드 감지

여기에서 다운로드되는 코드의 크기가 3MB가 넘는 것을 확인했습니다. 특히 모바일에서는 상당히 큰 크기입니다.

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

가장 빠른 요청은 요청하지 않는 것이라는 말이 있습니다.

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

이 경우 JavaScript 번들을 많이 다루기 때문에 JavaScript 커뮤니티에 다양한 JavaScript 번들 감사 도구가 있어 다행이었습니다.

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

webpack 번들 분석기로 시작했는데, 여기에서 파싱된 JavaScript가 1.6MB인 유니코드라는 종속 항목이 포함되어 있다는 것을 알게 되었습니다.

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

그런 다음 다른 도구인 BundlePhobia로 전환했습니다. 이 도구를 사용하면 NPM 패키지의 이름을 입력하고 최소화 및 gzip 압축된 크기를 추정할 수 있습니다. 사용 중인 슬러그 모듈의 대안으로 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 번들로 정적으로 가져오고 있었습니다.

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

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

코드 분할은 사용자에게 JavaScript 전체 피자를 제공하는 대신 필요할 때마다 한 조각씩 제공하는 개념입니다.

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

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

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

이로 인해 번들 크기가 줄어들 뿐만 아니라 JavaScript 부팅 시간도 단축되었습니다. 0.78초로 줄어들어 앱이 56% 더 빨라졌습니다.

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

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

이미지 최적화

이미지 로드 성능 관련 농담

Oodle 앱에서는 이미지를 많이 사용합니다. 안타깝게도 Lighthouse는 Google만큼 열정적이지 않았습니다. 실제로 이미지 관련 감사 3개 모두에서 실패했습니다.

이미지를 최적화하지 않았고, 크기를 올바르게 조정하지 않았으며, 다른 이미지 형식을 사용하면 이점을 얻을 수도 있습니다.

이미지 감사
그림 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 글꼴 스타일시트를 페이지에 추가할 때마다 최대 2개의 하위 도메인이 연결됩니다. Lighthouse에 따르면 이 연결을 워밍업할 수 있다면 초기 연결 시간을 최대 300밀리초까지 절약할 수 있습니다.

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

특히 Google Fonts와 같이 글꼴 CSS가 googleapis.com에 호스팅되고 글꼴 리소스가 Gstatic에 호스팅되는 경우 큰 영향을 미칠 수 있습니다. 그래서 이 최적화를 적용했고 수백 밀리초를 줄였습니다.

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

미리 로드 키 요청
그림 18. 키 요청 미리 로드

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

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

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

이러한 변경사항이 페이지에 미치는 영향은 상당히 큽니다.

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

일반적으로 링크 rel preload를 사용하지 않으면 웹 글꼴이 페이지에 중요한 경우 브라우저가 먼저 HTML을 가져오고 CSS를 파싱해야 하며, 훨씬 나중에 웹 글꼴을 가져옵니다.

링크 rel preload를 사용하면 브라우저가 HTML을 파싱하는 즉시 웹 글꼴을 훨씬 더 일찍 가져올 수 있습니다. 앱의 경우 웹 글꼴을 사용하여 텍스트를 렌더링하는 데 걸리는 시간을 1초 단축할 수 있었습니다.

Google Fonts를 사용하여 글꼴을 미리 로드하려고 하는 경우 한 가지 주의해야 할 점이 있습니다.

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

Google에서는 Google Web Fonts Helper 도구가 이러한 웹 글꼴을 오프라인으로 전환하고 로컬로 설정하는 데 매우 유용하다는 것을 알게 되었습니다. 이 도구를 확인해 보세요.

웹 글꼴을 중요 리소스의 일부로 사용하든 JavaScript를 사용하든 브라우저가 중요 리소스를 최대한 빨리 제공하도록 지원하세요.

실험용: 우선순위 힌트

오늘 특별한 소식을 전해드리려고 합니다. 리소스 힌트, 프리로드와 같은 기능 외에도 우선순위 힌트라는 새로운 실험용 브라우저 기능을 개발하고 있습니다.

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

이 기능은 리소스의 중요도를 브라우저에 힌트로 제공할 수 있는 새로운 기능입니다. 중요도(importance)라는 새로운 속성을 노출하며, 값은 low, high, auto입니다.

이를 통해 경합을 줄이기 위해 중요하지 않은 스타일, 이미지 또는 가져오기 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');
    }

글꼴 표시는 글꼴이 대체되는 데 걸리는 시간에 따라 웹 글꼴이 렌더링되거나 대체되는 방식을 결정하는 데 도움이 됩니다.

이 경우 글꼴 표시 스왑을 사용합니다. swap은 글꼴에 0초 블록 기간과 무한 스왑 기간을 부여합니다. 즉, 글꼴이 로드되는 데 시간이 걸리면 브라우저가 대체 글꼴을 사용하여 텍스트를 즉시 그립니다. 글꼴을 사용할 수 있게 되면 글꼴이 바뀝니다.

앱의 경우, 이 방법을 사용하면 의미 있는 텍스트를 매우 초기에 표시하고 준비가 되면 웹 글꼴로 전환할 수 있다는 장점이 있습니다.

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

일반적으로 웹의 상당 부분을 차지하는 웹 글꼴을 사용하는 경우 적절한 웹 글꼴 로드 전략을 마련하세요.

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

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

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

Lighthouse 타임라인 스트립을 보면 모든 리소스가 로드되는 처음 몇 초 동안은 사용자가 콘텐츠를 제대로 볼 수 없습니다.

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

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

스타일 일부를 조금 더 일찍 제공하여 중요한 렌더링 경로를 최적화할 수 있습니다.

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

여기서는 빌드 단계에서 index.html에 중요한 콘텐츠를 인라인 처리하기 위해 Critical이라는 NPM 모듈을 사용했습니다.

이 모듈이 대부분의 작업을 처리했지만, 여러 경로에서 원활하게 작동하도록 하는 것은 여전히 약간 까다로웠습니다.

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

따라서 처음부터 성능을 고려하는 것이 매우 중요합니다. 처음부터 성능을 고려하여 설계하지 않으면 나중에 문제가 발생할 가능성이 높습니다.

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

결과

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

Lighthouse 성능 점수가 23에서 91로 상승했습니다. 속도 면에서 꽤 많이 개선되었습니다. 이러한 모든 변경사항은 Lighthouse 보고서를 지속적으로 확인하고 따름으로써 이루어졌습니다. 모든 개선사항이 기술적으로 어떻게 구현되었는지 확인하려면 저장소, 특히 여기에 적용된 PR을 살펴보세요.

예측 성능 - 데이터 기반 사용자 환경

Google은 머신러닝이 여러 분야에서 미래를 위한 흥미로운 기회를 제공한다고 생각합니다. 앞으로 더 많은 실험을 촉진할 수 있기를 바라는 아이디어 중 하나는 실제 데이터가 우리가 만드는 사용자 환경을 안내할 수 있다는 것입니다.

오늘날 Google은 사용자가 원하거나 필요로 하는 것, 따라서 미리 가져오거나 미리 로드하거나 미리 캐시할 가치가 있는 것에 관해 많은 임의적인 결정을 내립니다. Google이 올바르게 추측하면 소량의 리소스에 우선순위를 지정할 수 있지만 전체 웹사이트로 확장하기는 매우 어렵습니다.

실제로 Google은 오늘 최적화에 더 나은 정보를 제공할 수 있는 데이터를 보유하고 있습니다. Google 애널리틱스 Reporting API를 사용하면 사이트의 모든 URL에 대한 다음 인기 페이지와 종료 비율을 살펴보고 우선순위를 지정해야 하는 리소스에 대한 결론을 도출할 수 있습니다.

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

웹 앱을 위한 데이터 기반 번들링
그림 25. 웹 앱용 데이터 기반 번들링

이러한 실험을 촉진하기 위해 Guess.js라는 새로운 이니셔티브를 발표하게 되어 기쁩니다.

Guess.js
그림 26. Guess.js

Guess.js는 웹의 데이터 기반 사용자 환경에 중점을 둔 프로젝트입니다. 데이터를 사용하여 웹 성능을 개선하고 그 이상을 달성하는 데 영감을 얻으시기를 바랍니다. 모두 오픈소스이며 지금 GitHub에서 사용할 수 있습니다. 이 기능은 민코 게체프, 개츠비의 카일 매튜스, 케이티 헴페니우스 및 여러 사용자가 오픈소스 커뮤니티와 공동으로 개발했습니다.

Guess.js를 확인하고 의견을 알려주세요.

요약

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

이동 중에 페이지 로드가 느린 경험은 누구나 해봤을 것입니다. 이제 페이지가 매우 빠르게 로드되는 더 즐거운 경험을 사용자에게 제공할 수 있습니다.

성능 개선은 여정입니다. 작은 변화를 많이 주면 큰 이익을 얻을 수 있습니다. 올바른 최적화 도구를 사용하고 Lighthouse 보고서를 주시하면 사용자에게 더 나은 포용적인 환경을 제공할 수 있습니다.

특별히 감사드립니다: 워드 피터스, 민코 게체브, 카일 매튜스, 케이티 헴페니우스, 돔 파롤리노, 요아브 바이스, 수지 루, 유스케 우쓰노미야, 톰 앵커스, Lighthouse 및 Google Doodles