Post

[JavaScript] 프론트엔드 성능 최적화

[JavaScript] 프론트엔드 성능 최적화

브라우저 동작 원리

브라우저 로딩 과정: 파싱 > 스타일 > 레이아웃(리플로우) > 페인트 > 합성 & 렌더

프론트엔드의 성능 최적화를 확인하기에 앞서 브라우저가 어떻게 화면을 사용자에게 보여주는지를 알아야 한다.

프론트엔드 성능 최적화

프론트 엔드 성능 최적화에는 웹 페이지 로드 최적화, 웹 페이지 렌더링 최적화가 있다.

1. 웹 페이지 로드 최적화

(1) 브라우저 상에서 최적화

  • DOMContentLoaded: html, css 파싱이 끝난 시점
  • Loaded: html 상에 모든 리소스가 load 된 시점

alt text https://velog.io/@kmlee95/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94

html, css 파싱을 진행하다가 script 태그와 만나게 되면 javascript 파싱을 진행하게 되고, 그 이후로 동기적으로 파싱이 된다.
그래서 html, css를 파싱하면서 자바스크립트로 인해 block resource가 발생하는걸 방지해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- block resource 방지 -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>My Test Page</title>
    <!-- css 위치 -->
  </head>
  <body>
    <div></div>
    
    <!-- js 위치 -->
    <script>
      // ...
    </script>
  </body>
</html>

(2) 리소스 용량 최적화

리소스 용량을 줄임으로써 리소스 다운로드 시간을 최적화 할 수 있다.

alt text 크롬 개발자 도구의 network 탭 -> 필터에서 리소스 유형 (https://velog.io/@kmlee95/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94)

  • 불필요한 코드는 제거한다.
  • 압축 및 난독화로 용량을 최소화 한다.
  • 간결한 셀렉터를 사용한다.
  • 모듈번들러(webpack) css, js 번들링 한다.
  • 캐싱할 필요 없는 style은 내부 스타일 시트를 사용한다.

2. 웹 페이지 렌더링 최적화

페이지 렌더링 최적화의 목표는 레이아웃을 최대한 빠르게, 최대한 적게 발생시키는 것

웹 페이지를 렌더링 하기 위해서는 DOM, CSS가 필요하다. 그러나 다양한 기능과 효과를 구현하기 위해서는 자바스크립트를 많이 사용한다. 자바스크립트는 브라우저에서 단일 스레드로 동작하기 때문에 자바스크립트의 실행 시간은 곧 렌더링 성능과 직결된다. 결국엔 자바스크립트에서 실행되는 코드가 최적화 될 수 있게 구성해야 한다.

3. 레이아웃 최적화

렌더링 과정에서 레이아웃은 DOM 요소들이 화면에 어느 위치에서 어떤 크기로 배치될지를 결정하게 되는 계산 과정이다. 레이아웃은 글자의 크기를 일일히 계산하고 요소 간 관계를 모두 파악해야 하는 과정이므로 시간이 오래 걸린다.

alt text https://velog.io/@kmlee95/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94

레이아웃 최적화의 목표는 자바스크립트 실행 과정과 렌더링이 다시 일어나는 과정에서 레이아웃에 걸리는 시간을 단축하고 최대한 발생시키지 않도록 하는 것이다. 아래 세 가지를 확인한다.

(1) 자바스크립트 실행 최적화

자바스크립트 실행 시간이 긴 경우, 한 프레임 처리가 오래걸려 렌더링 성능이 떨어진다. 많은 작업을 수행할 때 자바스크립트 실행 시간은 오래걸린다. 그래서 자바스크립트가 DOM 및 스타일 변경에 어떤 영향을 끼치는지 확인하고, 수정해야 한다.

문제1: 강제 동기 레이아웃 피하기
1
2
3
4
5
6
7
8
// 계산된 값을 반환하기 전에 변경된 스타일이 계산 결과에 적용되어 있지 않으면 변경 이전 값을 반환하기 때문에 브라우저는 동기로 레이아웃을 해야만 한다.

const tabBtn = document.getElementById("tab_btn");

tabBtn.style.fontSize = "24px";
console.log(testBlock.offsetTop);  // offsetTop 호출 직전 브라우저 내부에서는 동기 레이아웃이 발생한다.
tabBtn.style.margin = "10px";
// 레이아웃
문제2: 레이아웃 스래싱 피하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 한 프레임 내에서 강제 동기 레이아웃이 연속적으로 발생하면 성능이 더욱 저하된다.
function resizeAllParagraphs() {
  const box = document.getElementById("box");
  const paragraphs = document.querySelectorAll(".paragraphs");
  
  for (let i=0; i<paragraphs.length; i++) {
    paragraphs[i].style.width = box.offsetWidth + "px";
  }
}

// 레이아웃 스래싱을 개선한 코드
function resizeAllParagraphs() {
  const box = document.getElementById("box");
  const paragraphs = document.querySelectorAll(".paragraphs");
  const width = box.offsetWidth;
  
  for (let i=0; i<paragraphs.length; i++) {
    paragraphs[i].style.width = width + "px";
  }
}

(2) HTML, CSS 최적화

화면을 렌더링하기 위해서 필요한 데이터는 HTLM과 CSS로, 각각 DOM 트리와 CSSOM 트리를 만들고 렌더링할 때 사용된다. DOM 트리와 CSSOM 트리를 변경하면 렌더링을 유발하고 트리가 클 수록 더 많은 계산이 필요하다. 그러므로 HTML과 CSS를 최적화하여 렌더링 성능을 향상시킬 수 있다.

  • css가 복잡하고 많을수록 스타일 계산과 레이아웃이 오래 걸린다. 복잡한 선택자는 시간이 많이 걸리므로 피한다.
  • dom이 작고 깊이가 얕을수록 계산이 빠르므로 불필요한 래퍼 엘리먼트는 제거한다.

(3) 애니메이션 최적화

한 프레임 처리가 16ms(60fps) 내로 완료되어야 렌더링 시 끊기는 현상 없이 자연스러운 렌더링을 만들어낼 수 있다. 애니메이션을 구현할 때 네이티브 자바스크립트 API를 사용하는 것보다 CSS 사용을 권장한다.



참고
This post is licensed under CC BY 4.0 by the author.