프론트엔드 CS - 웹사이트 성능 최적화

김서영·2024년 8월 26일
0

프론트엔드 CS

목록 보기
11/11

⚙️웹사이트 성능 최적화


웹 성능 최적화란 웹페이지를 더 빠르고 효율적으로 만드는 작업이다.
웹 페이지가 빠르고 효율적으로 로드되는지는 사용자 경험(UX)에 직접적인 영향을 미치기 때문에 아주 중요한 부분이다.

브라우저는 HTML파일을 위에서부터 아래로 해석을 진행하기 때문에 상단부터 차례대로 로드된다.
이 과정에서 CSS나 JS파일을 읽게되면, 그 동안 웹 페이지 렌더링이 읽시적으로 차단되기 때문에 화면이 렉 걸린것 처럼 보이게 된다.
이러한 현상을 차단하기 위해 웹 성능 최적화는 필수이다.

성능 최적화는 렌더링 최적화와 로딩 최적화 2가지로 나눠서 볼 수 있다.

성능 최적화 = 렌더링 최적화 + 로딩 최적화

📌웹 성능 평가 지표

LCP (Largest Contentful Paint)

페이지에서 핵심 요소가 얼마나 빨리 로딩되는지 속도를 측정하는 것이다.
=> 화면을 가장 많이 차지하는 콘텐츠가 로딩되는 시점을 측정
느린 LCP는 사용자 이탈율을 높일 수 있다.
2.5초 이하일 경우 좋은 상태이고, 4초 이상이면 문제가 있는 것이다.

FID (First Input Delay) or TBT (Total Blocking Time)

FID는 사용자 상호작용(터치, 스크롤 등)에 대한 응답성을 측정하는 것이다.
링크를 누르는 등, 사용자가 페이지와 처음 상호작용할 때 얼마나 시간이 걸리는지 측정한다.
TBT는 마우스 클릭, 화면 탭 또는 키보드 누름과 같은 사용자 입력으로부터 페이지가 응답하지 못하도록 차단된 총 시간을 측정한다.
100ms 이하일 때 좋은 상태이고, 300ms 이상이면 나쁜 상태라고 볼 수 있다.

CLS (Cumulative Layout Shift)

페이지의 시각적 안정성을 측정한다.
페이지가 로딩되면서 이미지나 텍스트 위치가 바뀌는 정도를 의미한다.
0.1이하의 CLS라면 괜찮은 상태이고, 0.1 ~ 0.25라면 개선이 필요한 상태, 0.25 이상이면 나쁜 상태라고 말할 수 있다.

📌렌더링 최적화

웹 페이지 렌더링 최적화의 목표는 리플로우를 최대한 적게 발생시키면서, 빠르게 화면을 그리는 것이다.

1. CSS 최적화

리플로우, 리페인트를 고려한 스타일 작성

위의 이미지는 브라우저의 스타일이 그려지는 순서이다.
이 때 레이아웃의 넓이, 높이, 위치 등에 영향을 주는 css 속성을 변경할 경우 Layout부터 다시 그려지게 되는데, 이를 리플로우(또는 레이아웃)이라고 한다.

반면 레이아웃에 영향을 주지 않는 속성을 변경하면 레이아웃을 건너뛰고 페인트 작업부터 다시 수행하게 되는데 이를 리페인트라고 한다.

리플로우가 일어나면 브라우저가 전체 픽셀을 다시 계산해야 하기 때문에, 되도록 리페인트 속성을 사용해 스타일을 작성하는 것이 성능면에서 좋다.

- 리플로우를 발생시키는 속성(레이아웃의 넓이, 높이, 위치 등에 영향을 미치는 속성들)

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 ...

- 리페인트를 발생시키는 속성(레이아웃에 영향을 주지 않는 속성들, 꾸미는 속성들)

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 / translate ...

2. 사용하지 않는 CSS 제거

CSS는 렌더링 차단 리소스이기 때문에 사용하지 않는 CSS는 제거하는 것이 좋다.

3. 간결한 스타일 작성

복잡한 셀렉터 사용은 하지 않는게 좋다.
CSS가 복잡하고 방대할수록 레이아웃을 그리는 데에 시간이 많이 소요된다.
선택자를 간결하게 사용하여 특이성을 낮게 유지하는 것이 좋다.

