자바스크립트에 더욱 의존하는 사이트를 구축함에 따라 Google은 때때로 쉽게 볼 수 없는 방식으로 전송된 항목에 대한 비용을 지불하기도 합니다. 이 도움말에서는 사이트가 휴대기기에서 빠르게 로드되고 상호작용이 가능하도록 하는 데 간단한 규칙이 도움이 되는 이유를 알아보겠습니다. 더 적은 자바스크립트를 제공하면 네트워크 전송에 드는 시간이 줄어들고, 코드 압축 해제에 드는 시간과 자바스크립트 파싱 및 컴파일에 드는 시간이 줄어듭니다.
네트워크
대부분의 개발자는 자바스크립트의 비용을 다운로드 및 실행 비용의 관점에서 생각합니다. 유선으로 더 많은 바이트의 자바스크립트를 전송하면 사용자의 연결이 느릴수록 시간이 오래 걸립니다.
사용자의 효과적인 네트워크 연결 유형이 실제로 3G, 4G 또는 Wi-Fi가 아닐 수 있으므로 이는 문제가 될 수 있습니다. 카페의 Wi-Fi를 사용하지만 2G 속도의 모바일 핫스팟에 연결되어 있을 수 있습니다.
다음을 통해 자바스크립트의 네트워크 전송 비용을 줄일 수 있습니다.
- 사용자에게 필요한 코드만 전송합니다.
- 축소
- UglifyJS를 사용하여 ES5 코드를 축소하세요.
- babel-minify 또는 uglify-es를 사용하여 ES2015 이상을 축소합니다.
- 압축
- 사용하지 않는 코드 삭제
- DevTools 코드 적용 범위를 통해 삭제하거나 지연 로드할 수 있는 코드의 기회를 파악합니다.
- babel-preset-env 및 browserlist를 사용하여 최신 브라우저에 이미 있는 기능 트랜스파일링을 방지합니다. 고급 개발자는 웹팩 번들의 신중한 분석을 통해 불필요한 종속 항목을 줄일 기회를 식별할 수 있습니다.
- 코드 제거에 관해서는 트리 쉐이킹, 클로저 컴파일러의 고급 최적화, 라이브러리 자르기 플러그인(예: lodash-babel-plugin 또는 Moment.js와 같은 라이브러리용 웹팩의 ContextReplacementPlugin)을 참고하세요.
- 코드 캐싱으로 네트워크 트립 최소화
파싱/컴파일
다운로드가 완료되면 자바스크립트의 가장 큰 비용 중 하나는 JS 엔진이 이 코드를 파싱/컴파일하는 시간입니다. Chrome DevTools에서 파싱 및 컴파일은 성능 패널의 노란색 '스크립팅' 시간에 포함됩니다.
Bottom-Up 및 Call Tree 탭은 정확한 파싱/컴파일 타이밍을 보여줍니다.
그런데 이것이 왜 중요할까요?
코드를 파싱/컴파일하는 데 오랜 시간을 소비하면 사용자가 사이트와 상호작용할 수 있는 시간이 크게 지연될 수 있습니다. 더 많은 자바스크립트를 전송할수록 사이트에서 상호작용하기 전에 파싱 및 컴파일하는 데 시간이 더 오래 걸립니다.
바이트 단위, 자바스크립트는 동일한 크기의 이미지나 웹 글꼴보다 브라우저에서 처리하는 비용이 더 높음 — Tom Dale
자바스크립트와 비교하면 동일한 크기의 이미지 (여전히 디코딩 필요)를 처리하는 데는 많은 비용이 들지만 평균적인 모바일 하드웨어에서 JS는 페이지 상호작용에 부정적인 영향을 미칠 가능성이 더 큽니다.
파싱과 컴파일이 느리다고 말할 때는 컨텍스트가 중요합니다. 여기서는 평균적인 휴대전화에 관해 이야기하고 있습니다. 평균적인 사용자는 CPU와 GPU가 느리고 L2/L3 캐시가 없으며 메모리 제약이 있는 휴대전화를 사용할 수 있습니다.
네트워크 성능과 장치 성능이 항상 일치하지는 않습니다. 우수한 광섬유 연결이 있는 사용자라고 해서 기기에 전송된 자바스크립트를 파싱하고 평가하기 위한 CPU가 반드시 최고의 것은 아닙니다. 그 반대의 경우도 마찬가지입니다. 네트워크 연결은 열악하지만 CPU는 엄청나게 빠릅니다. — Kristofer Baxter, LinkedIn
아래에서 저사양 하드웨어와 고급 하드웨어에서 약 1MB의 압축 해제된 (단순) JavaScript를 파싱하는 비용을 확인할 수 있습니다. 시중에서 판매 중인 가장 빠른 휴대전화와 평균 휴대전화 간에 코드를 파싱/컴파일하는 데 소요되는 시간은 2~5배 정도 다릅니다.
CNN.com과 같은 실제 사이트는 어떨까요?
CNN의 자바스크립트 파싱/컴파일이 평균적인 휴대전화 (Moto G4)에서 최대 13초인 반면 고급형 iPhone 8에서는 최대 4초밖에 걸리지 않습니다. 이는 사용자가 얼마나 빠르게 사이트와 완전히 상호작용할 수 있는지에 상당한 영향을 줄 수 있습니다.
이는 주머니에 있는 휴대전화뿐만 아니라 일반적인 하드웨어 (예: Moto G4)에서 테스트하는 것의 중요성을 강조합니다. 컨텍스트도 중요하지만, 사용자의 기기 및 네트워크 조건에 맞게 최적화해야 합니다.
너무 많은 자바스크립트를 전송하고 있지는 않나요? 그럴 수도 있겠네요 :)
HTTP 보관 파일 (상위 50만 개 사이트)을 사용하여 모바일의 자바스크립트 상태를 분석하면 사이트의 50% 가 상호작용을 하는 데 14초 이상 걸리는 것을 확인할 수 있습니다. 이러한 사이트는 JavaScript를 파싱하고 컴파일하는 데 최대 4초가 걸립니다.
JS 및 기타 리소스를 가져오고 처리하는 데 걸리는 시간을 고려하면 사용자가 페이지를 사용할 준비가 되었다고 느낄 때까지 한동안 기다려야 하는 것은 당연합니다. 이 부분은 확실히 개선할 수 있습니다.
페이지에서 중요하지 않은 자바스크립트를 삭제하면 전송 시간, CPU를 많이 사용하는 파싱 및 컴파일, 잠재적인 메모리 오버헤드를 줄일 수 있습니다. 이렇게 하면 페이지에서 상호작용이 더 빠르게 진행되도록 할 수도 있습니다.
실행 시간
파싱하고 컴파일하는 것뿐만 아니라 비용이 발생할 수 있습니다. 자바스크립트 실행(파싱/컴파일 후 코드 실행)은 기본 스레드에서 발생해야 하는 작업 중 하나입니다. 또한 실행 시간이 길면 사용자가 사이트와 상호작용할 수 있는 시간이 길어질 수 있습니다.
스크립트가 50ms 이상 실행되면 상호작용까지의 시간이 JS를 다운로드, 컴파일, 실행하는 데 걸리는 전체 시간만큼 지연됩니다. - Alex Russell
이 문제를 해결하기 위해 JavaScript는 작은 크기의 이점을 활용하여 기본 스레드에 고정되는 것을 방지합니다. 실행 중에 수행 중인 작업의 양을 줄일 수 있는지 살펴봅니다.
기타 비용
JavaScript는 다음과 같은 다른 방식으로 페이지 성능에 영향을 미칠 수 있습니다.
- 메모리. GC (가비지 컬렉션)로 인해 페이지에서 버벅거림이나 일시중지가 자주 발생하는 것처럼 보일 수 있습니다. 브라우저가 메모리를 회수하면 JS 실행이 일시중지되므로 가비지를 자주 수집하는 브라우저가 원하는 것보다 더 자주 실행을 일시중지할 수 있습니다. 메모리 누수와 빈번한 gc 중지를 방지하여 페이지에 버벅거림이 없도록 합니다.
- 런타임 시 장기 실행 자바스크립트는 기본 스레드를 차단하여 페이지가 응답하지 않게 할 수 있습니다. 작업을 더 작은 조각으로 나누면 (예약에
requestAnimationFrame()
또는requestIdleCallback()
사용) 응답성 문제를 최소화하여 다음 페인트에 대한 상호작용 (INP)을 개선할 수 있습니다.
자바스크립트 전송 비용 감소 패턴
JavaScript의 파싱/컴파일 및 네트워크 전송 시간을 느리게 유지하려는 경우 경로 기반 청크 또는 PRPL과 같은 도움이 될 수 있는 패턴이 있습니다.
사전 등록
PRPL (Push, Render, Pre-cache, Lazy-load)은 적극적인 코드 분할 및 캐싱을 통해 상호작용에 최적화된 패턴입니다.
어떤 영향을 줄 수 있는지 시각화해 봅시다.
V8의 Runtime Call Stats를 사용하여 인기 모바일 사이트 및 프로그레시브 웹 앱의 로드 시간을 분석합니다. 이와 같이 파싱 시간 (주황색)은 이러한 사이트 중 상당수가 소비하는 시간의 상당 부분을 차지합니다.
PRPL을 사용하는 사이트인 Wego는 경로의 파싱 시간을 낮게 유지하여 매우 빠르게 상호작용합니다. 위의 다른 많은 사이트에서는 자바스크립트 비용을 낮추기 위해 코드 분할 및 성능 예산을 채택했습니다.
점진적 부트스트랩
많은 사이트가 상호작용에 많은 비용을 들여 콘텐츠 가시성을 최적화합니다. 개발자는 대규모 JavaScript 번들이 있을 때 빠른 첫 페인트를 가져오기 위해 서버 측 렌더링을 사용하는 경우가 있습니다. 그런 다음 JavaScript를 최종적으로 가져올 때 이를 '업그레이드'하여 이벤트 핸들러를 연결합니다.
비용이 발생하므로 주의하세요. 1) 일반적으로 상호작용을 푸시할 수 있는 더 큰 HTML 응답을 전송하게 되고, 2) 사용자를 자바스크립트 처리가 완료될 때까지 경험의 절반이 실제로 상호작용할 수 없는 불명확한 계곡에 빠질 수 있습니다.
점진적 부트스트랩이 더 나은 접근 방식일 수 있습니다. 현재 경로에 필요한 HTML/JS/CSS로만 구성된 최소한의 기능 페이지를 전송합니다. 더 많은 리소스가 도착하면 앱이 더 많은 기능을 지연 로드하고 잠금 해제할 수 있습니다.
뷰에 보이는 내용에 비례하여 코드를 로드하는 것이 바로 가능합니다. PRPL 및 점진적 부트스트랩은 이를 달성하는 데 도움이 되는 패턴입니다.
결론
전송 크기는 저사양 네트워크에 매우 중요합니다. 파싱 시간은 CPU에 바인딩된 기기에 중요합니다. 낮게 유지하는 것이 중요합니다.
팀에서 엄격한 성능 예산을 채택하여 JavaScript 전송 및 파싱/컴파일 시간을 낮게 유지하는 데 성공했습니다. 자세한 내용은 Alex Russell의 'Can You Afford It?: 실제 웹 성능 예산'을 참조하세요.
휴대기기를 타겟팅하는 사이트를 빌드하는 경우 대표적인 하드웨어에서 개발하기 위해 최선을 다하고, 자바스크립트 파싱/컴파일 시간을 낮게 유지하고, 팀이 자바스크립트 비용을 확인할 수 있도록 성능 예산을 채택하세요.
자세히 알아보기
- Chrome Dev Summit 2017 - 최신 로드 권장사항
- JavaScript 시작 성능
- 웹 성능 위기 해결 — 놀란 로슨
- 비용이 충분한가요? 실제 성능 예산 — Alex Russell
- 웹 프레임워크 및 라이브러리 평가 — 크리스토퍼 백스터
- 압축을 위한 Cloudflare의 Brotli로 실험한 결과 (동적인 Brotli 품질이 높아지면 초기 페이지 렌더링이 지연될 수 있으므로 신중하게 평가하세요. 대신 정적으로 압축하는 것이 좋습니다.)
- Performance Futures - 샘 새콘