CSS 코드 최적화

패스트캠퍼스 강의를 정리한 내용입니다.
"The RED : 견고한 UI 설계를 위한 마크업 가이드 by 정찬명"

CSS Optimization

  • Remove unused CSS (미사용 CSS 제거)
  • Eliminate render-blocking resources (렌더 차단 리소스 제거)

CSS는 기본적으로 '렌더 차단 리소스'라고 부름
웹 브라우저가 CSS 파일을 다운로드하고 해석하는 동안 웹 페이지의 렌더링은 차단되기 때문!

CSS가 렌더 차단 리소스가 되지 않도록 만드는 방법

Remove unused CSS

Unused CSS는 왜 문제인가?

CSS는 페이지 렌더링을 차단하는 리소스인데
미사용 CSS 까지 있다면, 브라우저가 스타일을 계산하는데 잠재적으로 더 많은 시간을 소비하게 만들기 때문

웹 페이지 Unused CSS 확인 방법

Google lighthouse 활용
lighthouse는 2KB 이상 미사용 CSS가 포함된 파일을 검출하여 오류로 보고함
→ 자세한 내용은 본문 하단 'Google lighthouse' 설명 참고

Eliminate render-blocking resources

Render blocking은 왜 문제인가?

브라우저가 외부 리소스를 다운로드하고 파싱하는 동안 페이지 콘텐츠를 파싱하거나 렌더링하지 않기 때문에 페이지 표시 속도 저하의 원인이 됨
*Unused CSS는 Render blocking을 가중하는 요인

Render blocking <script>

  • 필수 스크립트는 html에 <script> 형식으로 작성
  • 기타 스크립트는 </body> 종료 태그 직전에 선언
  • 마지막에 파싱해도 문제 없으면 defer 속성 사용
  • 가능한 빠른 시점에 실행 필요하면 async 속성 사용

<script> No async. No defer.

<script> async : 병렬 다운로드, 즉시 실행

  • 스크립트를 다운로드 하면서 즉시 실행
    *병렬 다운로드: 스크립트를 다운로드 하면서 웹 페이지를 해석
// 병렬 다운로드, 즉시 실행
<scrit async src="script.js"></script>

<script> defer : 병렬 다운로드, 지연 실행

  • 웹 페이지가 모두 그려지고 DOM이 들어왔을 때 스크립트 실행
  • HTML 파싱이 중단없이 진행되므로 HTML이 빠르게 파싱되어 화면이 그려지는 속도가 빨라짐
// 병렬 다운로드, 지연 실행
<scrit defer src="script.js"></script>

이미지 출처 : https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

Render blocking <link rel="stylesheet">

  • CSS의 Render-blocking resources를 제거하는 방법
  • Media 속성이 없거나 값이 all이면 렌더 차단 리소스
    → 스타일시트를 다운로드 하고 해석하는 동안 웹 브라우저 화면에는 아무것도 그려내지 않음
<!-- 렌더 차단 리소스 예시 -->
<link href="style.css"      rel="stylesheet">
<link href="style.css"      rel="stylesheet" media="all">


<!-- 렌더 차단 리소스가 아닌 예시 -->
<!-- orientation:portrait일 때만 스타일시트 해석 -->
<link href="portrait.css"   rel="stylesheet" media="orientation:portrait">
<!-- print할 때만 스타일시트 해석 -->
<link href="print.css"      rel="stylesheet" media="print">

CSS 파일이 렌더링을 차단하는 과정

→ FCP 구간을 보면, style.css 파일이 다운로드 되고 해석되는 동안 아무것도 그려내지 않고 있기 때문에 사용자들은 해당 페이지가 느리다고 생각하게 됨
*FCP(First Contentful Paint) : 첫번째 내용물이 화면에 그려지는 시점

출처: https://web.dev/defer-non-critical-css/
CSS 렌더링 테스트 페이지: https://defer-css-unoptimized.glitch.me/

Render-blocking 이슈 해결 방법

방법 1)

반응형 웹인 경우 해상도 구간별로 CSS 파일을 분리하고 media 속성으로 분기

<link href="*.css" rel="stylesheet" media="(max-width:639px)"
<link href="*.css" rel="stylesheet" media="(max-width:640px) and (max-width:960px)"
<link href="*.css" rel="stylesheet" media="(max-width:961px)"

방법 2)

필수 스타일은 임베딩, 지연 스타일은 병렬 로딩 후 지연 적용
→ 외부 스타일 파일이 렌더링(FCP)을 차단하지 않음

  1. 필수 스타일은 페이지 <head><style> 형식으로 작성
  2. 지연 스타일은 <link rel="preload"> 속성으로 병렬 로딩 후 지연 적용
    예를 들어,
    팝업을 띄우거나 스크롤 아래쪽에 있는 내용들에 대한 스타일 코드는 당장 필요한 CSS가 아니므로,
    병렬로 로딩하고 가능한 늦게 화면에 적용 되도록 지연 로딩 처리
<style>
	/* 필수 스타일은 이곳에 작성 */
</style>
<link rel="preload" as="style" href="x.css" onload="this.onload=null; this.rel='stylesheet'">
<!-- 페이지 onload가 완료되면, rel 속성을 'stylesheet'로 변경 -->
<!-- 
  this.onload=null 할당 이유는?
  rel 속성을 변경할 때 일부 브라우저가 다시 onload 실행하는 것을 방어
-->

방법 2 적용 결과 예시 이미지


성능 최적화 확인 도구

Google lighthouse

  • Chrome 웹 스토어에서 다운로드

    크롬 웹스토어 바로가기

  • 개발자도구에서 Lighthouse 탭 활성화

  • Categories, Device 선택 후 Generate report 버튼 클릭

  • 검사 결과 (예시 이미지 참고)

  • Remove unused CSS 표시 조건

    • 사용하지 않는 코드가 2KB 이상 포함된 파일이 있는 경우
  • Render blocking Resources 표시 조건

    • defer, async 속성이 없는 <head> 요소의 <script> 태그
    • media 속성과 값이 없는 <link rel="stylesheet"> 태그

,
Remove unused CSS 목록이 구성됨
*Chrome lighthouse는 @font-face 규칙을 무조건 unused CSS로 보고하는 오류가 있으니 참고할 것

Show Coverage

  • 개발자도구에서 Ctrl+Shift+P 단축키 사용하여 'Show coverage' 검색하여 선택

  • 개발자도구에 'Show coverage' 탭이 활성화 되면 새로고침 클릭

  • 옵션 설정을 CSS로 변경하여 sorting 목록 확인

  • Usage Visualization의 붉은 색 표시로 사용하지 않는 CSS 비율 확인 후 목록 선택하여 Sources 탭에서 파일 내 미사용 CSS 코드 확인


Summary

  1. 웹 브라우저는 외부 JS, CSS 파일을 로딩하고 파싱하는 동안 렌더링 차단 상태를 유지함
  2. 사용하지 않는 JS, CSS는 제거
  3. 필수 코드에 해당하는 스타일과 스크립트는 페이지에 임베딩
    = 페이지에 <style>...</style>, <script>...</script> 작성
  4. 필수가 아닌 JS는 </body> 종료 직전에 작성하며 defer, async 속성을 적절히 사용
  5. 필수가 아닌 CSS는 병렬 로딩(preload) 하고 지연 적용(onload) 할 것

좋은 웹페이지 즐겨찾기