자바스크립트에 더욱 크게 의존하는 사이트를 빌드하면서, 때때로 쉽게 볼 수 없는 방식으로 보낸 것에 대한 대가를 치러야 합니다. 이 글에서는 사이트를 휴대기기에서 빠르게 로드하고 상호작용이 가능하게 하는 데 간단한 규칙이 도움이 되는 이유에 대해 다룹니다. 더 적은 자바스크립트를 전달하는 것은 네트워크 전송에 걸리는 시간, 코드 압축 해제의 비용, 자바스크립트의 파싱 및 컴파일에 드는 시간이 줄어든다는 것을 의미합니다.
네트워크
대부분이 개발자가 자바스크립트의 비용을 생각할 때는 다운로드 및 실행 비용의 관점에서 생각합니다. 유선으로 더 많은 용량의 자바스크립트를 전송하는 것은 사용자의 연결이 느릴수록 오래 걸립니다.
선진국에서조차 이것은 문제입니다. 사용자가 이용하는 효과적인 네트워크 연결 유형이 3G나 4G, Wi-Fi가 아닐 수 있기 때문입니다. 카페의 Wi-Fi가 있지만 2G 속도의 모바일 핫스팟에 연결되어있을 수 있습니다.
다음의 방법으로 자바스크립트의 네트워크 전송 비용을 감소시킬 수 있습니다.
- 사용자에게 필요한 코드만 전송합니다.
- 최소화
- UglifyJS를 사용하여 ES5 코드 최소화.
- babel-minify 또는 uglify-es를 사용하여 ES2015+ 최소화.
- 압축
- 사용되지 않은 코드 제거.
- DevTools 코드 대상 범위에서 삭제되거나 지연 로드될 수 있는 코드의 기회를 식별합니다.
- babel-preset-env 및 브라우저 목록을 사용하여 이미 최신 브라우저에 있는 트랜스파일 기능을 방지합니다. 웹팩 번들의 조심스러운 분석은 고급 개발자가 필요 없는 종속성을 잘라낼 기회를 식별하는 데 도움이 됩니다.
- 코드 스트립에 대한 정보는 tree-shaking, Closure Compiler의 고급 최적화 및 lodash-babel-plugin과 같은 라이브러리 트리밍 플러그인 또는 Moment.js와 같은 라이브러리를 위한 웹팩의 ContextReplacementPlugin을 참조하세요.
- 네트워크 트립 최소화를 위한 코드 캐싱.
파싱/컴파일
일단 다운로드되면, 자바스크립트의 가장 큰 비용 중 하나는 JS 엔진이 이 코드를 파싱/컴파일하는 시간입니다. Chrome DevTools에서는 파싱과 컴파일이 Performance 패널 내 노란색 "스크립팅" 시간에 포함됩니다.
Bottom-Up 및 Call Tree 탭은 정확한 파싱/컴파일 타이밍을 보여줍니다.

참고: Runtime Call Stats를 지원하는 Performance 패널은 현재 시험용입니다.
활성화하려면, chrome://flags/#enable-devtools-experiments -> Chrome 다시 시작 ->
DevTools로 이동 -> Settings -> Experiments -> Shift 6번 입력 ->
Timeline: V8 Runtime Call Stats on Timeline
이라는 옵션 체크 후 DevTools를 닫고 다시 엽니다.
그런데, 이것이 왜 중요할까요?
코드 파싱/컴파일에 오랜 시간을 소모하는 것은 사용자가 여러분의 사이트와 상호작용을 시작할 수 있는 시간을 크게 지연시킵니다. 더 많은 자바스크립트를 전송할수록 사이트를 이용할 수 있기까지 파싱과 컴파일에 더 많은 시간이 소요됩니다.
바이트별로 보면, 자바스크립트는 같은 크기의 이미지나 웹폰트보다 브라우저가 처리하는 비용이 많이 듭니다 — Tom Dale
자바스크립트와 비교하면 같은 크기의 이미지(여전히 디코딩 필요) 처리에는 수많은 비용이 들지만, 평균적인 모바일 하드웨어에서는 자바스크립트가 페이지 상호작용에 부정적인 영향을 미칠 가능성이 더 높습니다.

