CSS 의 fetching은 Asynchronous하지만 Blocking스럽다

devAnderson·2022년 4월 25일
1

TIL

목록 보기
90/106

🚍 0. 작성계기

이전 비슷한 주제로 신나게 공부하다가 블로그에 똥(?) 글을 작성한 적이 있다. 그때 당시에 정확하지도 않은 정보를 그냥 해외자료라는 이유 하나만으로 신뢰하고 읽고 작성했었는데 그 내용 자체가 완전히 틀렸다는 것을 깨닫고 부끄러워서 다 삭제하고 다시 쓴다.

🚍 1. 브라우저에 무언가가 뜨기까지

우선 너무나도 많이 봐왔듯이, 브라우저가 랜더링 엔진을 통해서 무언가를 나타내려면 우선적으로 html 문서를 받아와야 한다.
그리고 나서 이 받아온 html을 파싱하여 의미있는 토큰의 집단으로 만든 후, DOM 객체를 형성한다는 것은 이제 익숙한 사실이 되었다.

그런데 이 DOM을 파싱하다가 보면 CSS 파일을 요청하는 태그를 만나게 된다

    <link rel="stylesheet" href="./index.css">

계속 책을 살펴봤을 때에는, 동일하게 html처럼 서버에서 자원을 요청한 뒤 이것을 똑같이 파싱하여 CSSOM을 만든 후 DOM과 결합해 레이아웃 계산에 필요한 요소만 남겨놓은 "Render tree" 를 형성한다고 되어 있었다.

그렇다면 이 css는 동기적인것인가 비동기적인 것인가? 그것은 바로

fetching 자체는 비동기적이지만, css가 늦게오면 first paint도 그만큼 늦어진다.

라는 것이다. (이게 무슨 멍멍이 소리인가)

1. CSS를 요청하는 행위 자체는 HTML parsing을 blocking 하지 않는다.

브라우저는 해당 link의 어트리뷰트에 있는 href 를 통해서 자원을 요청한다. 즉, 이 내용 자체에서는 브라우저가 DOM을 생성하는 과정과 css가 요청이 날아가는 것 자체에서 서로간에 간섭을 받지 않는다. (즉, css는 비동기적으로 요청되어 받아와진다)

브라우저 입장에서는 html을 파싱할 때에 "link" 태그를 만나면, 그저 href 경로를 통해서 서버에 요청을 날린 후 해당 태그를 분해하여 노드 객체를 만들고
넘어가는 것에 불과하다 (href의 요청으로 인한 response가 오는 것을 기다리고 있지 않고 parsing을 계속 이어나간다)

2. 하지만 CSS 로딩의 지연은 전체 페이지의 Rendering을 Blocking한다.

이 css가 받아와진 이후에 랜더트리를 만드는 과정이 문제이다.

랜더 트리를 만들기 위해서는 위에서 언급했듯, DOM이 만들어져야 하고 CSSOM이 만들어져야 한다.
둘중 하나라도 만들어지지 않는다면 랜더트리를 만들지 못하기 때문에 만약 css의 도착이 늦어지게 된다면 그만큼 CSSOM을 만들어내는 시간도 늦어지게 되므로 전체적인 랜더링 과정은 "blocking" 된다.

또한 예외 케이스로 css가 DOM의 형성을 지연시키는 경우는 바로 "script를 통한 js 파일의 요청" 을 랜더링 프로세스가 마주치게 되는 경우이다.

다시금 말하지만 css파일을 요청하는 행위 자체는 DOM 형성을 막지 않고 비동기적으로 요청되게 된다.

그런데 문제는 link태그와 바로 붙어있는 script 태그를 만나게 될 경우, 이 script태그는 css 파일이 받아와져서 CSSOM을 형성하기 전까지 실행되지 않는다.

<link type="text/css" rel="stylesheet" href="style.css">
<script>
  alert(getComputedStyle(document.body).marginTop);
</script>

즉, 다시금 말하자면

  1. 랜더링 프로세스가 해당 html을 파싱하면서 노드를 형성하며 DOM으로 전환시키는 과정을 갖는다
  2. link 태그를 만났으므로 href 를 통해 자원을 요청시키고 해당 link 태그를 노드로 만들어 DOM에 삽입한 뒤 다음 파싱으로 넘어간다
  3. script 태그를 만났으므로, 랜더링 엔진의 주도권에서 js 엔진으로 주도권이 넘어간다. 그리고 src를 통해 외부 자원을 요청한 뒤 이것이 받아와지든 말든 상위에 있던 link 태그의 css 파일이 받아와지고 CSSOM을 만드는 것까지 기다린다.

이렇게 작동하는 이유는 javascript가 DOM 뿐만 아니라 style 역시 조작할 수 있는 스크립트이기 때문이다. 만약 javascript 내부에서 노드의 스타일과 관련된 어떤 작업을 할 경우, CSS의 결과에 따라서 그 값이 달라지게 될 가능성이 있으므로 CSS 가 받아와지고 CSSOM을 만들기까지 그 형성을 기다리게 된다.

3. 여담으로, CSS 리소스의 패칭을 빨리 받고 싶다면

  <link rel="preload" href="style.css" as="style">

현재 IE 11에서는 사용할 수 없지만, 크롬에서는 사용가능한 해당 어트리뷰트는 브라우저에게 해당 CSS 파일이 지금 당장 사용되게 될 것이므로 우선순위를 높여서 가져와달라는 힌트를 적용할 수 있다.

사용할 때에는 항상 "as" 어트리뷰트를 통해 해당 파일이 어떤 타입인지를 명시해줘야 한다.

reference

CSS blocking
DOMContentLoaded

profile
자라나라 프론트엔드 개발새싹!

0개의 댓글