자바스크립트에 더 많이 의존하는 사이트를 구축할 때, 우리는 때때로 우리가 전송하는 것에 대해 쉽게 볼 수 없는 방식으로 비용을 지불합니다. 이 도움말에서는 모바일 기기에서 사이트를 빠르게 로드하고 상호작용할 수 있도록 하려면 약간의 규율이 필요한 이유를 설명합니다. JavaScript를 적게 전송하면 네트워크 전송 시간이 줄어들고, 코드 압축 해제에 소요되는 시간이 줄어들며, 이 JavaScript를 파싱하고 컴파일하는 시간이 줄어듭니다.
네트워크
대부분의 개발자는 JavaScript 비용을 생각할 때 다운로드 및 실행 비용 측면에서 생각합니다. 사용자의 연결 속도가 느릴수록 유선으로 더 많은 바이트의 JavaScript를 전송하는 데 시간이 더 오래 걸립니다.

사용자가 보유한 유효 네트워크 연결 유형이 실제로 3G, 4G 또는 Wi-Fi가 아닐 수 있으므로 문제가 될 수 있습니다. 커피숍 Wi-Fi를 사용 중이지만 2G 속도의 모바일 핫스팟에 연결되어 있을 수 있습니다.
다음과 같은 방법으로 자바스크립트의 네트워크 전송 비용을 줄일 수 있습니다.
- 사용자에게 필요한 코드만 전송.
- 압축
- UglifyJS를 사용하여 ES5 코드를 압축합니다.
- babel-minify 또는 uglify-es를 사용하여 ES2015+를 축소합니다.
- 압축
- 사용하지 않는 코드 삭제
- DevTools 코드 적용 범위를 사용하여 삭제하거나 지연 로드할 수 있는 코드의 기회를 식별합니다.
- babel-preset-env 및 browserlist를 사용하여 최신 브라우저에 이미 있는 기능을 트랜스파일하지 않도록 합니다. 고급 개발자는 웹팩 번들을 신중하게 분석하여 불필요한 종속 항목을 제거할 기회를 파악할 수 있습니다.
- 코드 제거는 트리 흔들기, 클로저 컴파일러의 고급 최적화, lodash-babel-plugin과 같은 라이브러리 자르기 플러그인 또는 Moment.js와 같은 라이브러리를 위한 webpack의 ContextReplacementPlugin을 참고하세요.
- 네트워크 이동을 최소화하기 위한 코드 캐싱
파싱/컴파일
다운로드 후 JavaScript의 가장 큰 비용 중 하나는 JS 엔진이 이 코드를 파싱/컴파일하는 데 걸리는 시간입니다. Chrome DevTools에서 파싱 및 컴파일은 Performance 패널의 노란색 '스크립팅' 시간의 일부입니다.

Bottom-Up 및 Call Tree 탭에는 정확한 파싱/컴파일 타이밍이 표시됩니다.
그런데 이것이 왜 중요할까요?