파싱과 컴파일이 느리다고 할 때의 맥락이 중요합니다. 여기서는 평균적인 모바일 전화에 관해 이야기하고 있습니다. 평균적인 사용자는 CPU 및 GPU가 느리고, L2/L3 캐시가 없으며 메모리 제약도 있는 전화를 가지고 있을 수 있습니다.
네트워크 성능과 기기의 성능이 항상 일치하는 것은 아닙니다. 우수한 광통신에 연결된 사용자일지라도 기기에 전송되는 자바스크립트를 파싱 및 평가하는 데 필요한 최고의 CPU를 가지고 있지 않을 수 있습니다. 그 반대의 경우도 마찬가지입니다. 네트워크 연결은 열악하지만 매우 빠른 CPU를 보유하고 있을 수 있습니다. — Kristofer Baxter, LinkedIn
아래는 저가 하드웨어 및 고급 하드웨어에서 1MB 미만의 압축 해제된(심플) 자바스크립트의 파싱 비용을 나타낸 것입니다. 시중에서 판매 중인 가장 빠른 기종의 전화기와 평균적인 기종의 코드 파싱/컴파일링 시간은 약 2~5배 정도 차이가 납니다.

CNN.com과 같은 실제 사이트는 어떤가요?
CNN의 자바스크립트 파싱/컴파일이 평균적인 전화(Moto G4)에서 최대 13초 걸리는 데 비해 고급 iPhone 8에서는 최대 4초밖에 걸리지 않습니다. 이는 사용자가 얼마나 빠르게 해당 사이트와 완벽하게 상호작용할 수 있는지에 상당한 영향을 미칩니다.

이 내용은 여러분이 사용하는 전화가 아니라 평균적인 하드웨어(Moto G4 등)에서 테스트하는 것의 중요성을 잘 나타냅니다. 컨텍스트도 중요하지만, 사용자가 보유한 기기와 네트워크 조건에 대해 최적화해야 합니다.

