웹 성능 개선을 위한 쉬운 방법 - Google I/O 2018 에디션

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

Ewa Gasperowicz

지난 한 해 동안 Google은 웹을 더 빠르고 성능을 높이는 방법을 찾기 위해 노력해 왔습니다. 이로 인해 새로운 도구, 접근 방식, 라이브러리가 탄생했으며 이 도움말에서 이를 공유하고자 합니다. 첫 번째 부분에서는 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에서 가장 좋아하는 대화형 Google 기념일 로고를 사용해 보고 게임도 한두 개 플레이할 수 있는 작은 데모 웹 앱입니다.

앱을 빌드하는 동안 최대한 성능이 우수하도록 만들고 싶었습니다. 최적화의 시작점은 Lighthouse 보고서였습니다.

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

Lighthouse 보고서에 표시된 앱의 초기 성능은 매우 좋지 않았습니다. 3G 네트워크에서 사용자는 유의미한 첫 페인트가 표시되거나 앱이 상호작용 가능해질 때까지 15초를 기다려야 했습니다. Lighthouse는 사이트의 수많은 문제를 강조 표시했으며 전체 실적 점수 23은 이를 정확하게 반영했습니다.

페이지 용량이 약 3.4MB였기 때문에 불필요한 부분을 줄여야 했습니다.

이로 인해 첫 번째 성능 문제가 발생했습니다. 전체 환경에 영향을 주지 않고 쉽게 삭제할 수 있는 항목을 찾는 것입니다.

실적 최적화 기회

불필요한 리소스 삭제

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

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

Lighthouse는 축소되지 않은 CSS 및 자바스크립트 감사에서 이 기회를 강조 표시합니다. 빌드 프로세스에 webpack을 사용하고 있었기 때문에 압축을 위해 Uglify JS 플러그인을 사용했습니다.

압축은 일반적인 작업이므로 사용 중인 빌드 프로세스에 맞는 기성 솔루션을 찾을 수 있습니다.

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

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

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

효율적인 캐시 정책 사용

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

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

이상적으로는 가능한 한 많은 리소스를 최대한 오랫동안 안전하게 캐시하고 업데이트된 리소스를 효율적으로 재검증하기 위한 검증 토큰을 제공하는 것을 목표로 해야 합니다.

사용하지 않는 코드 삭제

지금까지는 불필요한 다운로드에서 명확한 부분을 삭제했지만, 명확하지 않은 부분은 어떻게 해야 할까요? 예를 들어 사용하지 않는 코드입니다.

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

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

처음에는 Material 구성요소 라이브러리를 사용하여 앱의 프로토타입을 빠르게 제작했습니다. 시간이 지나면서 더 맞춤설정된 디자인과 분위기로 전환했고 그 라이브러리에 대해서는 완전히 잊어버렸습니다. 다행히 코드 적용 범위 검사를 통해 번들에서 이 코드를 다시 발견할 수 있었습니다.

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

Lighthouse는 사용되지 않는 CSS 규칙 감사에서도 이 문제를 발견했습니다. 400kb 이상의 잠재적 절감 효과를 보여주었습니다. 그래서 코드로 돌아가서 해당 라이브러리의 자바스크립트와 CSS 부분을 모두 삭제했습니다.

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

이로 인해 CSS 번들이 20배 줄어들었습니다. 이는 두 줄 길이의 작은 커밋에 비해 상당히 좋은 결과입니다.

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

하지만 이와 같은 변화가 있을 때는 측정항목과 점수를 확인하는 것만으로는 충분하지 않습니다. 실제 코드를 삭제하는 것은 위험이 따르므로 항상 잠재적인 회귀를 주의해야 합니다.

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

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

따라서 코드를 삭제하는 경우 적절한 테스트 워크플로를 마련하여 잠재적인 시각적 회귀를 방지해야 합니다.

막대한 네트워크 페이로드 피하기

