it계열 회사에 재직하면 웹성능 최적화는 쉽게 접할 수 있는 이슈이자 피할 수 없는 숙제이다.
성능 최적화 방법 몇가지를 알고 계신가요?
알고있는 성능 최적화 방법을 모두 설명해주세요.
( ...🤔 전부요..? )
성능 최적화
내지는 웹성능 최적화
를 구글에 검색하면 수많은 이론과 자료가 나온다. 어떤 부분에 초점을 맞추느냐에 따라서 그 방법은 분류 또한 다양해진다. 우선 생각나는대로 빠르게 나열을 해보자면
Webpack
) 사용LCP,FID,CLS
) 최적화이렇게 간단히 나열만해도 숨이 찰 정도이다.
이 포스팅에서는 성능최적화를 로딩 최적화
와 렌더링 최적화
로 구분하여 그 방법을 간략히 작성해본다.
덕질소비의 최종 정착지는 창작이기에..내가 작성하고 내가 소비하는 포스팅. 5분 완성⚡️ 웹성능 최적화 요약 정리 !
브라우저는 상단부터 차례대로 로드된다. HTML 파일을 읽어온 후, 위에서부터 아래로 한 줄씩 해석한다.
이 과정에서 css나 js파일을 만나면 해당 파일을 해석하는 동안 웹 페이지 렌더링은 일시적으로 차단된다.
이때 마치 렉걸린듯한 화면으로 보이는데 이러한 화면을 사용자가 보게되면 페이지를 이탈하거나 화가 나게 되는 것이다.. 사용자의 화를 돋구고싶은게 아니라면 좋은 성능의 웹을 만들기 위해서 웹성능 최적화는 필수로 거쳐야하는 과정이다.
마크업 개발자 입장에서 보는 성능 최적화는 크게 렌더링 최적화
와 로딩 최적화
두 가지로 나눌 수 있다.
성능 최적화 = 렌더링 최적화 + 로딩 최적화
렌더링, 로딩 과정을 종합적으로 최적화 하면 웹 성능을 평가하는핵심적인 웹 지표(LCP,FID,CLS)
수치를 개선할 수 있다.
렌더링 차단 리소스란 브라우저의 렌더링을 막는 소스들로 일반적으로 css
와 js
파일을 말한다. (모든 css와 js가 렌더링 차단 리소스인 것은 아니다.)
웹 페이지 렌더링 최적화의 목표는 리플로우를 최대한 적게 발생시키면서, 빠르게 화면을 그리는 것
반면 레이아웃에 영향을 주지 않는 속성을 변경하면 레이아웃을 건너뛰고 페인트 작업부터 다시 수행하게 되는데 이를 리페인트라고 한다.
리플로우가 일어나면 브라우저가 전체 픽셀을 다시 계산해야 하기때문에 되도록 리페인트 속성을 사용해 스타일을 작성하는 것이 성능면에서 좋다.
리플로우(Reflow)를 발생시키는 속성
position / width / height / margin / padding / display / top / left / right / bottom /
box-sizing / border-color / text-align / border / border-width /
font-family / float / font-size / font-weight / line-height / vertical-align /
white-space / word-wrap / text-overflow / text-shadow ...
리페인트(Repaint)를 발생시키는 속성
color / border-style / visibility / background / background-color /
background-image / background-position / background-repeat / background-size /
text-decoration / outline / outline-style / outline-color / outline-width /
border-radius / box-shadow ...
리플로우와 리페인트를 발생시키지 않는 속성
opacity / transform / cursor / z-index ...
앞서 말했듯 css는 렌더링 차단 리소스이기 때문에 사용하지 않는 css는 제거하는 것이 좋다. Unused css는 구글 크롬 라이트하우스(Lighthouse
)를 통해 확인할 수 있다.
Lighthouse
)는 2KB 이상 사용되지 않은 css가 있을 시 오류로 표기한다.복잡한 셀렉터 사용은 지양한다. css가 복잡하고 방대할수록 레이아웃을 그리는 데에 시간이 많이 소요된다. 선택자를 간결하게 사용하여 특이성을 낮게 유지하는 것이 좋다.
.mypage .mypage_item{...} /* 🔺 */
.mypage_item{...} /* ✅ */
위의 예시에서 .mypage .mypage_item 의 경우 .mypage_item가 부모요소인 .mypage 가지고 있는지 확인하기위해 DOM을 거슬러 올라가는 시간이 소요된다.
<div style="margin-top:20px;"></div> <!-- 🔺 -->
DOM 트리가 깊고, 자식 요소가 많을수록 DOM 트리는 커진다. DOM 트리가 커지면 DOM 변경 시 계산해야하는 것이 많아진다.
css
를 통해 구현하는 것이 성능면에서 이득이다.transform
은 리플로우와 리페인트 모두 발생시키지 않고 합성만 발생시키는 속성이다.absolute
나 fixed
로 설정하면 주변 요소에 영향을 주지 않는다.웹 페이지가 로드되면 html과 css가 동시에 파싱된다. html과 css는 바로 눈에 보이는 시각적 부분을 구현하기때문에 빠르게 그려질수록 좋다. 그래서 css는 head 내에서 임포트
한다.
웹 페이지는 파싱을 실행하면서 script를 만나면 html 파싱을 멈추고 해당 파일을 다운로드한 뒤 실행한다. 따라서 js를 제외한 구조들의 로드가 끝나고 js가 들어오는 것이 좋다. 일반적으로 body 태그를 닫기 직전에 script를 임포트
한다.
media
속성을 사용하면 조건별로 css를 불러올 수 있다.Lighthouse
) media 속성이 없는 < link rel=”stylesheet” >태그를 렌더 블로킹 리소스로 판단한다.media
속성이 없는 스타일시트는 해당 스타일시트를 브라우저가 해석하는 동안 화면에 스타일을 불러오지 않는다.<link href="style.css" rel="stylesheet"> <!-- 🔺 -->
<link href="style.css" rel="stylesheet" media="(min-width:320px) and (max-width:768px)"> <!-- 브라우저의 넓이가 320이상 769미만일때 스타일시트 해석 -->
<link href="style.css" rel="stylesheet" media="print"> <!-- 프린트할때만 스타일시트 해석 -->
async
와 defer
속성은 스크립트 파일을 병렬로 다운로드하게 해준다. 즉, 로딩 시 웹페이지 해석을 멈추지 않고 스크립트를 다운로드 하는것이다.
async는
다운로드 후 즉시 실행한다.defer
는 웹페이지가 모두 그려지고 DOM이 들어왔을 때 스크립트를 실행한다.<!-- 병렬 다운로드 & 즉시실행 -->
<script async src="test.js"></script>
<!-- 병렬 다운로드 & 지연실행 -->
<script defer src="test.js"></script>
이 내용을 그림으로 확인 시 아래와 같다.
■ HTML 파싱
■HTML 파싱 멈춤
■Script 다운로드
■Script 실행
즉,
<script> - 반드시 순서대로 실행되어야 할때
<script async> - 빨리 실행되어야 할때
<script defer> - 마지막에 파싱해도 상관없을때
사용하면 된다.
picture
태그의 type
속성을 통해 사용자 환경에 맞는 이미지를 제공할 수 있다.<!-- 브라우저가 avif를 지원하면 avif를 사용하고,
그렇지 않은 경우 webp,
둘 다 지원하지 않을 경우 jpg 이미지를 사용한다. -->
<picture>
<source srcset="aaa.avif" type="image/avif">
<source srcset="aaa.webp" type="image/webp">
<img src="aaa.jpg" alt>
</picture>
media
속성을 사용해서 브라우저 사이즈에 맞는 이미지를 제공할 수 있다.해상도
에 따라 출력할 이미지를 지정할 수 있다. <picture>
<source srcset="mob.webp" media="(max-width: 760px)"> <!-- 브라우저의 넓이가 760px 이하일때 mob.webp 이미지 출력-->
<img src="pc.webp" alt>
</picture>
loading
속성을 사용해서 이미지를 브라우저 화면에 지연/병렬 로딩할 수 있다.auto
, lazy
, eager
가 있다.auto
: 디폴트 값. loading
속성을 쓰지 않은 것과 같다.lazy
: 화상에 보이는 부분만 먼저 출력하고 화면 바깥쪽 이미지들은 로딩하지 않는다. 사용자가 화면을 위로 올리면 아래쪽에 있던 이미지가 올라오면서 로딩된다.eager
: 화면 위치에 상관없이 페이지가 로딩되자마자 이미지를 로드한다. <img src="item.jpg" loading="lazy" alt>
하나의 이미지
로 만들어서 css의 background-position
속성을 사용해 부분적으로 이미지를 사용하는 방법이다..icon_rotate{...width:15px;height:15px;background-position:-23px -50px;}
.icon_del{...width:10px;height:13px;background-position:-65px -15px;}
...
번들링
해(하나의 파일들로 묶어서) 리소스 요청을 줄일 수 있다<html>
<head>
<link href="main.css" rel="stylesheet">
<link href="sub.css" rel="stylesheet">
<link href="sub2.css" rel="stylesheet">
</head>
<body>
<div id="content">
...
</div>
<script async src="sample1.js" type="text/javascript"></script>
<script async src="sample2.js" type="text/javascript"></script>
</body>
</html>
⬇️ 웹팩 등을 통한 css/js 번들링 후 ⬇️
<html>
<head>
<link href="bundle.css" rel="stylesheet">
</head>
<body>
<div id="content">
...
</div>
<script async src="bundle.js" type="text/javascript"></script>
</body>
</html>
Gzip
을 사용해 텍스트 기반의 리소스로 압축한다.UglifyJS
등을 사용해서 js 파일을 압축한다.CDN(Content Delivery Network)
은 유저에게 많은 콘텐츠를 손실없이 빠르게 전달하는 서비스이다.
🐱 : 이번 포스팅을 통해 머릿속에 중구난방으로 흩어져있던 웹 성능 최적화에 대한 정보들을 정리할 수 있었다. 대략적으로 알고는 있지만 그 양이 방대하고, 분류에 따라 항목들이 겹치기도 해서 헷갈렸던 부분이 있었는데 적으면서 요약해보니 정리가 어느정도 된 느낌이다.
이론과 실전은 다르기때문에 적어둔 방법을 실무에 자연스럽게 적용할 수 있을때까지 익히는 과정이 필요하다.
최적화 고수되고 싶다 !!
마크업 개발자분의 게시글들 너무 재밌네요 항상 많이 배워갑니다 !