요약
Vite는 개발 환경에서 ESM과 esbuild를 통해 빠른 빌드를 제공하며, 프로덕션 빌드에서는 Rollup을 활용해 최적화된 번들링을 수행하여 Webpack보다 효율적입니다. Webpack은 설정과 처리 방식에서 더 복잡하고 상대적으로 느리지만 다양한 기능을 제공해야 하는경우 활용될수 있습니다.
2012년에 등장하여 다양한 제공하면서 빠르게 인기를 끌어 번들러의 선두를 달렸습니다. 현재는 다양한 대안의 번들러 도구들(vite, rollup, esbuild, parcel) 들이 탄생했습니다.
결국 모든 번들러들은 같은 문제를 해결하기 위해 만들어 졌습니다.
Webpack같은 도구들이 “번들러(Bundler)”라고 불리는 이유는 여러 개의 파일로 이루어진 애플리케이션을 하나의 단일 파일 또는 몇 개의 최적화된 파일로 묶는(번들링하는) 역할을 하여 브라우저 로딩시 시간을 단축하고 관리 용이성을 높입니다.
웹 애플리케이션은 다양한 자원과 파일(HTML, CSS, JavaScript, Typescript, 이미지 등)으로 구성되어 있으며, 이를 효과적으로 관리하고 최적화된 상태로 배포할 필요가 있습니다.
규모가 작은 애플리케이션에는 필수적이지 않지만, 규모가 큰 애플리케이션에서는 꼭 필요한 도구 중 하나라고 생각합니다. 아래 기능들은 Webpack이 웹 서비스 성능에 긍정적인 영향을 미치는 주요 이유입니다.
프로젝트가 커질수록 파일 간의 종속성이 복잡해지기 때문에, 이를 적절히 관리하고 각 파일이 의존하는 다른 파일을 함께 로드하는 것이 중요합니다. 즉 A.js 라는 파일에 B.js C.js 파일에 기능을 사용한다면 A파일을 불러 올때 한번에 B,C 파일의 내용이 있다면 좀더 효율적 입니다.
모듈 종속성과 이어지는 이점으로 브라우저가 여러 파일을 로드하면, 네트워크 요청이 많이 발생해 로딩 시간이 길어지고, 병목 현상이 발생합니다. 그리하여 번들러는 수백 개의 모듈을 하나 또는 몇 개의 파일로 묶어 네트워크 요청 수를 줄입니다. 이를 통해 로딩 속도를 개선하고, 초기 로딩 성능을 높일 수 있습니다.
애플리케이션에는 불필요하게 남아 있는 코드나 중복된 코드가 있을 수 있습니다. 번들러는 사용되지 않는 코드를 제거하는 트리 쉐이킹(Tree Shaking), 코드 압축(Uglify) 등 최적화 작업을 통해 파일 크기를 최소화합니다. 작아진 파일의 용량은 리소스 로딩시간에 이점을 가져다 줍니다.
CSS, 이미지, Sass, TypeScript 등 다양한 파일 형식이 프로젝트에 포함되지만, 브라우저는 기본적으로 HTML, CSS, JavaScript, 그리고 JSON만 직접 이해할 수 있습니다.
번들러는 다양한 파일 형식을 변환하고, 이를 브라우저가 이해할 수 있는 JavaScript 파일로 변환하여 하나의 번들 파일로 묶습니다. 예를 들어, scss 파일을 css로 컴파일한 후 JavaScript로 불러올 수 있도록 도와줍니다. 이로써 개발자는 좀더 개발 친화적인 기술을 사용하여 개발할 수 있게 되었습니다.
JavaScript는 기본적으로 파일 간의 스코프 격리가 어려워, 전역 스코프에 변수가 많아질 경우 충돌 위험이 있습니다.
번들러는 각 모듈을 즉시 실행 함수로 감싸 독립적인 스코프에서 작동하도록 구성하여 전역 스코프 오염을 방지합니다.
이러한 이유로 번들러는 거의 필수적으로 사용되어지고 있습니다.
다음으로 대중적인 웹팩의 동작 절차에 대해 알아보겠습니다.
Webpack은 가장 처음으로 번들링을 시작할 시작 파일을 결정하게 되는데 이를 엔트리라고 합니다. 엔트리로 설정된 파일을 기준으로 모든 의존성을 탐색하며 전체 번들을 구성하게 됩니다.
엔트리 파일부터 시작해 모든 import와 require 구문을 따라가면서 필요한 파일들을 하나씩 모아 의존성 그래프를 만듭니다. 이 과정에서 Webpack은 각 파일을 모듈 단위로 취급하여 의존성 관계를 파악합니다.
의존성 그래프에 포함된 파일들 중에서 JavaScript와 JSON 이외의 파일(CSS, 이미지, TypeScript 등)에 대해, 각 파일 형식에 맞는 로더(Loader)가 적용됩니다. 로더는 파일을 JavaScript로 변환하거나, 번들링 가능한 형태로 가공해줍니다.
예를 들어, .scss 파일에는 sass-loader가 적용되어 CSS로 변환됩니다.
웹팩은 모듈 최적화로 코드 스플리팅과 트리 쉐이킹 기법을 사용합니다
코드 스프리팅 (code splitting)
웹팩은 코드 스플리팅을 통해 모듈을 여러 개의 청크(Chunk)로 분리합니다. 필요한 시점에 해당 파일만 로드하도록 만드는 기능입니다. 이로 인해 초기 로드 시간을 줄이고, 애플리케이션이 점진적으로 필요한 코드만 불러오도록 최적화할 수 있습니다.
트리 쉐이킹 (Tree Shaking)
생성된 각 청크에 대해 트리 쉐이킹이 수행하게 되는데 사용하지 않는 정적 코드(dead code)를 제거하여 최종 번들 크기를 줄이는 효과를 냅니다. ‘나무를 흔들어 죽은 잎사귀를 떨어트리는 모습‘ 과 비슷하여 붙여진 이름입니다.
플러그인은 Webpack의 번들 전체에 영향을 주는 작업을 처리합니다. 예를 들어, HTML 파일을 자동 생성하는 HtmlWebpackPlugin, 코드 압축을 위한 TerserPlugin 등이 사용됩니다. 이 단계에서 최적화, 파일 생성, 환경 설정 등의 작업이 플러그인에 의해 수행됩니다.
플러그인까지 적용된 최종 번들은 마지막으로 즉시 실행 함수(IIFE)로 감싸져서 작업을 마무리합니다.
즉시 실행 함수를 모듈별로 감싸는 이유는 전역 스코프 오염을 방지하기 위해서 입니다.
여기까지 웹팩이 웹 애플리케이션에 미치는 영향과 동작 절차에 대해서 알아 봤습니다. 정말 매력적인 도구 입니다. 하지만 현재는 느리고 설정이 복잡하다는 이유로 예전만큼 관심을 받지 못하는것 같습니다.
웹팩이 성능에서 제약을 받는 주요 이유는 싱글 스레드 처리 방식으로 의존성 분석 하고 파일 변화를 처리하기 때문입니다. 또한, 웹팩은 로더와 플러그인 체인을 통해 여러 파일 형식을 변환하므로, 이 과정의 복잡성이 추가적인 성능 저하를 야기합니다.
현재는 성능 문제와 설정의 어려움을 해결한 vite가 인기를 얻고 있습니다. Vite는 전통적인 번들링 방식을 사용하지 않고, 더 빠르고 효율적인 개발 환경을 제공하기 위해 개발모드와 프로덕션 모드에서 각각 최적화된 접근 방식을 사용합니다.
Vite는 개발 모드에서 트랜스파일러이자 모듈 번들러로 esbuild를 사용하여 빠르고 효율적인 개발 환경을 제공합니다.
Go 언어로 작성된 esbuild는 기존의 JavaScript 기반 트랜스파일러보다 훨씬 빠른 성능을 자랑하며, ESM을 활용해 브라우저가 필요한 파일만 직접 가져오도록 설계되어 성능을 더욱 향상시킵니다.
또한, Dependencies(패키지)와 Source Code(소스 코드)를 분리하여 빌드하는 방식으로 최적화를 제공합니다.
빌드에서는 Rollup을 사용하여 최적화된 번들링을 수행합니다. Rollup은 ES 모듈을 기본으로 최적화된 번들링을 수행하고, 웹팩과 동일하게 트리 쉐이킹, 코드 스플리팅 기능도 제공하여, 더 작고 효율적인 번들을 생성합니다.
Vite가 Webpack보다 빠른 이유는 개발모드, 프로덕션 모드별 각각 상황에 맞는 전략으로 가능을 제공해주기 때문입니다.
개발 모드에서는 변하지 않는 의존성 패키지와 라이브러리를 미리 번들링하고, 소스 코드를 개별적으로 분리해 관리합니다. 또한, 브라우저 요청 시점에 필요한 모듈만 변환하여 초기 빌드와 코드 변경 속도를 크게 향상시킵니다. 특히, 변경된 모듈만 갱신하는 HMR(Hot Module Replacement) 기능을 통해 즉각적인 피드백을 제공합니다.
E
프로덕션 번들링에서는 Babel과 같은 트랜스파일러를 통해 코드를 변환하고, 최적화된 EMS를 활용한 Rollup을 사용해 번들 크기를 최소화합니다. 최근에는 SWC를 사용해 프로덕션 빌드 성능을 더욱 높일 수 있습니다. SWC는 Rust로 작성된 고성능 트랜스파일러로, Babel보다 훨씬 빠르게 코드를 변환하며, 특히 대규모 프로젝트에서 빌드 시간을 크게 단축시킬 수 있습니다. Rollup의 간결한 플러그인 시스템 덕분에 복잡한 로더 체인을 사용할 필요가 없어 빌드 성능을 한층 더 끌어올릴 수 있습니다.
현재는 Vite가 더 인기를 끌고 있지만, 번들러들은 계속해서 발전하고 있습니다. 이번 기회를 통해 Vite가 왜 더 빠른지 큰 틀에서 이해할 수 있었습니다.
내부적으로 esbuild와 Rollup이 사용되는 방식이나 최적화 알고리즘에 대해서는 깊이 다루지 않았지만, 지금까지의 내용만으로도 번들러에 대해 조금 더 알아 볼수 있었습니다.