웹 성능 최적화 회고

장택진·2024년 8월 6일
0
post-thumbnail

Web Vitals 란?

웹 바이탈은 웹에서 우수한 사용자 환경을 제공하는 데 필수적인 웹페이지 품질 신호에 관한 통합 가이드를 제공하기 위한 Google 이니셔티브입니다.

이 지표는 사용 가능한 다양한 성능 측정 도구를 단순화하고 사이트 소유자가 가장 중요한 측정 항목인 코어 웹 바이탈에 집중할 수 있도록 돕는 것을 목표로 합니다.

참고 : https://web.dev/vitals/

  • BEFORE

  • AFTER

성능 지표 중 주요 항목으로는 6가지가 있습니다.

  • FCP (First Contentful Paint) : 첫번째 텍스트 또는 이미지가 표시되는 시간
  • TTI (Time to Interactive) : 사용자 인터렉션이 가능해질 때까지 걸리는 시간
  • SI (Speed Index) : 웹 페이지 로드 중에 콘텐츠가 시각적으로 표시되는 속도에 대한 지표
  • TBT (Total Blocking Time) : FCP ~ TTI 사이에서 메인 스레드를 블로킹하는 작업 시간
  • LCP (Largest Contentful Paint) : 가장 큰 텍스트 또는 이미지가 표시되는 시간
  • CLS (Comulative Layout Shift) : 누적 레이아웃 이동. 뷰포트 안에 보이는 요소의 이동을 측정. 사용자가 예상치 못한 레이아웃 이동을 경험하는 빈도를 수량화해서 보여준다.

전체적인 LightHouse 측정 값 비교 (BEFORE → AFTER)

  • FCP : 1.2sec -> 0.3sec
  • TBT : 400ms -> 140ms
  • SI : 1.8sec -> 1.3sec
  • CLS : 0.007 -> 0
  • Main Bundle : 4.9Mb -> 1.3Mb
  • 사용하지 않는 자바스크립트 줄이기 786Kb 절감

이미지 최적화


이미지 최적화는 개발비용 대비 성능 효율이 가장 좋습니다.

리소스 중 용량이 큰 편에 속하기 때문에 로드 속도가 오래 걸리고, 컨텐츠 중 가장 큰 영역을 차지하는 경우 LCP 성능에 큰 영향 을 주기 때문입니다.

이미지 최적화 방법으론 lazyLoading, 스프라이트 이미지, 이미지 압축 포맷 설정, 이미지 태그 설정 등이 있습니다.

💡 현재 미니언즈 페이지는 이미지가 많지 않기 때문에 이미지 태그 설정만 수정했습니다.

이미지 태그 설정

  • CSS background-image → img 태그로 변경
    • img 태그를 사용하면 브라우저 프리로드 스캐너에 의해 빠르게 요청된다.
    • 프리로드 스캐너는 마크업을 스캔하고 CSS는 스캔하지 않음.

프리로드 스캐너란?

  • 모든 브라우저는 파일을 토큰화 하고 객체 모델로 처리하는 HTML 파서를 기본적으로 가지고 있습니다.
    작업은 블로킹 리소스 (ex.async, defer, type="module" 같은 옵션이 없는 script 태
    그)
    를 만나기 전 까지 계속 됩니다.
  • css파일 같은 경우는 스타일링이 적용되지 않은 콘텐츠가 잠깐 뜨는 현상을 방지하기 위해 파싱과 렌더링이 차단됩니다. 그리고 script 태그를 만나게 되어도 렌더링 작업이 중단됩니다.
  • 하지만 이러한 작업은 다른 중요한 리소서를 찾는 과정을 지연시킴으로써 퍼포먼스를 저하 시킬 수 있습니다. preload scanner를 사용해서 필요한 요청을 병렬적으로 처리합니다.
    예를들면 HTML 문서에 img 또는 link 와 같은 태그가 있으면 프리로드 스캐너는 HTML 파서가 생성한 토큰을 확인하고 브라우저 프로세스의 네트워크 스레드에 요청을 보냅니다.

웹 폰트 최적화


웹 폰트는 사용자 로컬 기기에 저장되어 있는 폰트 파일을 사용하는 것이 아닌, 온라인상에서 폰트 파일을 다운로드해서 사용하게 됩니다.

즉, 웹 폰트를 사용하는 페이지에 접근할 때 웹 서버 이외에 또 다른 서버로 웹 폰트를 요청하는 것입니다.

출처 : https://web.dev/articles/optimize-webfont-loading?hl=ko

  1. 브라우저가 HTML을 요청하고, 응답받은 HTML로 DOM 구성

  2. 브라우저가 필요한 CSS를 확인하여 요청하고, 응답받은 CSS로 CSSOM 구성

  3. 브라우저는 렌더링에 필요한 폰트 요청

  4. 브라우저는 폰트 요청의 응답을 기다리지 않고 렌더링 진행

    (폰트가 준비되지 않았다면 대체 폰트 렌더링하거나 렌더링하지 않음)

  5. 브라우저는 폰트가 준비되면 비어있는 텍스트를 채우거나 대체 폰트를 기존 폰트로 다시 렌더링

