노트

트리 셰이킹

Tree Shaking

프론트엔드#performance#tooling · 연결된 개념 7개

쉽게 말하면

트리 셰이킹은 나무를 흔들어 말라붙은 잎을 떨어뜨리듯, 아무 데서도 쓰지 않는 코드를 최종 번들에서 빼내는 거예요. 사용자가 내려받는 파일이 그만큼 가벼워져요.

비유의 한계 흔든다고 다 떨어지진 않아요. 불러오기만 해도 뭔가 실행되는 부수 효과가 있거나 CommonJS, 동적 접근처럼 정적으로 분석할 수 없으면 번들러가 남겨 둬요. 그래서 ESM과 sideEffects 표시가 필요해요.

트리 셰이킹(Tree Shaking)은 번들러가 import/export를 정적으로 분석해, 어디서도 쓰지 않는 export를 최종 번들에서 빼는 최적화다. 빌드 단계의 죽은 코드 제거라고 보면 된다.

  • 전제는 ESM(ECMAScript Modules)이다. ES 모듈의 import/export는 최상위에 고정돼 있어서 실행 전에 의존 그래프를 만들 수 있다. CommonJS의 require는 런타임에 조건부로도 호출할 수 있어서 무엇이 쓰이는지 확정하기 어렵다
  • 부수 효과가 있으면 지우지 못한다. 모듈을 불러오기만 해도 무언가 실행된다면 지우면 안 된다. 그래서 package.json의 "sideEffects": false(또는 폴리필 같은 예외 파일 목록)로 번들러에게 안전하다고 알려 준다
  • 깨지기 쉬운 경우
    • import * as utils처럼 namespace 전체를 가져오면 번들러가 보수적으로 판단할 수 있다
    • utils[key]처럼 동적으로 접근하면 정적 분석이 안 된다
    • Babel 같은 변환 단계가 ESM을 CommonJS로 바꿔 버리면 효과가 사라진다(@babel/preset-env의 modules 옵션)
    • TS의 enum은 IIFE(Immediately Invoked Function Expression)로 컴파일돼 지우기 어렵다
  • named import와 ESM 배포판(lodash-es, date-fns)을 쓰면 효과가 좋다
구분트리 셰이킹코드 스플리팅
목적안 쓰는 코드 제거번들을 여러 조각으로 분리
시점빌드로딩 전략

코드 스플리팅과 동적 임포트과 같이 쓰고, 배럴 파일과 re-export은 잘못 구성하면 트리 셰이킹을 방해할 수 있다. 번들러별 차이는 번들러 참고.

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 이진 탐색 트리

    각 노드가 자식을 최대 둘 가지고, 왼쪽 서브트리의 모든 값은 노드보다 작고 오른쪽은 크다는 규칙을 지키는 트리. 비교할 때마다 한쪽 가지를 버리므로 정렬된 데이터를 빠르게 찾고 넣을 수 있다.

  • Turborepo

    Turborepo는 JavaScript·TypeScript 모노레포의 태스크(빌드·린트·테스트)를 빠르게 돌리는 빌드 시스템이다. 기존 package.json 스크립트는 그대로 두고, 스크립트 사이의 의존 관계만 알려 주면 병렬 실행과 캐싱을 맡는다. 의도적으로 "빠른 실행과 캐시" 한 가지에 집중한다.

  • Nx

    Nx는 캐싱과 병렬 실행에 더해 프로젝트 그래프, 코드 생성, 아키텍처 규칙 강제까지 제공하는 모노레포 플랫폼이다. "빠른 실행 한 가지"에 집중하는 turborepo와 달리 기능을 두루 갖춘 쪽을 지향한다.

  • 청킹과 코드 읽기

    여러 정보를 의미 있는 덩어리 하나로 묶어 기억하는 것. 아는 것이 많을수록 코드를 큰 덩어리로 읽는다.

  • loading 경계와 레이아웃 끌어올리기

    Next.js App Router에서 loading.tsx는 같은 세그먼트의 page와 그 아래(하위 layout 포함)를 suspense로 감싸지만, **같은 세그먼트의 layout은 감싸지 않는다**. 그래서 데이터를 기다릴 필요가 없는 공통 UI(탭바, 필터 헤더)를 page에서 layout으로 끌어올리면 스켈레톤에 덮이지 않고 처음부터 실제 모습으로 보인다.

보기 옵션