Post

NextJs 웹 어플리케이션 최적화 (트리셰이킹)

Tree Shaking

Tree Shaking은 실제로 사용하는 모듈만 로딩하게 하여 번들의 사이즈를 줄여 빌드 시간렌더링 시간감소를 기대할 수 있다.

bundle-analyzer을 통해 번들 크기 시각화

  • @next/bundle/analyzer

NextJs에선 위의 패키지를 통해 빌드 프로세스에서 번들링 된 패키지 크기를 시각화 하여 볼 수 있다.

  • webpack-bundle-analyzer

NextJS가 아닌 Webpack을 집적 구성해 프로젝트를 빌드할 때는 위의 패키지를 사용해 동일한 결과를 가질 수 있다.

번들 사이즈 측정

먼저 @next/bundle-analyzer를 사용하기 위해서는 next.config.js 파일에 bundle analyzer를 intergrate 해주어야 한다.

nextconfig.js

1
2
3
4
5
const withBundleAnalyzer = require("@next/bundle-analyzer")({
  enabled: process.env.ANALYZE === "true",
});

module.exports = withBundleAnalyzer(nextConfig);

실행은 아래와 같이 package.json 파일에 스크립트로 설정하거나 .env 파일로 설정하여 실행하면 된다. package.json

1
"analyze" : "cross-env ANALYZE=true next build"

위와 같이 실행 후에는 빌드 결과물의 .next 폴더 아래에 analyze 폴더에서 확인할 수 있다. bundle

그리고 해당 파일을 누르면 아래와 같은 결과들을 확인할 수 있다.

bundle bundle

사이즈를 표시하는 속성으로 stat과 parsed, Gzipped을 확인할 수 있다. stat은 축소와 같은 변환 이전의 파일의 ‘입력’크기 이고, parsed는 파일의 ‘출력’ 크기 이며, Webpack이 트리셰이킹을 마친 상태의 크기다. gzip은 압축을 통해 구문 분석된 번들/모듈을 실행하는 크기이다. 우리는 parsed size만을 보면된다.

작업 진행

가장 큰 번들의 크기가 aws-sdk2.69MB이다.
S3를 사용하기 위한 모듈이였는데 EC2, utils 등의 여러 모듈도 함께 빌드되어 가장 큰 용량을 차지하는 것으로 보인다. 각자 사용하는 모듈이 다르고 모듈마다 트리셰이킹 방식도 다르기 때문에 이번 포스팅에선 가장 큰 패키지 하나만 진행하려고한다.

변경 전

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { config, S3 } from "aws-sdk";

const region = "ap-northeast-2";
const bucket = "blockjobsawsbucket";

config.update({
  region: region,
  accessKeyId: process.env.NEXT_PUBLIC_AWS_ACCESS_ID,
  secretAccessKey: process.env.NEXT_PUBLIC_AWS_ACCESS_KEY,
});

const handleFileInput = async (e: ChangeEvent<HTMLInputElement>) => {
  const file = e.target.files?.[0];

  const upload = new S3.ManagedUpload({
    params: {
      Bucket: bucket, // 버킷 이름
      Key: id + ".png", // 유저 아이디 혹은 enterpriseid
      Body: file, // 파일 객체
    },
  });

  const promise = upload.promise();
  promise.then(
    function () {
      // 종료를 상위 컴포넌트에 callback
      uploadComplete();
    },
    function (err) {
      // 이미지 업로드 실패
      console.log(err);
    }
  );
};

aws-sdk 패키지를 제거한 후 S3 업로드에 필요한 @aws-sdk/client-s3 @aws-sdk/lib-storage 패키지를 다운받아 해당 패키지에 맞게 코드를 수정했다.

변경 후

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { S3Client } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";

export const useS3 = () => {
  const region = "ap-northeast-2";
  const bucket = "blockjobsawsbucket";
  const fileBaseUrl = `https://${bucket}.s3.${region}.amazonaws.com/`;

  const handleFileInput = async ({
    id,
    uploadComplete,
    e,
  }: ProfileUpload_props) => {
    const file = e.target.files?.[0];

    const s3 = new S3Client({
      region: region,
      credentials: {
        accessKeyId: process.env.NEXT_PUBLIC_AWS_ACCESS_ID ?? "",
        secretAccessKey: process.env.NEXT_PUBLIC_AWS_ACCESS_KEY ?? "",
      },
    });

    try {
      const mulitpartUpload = new Upload({
        client: s3,
        params: {
          Bucket: bucket, // 버킷 이름
          Key: id + ".png", // 유저 아이디 혹은 enterpriseid
          Body: file, // 파일 객체
        },
      });

      await mulitpartUpload.done();
      await uploadComplete(id);
    } catch (e) {
      console.log(e);
    }
  };

  return { handleFileInput, fileBaseUrl };
};

작업 결과

위와같이 작업을 진행한 결과

  • Parsed Szie: 2.69MB -> 373.61KB (-86%) bundle

사실 위와 같은 결과는 나오기 쉽지 않고 (사실 나오면 안된다. 큰 모듈을 통째로 사용하는 것을 지양해야 한다.) 10% ~ 20%의 감소정도를 기대해 볼 수 있는 것 같다. (실무 프로젝트에서도 최대가 15% 정도 였다…)

This post is licensed under CC BY 4.0 by the author.