.mypage .mypage_item{...} /* ❌ */
.mypage_item{...} /* ✅ */

위의 예시에서 .mypage .mypage_item의 경우 .mypage_item이 부모 요소인 .mypage를 가지고 있는지 확인하기 위해 DOM을 거슬러 올라가는 시간이 소요된다.

4. HTML 최적화

인라인 스타일을 사용하지 않는다.
HTML요소에 style을 통해 인라인 스타일을 작성하면 불필요한 코드 중복이 발생하기 쉽다.
인라인 스타일은 웹 페이지가 그려지면서 레이아웃에 영향을 미치며, 추가로 리플로우를 발생시킨다.
애초에 스타일은 스타일 시트에 작성하는 것이 표준에도 맞고, 유지보수 측면에서도 좋다.

5. 복잡한 DOM 트리 지양

DOM 트리가 깊고, 자식 요소가 많을수록 DOM 트리가 커진다.
DOM 트리가 커지면 DOM 변경 시 계산해야 하는 것이 많아진다.
=> DOM을 작고 얕게, 불필요하게 감싸는 요소는 제거

6. 애니메이션 최적화

애니메이션을 구현할 때에는 자바스크립트 api나 라이브러리보다 css를 사용해 구현하는 것이 성능면에서 좋다.

7. JS 최적화

레이아웃 단계가 완료되기 전에 요소의 위치나 크기를 변경 후 바로 가져오려고 하면 강제로 레이아웃이 발생하는데 이를 강제 동기 레이아웃이라고 한다.
그리고 이 레이아웃을 반복적으로 발생시키는 것이 레이아웃 스레싱이다.
이는 자바스크립트 실행 시간을 늘어나게 하므로 주의해야 한다.
아래 포스트를 통해 더 자세히 알아볼 수 있다!

JS 최적화를 위한 방법

📌로딩 최적화

1. CSS는 head내에, JS는 body의 하단에 위치시킨다.

웹 페이지가 로드되면 html과 css가 동시에 파싱된다.
html과 css는 바로 눈에 보이는 시각적 부분을 담당하기 때문에 빠르게 그려질수록 좋다. 때문에 css는 head내에서 임포트하는게 좋다!
웹 페이지는 파싱을 진행하면서 script를 만나면 html 파싱을 멈추고 해당 파일을 다운로드한 뒤 실행한다. 때문에 js를 제외한 구조들의 로드가 끝난 후 js가 들어오는 것이 좋다! 그래서 body 태그를 닫기 직전에 script를 임포트하는 것이 좋다.

2. media 속성을 사용한다.

media 속성을 사용하면 조건별로 css를 불러올 수 있다.
반응형 웹 제작 시 유용하게 사용할 수 있다.
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"> <!-- 프린트할때만 스타일시트 해석 -->

3. Async / defer

async와 defer 속성은 스크립트 파일을 병렬로 다운로드하게 해준다.
즉, 로딩 시 웹 페이지 해석을 멈추지 않고 스크립트를 다운로드하는 것이다.

  • async는 다운로드 후 즉시 실행
  • defer는 웹 페이지가 모두 그려지고 DOM이 들어왔을 때 스크립트를 실행
    // 병렬 다운로드 & 즉시실행
    <script async src="test.js"></script>

// 병렬 다운로드 & 지연실행


💚 HTML 파싱
🩶 HTML 파싱 멈춤
💙 Script 다운로드
❤️ Script 실행

  1. script : 반드시 순서대로 실행되어야 할 때(HTML 파싱이 되는 도중, 스크립트가 다운로드 되고 실행되는 과정에서 파싱이 멈춤)
  2. script async : 빨리 실행되어야 할 때(HTML 파싱이 되는 도중, 스크립트를 다운로드 하는 과정에서 파싱이 멈추지는 않지만 실행되는 과정에서 파싱이 멈춤)
  3. script defer : 마지막에 파싱해도 상관없을 때(HTML 파싱이 되는 과정과 동시에 스크립트 다운로드를 병렬적으로 수행하고, HTML 파싱이 끝나면 그 때 스크립트 실행)

