hmk run dev

프론트엔드 성능최적화 본문

Front-End

프론트엔드 성능최적화

hmk run dev 2024. 2. 26. 22:42

성능 측정 & 개선이 필요한 이유

  • 출처 - akamai

 

 

 


프론트엔드 성능 추상화 이미지

 

 

 

로딩 최적화

  • 브라우저 기준 최적화의 문제점
  • 사용자 기준 최적화
  • 프리 렌더러
  • PWA 사례
  • 이미지 스프라이트

 


브라우저 기준 최적화

 

 

브라우저 기준의 최적화는 보편적으로

Navigation Timing 모델에 따른 것이다.

 

왼쪽의 노란 부분이 리소스 타이밍 부분이며

tcp를 포함한 네트워킹을 하는 부분

사실 프론트가 리소스 타이밍에서 최적화할 부분은 크지않다.

 

신경써야 할 부분은

 

processing & load

 

 

 

Processing

렌더 트리 완성하고 js파싱까지 완료하는 작업

어떤것을 그릴 준비가 완료됨을 의미

 

Load

이미지 등의 리소스를 로드 하는 작업

 

유저는 빈 화면이 보이는 것을 원하지 않음

두 이벤트의 시점을 앞당기고 빠르게 수행할 수 있게 해야함

 


 

간단한 브라우저 렌더링 과정

 

  •  

  1. html을 파싱해 Dom Tree 만듦
  1. css를 파싱해 CSSOM Tree를 만듦(블록 발생)
    • 중간에 js 파일을 만나도 블록 발생함(script 태그를 body 최하단에 사용하길 권함)
  1. 두 트리를 가지고 Render Tree를 만듦
  1. Layout 수행 → 요소의 각위치와 크기를 계산함
  1. Painting 수행 → 화면에 그림

 


Processsing 개선

 

브라우저 기준의 최적화

 

 

 

html 파싱 도중에 css파일(블록 리소스)을 만나서 파싱이 중단 됨

 

 

 

보통 SPA를 사용하는 경우

html 파싱 & 번들링된 파일이 로드 되기 전까진 유저는 스피너도 안보이는 빈화면을 경험할 것임

 

 

html 파싱을 지연 시키는 블록 리소스들을 최적화 해보자

 

 

해결방법

  • javascript 로드시점 최적화

 

 

  • css 최적화

bundle.css 파일 다운로드 시간은 191이지만 총 소요시간은 758ms로 측정됩니다.

중간의 비어있는 시간은 Waiting time이다.

 

Waiting time(Time To First Byte) - 브라우저가 css 다운로드 요청을하고 그걸 첫 바이트를 받아서 처리하는 시간

 

 

 

 

해결방법

  • 인라인 css 지양
  • html에 스타일을 직접 포함

 

 

 


사용자 기준의 최적화

 

브라우저 기준의 최적화를 거친다해도 보여주고자 하는 컨텐츠에 따라서

절대적인 로딩시간은 짧아지지 않는 경우가 많다.

 

그렇다면, 사용자 입장에서 중요한 컨텐츠를 먼저 보여주는 것이 중요하다.

 

 

 

위에서 가장 주목해야할 시점은 First Meaningful Paint 입니다.

사용자에게 가장 의미있을 만한 컨텐츠를 보여주는 시점이라고 할 수 있겠네요

 

First Meaningful Paint시점을 보다 앞당겨야한다.

 

  • SSR(server side rendering)

 

 

 

  • prerender
    • 런타임이 아님 소스 빌드 타임에 html을 생성하게 된다.
    • webpack 사용 시 html webpack 플러그인과 사용가능
    • html 컨텐츠 양이 늘어나 로딩바만 보여주는 것 보다 의미있는 컨텐츠를 미리보여주기 가능

 

 

 

CSR 어플리케이션에 prerender 적용하기

 

plugins: [
new PrerenderSPAPlugin({
	staticDir: path.join(__dirname, 'build'),
		routes: ['/about', '/contact'], // prerendering 대상 URL
		minify: {
			collapseBooleanAttributes: true,
			collapseWhitespace: true,
			decodeEntities: true,
			keepClosingSlash: true,
			sortAttributes: true
		}
	})
]

 

 

