개발/Web

[독서] 프론트엔드 최적화 가이드

gomanbo 2024. 1. 14. 23:56
반응형

1장. 블로그 서비스 최적화 

 

지금 보이는 77이라는 점수는 이 웹 페이지의 성능 점수이다.

 

First Contentful Paint(FCP)

페이지를 처음 띄우고 0.6초가 걸렸음을 알 수 있다.

 

Largest Contentful Paint(LCP)

LCP는 페이지가 렌더링되고 큰 이미지나 텍스트 요소가 렌더링되기까지 걸리는 시간을 의미한다.

 

Total Blocking Time(TBT)

페이지가 클릭, 키보드 입력등 사용자의 행동을 방해하는 시간을 의미한다. 

 

Cumulative Layout Shift(CLS)

페이지로드 과정에서 발생하는 예기치 못한 레이아웃 이동을 측정한 지표. 예를 들어 화면상의 요소나 크기가 예상치 못하게 순간적으로 변하는 것을 의미한다.

 

Speed Index(SI)

페이지 로드 중에 콘텐츠가 시작적으로 표시되는 속도를 의미한다. 페이지가 더 빠르게 로드되면 더 높은 점수를 받는다.


이미지 CDN(Content Delivery Network)

물리적 한계를 극복하기 위해 근처에 서버를 뒤는 기술을 의미. 이를 통해 이미지를 다운로드하는 시간을 단축시킬 수 있다.

 

Performace 채널 분석

 

 

코드분할

페이지에서 필요한 코드만 따로 로드하면 더욱 빨라진다. 

코드 분할 기법은 하나의 번들 파일을 여러개로 쪼개는 방법이다. 이렇게 사용되는 지연로딩이란 사용자가 서비스를 이용하는 도중 해당 코드가 필요해지는 시점에 로드되는 것을 의미한다.

리액트에서는 lazysuspense를 통해 코드 분할의 예를 보여줄 수 있다.