대용량 리소스는 웹페이지 로드 속도를 늦출 수 있습니다. 사용자에게 비용이 발생할 수 있고 데이터 요금제에 큰 영향을 미칠 수 있으므로 이 점을 유의하는 것이 매우 중요합니다.

Lighthouse는 엄청난 네트워크 페이로드 감사를 사용하여 일부 네트워크 페이로드에 문제가 있음을 감지할 수 있었습니다.

대규모 네트워크 페이로드 감지
그림 8. 대규모 네트워크 페이로드 감지

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

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

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

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

이 사례에서는 많은 JavaScript 번들을 처리하고 있었기 때문에 JavaScript 커뮤니티에 다양한 JavaScript 번들 감사 도구가 있다는 점이 다행이었습니다.

자바스크립트 번들 감사
그림 9. JavaScript 번들 감사

먼저 webpack bundle analyzer를 사용했는데, 이 도구는 1.6MB의 파싱된 JavaScript인 unicode라는 종속 항목이 포함되어 있음을 알려주었습니다. 이는 상당히 큰 크기입니다.

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

그런 다음 다른 도구인 BundlePhobia로 전환했습니다. 이 도구를 사용하면 NPM 패키지의 이름을 입력하고 실제로 압축 및 gzip으로 압축된 크기가 어느 정도인지 확인할 수 있습니다. 우리는 사용 중이던 슬러그 모듈을 대체할 수 있는 2.2kb의 가벼운 모듈을 찾아 전환했습니다.

이는 실적에 큰 영향을 미쳤습니다. 이 변경사항과 JavaScript 번들 크기를 줄일 수 있는 다른 기회를 발견한 덕분에 2.1MB의 코드를 절약할 수 있었습니다.

이러한 번들의 gzip 및 축소된 크기를 고려하면 전반적으로 65% 개선되었습니다. 이러한 과정은 정말 가치 있는 작업이었습니다.

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

코드 분할로 자바스크립트 부팅 시간 단축

대규모 네트워크 페이로드는 앱에 큰 영향을 미칠 수 있지만, 정말 큰 영향을 미칠 수 있는 또 다른 요소는 JavaScript입니다.

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

브라우저가 자바스크립트를 처리하는 방식입니다.

JavaScript 처리
그림 10. JavaScript 처리

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

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

앱에서 이러한 문제를 발견하는 데 도움이 되도록 Lighthouse에 새로운 JavaScript 부팅 시간 감사를 도입했습니다.

자바스크립트 부팅 시간
그림 11. JavaScript 부팅 시간 감사

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

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

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

코드 분할은 사용자에게 자바스크립트라는 피자 한 판을 통째로 제공하는 대신, 필요할 때마다 한 조각씩 제공하는 개념입니다.

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

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

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

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

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

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

이미지 최적화

이미지 로드 성능 농담

Oodle 앱에서는 많은 이미지를 사용합니다. 안타깝게도 Lighthouse는 우리만큼 열정적이지 않았습니다. 실제로 이미지 관련 감사 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의 Network 패널에서도 직접 확인할 수 있습니다. 페이지에 표시되는 이미지는 몇 개뿐인데 많은 이미지가 들어오는 경우 대신 지연 로드를 고려할 수 있습니다.

지연 로드는 아직 브라우저에서 기본적으로 지원되지 않으므로 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 Fonts 스타일시트를 페이지에 삽입할 때마다 최대 2개의 하위 도메인에 연결됩니다. Lighthouse는 이 연결을 미리 준비할 수 있다면 초기 연결 시간을 최대 300밀리초까지 절약할 수 있다고 알려줍니다.

link rel preconnect를 활용하면 연결 지연 시간을 효과적으로 가릴 수 있습니다.

특히 글꼴 모음 CSS가 googleapis.com에서 호스팅되고 글꼴 리소스가 Gstatic에서 호스팅되는 Google Fonts와 같은 경우 이로 인해 큰 영향을 받을 수 있습니다. 이 최적화를 적용하여 몇 백 밀리초를 줄였습니다.

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

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

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

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

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