자세한 내용을 아래 자료를 확인하면 좋다!

async vs defer 관련 자료

4. 이미지 최적화

- Picture 태그 사용하기

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 속성 사용하기

media 속성을 사용해서 브라우저 사이즈에 맞는 이미지를 제공할 수도 있다.
media 조건에 따라 출력할 이미지를 지정할 수 있다.

<picture>
	<source srcset="mob.webp" media="(max-width: 760px)"> <!-- 브라우저의 넓이가 760px이하일 때, mob.webp 이미지 출력-->
    <img src="pc.webp" alt>
</picture>

- 이미지 지연 로딩 활용하기

loading 속성을 사용해 이미지를 브라우저 화면에 지연/병렬로 로딩할 수 있다.
사용 가능한 값으로 auto, lazy, eager가 있다.

  • auto : default 값(작성하지 않아도 됨)
  • lazy : 화상에 보이는 부분만 먼저 출력하고 화면 바깥쪽 이미지들은 로딩하지 않는다. 사용자가 화면을 위로 올리면 아래쪽에 있던 이미지가 올라오면서 로딩된다.
  • eager : 화면 위치에 상관없이 페이지가 로딩되자마자 이미지를 로드한다.

이미지 지연 로딩 관련 자료

- 스프라이트 이미지 사용

여러 개 이미지를 하나의 이미지로 만들어 CSS의 background-position 속성을 사용해 부분적으로 이미지를 사용하는 방법이다.
이미지 파일 개수 자체를 줄이기 때문에 리소스 요청 개수를 줄일 수 있다.

.icon_rotate{...width:15px;height:15px;background-position:-23px -50px;}
.icon_del{...width:10px;height:13px;background-position:-65px -15px;}
...

위와 같은 형식으로, css 스타일 시트에서 background-image로 지정하고 클래스 또는 아이디 값을 지정해 해당 이미지에서 background-position을 통해 하나의 이미 파일에서 이미지를 잘라오는 형식!!

스프라이트 이미지 관련 자료

5. 웹팩(Webpack) 사용

모듈 번들러인 웹팩을 사용해 css와 js 파일을 번들링해 리소스 요청을 줄일 수 있다.

<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>

이런식으로 bundle이라는 파일 하나씩으로 묶어 사용 가능!

6. Gzip 사용

Gzip을 사용해 텍스트 기반의 리소스로 압축한다.
이미지, pdf 등은 이미 압축된 파일일 경우가 많기 때문에 Gzip을 사용하지 않는다.

7. JS 압축

UglifyJs 등을 사용해 js 파일을 압축한다.
불필요한 공백이나 줄바꿈을 제거해서 파일의 용량이 감소하고, 난독화를 하면 민감한 코드를 알아보기 어렵게 만들 수 있다.

8. CDN(Content Delivery Network) 사용

CDN은 유저에게 많은 콘텐츠를 손실없이 빠르게 전달하는 서비스이다.
대용량 콘텐츠 다운 또는 스트리밍 등에 사용한다.
사용한 만큼 비용을 지불해야 한다.

9. 캐싱

캐쉬란 사용자가 요청하는 html, css, js, image 등을 첫 요청 시에 내려받은 뒤 특정 위치에 복사본을 저장해놓고, 이후 동일한 리소스 요청이 왔을 때 이전에 저장해둔 파일을 사용해 더 빠르게 로딩하는 데에 사용된다.
브라우저가 다운로드 할 파일의 개수 자체를 줄이기 때문에 시간적 측면에서 좋다.

캐쉬 관련 자료

마무리하며..

웹사이트 최적화에 대해 자세히 조사하며, 알았던 것들도 있지만 모르는 부분이 굉장히 많았다.
지금까지 프로젝트를 진행해오며 최적화 하지 못한 부분이 정말 많구나 라는 사실을 깨닿게 되며... 다음 프로젝트를 진행할 때에는 최적화를 꼭 해봐야겠다는 생각이 들었다!

참조한 사이트

웹 성능 최적화: 성공적인 웹 경험을 위한 필수 전략
웹 성능 최적화 방법 5분 완성

profile
개발과 지식의 성장을 즐기는 개발자

0개의 댓글