const Somcomponent = React.lazy(() => import('./SomeComponet));

<Suspense fallback={<div>Loading...</div}>
	<Somcompnent/>
</Suspense>

Somecomponent가 온전히 로드됐을 때 fallback으로 렌더링된 suspense가 정상적으로 Somecomponent로 렌더링 된다.

 

텍스트 압축

-u, -s 옵션을 보면 s 옵션은 SPA 서비스에 매칭되지 않는 요소는 index.html로 보낸다는 의미이고, u 옵션은 텍스트 압축을 하지 않겠다는 의미이다.

 


2장.  올림픽 통계 서비스 최적화

CSS 애니메이션 최적화

애니메이션을 그렸는데 브라우저가 버벅거릴 때, 화면을 어떻게 그리는지 학습하고 이를 바탕으로 해결책을 적용하자.

 

브라우저 렌더링 과정

Dom + CSSOM > 렌더트리 > 레이아웃 > 페인트 > 컴포지트

 

 

Dom + CSSOM 

HTML 파일과  CSS 파일등 화면에 필요한 리소스드을 다운로드한다. 이 다운로드된 파일들은 브라우저가 이해할 수 있도록 변환하는 파싱 과정을 거친다.

그래서 요소들간의 관계가 트리구조로 되어있는 DOM(Document Object Model)을 만든다.

 

DOM 구조

 

마찬가지로 CSS도 브라우저가 이해할 수 있는 형태로 파싱되는데 이를 CSSOM(CSS Object Model) 이라는 트리구조가 형성된다.

CSSOM 구조

렌더 트리

렌더트리는 DOM과 CSSOM의 결합으로 생성됩니다.

페이지가 로드되는데 필요한 요소들만 포함되는데 display:none 으로 설정시 렌더트리에 포함되지 않지만, opacity: 0, visibility: hidden인 요소는 렌더트리에 포함됩니다. 이는 사용자 눈에는 보이지 않지만 요소자체가 없어지는 것은 아니기 때문입니다.

렌더 트리

레이아웃

화면의 레이아웃을 잡는 과정인데, 화면 구성 요소의 위치나 크기등을 계산하고 해당 위치에 요소를 배치하는 과정을 거칩니다.

페인트

화면에 배치된 요소에 색을 입힌다. 배경색, 글자색, 테두리색 변경 등

브라우저는 효율적으로 페인트하기 위해 그림처럼 여러개의 레이어로 나누어 작업하기도 한다.

 

컴포지트

각 레이어를 합성하는 작업이다. 브라우저는 화면을 그릴 때 레이어를 여러개로 쪼개어 그리는데 이를 합성하는 작업을 의미한다.

 

리플로우 / 리페인트

화면이 다 그려진 후, 애니메이션 요소처럼 일부 요소의 스타일을 변경하거나 추가, 제거한 경우 주요 렌더링 경로에서 거친 과정을 다시 한번 실행하면서 새로운 화면을 그리는데 이를 리플로우, 리페인트라고 한다.

 

화면이 다 그려졌는데 넓이랑 높이가 변경되었다. 변경된 CSSOM을 이용하여 새로운 렌더트리를 만든다. 요소의 가로와 세로가 변경됬으니 레이아웃에서 요소의 크기와 위치가 변경될 것이고, 그 다음 화면에 구성에 맞게 색을 칠하는 페인트과정 , 레어를 하나로 합성하는 컴포지트 과정을 하는 이러한 과정들을 리플로우 라고 한다.

이렇게 리플로우는 브라우저의 모든 렌더링 과정을 모두 거치게 된다. 그렇기 때문에 브라우저 리소스를 많이 사용하게 된다.

만약 글자색이나 배경 색이 변경되었다고 해보자. 스타일 속성이 변경됬으므로 CSSOM이 변경되고 레이아웃단계는 필요없겠죠?

대신 페인트 단계 레이어를 합치는 컴포지트 단계를 거치게 됩니다. 이것을 리페인트 라고 합니다.

 

리페인트 과정은 리플로우 과정과 달리 레이아웃 단계를 거치지 않기 때문에 리플로우보다는 빠르지만 역시 거의 모든 단계를 거치기 때문에 리소스를 꽤 잡아먹는다.

 

이를 피하기 위해 transfrom, opacity 와 같은 속성을 이용하면 되는데 하드웨어 가속(GPU)가 CPU의 일을 대신해줌으로써 레이아웃단계, 페인트 단계를 건너뛸수 있다.

 

하드웨어 과속(GPU)

GPU는 애초에 그래픽 처리를 하기 위해 만들어진 것이므로 화면을 그릴 때 활용하면 굉장히 빠르다.

애니메이션의 막대 그래프를 그렸을 때 width속성을 변경하여 그리면 모든 단계를 보여줄 수 없는 쟁크현상이 발생하게 된다. 따라서 tranform이나 opacity를 통해 성능을 높이는 것이 좋다.

const barGraph = styled.div`
  width: ${({width})) => width}%
`

// transform으로 변경
const barGraph = styled.div`
  transform: scaleX(${({width})) => width / 100 })
`

 

컴포넌트 지연 로딩 (lazy loading)

컴포넌트가 쓰이는 순간 불러오도록 하는 방법이다.

예를들어 화면을 불러오고 버튼을 클릭했을 때 모달 창을 띄워야하는데 처음부터 모달창을 불러와서 그만큼 용량을 잡아먹는 경우에 효율적으로 쓰이기 위해 쓰인다.

import React, { useState, Supspense, lazy } from 'react';

const LazyImageModal = lazy(() => import('./comonents/ImageModal'));


function App() {
  const [showModal, setShowModal] = useState(false);
    
  return (
      <>
       <Suspense fallback={null}>
         {
          showModal ? (
          	<LazyImageModal/>
          )}
       </Suspense>
      </>
    )
}

 

컴포넌트 사전 로딩

컴포넌트를 분할하여 첫 화면 진입시에는 다운로드하지않고 해당 코드가 필요한 시점 전에 코드를 불러와 지연없이 로드될 수 있도록 합니다.

예를 들어 사용자가 버튼을 마우스에 올려놨을 때 미리 모달 컴포넌트를 불러오자.

const handleMouseEnter = () => {
 const component = import('./componetns/ImageModal')
}

<ButtonModal
  onMouseEnter={handleMouseEnter}
>
  올림픽 사진 보기
</ButtonModal>

 

그런데 모달 이미지가 커서 버튼을 마우스에 올려놨을 때보다 더 먼저 올려놔야 할 경우는 최초의 페이지가 모두 로드되고 모든 컴포넌트 마운트가 끝났을 때 불러오면 된다. 리액트에서 함수형 컴포넌트인 경우 useEffect 시점이라 볼 수 있다.

useEffect(() => {
  const component = import('./componetns/ImageModal')
},[]);

 

이미지 사전 로딩

이미지를 필요할 때 불러오면 이미지가 늦게 불러와지므로 이미지가 필요한 시점보다 먼저 다운로드해주고 필요할 때 바로 이미지를 보여주도록 하는 것.

예를 들어 모달을 열기전에 모달을 사전 로드하는 타이밍에 useEffect에 넣어주면 이미지 다운로드 시간이 매우 짧아질 것이다.

useEffect(() => {
  const component = import('./componetns/ImageModal')
  
  const image = new Image();
  img.src = "http:// ~~~~"
  
},[]);

 


3장.  홈페이지 최적화

이미지 지연 로딩

화면에 당장 필요하지 않은 이미지가 로드되지 않도록 지연시키는 기법

 

Intersection Observer 적용하기

Intersection Observer 는 브라우저에서 제공하는 API

예를들어, 동영상을 보여주고 스크롤이 내려갔을 때 이미지를 보여줘야하는 경우인데 이미지가 먼저 다운되고 동영상이 나중에 다운되는 현상이 있음. 따라서 동영상이 먼저 다운되고 사용되지 않는 이미지는 나중에 다운되도록 해보자. 스크롤을 내려 이미지가 로딩되었을 때 이미지를 로딩하면 된다.

 useEffect(() => {
    const option = {};
    const callback = (entries, observer) => {
      console.log("Entreis :", entries);
      if (entries.isIntersecting) {
        entries.forEach((entry) => {
          entries.target.src = entry.target.dataset.src;
          observer.unobserve(entry.target);
        });
      }
    };

    const observer = new IntersectionObserver(callback, option);

    observer.observe(imgRef.current);

    return () => observer.disconnect();
  }, []);

 

 

PurgeCSS

사용하지 않는 CSS를 제거해주는 툴.

 

이미지 사이즈 최적화

이렇게되면 이미지가 보이는 위치에서 이미지를 다운로드하기 때문에 이미지 로드가 느려보일 수 있다. Network 패널을 통해 이미지의 크기를 보면 이미지가 크다는 것을 알 수 있다.

이미지 사이즈 최적화는 가로 세로 사이즈를 줄여 이미지 용량을 줄이고 그 만큼 더 빠르게 다운로드되도록 하는 기법이다.

이미지 포맷

- PNG

PNG는 무손실 압축 방식

- JPG(JPEG)

압축 과정에서 정보 손실이 일어남

- WebP

무손실 압축, 손실 압축 모두를 제공하는 최신 이미지 포맷

 

사이즈 : PNG > JPG > WebP

화질 : PNG = WebP > JPG

호환성 : PNG = JPG > Webp

스쿠시 

이미지 포맷 및 사이즈 변경 도구

https://squoosh.app/

 

동영상 최적화

동영상 압축

Media.io
https://www.media.io/video-converter.html 

폰트 최적화

커스텀 폰트사용시 성능 문제 최적화

 

FOUT(Flash Of Unstyled Text)

폰트 다운로드와 상관없이 폰트를 보여준다. 엣지 브라우저에서 로드하는 방식

FOIT(Flash Of Invisible Text)

폰트가 다운로드 되기전까지 텍스트를 안보여준다. 크롬, 사파리, 파이어폭스에서 폰트 로드하는 방식

캐시 최적화

캐시는 자주사용되는 리소스를 브라우저에 저장해두고 재사용시 새로 다운하지 않고 저장되어있는 것을 사용하는 기술.

캐시 종류

메모리 캐시 - 메모리에 저장한느 방식

디스크 캐시 - 파일 형태로 디스크에 저장하는 방식

 

Cache Control

브라우저가 캐시를 하기 위해서는 Cache Control이라는 헤더가 응답헤더에 있어야한다.

no-cache : 캐시를 사용하기 전 서버에 검사 후 사용

no-store : 캐시 사용 안함

public : 모든 환경에서 캐시 사용 가능

private : 브라우저 환경에서만 캐시 사용 가능, 외부 캐시 서버에서는 사용 불가

max-age : 캐시의 유효 시간

 

불필요한 CSS 제거

PurgeCSS

불필요한 CSS를 제거해주는 툴

 

4장.  이미지 갤러리 최적화

레이아웃 이동 해결

-> 해당 사이즈만큼 공간을 확보해 둔 뒤 이비지를 적용.

aspect-ratio 속성

이미지 지연 로딩

화면에 표시되기 전에는 렌더링 되지 않다가 스크롤 통해 화면에 들어온 순간 로드됨.

yarn add react-lazyload

react-lazyload

offse은 화면에 들어오기 1000px전에 이미지를 로드하도록 해준다.

리덕스 렌더링 최적화

컴포넌트들은 리덕스의 상태를 구독하여 상태가 변했을 때 리렌더링을 합니다.

 

useSelector 문제 해결

1. 객체를 새로 만들지 않고 반환 값 나누기

2. shallowEqual 

참조값이 아닌, 객체 내부에 있는 값을 직접 비교

병목코드 최적화

메모이제이션으로 코드 최적화 하기

메모이제이션이란 실행된 함수의 반환값을 기억해 두고 있다가 똑같은 조건으로 실행되었을 때 함수의 코드를 모두 실행하지 않고 바로 전에 기억해 둔 값을 반환하는 기술. ->결과를 저장해 두고 재활용

반응형

'개발 > Web' 카테고리의 다른 글

[WEB] DNS 작동원리, 호스팅  (0) 2020.04.27
[WEB] 브라우저 동작 원리  (0) 2020.04.22
인터넷은 어떻게 작동하는가?  (0) 2020.04.20