코드를 파싱/컴파일하는 데 시간이 오래 걸리면 사용자가 사이트와 상호작용할 수 있는 시간이 크게 지연될 수 있습니다. 전송하는 JavaScript가 많을수록 사이트가 상호작용하기 전에 파싱하고 컴파일하는 데 시간이 더 오래 걸립니다.
바이트 단위로 보면 브라우저에서 JavaScript를 처리하는 데 드는 비용이 동일한 크기의 이미지나 웹 글꼴보다 더 많이 듭니다. - 톰 데일
JavaScript와 비교하면 비슷한 크기의 이미지를 처리하는 데 드는 비용이 많지만 (여전히 디코딩해야 함) 평균적인 모바일 하드웨어에서는 JS가 페이지의 상호작용에 부정적인 영향을 미칠 가능성이 더 높습니다.
파싱과 컴파일이 느리다고 말할 때 맥락이 중요합니다. 여기서는 평균적인 휴대전화를 말합니다. 일반 사용자는 CPU와 GPU가 느리고 L2/L3 캐시가 없으며 메모리가 제한된 휴대전화를 사용할 수 있습니다.
네트워크 기능과 기기 기능이 항상 일치하는 것은 아닙니다. Fiber 연결이 우수한 사용자라고 해서 기기로 전송된 JavaScript를 파싱하고 평가하는 데 가장 적합한 CPU를 가지고 있는 것은 아닙니다. 반대의 경우도 마찬가지입니다. 네트워크 연결은 끔찍하지만 CPU는 엄청나게 빠릅니다. — 크리스토퍼 백스터, 링크드인
아래에서는 저사양 및 고사양 하드웨어에서 압축 해제된 (단순) JavaScript 약 1MB를 파싱하는 비용을 확인할 수 있습니다. 시중에 판매되는 가장 빠른 휴대전화와 평균적인 휴대전화 간에는 코드를 파싱/컴파일하는 데 걸리는 시간이 2~5배 차이가 납니다.
CNN.com과 같은 실제 사이트는 어떨까요?
고급형 iPhone 8에서는 CNN의 JS를 파싱/컴파일하는 데 약 4초가 걸리는 반면, 평균적인 휴대전화 (Moto G4)에서는 약 13초가 걸립니다. 이는 사용자가 이 사이트와 완전히 상호작용할 수 있는 속도에 큰 영향을 미칠 수 있습니다.
이는 주머니에 있는 휴대전화뿐만 아니라 평균적인 하드웨어 (예: Moto G4)에서 테스트하는 것이 중요하다는 점을 보여줍니다. 하지만 맥락이 중요합니다. 사용자의 기기 및 네트워크 상태에 맞게 최적화하세요.
정말 자바스크립트를 너무 많이 전송하고 있나요? 어, 그럴 수도 있겠네요 :)
HTTP Archive (상위 약 50만 개 사이트)를 사용하여 모바일에서 JavaScript 상태를 분석하면 사이트의 50% 가 상호작용하는 데 14초 이상 걸리는 것을 확인할 수 있습니다. 이러한 사이트는 JS를 파싱하고 컴파일하는 데만 최대 4초를 소비합니다.

JS 및 기타 리소스를 가져오고 처리하는 데 걸리는 시간을 고려하면 사용자가 페이지를 사용할 준비가 되었다고 느끼기까지 시간이 걸리는 것은 당연한 일입니다. 이 부분은 확실히 개선할 수 있습니다.
페이지에서 중요하지 않은 JavaScript를 삭제하면 전송 시간, CPU 집약적인 파싱 및 컴파일, 잠재적인 메모리 오버헤드를 줄일 수 있습니다. 이렇게 하면 페이지가 더 빠르게 상호작용할 수 있습니다.
실행 시간
비용이 발생하는 것은 파싱과 컴파일뿐만이 아닙니다. JavaScript 실행(파싱/컴파일된 코드 실행)은 기본 스레드에서 실행되어야 하는 작업 중 하나입니다. 실행 시간이 길면 사용자가 사이트와 상호작용할 수 있는 시점이 지연될 수도 있습니다.

스크립트가 50ms 넘게 실행되면 상호작용까지의 시간은 JS를 다운로드, 컴파일, 실행하는 데 걸리는 전체 시간만큼 지연됩니다. - 알렉스 러셀
이 문제를 해결하기 위해 JavaScript는 기본 스레드가 잠기지 않도록 작은 청크로 구성하는 것이 좋습니다. 실행 중에 수행되는 작업량을 줄일 수 있는지 살펴봅니다.
기타 비용
자바스크립트는 다음과 같은 다른 방식으로 페이지 성능에 영향을 미칠 수 있습니다.
- 메모리 GC (가비지 컬렉션)로 인해 페이지가 자주 버벅거리거나 일시중지될 수 있습니다. 브라우저가 메모리를 회수하면 JS 실행이 일시중지되므로 가비지를 자주 수집하는 브라우저는 원하는 것보다 더 자주 실행을 일시중지할 수 있습니다. 메모리 누수와 빈번한 GC 일시중지를 방지하여 페이지 버벅거림을 방지합니다.
- 런타임 중에 장기 실행 JavaScript가 기본 스레드를 차단하여 페이지가 응답하지 않을 수 있습니다. 작업을 더 작은 단위로 나누면 (예약에
requestAnimationFrame()또는requestIdleCallback()사용) 응답성 문제를 최소화하여 다음 페인트에 대한 상호작용 (INP)을 개선할 수 있습니다.
JavaScript 전송 비용을 줄이는 패턴
JavaScript의 파싱/컴파일 및 네트워크 전송 시간을 느리게 유지하려는 경우 경로 기반 청킹 또는 PRPL과 같은 패턴이 도움이 될 수 있습니다.
PRPL
PRPL (푸시, 렌더링, 사전 캐시, 지연 로드)은 적극적인 코드 분할 및 캐싱을 통해 상호작용을 최적화하는 패턴입니다.