너무 많은 자바스크립트를 전송하고 있지는 않나요? 음, 그럴지도 몰라요 :)
HTTP 아카이브(상위 최대 500,000개 사이트)를 사용하여 모바일의 자바스크립트 상태를 분석하면, 50%의 사이트가 상호작용에 도달하기까지 14초 이상이 소요된다는 것을 알 수 있습니다. 이러한 사이트는 자바스크립트의 파싱 및 컴파일링에만 최대 4초를 소모합니다.
자바스크립트와 기타 리소스를 가져오고 처리하는 데 드는 시간을 고려하면, 사용자가 페이지를 사용할 수 있다고 느끼기 전까지 한동안 기다려야 하는 것은 당연합니다. 이 부분은 확실히 개선할 수 있습니다.
페이지에서 필수적이지 않은 자바스크립트를 제거하여 전송 시간, CPU를 많이 소모하는 파싱과 컴파일, 잠재적인 메모리 오버헤드를 감소시킬 수 있습니다. 또한, 페이지가 더 빠르게 상호작용할 수 있게 됩니다.
실행 시간
비용이 드는 것은 파싱과 컴파일뿐만이 아닙니다. 자바스크립트 실행(파싱/컴파일 후 코드 실행)은 기본 스레드에서 발생해야 하는 작업 중 하나입니다. 긴 실행 시간 역시 사용자가 사이트를 이용할 수 있게 되는 데 걸리는 시간을 늘립니다.
스크립트가 50ms 이상 실행되면 상호작용까지의 시간(time-to-interactive)이 자바스크립트를 다운로드, 컴파일, 실행하는 데 걸리는 전체 시간만큼 지연됩니다 — Alex Russell
이 문제를 해결하기 위해 자바스크립트는 작은 크기의 이점을 이용하여 기본 스레드에 고정되는 것을 방지합니다. 실행 중 얼마나 많은 작업을 줄일 수 있는지 탐색해 보세요.
기타 비용
자바스크립트는 다른 방식으로 페이지 성능에 영향을 미칠 수 있습니다.
- 메모리. GC(가비지 컬렉션) 때문에 페이지에 쟁크(jank) 현상이 일어나거나 일시적으로 중지되는 일이 자주 있는 것처럼 보일 수 있습니다. 브라우저가 메모리를 회수할 때, 자바스크립트 실행이 일시 정지되어, 가비지를 자주 수집하는 브라우저가 원하는 것 보다 더 자주 실행을 멈출 수 있습니다. 메모리 누수 및 잦은 gc 중지를 방지하여 페이지에 쟁크 현상이 일어나지 않게 하세요.
- 런타임 시, 장기 실행 중인 자바스크립트는 기본 스레드를 차단하여
페이지가 반응하지 않게 됩니다. 작업을 작은 조각으로(스케줄링에
requestAnimationFrame()
또는requestIdleCallback()
사용) 나눔으로써 응답 문제를 최소화할 수 있습니다.
자바스크립트 전달 비용 감소 패턴
자바스크립트의 파싱/컴파일 및 네트워크 전송 시간을 느리게 유지하고자 할 때 도움이 되는 경로 기반 청킹 또는 PRPL과 같은 패턴이 있습니다.
PRPL
PRPL(푸시, 렌더링, 사전 캐시, 지연 로드)는 적극적인 코드 분할 및 캐싱을 통해 상호작용을 최적화하는 패턴입니다.
이 패턴이 미칠 수 있는 영향을 시각화해 봅시다.
V8의 Runtime Call Stats를 사용하여 인기 있는 모바일 사이트 및 프로그레시브 웹 앱의 로드 타임을 분석했습니다. 보시다시피, 파싱 시간(주황색 표시)이 이러한 여러 사이트가 소비하는 시간의 상당 부분을 차지합니다.
PRPL을 이용하는 사이트인 Wego는 경로의 파싱 시간을 낮게 유지하여 매우 빠르게 상호작용합니다. 위의 여러 다른 사이트는 자바스크립트 비용을 낮추기 위해 코드 분할 및 성능 예산을 적용했습니다.
점진적 부트스트랩
많은 사이트가 상호작용에 많은 비용을 들여 콘텐츠 가시성을 최적화합니다. 개발자는 대규모 자바스크립트 번들이 있을 때 빠르게 첫 번째 페인트를 가져오기 위해 때때로 서버 측 렌더링을 사용하며, 최종적으로 자바스크립트를 가져오면 이를 '업그레이드'하여 이벤트 핸들러에 첨부합니다.
주의하세요. 이 방식도 비용이 발생합니다. 왜냐하면 1) 일반적으로 더 큰 HTML 응답을 전송하는데 이 때문에 상호작용이 지연될 수 있고, 2) 사용자는 자바스크립트가 처리를 마칠 때까지 환경의 절반이 실제로는 상호작용이 되지 않는 모호하고 불안한 상태에 빠진 채 방치될 수 있기 때문입니다.
점진적 부트스트랩은 더 나은 접근 방식입니다. 최소한의 기능 페이지(현재 경로에서 필요한 HTML/자바스크립트/CSS만으로 구성)를 전송합니다. 더 많은 리소스가 도착하면, 앱이 더 많은 기능을 지연 로드하고 개방할 수 있습니다.

누구나 보이는 부분에 비례하여 코드를 로딩하는 것을 원합니다. PRPL 및 점진적 부트스트랩은 이것을 달성하는 데 도움이 되는 패턴입니다.
결론
전송 크기는 저사양 네트워크에 매우 중요합니다. 파싱 시간은 CPU 바운드 기기에 중요합니다. 이 값을 낮게 유지하는 것이 중요합니다.
팀에서 엄격한 성능 예산을 적용함으로써 자바스크립트 전송 및 파싱/컴파일 시간을 낮게 유지하는 데 성공했습니다. 모바일 예산에 대한 지침은 Alex Russell의 "Can You Afford It?: Real-world Web Performance Budgets"을 참조하세요.

휴대기기를 대상으로 사이트를 빌드하는 경우, 대표적인 하드웨어에서 개발할 수 있도록 최선을 다하고 자바스크립트 파싱/컴파일 시간을 낮게 유지하며, 자바스크립트 비용을 주시할 수 있도록 하는 성능 예산을 적용해야 합니다.
자세히 알아보기
- Chrome Dev Summit 2017 - Modern Loading Best Practices
- JavaScript Start-up Performance
- Solving the web performance crisis — Nolan Lawson
- Can you afford it? Real-world performance budgets — Alex Russell
- Evaluating web frameworks and libraries — Kristofer Baxter
- Cloudflare’s Results of experimenting with Brotli 압축 관련(참고: 더 높은 품질에서는 동적 Brotli가 초기 페이지 렌더링을 지연시킬 수 있으므로 신중하게 평가해야 합니다. 그런 경우에는 대신 통계적인 압축을 하는 것이 나을 것입니다.)
- Performance Futures — Sam Saccone