프론트엔드 성능 개선

Nine·2022년 9월 15일
0

React

목록 보기
21/22

🤔왜 성능과 속도가 중요할까요?

사용자에게 있어서 모바일 속도 지연으로 인한 스트레스 반응은 거의 공포영화를 보거나 수학 문제를 푸는 정도의 스트레스라고 합니다😱

여러분들도 웹 페이지를 접속할때 너무 느리다면 그 서비스를 사용하지 않을거예요.

즉, 속도 개선은 사용자 이탈을 방지하고 가입율을 증가시킵니다. 이는 곧 서비스의 수익과 직결되는 문제죠.

참고자료

https://web.dev/fast/


성능 개선

이 포스팅은 React에 기반한 성능 개선입니다.

코드 사이즈 줄이기

🔨HTML

✨설치

npm install --save-dev html-webpack-plugin

✨HTMLWebpackPlugin

  • minify를 통해서 빈칸과 주석을 압축합시다!
// webpack.config.js에서
plugins: [
    new HtmlWebpackPlugin({
      minify: {
        collapseWhitespace: true, // 빈칸 제거
        removeComments: true // 주석 제거
      },
      template: './index.html',
      hash: true // 캐싱 방지: 매 빌드마다 다른 queryString과 함께 bundle.js를 불러와서 파일이 동일하지 않음을 알려줍니다.
    }),
    new CopyWebpackPlugin({
      patterns: [{ from: './public', to: './public' }]
    }),
    new Dotenv()
  ],

🔨CSS

✨설치

npm install --save-dev mini-css-extract-plugin 
npm install --save-dev optimize-css-assets-webpack-plugin

✨mini-css-extract-plugin

  • 브라우저가 css파일과 js파일을 병렬적으로 동시에 다운로드 받도록 해줍니다.
  • js에서 스타일 코드들을 css파일로 분리해주기 때문이죠.
module: {
     rules:[
        {
        test: /\.css$/i,
        use: [MiniCSSExtractionPlugin.loader, 'css-loader']
      	},
    	...
      ]
       ...
    }
  plugins: [
    new MiniCSSExtractionPlugin({
      filename: '[name].[contenthash].css' // contenthash를 통해 네이밍을 짓는다면 캐싱할때 유리해요. (파일이 변경되면 네이밍이 바뀜)
    })
  ],
  • style-loader를 사용해도, production에서는 MiniCSSExtractionPlugin을 사용하는 것이 좋아요.

🤔 style-loader vs MiniCSSExtractionPlugin

style-loader는 빌드 시 인라인으로 스타일을 주입해줍니다.

반면에 MiniCSSExtractionPlugin은 JS파일에서 호출되는 style 코드들을 CSS 파일을 따로 분리하여 만들어주죠.

개발할 때에는 CSS 파일 수정이 많아서 DOM에 <style>로 직접 주입하는 것이 빠르므로 style-loader를 사용하면 좋아요.

프로덕션 빌드에는 병렬 로딩을 통해서 분리도니 CSS 코드들을 더 빠르게 가져올 수 있기 때문에 MiniCSSExtractionPlugin를 사용하는 것이 좋아요.

✨optimize-css-assets-webpack-plugin

  • HTML의 빈칸을 압축한 것처럼, css 파일도 빈칸을 없애는 압축을 할 수 있어요.

  • 이를 이용한다면 css 코드를 하나로 묶은 파일이 생성되고, 빌드된 HTML 파일의 head tag안에서 불러오게 됩니다.

  optimization: {
    minimizer: mode === 'production' ? [new OptimizeCSSAssetsPlugin()] : []
  }

하지만 2022-09-25 기준으로 npm 페이지에 들어가보니 optimize-css-assets-webpack-plugin 보다는 css-minimizer-webpack-plugin을 사용하도록 권장하고 있더라구요! 참고하면 좋을 것 같아요.

참고자료

https://yamoo9.gitbook.io/webpack/webpack/webpack-plugins/extract-css-files


🔨JS

웹팩 5 이전 버전이라면 아래처럼 해야해요.

✨설치

npm install -D terser-webpack-plugin

✨terser-webpack-plugin

 optimization: {
    minimizer: mode === 'production' ? [new OptimizeCSSAssetsPlugin(), new TerserPlugin()] : []
  }
  • js 번들을 난독화시킬 수 있어요.
  • 개발할 때에는 난독화시키지 않고 번들 파일을 열어볼 수도 있으니 적용하지 않아요.

✨mode 설정

const PRODUCTION = 'production';
const DEVELOPMENT = 'development';
const mode = process.env.NODE_ENV || DEVELOPMENT;

module.exports = {
  mode,
  ...
}

하지만 웹팩 5부터는 자동으로 적용해요! (웹팩은 신인가..?)

  • 웹팩은 mode가 production으로 설정되어있다면 여러 플러그인들을 기본으로 적용시켜요.

FlagDependencyUsagePlugin
FlagIncludedChunksPlugin
ModuleConcatenationPlugin
NoEmitOnErrorsPlugin
OccurrenceOrderPlugin
SideEffectsFlagPlugin
TerserPlugin

따라서 그저 아래와 같이 하면 된답니다.

 minimizer: [
      '...',
   ]

이미지 최적화

✨image-webpack-loader

module: {
  rules: [
    {
      test: /\.(png|jpg|jpeg|gif|webp)$/i,
      loader: 'image-webpack-loader',
      enforce: 'pre',
    },
  ]
}
  • 이미지 파일의 사이즈를 줄일 수 있습니다.

✨직접 수정하기 -> 파일 형식을 webp로!

  • 이미지 변환 사이트
  • 구글에서 개발한 이미지 포맷으로, 기존의 이미지 포맷보다 파일 크기가 더 작아요.

✨ImageMinimizerPlugin

  • 파일의 확장자를 직접 수정하고 싶지않다면 이 플러그인을 써봐요.
  • webp,gifsicle 확장자에 따라 다양한 플러그인들을 추가 설치해서 options에 넣어줘야해요.
 optimization: {
    minimize: true,
    minimizer: [
      '...',
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ['webp', { quality: 40, resize: { width: 1200, height: 0 } }]
            ]
          }
        }
      })
    ]
  }

✨img 태그의 srcset 속성

  • 이미지 해상도가 1980 x 1080은 화면이 큰 데스크탑에서는 적합하지만, 모바일 환경에서는 좀 과하겠죠?
  • 이 때 img srcset 속성을 사용할 수 있습니다.

✨webp를 못 읽는 구형 브라우저라면?

  • 대체 이미지를 보여줄 수 있어요.
  • picture 태그 + source 태그를 사용합니다.
  • source의 srcSet에서 사용이 불가능하다면 다음의 img 태그의 이미지를 보여주게 됩니다.

필요한 것만 요청하기

🔨Code Splitting

SPA는 js 번들 파일에 모든 app에 대한 로직을 처음에 불러오기 때문에 로딩 속도가 지연될 수 있다는 단점이 있죠. 하지만 필요한 것만을 그때 그때 불러오면 어떨까요?

코드 스플리팅은 번들 파일을 여러 개의 파일로 분리하는 기법이예요.

React에서는 React.lazy와 Suspense를 활용하여 코드 스플리팅을 구현할 수 있어요.


🔨Tree Shaking

트리 쉐이킹이 "나무를 흔든다"는 뜻이죠.

즉, 사용하지 않는 코드들을 번들에 포함시키지 않고 나뭇잎처럼 떨어뜨리겠는 의미입니다.

// loadash의 모든 것을 가져올 거예요.
import { sortBy } from 'lodash'

// sortBy만 가져옵니다.
import sortBy from 'lodash-es/sortBy'

Side-Effect를 일으키지 않는 모듈은바깥의 변수를 변경하지 않고 인풋을 넣었을 때 아웃풋이 예측할 수 있도록 되어 있어야 Tree-Shaking 하기에 안전한 모듈이라고 합니다.

// in package.json...
{
  "name": "webpack-tree-shaking-example",
  "version": "1.0.0",
  "sideEffects": [
    "./src/utils/utils.js"
  ]
}

이렇게 설정하면 특정 모듈이 sideEffects를 발생시키지 않는다고 명시할 수 있어요.

참고자료

https://helloinyong.tistory.com/305

https://yceffort.kr/2021/08/javascript-tree-shaking#%ED%8A%B8%EB%A6%AC-%EC%89%90%EC%9D%B4%ED%82%B9%EC%9D%84-%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%B4%EB%B3%B4%EA%B8%B0

gzip

gzip은 파일 압축에 쓰이는 응용 소프트웨어예요.

현재 대부분의 모든 브라우저에서 Gzip압축을 지원하고 있어요.

브라우저가 Gzip을 지원한다면 서버에게 Accept-Encoding:gzip 헤더를 통해서 Gzip 지원 여부를 알려줘요.

웹 서버는 그에 답하여 Content-Encoding:gzip 응답헤더에 넣어주게 되죠!

보통 Nginx, CloudFront 등에서 Gzip을 설정해줄 수 있어요.

하지만 안 된다면 다음의 플러그인을 설치해봐요.
✨설치

npm install compression webpack plugin

Content-Encoding 헤더에 상응하도록 assets들의 압축을 도와주는 플러그인이예요.

compression webpack plugin - npm

plugins:[
  	  ...,
      new CompressionPlugin(),
  ]

캐싱

성능에 있어서 캐싱은 너무나 중요한 포인트죠.

데이터가 유효할 때까지 클라이언트와 가까운 곳에 데이터를 저장해두고

빠르게 꺼내 쓸 수 있도록 합니다.

Cloudfront에서는 캐시 정책을 생성할 수 있어요.
TTL, 최대 TTL, 기본 TTL을 설정할 수 있습니다.

브라우저에서도 캐시를 제공합니다.

👀 캐싱은 다른 포스팅에서 조금 더 자세하게 다루겠습니다.

참고자료

https://parkadd.tistory.com/116


s3 메타데이터 설정

S3에서는 각 파일마다 메타데이터를 설정할 수 있어요.

https://toss.tech/article/smart-web-service-cache

profile
함께 웃어야 행복한 개발자 장호영입니다😃

0개의 댓글