이러한 영향력을 시각화해 보겠습니다.
V8의 런타임 호출 통계를 사용하여 인기 있는 모바일 사이트와 프로그레시브 웹 앱의 로드 시간을 분석합니다. 보시다시피 파싱 시간 (주황색으로 표시)은 이러한 사이트에서 많은 시간을 소비하는 부분입니다.

PRPL을 사용하는 사이트인 Wego는 경로의 파싱 시간을 짧게 유지하여 상호작용을 매우 빠르게 수행합니다. 위의 다른 많은 사이트에서는 JS 비용을 낮추기 위해 코드 분할 및 성능 예산을 채택했습니다.
점진적 부트스트랩
많은 사이트에서 상호작용을 희생하면서 콘텐츠 가시성을 최적화합니다. 대규모 JavaScript 번들이 있는 경우 빠른 첫 페인트를 얻기 위해 개발자는 서버 측 렌더링을 사용한 다음 JavaScript를 가져올 때 이벤트 핸들러를 연결하도록 '업그레이드'하는 경우가 있습니다.
이 방법에는 비용이 발생하므로 주의해야 합니다. 1) 일반적으로 상호작용을 푸시할 수 있는 더 큰 HTML 응답을 전송하고, 2) JavaScript가 처리를 완료할 때까지 환경의 절반이 실제로 상호작용할 수 없는 불쾌한 골짜기에 사용자를 남겨둘 수 있습니다.
점진적 부트스트래핑이 더 나은 접근 방식일 수 있습니다. 최소한의 기능만 있는 페이지 (현재 경로에 필요한 HTML/JS/CSS로만 구성됨)를 전송합니다. 더 많은 리소스가 도착하면 앱은 더 많은 기능을 지연 로드하고 잠금 해제할 수 있습니다.
뷰에 표시되는 내용에 비례하여 코드를 로드하는 것이 가장 이상적입니다. PRPL 및 점진적 부트스트래핑은 이를 달성하는 데 도움이 되는 패턴입니다.
결론
전송 크기는 저사양 네트워크에서 매우 중요합니다. 파싱 시간은 CPU 바인딩 기기에 중요합니다. 이러한 수치를 낮게 유지하는 것이 중요합니다.
팀은 JavaScript 전송 및 파싱/컴파일 시간을 낮게 유지하기 위해 엄격한 성능 예산을 채택하여 성공을 거두었습니다. Alex Russell의 'Can You Afford It?: 실제 웹 성능 예산'을 참고하세요.
휴대기기를 타겟팅하는 사이트를 구축하는 경우 대표적인 하드웨어에서 개발하고, JavaScript 파싱/컴파일 시간을 짧게 유지하고, 팀이 JavaScript 비용을 주시할 수 있도록 성능 예산을 채택하세요.
자세히 알아보기
- Chrome Dev Summit 2017 - 최신 로드 권장사항
- 자바스크립트 시작 성능
- 웹 성능 위기 해결 — 놀란 로슨
- 감당할 수 있는가? 실제 성능 예산 — 알렉스 러셀
- 웹 프레임워크 및 라이브러리 평가 — 크리스토퍼 백스터
- 압축을 위한 Cloudflare의 Brotli 실험 결과(고품질의 동적 Brotli는 초기 페이지 렌더링을 지연시킬 수 있으므로 신중하게 평가하세요. 대신 정적으로 압축하는 것이 좋습니다.)
- Performance Futures — 샘 사코네