Cometin'

bundle-analyzer, gzip으로 Next.js 최적화하기

2022-02-25 at React category

Next.js를 사용해 개발한 교내 동아리 랜딩 페이지를 최적화한 경험을 공유합니다.

해당 랜딩 페이지는 다음 링크를 통해 확인하실 수 있습니다!

들어가기 앞서

Lighthouse 지표와 추천 사항을 기준으로 최적화 작업을 진행하였습니다.

저는 최적화 작업이 익숙하지 않지만, 제가 검색한 결과 최신의, 깔끔하게 정리된 내용이 없어 공유하기로 생각했다는 점 말씀드립니다.

Bundle-analyzer 적용하기

bundle analyzer

bundle analyzer를 사용하면 위 사진처럼 빌드 후 각 모듈의 번들링된 사이즈를 확인하실 수 있으며, 이는 번들링 크기를 줄이는 tree shaking의 지표로 활용할 수 있습니다.

Next.js에서 적용 방법

npm install @next/bundle-analyzer --save-dev
# or
yarn add @next/bundle-analyzer -D

우선 설치를 진행해준 후,

// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer({});

루트 디렉토리에 위치한 next.config.js 파일에 위 내용과 함께 .env.local, .env와 같은 환경 변수 파일에서 아래 내용을 추가해주시면 적용하실 수 있습니다.

ANALYZER=true

적용 후, Build 해보시면 아래 사진과 같이 analyze 디렉토리에 client, server로 나뉜 html 파일을 확인해보실 수 있으실 겁니다.

analyze image

Next.js github에서 적용 방법에 대한 문서를 확인하실 수 있으며, npm 주소는 다음 링크에서 확인하실 수 있습니다.

다른 Plugin들과 함께 사용하기

위 문서를 확인하셨다면 아시겠지만, next-compose-plugins를 사용하여 다른 plugin들과 함께 next.config.js 파일을 더욱 깔끔히 관리할 수 있습니다.

npm install next-compose-plugins --save-dev
# or
yarn add next-compose-plugins -D

설치 후 다음과 같은 형태로 next.config.js 파일을 작성하실 수 있습니다.

// next.config.js
const withPlugins = require('next-compose-plugins');

const { withSentryConfig } = require('@sentry/nextjs');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

// 해당 내용은 아래 공식 문서를 참고하시면 좋을 것 같습니다.
// https://nextjs.org/docs/api-reference/next.config.js/introduction
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

const moduleExports = {
  nextConfig,
};

const sentryWebpackPluginOptions = {
  silent: true,
};

module.exports = withPlugins(
  [
    withSentryConfig(moduleExports, sentryWebpackPluginOptions),
    withBundleAnalyzer({}),
    // 추가적인 Plugin들을 작성해주시면 됩니다.
  ],
  nextConfig
);

보시는 것처럼 저는 sentry plugin이 기존에 작성되어 있었으며, next-compose-plugins를 사용하여 bundle-analyzer와 함께 작성한 모습을 확인하실 수 있습니다.

next-compose-plugins의 npm 주소는 다음 링크에서 확인하실 수 있습니다.

Gzip compress(압축)하기

Gzip이란 파일 압축에 쓰이는 응용 소프트웨어로써, HTML, CSS, JS 등을 압축하여 리소스를 받는 시간을 줄여주는 방식으로 성능을 개선할 수 있습니다.

gzip은 파일 압축에 쓰이는 응용 소프트웨어이다. gzip은 GNU zip의 준말이며, 초기 유닉스 시스템에 쓰이던 압축 프로그램을 대체하기 위한 자유 소프트웨어이다. gzip은 ... 더욱 자세한 내용은 출처 위키피디아를 확인해보시면 좋을 것 같습니다.

Next.js에서 적용 방법

npm install compression-webpack-plugin --save-dev
# or
yarn add compression-webpack-plugin -D

설치 후 다음과 같은 형태로 next.config.js 파일을 작성하실 수 있습니다.

// next.config.js
const withPlugins = require('next-compose-plugins');
const { withSentryConfig } = require('@sentry/nextjs');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

const CompressionPlugin = require('compression-webpack-plugin');

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

const moduleExports = {
  nextConfig,
};

const sentryWebpackPluginOptions = {
  silent: true,
};

module.exports = withPlugins(
  [
    withSentryConfig(moduleExports, sentryWebpackPluginOptions),
    withBundleAnalyzer({
      compress: true,
      webpack(config) {
        const plugins = [...config.plugins, new CompressionPlugin()];

        return { ...config, plugins };
      },
    }),
  ],
  nextConfig
);

작성하신 후, Build를 해보시면 아래 사진과 같이 gz 확장자의 파일을 확인하실 수 있으실 겁니다.

gz image

해당 Plugin의 자세한 옵션과 사용방법들은 Webpack 문서에서 확인하실 수 있습니다.

gzip은 무조건 좋은 것인가?

  • 사용자 측면

    gzip brower support

    이미지 출처 : https://caniuse.com/?search=gzip

    보시는 것처럼 대부분의 브라우저가 gzip을 지원하고 있습니다. 하지만 웹프록시, 보안 소프트웨어 등의 문제로 요청 헤더가 깨져서 사용하지 못하는 사용자가 전체 사용자의 15%가 된다고 합니다.

  • 성능 향상 측면

    Gzip은 많은 경우에서 성능 향상을 야기할 수 있지만, 통상적으로 1~2kb 이하의 파일은 압축하지 않는 것이 좋다고 합니다. 그 이유는 압축해서 얻는 효과보다 압축을 푸는 데 사용되는 리소스, 서버 혹 웹 브라우저의 CPU가 더욱 크기 때문입니다.

    또한 이미지, Binary 파일 등은 효과를 볼 수 없다고 합니다.

더욱 자세한 사항은 스티브 사우더스 저의 초고속 웹사이트 구축 서적과 함께 해당 블로그 아티클에서 확인하시면 좋을 것 같습니다.

Before and After

제 경우에는 랜딩 페이지로써 bundle analyzer를 통해 확인한 결과 사용한 라이브러리 중 Tree shaking을 필요로하진 않은 것 같다는 판단하에 gzip 압축만을 진행하였습니다.

before

적용 전

after

적용 후

상호 작용까지의 시간을 가르키는 TTI 지표가 0.3초, 페이지의 메인 콘텐츠(가장 큰 콘텐츠)가 표시되는 시점인 LCP 지표가 0.5초 단축된 것을 확인할 수 있었습니다.

동일한 환경, 네트워크에서 확인하였으나 Lighthouse 지표는 절대적이지 않습니다.

마치며

최적화 작업은 익숙하지 않지만, 신선하고 재밌었던 경험이였습니다.

웹 서비스의 성능은 사용자 경험, 유추와 직접적으로 관계가 있다고 생각하기 때문에 항상 도전의식이 있는 영역이였는데, 부족하게나마 성능을 개선하고 lighthouse를 통해 시각적으로 확인할 수 있어 더욱 재밌게 느껴졌던 것 같습니다.

앞으로는 더욱 다양한 방법으로 성능 개선을 해보고 싶은 마음이 들었으며, 저와 같이 익숙하지 않은 분들에게 도움이 되길 바라며 글을 마치도록 하겠습니다. 감사합니다.

성능 개선에 관심이 있으신 분들은 Bundle Diet라는 주제의 Toss SLASH 21 세션을 꼭 들어보시길 추천드립니다.

참고

hyesungoh

Personal blog by hyesungoh.

I like to share my knowledge for those who wandering in issue.