위 글은 NPM DEEP DIVE를 읽고, 그 중 6장의 내용을 정리한 글이다.
번들링
번들링의 역사
초기 자바스크립트 환경
- 파일 분리의 모호함
- 순차적인 로드로 인한 성능 저하
- 전역 네임스페이스 오염
- 복잡한 의존성 관리
번들링 라이브러리 등장
- Browserify(2011 ~ ???)
애플리케이션에서 사용된 모든 require() 함수의 호출을 재귀적으로 분석해 브라우저에서 하나의 <script>태그로 사용할 수 있도록 번들을 생성함 => '여러 개로 흩어진 파일을 하나로 합쳐서 제공한다' => 훨씬 빠른 번들링 도구들의 등장과 import 구문 추세에 밀려 자주 사용되지 않음
- Webpack(2012 ~ )
자바스립트뿐만 아니라 정적 자원(HTML + CSS + 이미지 등)도 번들링하며, 다양한 플러그인 제공하여 DX를 개선하여 현재까지도 각광받고 있음
- Rollup(2015 ~ )
ESMoudle 번들링 목적으로 만들어져, ESModule 기반 작성 코드를 원하는 방식(CJS / AMD / IIFE 등)으로 컴파일 할 수 있어 DX를 향상시키고, ESModule의 특징을 취할 수 있으며, 구형 시스템에서도 호환되는 장점이 있음
- Parcel(2017 ~ )
웹서비스에서 주로 사용되는 정적 자원을 내장하여 별도 설정이 필요 없는 것이 특징
- Vite(2020 ~ )
번들러가 제공해야하는 필수 기능(모듈 번들링, 트랜스파일링, 컴파일링, 코드 스플리팅, 트리 셰이킹, HMR, 최적화된 빌드, 플러그인 시스템 등) 모두 제공 + 타 번들러 대비 빠른 속도 + 다양한 프레임워크 지원
번들링의 역할
- 코드 분할 (Code Splitting)
코드를 적절히 분할하여 초기 로딩 속도를 향상, 리소스 사용 효율성 향상, 캐싱 효율성 향상시키기 위한 목적으로 사용하며,
동적 로딩이 필요한 경우, 대규모 라이브러리를 사용해야하는 경우, 네트워크 오버헤드를 감소 시켜야하는 경우에 필요함
- 트리 셰이킹 (Tree Shaking)
코드를 빌드한 결과물을 정적 분석을 거쳐 사용되지 않거나, 의미없는 코드를 완전히 제거하는 작업
- 난독화 및 압축 (Tree Shaking)
번들러의 본질 중 하나는 "리소스를 최대한 줄이는 것"으로,
난독화를 통해 공백 및 주석 제거, 변수/함수명 단축, 불필요 코드 제거, 코드 최적화 등을 수행하며,
압축을 통해 HTTP 압축(서버에 보내는 파일을 압축시켜 전송하고 브라우저에 이를 해제하여 사용)
웹팩
소개 및 주요 특징
- 다양한 모듈 번들링
- 코드 분할
- 로더(다양한 파일 형식을 처리할 수 있는 별도 로더 제공)
- 플러그인(로더에 필요한 기능을 추가하고 프로세스를 확장할 수 있게 도와줌)
- 개발환경 지원(HMR(실시간 번들링 확인) 기능도 제공하여 DX 향상)
- 최적화(트리 셰이킹, 코드 압축, 난독화 등 애셋 최소 크기 압축)
개념과 동작원리
의존성 그래프
번들의 시작점을 기준으로 하나의 파일이 다른 파일이 의존할 때마다 의존성으로 간주하여 이들 간의 그래프를 생성하며,
자바스크립트라면 가능한 작은 수의 번들로 묶거나, 애플리케이션의 의존성에 제공
엔트리
의존성 그래프를 만들기 위한 시작점으로서 한 개 이상의 파일을 선언하는 용도로 사용
아웃풋
생성된 번들 파일을 어디에 생성하고 이름을 어떻게 지정할지 선언할 수 있는 설정
로더
특정 유형의 파일을 모듈로 처리하기 위한 목적으로 사용됨.
test에 어떤 파일을 불러올지 정규표현식으로 선언하고, use에 해당 파일을 읽기 위해 사용할 로더를 지정하여, 자바스크립트와 JSON 파일 외의 파일도 하나의 모듈로 이해해서 의존성 그래프를 그릴 수 있도록 함
babel-loader, ts-loader, sass-loader, css-loader, style-loader 등이 자주 쓰임
플러그인
웹팩 기능 확장 목적으로 사용됨.
환경변수나 전역 상수를 빌드 시점에 미리 설정하여 코드에 주입할 수 있는 DefinePlugin,
개발환경에서 변경된 모듈만 새로고침해서 DX를 향상시키는 HMRPlugin,
자바스크립트를 일일이 삽입하지 않아도 자동으로 HTML 완성품을 만들어주는 HtmlWebpackPlugin,
번들로 생성되는 자바스크립트와 쌍을 이루는 .css파일을 내보낼 수 있게해주는 MiniCssExtractPlugin 등이 자주 쓰임
mode
내장돼 있는 최적화를 활성화.
3가지(development, production, none)만을 사용할 수 있으며,
값에 따라 devtool(실제 브라우저에서 제공되는 번들 파일과 실제 파일을 매핑하기 위한 파일), cache(웹팩에서 만든 모듈과 청크를 캐시할지 여부), optimization(최적화를 할지 말지) 등이 달라짐
롤업
코드 최적화와 효율적인 번들링에 특화돼 있으며, ESM을 기본적으로 지원
등장과 배경
CJS 기반의 웹팩은 결과물에서 __webpack_require__가 거대 객체(__webpack_modules__)를 사용하여 moduleId를 인수로 받아서 모듈을 평가하고, 캐시된 모듈이 있으면 반환하는 구조로, 모듈 개수가 많아질수록 불필요한 오버헤드를 초래하고, 트리 셰이킹이 어렵고, 정적분석이 어려운 단점이 있었음
=> 모든 코드를 하나씩 모듈화 하는 대신, 같은 장소에 배치해서 하나의 코드 덩어리로 쉽고 간단하게 실행되게 만듬
개념과 특징
ESM 기반
정적으로 모듈 분석 수행 가능하며, 트리 셰이킹과 같은 기법을 사용할 수 있음. 모든 모듈을 평탄화해서 기존 모듈 시스템이 가지고 있는 모듈 오버헤드를 최소화시킴
여러 형태의 번들 형태 지원
CJS, ESM 외에도, SystemJS, AMD, IIFE 지원
트리 셰이킹
주요 필드
input
웹팩의 엔트리와 마찬가지로 번들의 시작점. 하나의 파일일 수도, 배열을 넣어 여러 개의 파일을 넣을 수도, 객체를 넣어 이름과 파일명을 맵 형태로 넣을 수도 있음.
=> src 내부 구조를 유지한 채로 input 진입점 내부의 모든 파일을 객체로 정의하여 빌드하면 개별 파일이 의도치 않게 트리 셰이킹되는 것을 방지하고, 원본 프로젝트 구조를 보존하고, 각 파일을 독립적으로 번들링 할 수 있음
external
외부 의존성 명시를 위해 사용. 배열이나 함수를 값으로 받으며, 반환 받은 값은 번들링에 포함시키지 않음
output
웹팩과 마찬가지로 번들된 결과물을 어떻게 만들지를 나타냄.
plugins
번들링에 필요한 작업을 추가할 수 있음
@rollup/plugin-terser(.min.js와 같은 압축된 번들 파일 만들 때 사용)
@rollup/plugin-node-resolve(node_modules 내 서드파티 모듈을 해석할 수 있도록 도와줌)
@rollup/plugin-commonjs(CJS로 작성된 파일을 ES6 모듈 형식으로 변환하는 플러그인)
rollup-plugin-visiaulizer(번들된 결과물 분석을 도와주는 플러그인. 다양한 형태의 분석 그래프를 HTML 형태로 제공)
비트
"빠르다"라는 뜻으로,
개발환경에서 번들링 하지않고 ESModule 방식으로 코드를 제공하며, 타입스크립트를 자바스크립트로 변환하기 위해 tsc가 아닌 esbuild를 사용함.
개념과 특징
롤업, esbuild의 공존
빌드에 필요한 모든 내장 기능을 esbuild만으로 구현이 어려웠어서, 롤업과 함께 사용함.
=> Rust 기반으로 새로 작성하는 롤다운으로 훗날에 대체할 예정
의존성 사전 번들링
- 번들러들이 ESM 기반으로 프로젝트를 시작하기 위해 CJS와 UMD 형식의 모듈을 ESM으로 변환하는 작업을 수행하기 위해 개발 모드에서는 미리 esbuild로 처리
- 개발모드에서 ESM을 불러오는 것에 대한 성능 최적화
모던 웹 환경을 위한 도전
"Pushing the Moderwn Web(현대적인 모든 웹 기술을 선도)"한다는 철학으로 소스코드는 ESM으로만 작성할 수 있으며, ESM 아닌 의존성은 사전 번들링이 되어야한다.
주요 필드
root
프로젝트 최상위 디렉터리 위치. index.html이 있는 곳이 일반적
base
Next.js의 basePath와 유사한 기능으로, 값 지정시 localhost:5173이 아닌 localhost:5173/설정값으로 서빙됨
mode
사용자가 원하는 값을 지정해서 선언해서 사용할 수 있음. process.env.NODE_DEV보다 자유도가 높음.
define
코드 내부에 있는 전역 상수로 대체될 수 있는 값을 열거.
plugins
publicDir / cacheDir
publicDir은 정적 애셋을 제공하기 위한 디렉터리로 기본값은 /public. 빌드 후에는 outDir, 기본값으로는 /dist에 위치함.
cacheDir은 캐시 파일을 저장하는 디렉터리. 번들링된 내용도 이 캐시에 담김.
build
target, outDIr, minify, lib, rollupOption 등을 통해 결과물을 달리 할 수 있음