How loading JavaScript can slow websites down (even if it’s asynchronous)

어떻게 JavaScript 로딩은 사이트를 느리게 하는가 (비동기임에도 불구하고!)

https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2016/september/how-loading-javascript-can-slow-websites-down/을 번역한 내용입니다.

JavaScript를 사용하지 않는 사이트를 상상할 수 있을까? JavaScript는 정적인 문서만 있던 인터넷 환경을 풍부하고 인터렉티브(interactive)한 경험의 장으로 만드는데에 크게 일조하였다. 하지만 이제 사이트들은 JavaScript에 지나치게 의존하고 있는건 아닐까??

Blocking Scripts(로딩을 막는 스크립트)

오늘 날, 한 웹 페이지에 12개 이상의 Javascript 파일들을 포함하고 있는 경우를 많이 볼 수 있다. (더 나은 사용자 경험을 제공하기 위해 이리 많은 파일들을 포함시킴..)

하지만, 몇 년 전부터 여러 JavaScript 파일들을 한 페이지에 포함하는 것은 문제를 발생시킬 수 있다는 것을 알게 되었다. 브라우저는 우선 외부 JavaScript파일이 로딩이 될 때 까지 기다렸다가 페이지의 나머지 부분을 로딩한다. 외부 JavaScript파일 로딩이 늦어질 경우, 사용자는 빈 화면을 계속 보고 있게 된다.

이 문제를 해결하는 가장 쉬운 방법은 페이지(html file)의 맨 아래에 scripts들을 넣는 것이다. 또 하나의 방법은, inline JavaScript를 사용하여 외부 JavaScript파일에 대한 참조를 넣는 것이다. 이 방법을 사용하면 웹페이지를 만들고 그리는 것에 영향을 주지 않으면서 외부 JavaScript파일을 로딩할 수 있다.

요새는 async, defer attributes도 제공된다. Async는 script이 로딩되면서 동시에 페이지가 그려질 수 있는 기능을 제공한다. script가 로딩이 다 되면(비동기적으로 로딩이 되면) 바로 실행이 된다. 즉, 로딩되는 여러 외부 JavaScript script 간의 순서를 보장할 수 없다. (비동기적으로, 로딩이 준비되는대로 준비된 파일을 실행하는 것이기 때문에)

Defer도 비슷하게 동작하는데, 차이점은 DOMContentLoaded event 이후 스크립트들을 실행하기 때문에 html에 적힌 순서대로의 JavaScript script의 순서가 보장된다.

하지만 이것은 blocking scripts의 이슈를 해결하지 못했다. JavaScript가 feature detection으로 쓰일 경우(예를 들어, 동적으로 컨텐츠를 더하고자 할 때) 동기적으로, (맨 위에 script를 포함시켜) 로딩되야 한다.

하지만 async, deferred scripts는 사용자 경험에 부정적인 영향을 설마 끼치지 않겠지?!!

Why asynchronous scripts can still be ‘blocking’(스크립트가 async여도 blocking일 수 있는 이유)

웹페이지를 방문하면 사람들은 그 컨텐츠에 관심이 있다. JavaScript로 훨씬 더 풍부한 페이지를 만들 수 있게 되었지만, 사람들의 최우선 관심사는 페이지의 내용이다.

안타깝지만 브라우저들은 이 사실을 모른다. 로딩할 때, 브라우저는 이미지보다 script에 우선권을 준다.

하지만 여러 개의 scripts와 이미지가 있을 경우에는?? 페이지를 더 풍부하게 해주는 script는 중요하다. 하지만 사용자들에게 이미지가 더 중요한 경우라면?!!

이미지가 더 중요한 상황이 있을 수 있음에도 불구하고, 브라우저는 여전히 scripts들을 먼저 로딩할 수 있다. 이미지 뒤에, 페이지의 맨 아래에 넣었음에도 불구하고…

이럴 경우, 스크립트에 의해 이미지의 로딩이 늦춰진다. 다시 blocking javaScript로 돌아간 것이다.

하나의 예시를 들겠다. 이 페이지는 여러 이미지와, 여러 JavasScript파일들이 아래에 참조되어 있는 페이지이다. Chrome의 WebPagetest를 사용했다. 총 이미지 개수 중 반 정도는 script가 로딩되기 전에 로딩이 되었으나 나머지는 script로딩 이후 로딩되었다.

connection view waterfall(이미지보다 script files이 먼저 커넥션을 맺는 것을 볼 수 있음)을 참조하는 것도 도움이 된다.

이 이슈는 어떻게 해결할 수 있을까??

Domain Sharding(도메인 나누기)

부분적 해결법은 같은 도메인으로부터 많은 양의 스크립트와 이미지를 로딩하지 않는 것이다. 이미지와 script를 다른 도메인으로부터 로딩하면 적어도 이미지가 같은 connection아래의 script뒤에 줄 설 필요는 없으니깐.

하지만, script를 모두 다른 도메인으로 옮긴다고 하더라도, 브라우저가 열 수 있는 connection의 총 개수에는 제한이 있다. 그리고 scripts를 다른 도메인으로 옮길 경우, 그 connection들이 이미지들에게로 양보될 것이란 보장이 없다. 예를 들어, 그 connection들이 third party script를 로딩하는데에 쓰일 수도 있는 것이다.

Adding scripts using inline JavaScript(inline으로 스크립트 추가하기)