PWA (Progressive Web App)

 

  • 기존의 있던 여러가지 기능을 조합해 웹이 앱과 같은 성능을 만들 수 있게 하는 개발 패턴
  • 핵심
    • 블록 리소스를 최적화하고 first meaingful paint를 앞당김
    • 초기 로딩에 불필요한 것들을 뒤로 옮기고 로딩성능을 점진적으로 향상

 

 

 

스프라이트 이미지


스프라이트 이미지(Sprite Image)는
여러 개의 작은 이미지를 하나의 이미지 파일로 합친 것입니다. 이를 사용하는 이유는 HTTP 요청 수를 줄이고, 페이지 로딩 속도를 향상시키기 위해서입니다. 여러 개의 작은 이미지를 하나의 이미지 파일로 합치면, 해당 이미지 파일 하나만을 다운로드하면 되기 때문에 HTTP 요청 수를 줄일 수 있습니다.

 

스프라이트 이미지는 다음과 같은 방법으로 만들 수 있습니다.

  1. 작은 이미지들을 하나의 큰 이미지로 합치기
  1. 합친 이미지에서 CSS를 사용하여 각각의 작은 이미지를 나타내는 영역을 잘라내기

스프라이트 이미지를 사용하는 방법은 다음과 같습니다.

 

  1. CSS의 background-image 속성을 사용하여 스프라이트 이미지 파일을 지정합니다.
  1. 각각의 작은 이미지를 나타내는 영역을 CSS의 background-position 속성을 사용하여 지정합니다.

예를 들어, 다음과 같은 CSS 코드를 사용하여 스프라이트 이미지를 나타내는 요소를 만들 수 있습니다.

 

.sprite {
  width: 50px;
  height: 50px;
  background-image: url("sprite.png");
}

.icon1 {
  background-position: 0 0;
}

.icon2 {
  background-position: -50px 0;
}

.icon3 {
  background-position: -100px 0;
}

렌더링 최적화

  • 레이아웃 스래싱
  • 가상돔
  • 웹워커

 

 

렌더링 스래싱

 

fps가 감소하고 버벅거리는 이유는

강제 동기 레이아웃 때문..!

 

 

 

강제 동기 레이아웃을 유발하는 코드

  • 이 값을 읽는 순간 브라우저는 최신값을 계산해 가져와 리턴해야 하기 때문
function onDraw() {
	let offsetLeft = this.element.offsetLeft;
	let offsetTop = this.element.offsetTop;
	let containerWidth = this.element.parentElement.containerWidth;
}

 

값을 캐싱해 강제 동기 레이아웃을 방지하는 코드

function onDraw() {
	let offsetLeft = this.x;
	let offsetTop = this.y;
	let containerWidth = this.containerWidth;
}

 

강제 동기 레이아웃

실제로 Dom을 변경하지 않았음에도 레이아웃 과정이 실행되는 것

어떤 dom엘리먼트의 프로퍼티는 읽기만해도 레이아웃이 발생


강제 공기 레이아웃이 빈번하게 발생시

레이아웃 스래싱”이 발생한다.

렌더링 성능에 치명적!

 

브라우저 렌더링 과정은 글자하나하나 크기를 계산하는 등의

연산량이 많은 작업!

브라우저에서 가져올 수 있는 값은 캐싱해서 사용하자

 

 


가상돔(Virtual Dom)

 

  • 간단하게 시계를 만드는 코드
  • 시계의 초가 변경 되는데 하위 노드까지 전부 업데이트 되는 현상 발생

 

 

 

실제로 업데이트되는 부분만 바꿔주고싶다.

가상돔을 사용해보자

 

  • 바뀐 부분만 업데이트 된다.
  • dom 변경을 최소화하고 불필요한 렌더링 방지

 


WebWorker

부드럽고 자연스러운 화면을 위해서 60fps가 되어야한다.

이를 js실행 시간으로 환산해보면 코드 실행시간이 10ms 이하여야 한다.

 

 

 

무거운 작업을 webworker로 위임

워커 스레드에서 연산처리


 

Reference

 

https://www.youtube.com/watch?v=G1IWq2blu8c

Comments