웹 개발 스킬을 한 단계 높여 주는 프론트엔드 성능 최적화 가이드

목적

개발 역량 향상과 지식 쌓기

기간

웹 개발 스킬을 한 단계 높여 주는 프론트엔드 성능 최적화 가이드

2023.5.22 - 2023.6.30 10일에 한 챕터씩 읽고, 관련 내용 의견 나누기

최적화 기법

  • 로딩 성능 : 이미지 사이즈 최적화, 코드 분할, 텍스트 압축
  • 렌더링 성능 : 병목 코드 최적화

분석 툴

  • 크롬 개발자 도구 : Network, Performance, Lighthouse 패널들
  • webpack-bundle-analyzer : 번들링된 파일 시각화

웹 바이탈 (Lighthouse 지표)

FCP(First Contentful Paint)

  • 페이지 로드시 브라우저가 DOM 콘텐츠의 첫 번째 부분을 렌더링하는 데 걸리는 시간

SI(Speed Index)

  • 페이지 로드 중 콘텐츠가 시각적으로 표시되는 속도

LCP(Largest Contentful Paint)

  • 페이지 로드시 화면 가장 큰 이미지나 텍스트 요소가 렌더링되기까지 걸리는 시간

TTI(Time to Interactive)

  • 사용자가 페이지와 상호 작용이 가능한 시점까지 걸리는 시간

TBT(Total Blocking Time)

  • 페이지가 클릭, 키보드 입력 등 사용자 입력에 응답하지 않도록 차단된 시간을 총합
  • FCP ~ TTI 사이 시간

CLS(Cumulative Layout Shift)

  • 페이지 로드시 예기치 못한 레이아웃 이동 측정

그외 하단 Opportunities, Diagnostics 섹션

  • 웹 페이지의 문제점과 해결방안, 이점 설명

Performance 패널

  1. CPU 차트, Network차트, 스크린샷
  2. Frames, Timings, Main

npx cra-bundle-analyzer

React에서 lazy와 Suspense를 활용한 Code splitting

import React, { Suspense, lazy } from 'react'
import { Switch, Route } from 'react-router-dom'
import './App.css'
// import ListPage from './pages/ListPage/index'
// import ViewPage from './pages/ViewPage/index'

const ListPage = lazy(() => import('./pages/ListPage/index'))
const ViewPage = lazy(() => import('./pages/ViewPage/index'))

function App() {
  return (
    <div className="App">
      <Suspense fallback={<div>로딩중...</div>}>
        <Switch>
          <Route path="/" component={ListPage} exact />
          <Route path="/view/:id" component={ViewPage} exact />
        </Switch>
      </Suspense>
    </div>
  )
}

export default App
  • 웹에서 텍스트 압축시 Gzip , Deflate 두 가지 방식이 있음.

쟁크(jank)

화면이 끊기는 현상

  • 쟁크의 원인(애니메이션에서)을 알기 위해서는 브라우저에서 애니메이션의 동작원리, 브라우저 렌더링 과정 에 대해 알아야 한다.

브라우저에서 애니메이션의 동작원리

  • 여러가지 이미지를 연속으로 보여주면서 연속된 이미지가 움직이는 효과를 냄
  • 주사율 ex. 60Hz
  • 1초에 60장의 이미지를 보여줌
  • 60FPS(Frames Per Second)
  • 브라우저가 왜 그럼 정상적으로 60FPS로 화면을 그리지 못했을까?
  • CPU가 다른 일을 하느라 바빠서..?

브라우저 렌더링 과정

  • 주요 렌더링 경로 (Critical Rendering Path) 또는 픽셀 파이프라인(Pixel Pipline) 이라고 함

DOM + CSSOM

  • HTML 파일과 CSS 등 화면에 필요한 리소스 다운로드
  • 다운로드 후 브라우저가 이해할 수 있는 형태로 파싱
  • 파싱을 통해 요소 간의 관계를 트리 구조로 만듬 (DOM) (CSSOM)

렌더 트리

  • DOM + CSSOM 결합으로 생성
  • 화면에 표시하는 각 요소의 레이아웃을 계산하는데 사용
  • ex) display:none은 렌더 트리에 포함x / opacity:0 이나 visibility:hidden은 포함o