Inline으로 js script를 추가하는 방법이 있다. 이 방법을 사용할 경우, 이미지를 script위에 넣을 경우, script가 실행되기 전 이미지가 로딩되도록 할 수 있다. 단점은, 이렇게 로딩되는 script들은 브라우저의 preloader가 찾지 못한다. 이미지 후에 script가 실행되는 것은 보장할 수 있지만, script가 원하는 시점보다 훨 더 늦게 로딩될수도 있다. onload event에 걸어 실행되는 script도 똑같은 단점을 갖고 있다.

Preloading Images(이미지 preloading)

우리가 진짜로 해야하는 일은, 브라우저가 이미지에 로딩 최우선권을, 스크립트 보다 높은 우선권을 주어 스크립트들은 건너뛰고 먼저 로딩될 수 있도록 하는 것이다. Preloading이란 방법을 통해 이것이 이제 가능하다. Preloading을 통해 먼저 로딩되야할 할 리소스들을 브라우저에게 미리 알려주는 알려줄 수 있다. 폰트, 배경 사진, 이미지와 같이 나중에 로딩될 수 있는 것들에 있어 preloading을 사용하면 특히 더 유용하다.

각 객체의 우선권을 조정할 수 있게 해준다.

preload는 다음과 같이 사용된다.

ink rel="preload" href="images/mypic.jpg" as="image">

as attribute는 브라우저가 어떤 타입의 객체를 로딩하게 될지를 나타낸다. 하지만, 우리에게 이 것은 그닥 유용하지 않다. image라고 명시하면 전과 같이, 스크립트보다 후인 우선권을 갖게 되기 때문이다.

대신 이와 같이, as attribute를 뺀다.

<link rel="preload" href="images/mypic.jpg">

이렇게 하면 이미지에 더 높은 우선권을 부여하지는 않지만, 디폴트로 설정된 스크립트 보다 하위인 우선권보다는 높일 수 있다.

test page의 waterfall charts는 다음과 같다.

커넥션 뷰:

preload를 사용할 경우, 주의해야 할 점 두가지가 있다.

첫번째, 아직 지원하는 브라우저가 Chrome, Opera, Android 브라우저 뿐이다. (참조: caniuse.com)

둘째로, 꽤나 무딘 도구라는 것이다. 우리는 preloaded 객체들의 디폴트 우선권이 스크립트보다 높다는 점을 이용하는 것이다.

이렇게 하여 브라우저가 어떤 것이 가장 중요한지 결정하게 하는 것보다 , 우선권 부여에 있어 작성자가 더 세세하게 컨트롤 할 수 있게 하는 것이 더 유용할 것이다.

현재는 버려진 Resource Priorities specification에서 비슷한 것을 보았다.

pr attribute in the Resource Hints specification와 같이 해당 resource가 사용될 가능성을 명시하는 것이 있으면 유용할 수 있다.

HTTP/2

http/2를 사용할 경우, 몇 가지 문제는 해결될 수 있다. 왜냐면 requests, responses가 하나의 connection내에서도 복합 stream으로 보내지기 때문이다. 하지만 resource priorities 문제는 여전히 있다. (However, the concept of resource priorities remains, with bandwidth apportioned accordingly, and taking various dependencies into account.)

http/2에서 또 하나의 중요한 컨셉은 server push이다. 클라이언트 쪽에서 요청하기 전 서버쪽에서 자원을 보내는 것이다. 이것은 페이지 rendering시 필요한 css 파일을 보내는데 특히 유용하다. root 객체에 response header를 추가함으로써 http/2에서는 preloading, server push 둘을 동시에 사용할 수 있다.

Link: </ images/mypic.jpg>; rel=preload;

server push는 사용하지 않고 preloading만 사용하고 싶은 경우에는 다음과 같이 nopush를 쓰면 된다.

Link: </ images/mypic.jpg>; rel=preload; nopush

server push는 이미지 로딩(imagery)보다 css 파일 로딩에 더 연관이 높지만, 여기서 언급한 이유는 pushed resources의 우선권도 조작 가능하기 때문이다. -Apache 참조 H2PushPriority directive.

The Point to take away(결론)

웹페이지를 만들 땐, 사용자가 어떤 것을 볼 것이고, 어떤 액션을 할지에 대해 미리 생각해보는 것이 중요하다. 중요하지 않은(non-essential) 스크립트 로딩은 중요한 컨텐츠를 사용자에게 보여주는데에 방해가 될 수 있고, JavaScript script를 비동적으로 로딩한다고 사용자 경험에 부정적 영향을 미치지 않을 것 이란 보장을 할 수 없기 때문이다.

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Developing Vue Apps with the Quasar Library — Slide Item Color and Slide Transition

Live IoT Data Subscription with Apollo GraphQL and MQTT

Top React Libraries — Forms, Sliders, and Animations

Angular Material — Tooltips and Tree Display

How to add a spinner to an Angular Material button.

Making an SVG Snowman Sprite

I BUY AGAIN ;-)

Top Vue Packages for Adding Inputs, QR Codes, and Handle Events

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Yesl Koh

Yesl Koh

More from Medium

No Kid Hungry AmbassadorWeek Two

CS371p Spring 2022: Cristian Astorga: Final Entry

#racialdiscrimination This article is part of a collection on the events of Jan.

Working Title, first complete draft of book: