페이지 성능 개선 후기 ( 2 )

issol·2022년 12월 2일
0

Lighthouse

목록 보기
1/4
post-thumbnail

지난번에 이어서 성능개선 후기

  • 외부 스크립트의 상호작용 시점 변경

    • next의 경우 next/script 사용, afterInteractive 옵션 추가

      <Script
        id="GTM"
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
        new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
        j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
        'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
        })(window,document,'script','dataLayer','${GTMID}');`,
        }}
      />
      beforeInteractive : 페이지가 상호작용되기 전에 로드 됩니다.
      afterInteractive : 페이지가 상호작용이 가능해진 직후에 즉시 로드됩니다.
      lazyOnload: idle time 동안에 로드 됩니다.
      
    • react의 경우 defer와 async 옵션 사용

      <script defer type="text/javascript"></script>
  • 외부 리소스 미리 연결 혹은 로드
    <link
    as="font"
    rel="preload"
    href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
    />
    <link
    as="font"
    rel="preload"
    href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.5/dist/web/static/pretendard.css"
    />
    여기서 &display=swap 쿼리를 사용하면 웹폰트 로드 중 기본 폰트 노출 할 수 있습니다.
  • 번들 트리 쉐이킹

    • lodash의 경우 Full import 하면 69.6K 이지만 실제 사용하는 함수만 import하면 4배 가량 줄어드는 것을 확인 할 수 있습니다.
    • curly braket으로 import해도 사이즈는 동일합니다.
    • moment도 굉장히 큽니다. 요것도 대체해야 하는데요..
      • moment가 더이상의 신규 개발 없이 유지보수만 하는 상태 로 전환되었습니다.
      • Day.js가 대체 대상으로 고려중입니다.
  • 코드 스플리팅

    • 프로젝트를 완성한 후 서비스를 사용자에게 전달하고 보여주려면 빌드를 통해서 배포를 진행하는데요. 빌드 과정에서 나온 결과물의 크기를 가능한 작게 만드는게 좋습니다. 이 파일 크기가 성능을 결정하고 결과적으로는 사용자의 경험에까지 영향을 미치기 때문입니다. 또한 브라우저에서 JSX나 최신 자바스크립트 문법 등이 문제없이 잘 실행될 수 있도록 트랜스파일링 하는 작업도 해 주어야 합니다. 보통 이런 작업은 Webpack이 잘 도와주는데, 따로 설정하지 않으면 모든 Js가 합쳐지고, css 파일 역시 합쳐지게 됩니다. 하나의 파일로 합치게 되면 파일 크기가 커지고, 우리가 한 줄만 수정해도 다시 모든 코드를 빌드하는 비효율성을 가지게 됩니다. 그렇기 때문에 파일들을 잘 분리하고 현재 필요한 것만 로드될 수 있게 하면 아주 베스트겠죠..? 보통 SPA는 라우팅을 기준으로 스플리팅을 하게 됩니다.
    • next의 경우 코드 스플리팅이 지원(라우팅) 되지만 컴포넌트 코드를 더 분할하여 동적으로 로드하기 위해 next/dynamic와 suspense를 사용했습니다.
      import dynamic from 'next/dynamic'
       const MainGnbDynamic = dynamic(() => import('./layouts/main-gnb'), { suspense: true })
      
    • react의 경우 lazy와 suspense를 사용했습니다.
      import {lazy, Suspense} from 'react'
       const Request = lazy(() => import('./pages/request/request'))
  • Cloud Front를 통하는 미디어 리소스들에 대한 Cache-control 설정

    • CloudFront에서 객체를 캐싱하는 시간 지정을 해주었습니다. 요건 정해진 것은 없고 시간을 넉넉히 잡아두면 좋다고 Lighthouse에서 추천합니다.
    • ex) Cache-Control: max-age=3600
    • Expires 헤더를 사용하면 디테일한 시간도 설정 가능
  • 사용하지 않는 CSS 제거

    • purgecss를 사용해서 사용하지 않는 css 제거(https://www.npmjs.com/package/purgecss)
    • "scripts": {
          "start": "react-scripts start",
          "build": "react-scripts build",
          "test": "react-scripts test",
          "eject": "react-scripts eject",
          "postbuild": "purgecss --css build/static/css/*.css --content build/index.html build/static/js/*.js --output build/static/css"
        }
    • 정확한 수치는 기억나지 않지만 처음 coverage에 잡혔던 크기보다 3배 이상 줄어든 것을 확인했습니다.
    • 실제로 코드를 삭제하는 것이 아니라 빌드 전에 불필요한 css는 빌드 과정에 포함시키지 않는 방식입니다.
profile
프론트 엔드 개발자

0개의 댓글