실전 웹 성능 최적화 (feat. React) Part1

ddullgi·2022년 11월 28일
2
post-thumbnail

본 포스트는 인프런의 프론트엔드 개발자를 위한, 실전 웹 성능 최적화(feat. React) - Part. 1 강의(링크)를 듣고 정리한 내용입니다.

웹 성능 최적화는 왜 필요할까?

  • 사용자가 떠나지 않도록 하기 위해 (수익 증대를 위해)
    • 웹 성능이 안 좋으면 사용자가 떠나게 됨
  • 프론트엔드 개발자로서, 경쟁력을 갖추기 위해

웹 성능 결정 요소

로딩 성능

  • 각 리소스를 불러오는 성능

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016132128666.png


랜더링 성능

  • 불러온 리소스들을 화면에 보여주는 성능

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016132210351.png


분석 툴

크롬 Network

  • 네트워크 리소스들 상세한 정보들을 알려줌

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016133055940.png


크롬 Performance

  • 웹페이지가 동작할 때 실행되는 모든 작업들을 알려줌

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016133144075.png


크롬 Audit 탭 (Light house)

  • 웹페이지의 서비스 성능 파악 가능

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016133308223.png


webpack-bundle-analyzer

  • webpack을 통해 번들링된 파일들이 무엇을 담고 있는지 한눈에 보여줌

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016133455404.png


이미지 사이즈 최적화

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016141224230.png

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016161304363.png

  • 렌더링 되는 사이즈에 비해 원본 사이즈가 커서 최적화가 필요함(사이즈 차이가 100 배여서 낭비가 심)
  • 레티나 디스플레이의 경우 렌더링 픽셀의 두 배까지 불 수 있기 때문에 렌더링 사이즈의 두 배로 최적화 해야 됨
  • 서버에 직접 저장된 이미지라면 이미지 자체 사이즈를 줄이면 되지만 API를 통해 불러올 경우 image CDN을 사용한다

CDN이란?

  • Contents Delivery Network
  • 물리적 거리의 한계를 극복하기 위해 소비자(사용자)와 가까운 곳에 컨텐츠 서버를 두는 기술
  • Image CDN = image processing CDN
  • 실제로 브런치에서 image CDN을 사용하고 있는 것을 볼 수 있다.
<http://cdn.image.com?src=>[img src]&width=200&height=100
/* 파라미터 참고: <https://unsplash.com/documentation#supported-parameters> */
function getParametersForUnsplash({ width, height, quality, format }) {
  return `?w=${width}&h=${height}&q=${quality}&fm=${format}&fit=crop`;
}

<img src={props.image + getParametersForUnsplash({width: 1200, height: 1200, quality: 80, format: 'jpg'})} alt="thumbnail" />
  • getParametersForUnsplash 을 이용해 이미지 사이즈 조절 가능

자바스크립트 줄이기

  • cra의 경우 빌드시 자동으로 줄여주기 때문에 신경 안써도 된다.

Bottleneck(병목 현상) 해결 방안

  • 특수 문자를 효율적으로 제거하기
    • replace 함수와 정규식 사용
    • 마크 다운의 특수 문자를 지우는 라이브러리 사용(remove-markdown)
  • 작업하는 양 줄이기
    • 우리는 미리 보기에 보이는 양만 제거하면 된다.

특수 문자를 효율적으로 제거하기(정규식 사용)

/*
 * 파라미터로 넘어온 문자열에서 일부 특수문자를 제거하는 함수
 * (Markdown으로 된 문자열의 특수문자를 제거하기 위함)
 */
function removeSpecialCharacter(str) {
  const removeCharacters = ["#", "_", "*", "~", "&", ";", "!", "[", "]", "`", ">", "\\n", "=", "-"];
  let _str = str;
  let i = 0,
    j = 0;

  for (i = 0; i < removeCharacters.length; i++) {
    j = 0;
    while (j < _str.length) {
      if (_str[j] === removeCharacters[i]) {
        _str = _str.substring(0, j).concat(_str.substring(j + 1));
        continue;
      }
      j++;
    }
  }

  return _str;
}

에서

function removeSpecialCharacter(str) {

  let _str = str;
  _str = _str.replace(/[\\#\\_\\*\\~\\&\\;\\!\\[\\]\\`\\>\\/n\\=\\-]/g, "");

  return _str;
}

작업하는 양 줄이기

function removeSpecialCharacter(str) {

  let _str = str.substring(0, 300);
  _str = _str.replace(/[\\\\#\\\\_\\\\*\\\\~\\\\&\\\\;\\\\!\\\\[\\\\]\\\\`\\\\>\\\\/n\\\\=\\\\-]/g, "");

  return _str;
}

Code Splitting(코드 분할)

  • 하나의 번들 파일을 페이지 별로 분할하여 내가 접속한 페이지의 모듈만 불러옴
  • 불필요한 코드 또는 중복되는 코드가 없이 적절한 사이즈의 코드가 적절한 타이밍에 로드될 수 있도록 하는 것
  • https://ko.reactjs.org/docs/code-splitting.html

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016225003484.png

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016235251873.png

  • Code Splitting의 패턴

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221016235409283.png

  • 적용법

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221017014608254.png

  • CRA를 사용하지 않고 직접 번들링 했다면 따로 세팅을 해줘야 한다. 링크

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221017020733341.png


텍스트 압축 적용

  • 웹상에서의 압축

    • GZIP
    • Deflate(LZ77)
  • 관련 설정은 ./node_modules/serve/bin/serve.js에서 설정 되고 있다.

    https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221017021840085.png

node ./node_modules/serve/bin/serve.js —help
  • 텍스트를 압축 푸는 과정에서도 시간이 소요되기 때문에 2kb이상의 파일만 압축하는 것이 좋다.

브라우저의 렌더링 과정

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221017025040763.png

  • 위 모든 과정을 실행하는데 벅차서 브라우저 애니메이션에 쟁크가 발생한다.

Reflow

  • width, height(위치나 크기 변경)
  • 모두 재실행

Repaint

  • color, background-color(색깔) 변경
  • Layout 생략

Reflow, Repaint 피하기 (GPU 도움받기)

  • transform, opacity(GPU가 관여할 수 있는 속성) 변경
  • Layout, Paint 생략

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221017030334072.png


애니메이션 최적화

  • 그래프의 길이 변경을 width 값 변경에서 transform: scaleX() 로 변경
const BarGraph = styled.div`
    width: ${({width}) => width}%;
    transition: width 1.5s ease;
`

// tramsform을 사용하여 최적화
const BarGraph = styled.div`
  transform: scaleX(${({ width }) => width / 100});
  transform-origin: left;
  transition: transform 1.5s ease;
`;

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221017033015249.png

https://raw.githubusercontent.com/ddullgi/image_sever/master/img/image-20221017033132735.png


컴포넌트 Preload 타이밍


버튼 위에 마우스를 올려 놨을 때

function App() {
  const [showModal, setShowModal] = useState(false);
  const handleMouseEnter = () => {
    const Component = import("./components/ImageModal");
  };

  return (
    <div className="App">
      <ButtonModal
        onClick={() => {
          setShowModal(true);
        }}
        onMouseEnter={handleMouseEnter}
      >
        올림픽 사진 보기
      </ButtonModal>
 ...
    </div>
  );
}

만약 로드 해야 하는 파일이 너무 커서 1초 이상 걸릴 경우 다음 방법으로


최초 페이지가 Load가 되고, 모든 컴포넌트의 Mount가 끝났을 때

function App() {
  const [showModal, setShowModal] = useState(false);
useEffect(() => {
    const Component = import("./components/ImageModal");
  }, []);

  return (
    <div className="App">
      <ButtonModal
        onClick={() => {
          setShowModal(true);
        }}
      >
        올림픽 사진 보기
      </ButtonModal>
 ...
    </div>
  );
}

만약 여러 컴포넌트를 import해야 한다면 함수를 만들어서 쓰자

function lazywithPreload(importFunction) {
  const Component = React.lazy(importFunction);
  Component.preload = importFunction;
  return Component;
}

const LazyImageModal = lazywithPreload(() => import("./components/ImageModal"));

function App() {
  const [showModal, setShowModal] = useState(false);
useEffect(() => {
    LazyImageModal.preload();
  }, []);

  return (
    <div className="App">
      <ButtonModal
        onClick={() => {
          setShowModal(true);
        }}
      >
        올림픽 사진 보기
      </ButtonModal>
 ...
    </div>
  );
}

이미지 Preloading

이미지는 원래 이미지가 노출되는 시점이 아니면 로드 X

자바스크립트의 이미지 Object를 사용하면 미리 로드 가능하다

profile
프론트엔드개발자를 꿈꾸는 예비 개발자

0개의 댓글