본문으로 건너뛰기

CSS

Render Blocking Resource

CSS는 존재 만으로도, CSS가 파싱되기 전까지 브라우저 렌더링이 지연된다. 대부분의 모던 웹 사이트에서 CSS가 존재하지 않는다면 정상적으로 페이지를 이용할 수 없을 것이다. 만약 브라우저가 CSS가 없는 페이지를 그대로 노출한다면, 잠깐 동안 CSS가 파싱되면서 스타일이 적용되는 페이지가 나타나기 전까지의 시간이 생기고 말 것이다. 이러한 것을 FOUC(Flash of Unstyled Content)라고 한다.

브라우저가 CSS가 파싱되기 전까지 콘텐츠를 보여주지 않더라도 HTML의 로딩된 부분만을 일단 보여줄 수도 있다. 그러나 스크립트의 경우 async, defer이 없다면 파싱을 막게 된다. 스크립트는 잠재적으로 페이지를 조작할 여지가 있으므로 브라우저는 스크립트 실행에 매우 주의를 기울일 필요가 있다.

스크립트가 페이지의 스타일에 영향을 줄 수 있기 때문에, 만약 브라우저가 CSS 관련 작업을 진행중이라면, 이 작업이 완료될 때 까지 기다렸다가 스크립트를 실행할 것이다. 스크립트가 실행되기 전까지 문서 파싱을 할 수 없기 때문에, CSS는 더 이상 렌더링을 차단하는 요소로 작용하지 않는다. 문서의 외부 스타일시트 및 스크립트 순서에 따라서 떄로는 HTML 파싱을 중지할 수 있다.

파싱을 차단하는 상황을 피하기 위해서는 CSS를 최대한 빨리 불러와야 하고, 리소스를 최적의 순서로 불러와야 한다.

CSS 사이즈 지켜보기

CSS 압축하고 최소화 하기

외부 스타일 시트를 다운로드 하는 작업은 필연적으로 네트워크 지연이 발생하지만, 네트워크에 전송되는 바이트의 양을 줄임으로써 이 과정을 최소화할 수 있다.

파일을 압축하는 것은 속도 향상에 지대한 영향을 미치며, 많은 호스팅 플랫폼과 CDN에서는 기본적으로 에셋을 압축해준다. 가장 널리 알려진 압축 솔루션은 GZip이다.

Minification(최소화)는 코드에서 필요없는 공백을 지우는 과정이다. 결과물은 이전 코드에 비해서 작아지지만, 브라우저는 충분히 코드를 파싱할 수 있으며 이를 통해 몇 바이트라도 더 절약할 수 있다. 가장 유명한 자바스크립트 압축 툴로 terser가 있고, 웹팩 v4 버전 이상부터는 빌드 파일을 작게하는 도구가 내장되어 있다.

사용하지 않는 CSS 제거

CSS 프레임워크를 사용하게 될 경우, 필요한 컴포넌트만 번들링 하지 않는 이상 사용되지 않는 CSS가 포함되는 것은 일반적인 문제이다. 이와 비슷하게, 오랜 시간에 걸쳐서 쌓이는 큰 베이스에도 안쓰는 CSS가 남는 경우가 더러 있다.

사용하지 않는 CSS를 제거하는 것은 수동 작업이다. 따라서 코드가 얼마나 복잡하느냐에 따라서 난이도가 증가하게 된다. 이 작업은 웹 사이트 전체에서, 가능한 모든 디바이스에서, 가능한 모든 환경에서, 가능한 모든 자바스크립트 실행 결과에 따라서 결정해야 한다. UnusedCSS나 PurifyCSS와 같은 유명한 툴이 있지만, 항상 visual regression 테스트도 병행해서 이뤄져야 한다.

바로 이것이 CSS-in-JS를 쓸 때 얻을 수 있는 가장 큰 이점이다. 각 컴포넌트가 렌더링에 필요한 CSS가 JS 내에 포함되어 있다. 따라서 컴포넌트 레벨로 관리하기 때문에 편하다. CSS-in-JS는 페이지 내부에 CSS를 인라인 처리하거나, 외부 CSS 파일로 번들링 해버린다. CSS를 자바스크립트 내부에 포함시켜버리면 CSS 파싱과 평가가 느려진다.

CSS의 우선순위 정하기

Critical CSS란 화면에 보이는 컨텐츠(above-the-fold content)의 CSS에 대해서만 inline 처리하는 것을 의미한다. HTML 문서의 헤드 태그에 있는 스타일을 따로 추출해서 인라이닝 하면 스타일을 가져오는 추가 요청을 할 필요 없어서 렌더링이 빨라진다.

첫 렌더링 시의 라운드 트립을 최소화 하기 위해서는, above-the-fold content의 크기를 14kb내로 유지해야 한다.

Critical CSS를 정확히 정의하는 것은 어렵다. 디바이스 크기에 따라서 사용자가 보이는 영역이 달라지기 때문이다. 이는 특히 매우 유동적인 사이트인 경우에는 더욱 어려워 진다. 그러나 이는 여전히 성능 향상에 중요한 부분이다.

CSS 비동기로 불러오기

위 above-the-fold content를 최대한 빠르게 불러오는데 집주앟ㄴ다면, 나머지 영역은 비동기로 로딩하는 것이 최선이다.

<link
  rel="stylesheet"
  href="non-critical.css"
  media="print"
  onload="this.media='all'"
/>

print 미디어 타입이란, 사용자가 페이지를 프린트하려고 하는 경우에만, 브라우저가 해당 스타일 시트를 불러오는 것으로 렌더링에는 영향을 미치지 않는다. 그리고 여기에 onload이벤트로 this.media = all을 추가한다면, 스타일 시트가 로드가 완료되면 미디어 속성을 다시 all로 바꾸면서 스타일 시트가 적용된다.

또 다른 방법은 preload를 적용하는 것이다. 그러나 이 방법은 아래와 같은 단점이 있다.

  • preload는 생각보다 매우 이른 타이밍에, 높은 우선순위로 다운로드 되므로 다른 중요한 에셋의 다운로드 우선순위를 밀어버릴 수 있다.
<link rel="preload" href="/path/to/my.css" as="style" />

Reference