웹 폰트 최적화가 필요한 이유

  • 웹 폰트를 사용할 때 FOIT와 FOUT 현상이 발생할 수 있는데, 이 현상들은 UX를 저하시키고, 개발자가 의도 하지 않은 동작입니다. 이를 최소화하기 위해 최적화가 필요합니다.
    • FOIT (Flash Of Invisible Text)
      • FOIT는 웹 페이지가 렌더링 되었을 때, 필요한 폰트가 아직 준비되지 않아 사용자에게 일시적으로 글자가 보이지 않는 현상을 말합니다.
    • FOUT(Flash Of Unstyled Text)
      • 필요한 폰트가 준비되지 않아 글자 자체가 보이지 않는 FOIT과 달리, FOUT는 글자가 보이지만 기본 시스템 폰트로 표시되는 현상을 말합니다.
      • 아예 글자가 보이지 않는 FOIT의 문제점을 해결하고자, 글자라도 보이게 하는 대안적인 현상입니다.

웹 폰트 최적화 방법

폰트 파일의 형식은 형식마다 용량이 다르고, 브라우저 호환 유/무도 다릅니다.

  • 아래 표는 미니언즈에서 사용하고 있는 웹 폰트 NotoSansKR 폰트의 확장자 별 용량입니다.
NameTagSize
NotoSansKR-Black.woff2woff21,032KB
NotoSansKR-Black.woffwoff1,360KB
NotoSansKR-Black.otfotf2,528KB

WOFF 형식과 WOFF2형식은 압축된 폰트 형식입니다. 따라서 용량이 현저히 작은 것을 알 수 있습니다.

  • 웹폰트 형식 별 브라우저 호환 표

출처 : https://www.w3schools.com/Css/css3_fonts.asp

WOFFWOFF2는 모든 브라우저에서 사용할 수 할 수 있지만, 브라우저의 레거시 버전까지 대응하진 못합니다. 브라우저의 버전에 따라 대응하고 싶은 버전까지 대응하면 됩니다.

현재 관리하는 페이지는 브라우저의 하위 버전을 모두 호완할 수 있게 otf 확장자까지 대비하고 있습니다.

  • 최적화 font-face 예시
@font-face {
  font-family: "Noto Sans KR";
  src: local("Noto Sans KR"), url("./NotoSansKR-Black.woff2") format("woff2"),
    url("./NotoSansKR-Black.woff") format("woff"), url("./NotoSansKR-Black.otf") format("opentype");
}
  • 폰트 다운로드 순서 - woff2 > woff > otf
    • 사용자의 local 기기에 폰트가 설치되어 있다면 리소스를 요청하지 않습니다.
    • 브라우저와 호환되는 리소스를 다운받을 시 다음 순서의 리소스는 다운 받지 않습니다.
    • font-style, font-weight 최적화
@font-face {
  font-family: "Noto Sans KR";
  font-style: normal;
  font-weight: 500;
  src: local("Noto Sans KR"), url("./NotoSansKR-Medium.woff2") format("woff2"),
    url("./NotoSansKR-Medium.woff") format("woff"),
    url("./NotoSansKR-Medium.otf") format("opentype");
}

@font-face {
  font-family: "Noto Sans KR";
  font-style: normal;
  font-weight: 700;
  src: local("Noto Sans KR"), url("./NotoSansKR-Bold.woff2") format("woff2"),
    url("./NotoSansKR-Bold.woff") format("woff"), url("./NotoSansKR-Bold.otf") format("opentype");
}

프로젝트에서 사용하는 굵기와 스타일의 폰트만 다운받아 용량을 최적화합니다.

코드 스플리팅 (lazy+suspense)

💡 웹 애플리케이션의 JavaScript 코드를 여러 부분으로 나누는 기술입니다. 이를 통해 사용자가 웹 페이지를 방문할 때 한 번에 모든 JavaScript 코드를 다운로드하는 대신 필요한 코드 조각만 로드할 수 있습니다. 이로 인해 초기 로딩 시간이 단축되고 사용자 경험이 향상됩니다.

React.lazy

컴포넌트를 렌더링할 때 비동기적으로 로딩하게 해주는 함수

Suspense

사용자의 네트워크 상태가 안좋거나 파일이 큰 경우, 오랫동안 빈화면 혹은 워터폴 현상으로 인해 사용자의 경험을 해칠 수 있습니다. 이러한 것을 방지하기 위해 Suspense를 사용합니다.

SuspenseLoading UI를 선언적으로 작성할 수 있게 해주는 컴포넌트로, 하위에 선언된 컴포넌트들의 비동기적 활동이 완료되어 렌더링할 수 있는 상태가 될 때까지 렌더링을 멈추고 fallback props로 넘겨진 컴포넌트를 대신하여 보여줍니다.

메인 번들 크기 비교

profile
필요한 것은 노력과 선택과 치킨

0개의 댓글