레이아웃

  • 화면 구성 요소의 위치나 크기를 계산, 해당 위치에 요소를 배치함
  • 말 그대로 화면 레이아웃을 잡는 과정

페인트

  • 화면에 배치된 요소에 색을 채우는 과정
  • 효율적인 페인트 과정을 위해 구성 요소를 여러 개의 레이어(layer)로 나눠서 작업하기도 함

컴포지트

  • 각 레이어를 합성하는 작업

리플로우와 리페인트

  • 리플로우와 리페인트를 발생시키는 속성이 있음

    • 리플로우
    • Js로 특정 요소의 너비와 높이가 변했다고 가정
    • 요소의 스타일이 변했으니 CSSOM이 다시 만들어짐
    • 리플로우의 과정은 따라서
    • DOM+CSSOM > 렌더 트리 > 레이아웃 > 페인트 > 컴포지트
    • 리페인트
    • 글자 색이 변했다고 가정
    • DOM+CSSOM > 렌더 트리 > 레이아웃 > 페인트 > 컴포지트
    • 리페인트역시 모든 단계를 거의 거치기 떄문에 리소스를 꽤 잡아먹는다
  • 리플로우와 리페인트는 브라우저 리소스를 많이 잡아먹는다.
  • 이를 피하는 법은 transform, opacity와 같은 속성을 사용하는 법이다.
  • 이런 속성을 사용해 해당 요소를 별도의 레이어로 분리하고 작업을 GPU에 위임 처리함으로써 레이아웃 단계와 페인트 단계를 건너뛸 수 있다. (하드웨어 가속 / GPU 가속)

지연로딩의 단점

  • 분리된 코드를 다시 다운받아야하기때문에 필요한 동작과 약간의 지연이 발생함
  • 해결방법 (2가지)

    • 버튼클릭과 관련된 동작 → mouse on시 사전 로딩
    • 첫번째 페이지 모든 컴포넌트 마운트 끝 시점에 사전 로딩

이미지 사전로딩

  • 자바스크립트 Image 객체 사용하기
const img = new Image()
img.src = '{이미지 주소}'

// 생략...

useEffect(() => {
  // 이미지 사전로딩
}, [])
  • 이미지 사전로딩은 필요한 만큼만.. 성능문제..

Coverage 패널

  • 어떤 코드가 실행되었는지 비유로 보여줌

Squoosh

  • 구글에서 만든 웹에서 서비스되는 이미지 압축도구

PurgeCSS

  • 사용하지 않는 CSS제거 툴

Intersection Observer

  • 스크롤 이벤트에 많은 로직을 넣으면 메인 스레드에 무리가감
  • 성능 악화로 이어짐
  • lodash throttle과 같은 방식으로 처리할 수 있긴함

    • 짧은 시간 여러번 발생하는 연산을 일정 시간동안 한번만 실행하도록 하는 함수
  • Intersection Observer 은 브라우저 제공 API

// 간단 사용법

const optinos = {
  root: null, // 뷰표트 요소, 기본값 null, 브라우저 뷰포트
  rootMargin: '0px', // root 요소의 여백
  threshold: 1.0, // 가시성 퍼센티지 (1.0은 대상 모두가 보일때)
}

const callback = (entries, observer) => {
  console.log('Entries', entries)
  // 가시성이 변한 요소를 배열로 받음
}s

const observer = new IntersectionObserver(callback, options)

observer.observe(document.querySelector('#target-elemnt1'))
observer.observe(document.querySelector('#target-elemnt2'))

프론트엔드 개발자 이지원입니다.@thinkanddoit
🍎 경험주의자 입니다. 🙋🏻‍♂️ 함께 성장하는 것을 좋아합니다. 📈 꾸준히 성장하기 위한 습관을 들입니다. 🤔 프로세스 고도화에 관심이 있습니다. 🗣 스몰톡을 좋아합니다. ☕️ 커피를 좋아합니다. ⚽️ 축구를 좋아합니다.

GitHubVelogResume