이러한 차이는 페이지에 상당한 영향을 미칠 수 있습니다.

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

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

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

Google Fonts를 사용하여 글꼴을 미리 로드하려는 경우 그렇게 간단하지는 않습니다. 한 가지 주의할 점이 있습니다.

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

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

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

실험용: 우선순위 힌트

오늘 여러분과 공유할 특별한 소식이 있습니다. 리소스 힌트, 미리 로드와 같은 기능 외에도 우선순위 힌트라는 새로운 실험용 브라우저 기능을 개발하고 있습니다.

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

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

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

Oodle 앱의 경우 이로 인해 실제로 최적화할 수 있는 실용적인 부분이 생겼습니다.

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

이미지에 지연 로드를 추가하기 전에 브라우저가 수행한 작업은 모든 기념일 로고가 있는 이미지 캐러셀이 있었고 브라우저는 초기에 높은 우선순위로 캐러셀의 맨 처음에 있는 모든 이미지를 가져오는 것이었습니다. 안타깝게도 사용자에게 가장 중요한 것은 캐러셀 중간에 있는 이미지였습니다. 그래서 저희는 배경 이미지의 중요도를 매우 낮게, 전경 이미지의 중요도를 매우 높게 설정했습니다. 그 결과 느린 3G에서 이미지를 가져오고 렌더링하는 속도가 2초 빨라졌습니다. 긍정적인 경험이었습니다.

몇 주 내에 이 기능을 Canary에 도입할 예정이니 계속 지켜봐 주세요.

웹 글꼴 로드 전략 수립

타이포그래피는 좋은 디자인의 기본이며, 웹 글꼴을 사용하는 경우 텍스트 렌더링을 차단하지 않는 것이 이상적이며 보이지 않는 텍스트를 표시하지 않는 것이 좋습니다.

이제 Lighthouse에서 웹 글꼴이 로드되는 동안 보이지 않는 텍스트 방지 감사를 통해 이 문제를 강조 표시합니다.

웹 글꼴이 로드되는 동안 보이지 않는 텍스트 방지
그림 22. 웹 글꼴이 로드되는 동안 보이지 않는 텍스트 방지

글꼴 페이스 블록을 사용하여 웹 글꼴을 로드하는 경우 웹 글꼴을 가져오는 데 시간이 오래 걸리면 브라우저에서 수행할 작업을 결정할 수 있습니다. 일부 브라우저는 시스템 글꼴로 대체하기 전에 최대 3초 동안 기다렸다가 다운로드되면 글꼴을 교체합니다.

이러한 보이지 않는 텍스트를 방지하려고 하므로, 이 경우 웹 글꼴이 너무 오래 걸리면 이번 주의 클래식 기념일 로고를 볼 수 없었을 것입니다. 다행히 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 애널리틱스 Reporting API를 사용하면 사이트의 모든 URL에 대한 다음 상위 페이지 및 이탈률을 확인하여 우선순위를 지정해야 하는 리소스를 파악할 수 있습니다.

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

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

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

Guess.js
그림 26. Guess.js

Guess.js는 웹의 데이터 기반 사용자 환경에 중점을 둔 프로젝트입니다. 이러한 노력이 데이터를 사용하여 웹 성능을 개선하고 그 이상을 달성하는 데 영감을 주기를 바랍니다. 모두 오픈소스이며 현재 GitHub에서 사용할 수 있습니다. 이 도구는 민코 게체프, Gatsby의 카일 매튜스, 케이티 헴페니우스 등 여러 사람이 오픈소스 커뮤니티와 협력하여 만들었습니다.

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

요약

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

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

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

도움을 주신 분들: 워드 피터스, 민코 게체프, 카일 매튜스, 케이티 헴페니우스, 돔 파롤리노, 요아브 바이스, 수지 루, 유스케 우츠노미야, 톰 앵커스, Lighthouse, Google 기념일 로고