Vite는 프랑스어로 "빠르다(Quick)" 를 의미하며, 발음은 "veet"와 비슷한 /vit/
입니다. 빠르고 간결한 모던 웹 프로젝트 개발 경험에 초점을 맞춰 탄생한 빌드 도구입니다.
Vite는 esbuild와 roleup을 사용해서 cra에서 사용하는 webpack보다 빠른 빌드 속도를 자랑하는데요!
Vite에 대해 본격적으로 알아보기 전에 번들러가 무엇인지에 대해 다시 개념을 잡고가는게 좋을 것 같습니다.
<script>
태그로 연결하는 방식 → 전역 오염이나 의존성 관리가 어려웠음 → 모듈 시스템의 등장.
모듈시스템이란?
외부에서 사용할 수 있게 특정 함수나 오브젝트 등을 모듈화 하고,
해당 모듈을 사용하려는 쪽에서는 필요한 모듈만 불러 와서 사용하게하는 시스템
간단하게 우리가 지금 import export 하고 있는 그것들을 가능하게 하는것을 말합니다.
이러한 모듈 시스템을 관리하기 위해 번들러 등장.
번들러(Bundler)는 웹 애플리케이션을 개발하기 위해 필요한 HTML, CSS, JS 등의 파편화된(모듈화된) 자원들을 모아서, 하나 혹은 최적의 소수 파일로 결합(번들링)하는 도구입니다.
결국, 현대의 번들러는 개발자의 작업 효율성을 높이고 브라우저의 호환성이나 성능 등을 개선하는데 크게 도움을 줍니다.
npmtrends 에서도 확인가능 하듯이 2020년에 나온 vite의 다운로드 수가 2012년에 나온 webpack을 따라잡고있음.
인기와 커뮤니티 지원을 측정하는 star-history를 사용하여 GitHub 스타를 살펴보면 vite는 webpack에 필적한다.
vite는 애플리케이션의 모듈을 dependencies와 source code 두 가지 카테고리로 나누어 개발 서버의 시작 시간을 개선합니다.
기존의 번들러 기반으로 개발을 진행할 때, 소스 코드를 업데이트 하게 되면 번들링 과정을 다시 거쳐야 했었습니다. 따라서 서비스가 커질수록 소스 코드 갱신 시간 또한 선형적으로 증가하게 됩니다.
일부 번들러는 메모리에서 작업을 수행하여 실제로 갱신에 영향을 받는 파일들만을 새롭게 번들링하도록 했지만, 결국 처음에는 모든 파일에 대한 번들링을 수행해야 했습니다. "모든 파일"을 번들링 하고, 이를 다시 웹 페이지에서 불러오는 것이 얼마나 비효율적인 것인지 느껴지시나요? 이러한 이슈를 우회하고자 HMR(Hot Module Replacement) 이라는 대안이 나왔으나, 이 역시 명확한 해답은 아니었습니다.
물론, vite는 HMR을 지원합니다. 이는 번들러가 아닌 ESM을 이용하는 것입니다. 어떤 모듈이 수정되면 vite는 그저 수정된 모듈과 관련된 부분만을 교체할 뿐이고, 브라우저에서 해당 모듈을 요청하면 교체된 모듈을 전달할 뿐입니다. 전 과정에서 완벽하게 ESM을 이용하기에, 앱 사이즈가 커져도 HMR을 포함한 갱신 시간에는 영향을 끼치지 않습니다.
또한 vite는 HTTP 헤더를 활용하여 전체 페이지의 로드 속도를 높입니다. 필요에 따라 소스 코드는 304 Not Modified
로, 디펜던시는 Cache-Control: max-age=31536000,immutable
을 이용해 캐시됩니다. 이렇게 함으로써 요청 횟수를 최소화하여 페이지 로딩을 빠르게 만들어 줍니다.
이렇게나 빠른 Vite를 사용하지 않을 이유가 있나요?
이제 기본적으로 ESM이 대부분의 환경에서 지원되지만, 프로덕션에서 번들 되지 않은 ESM을 가져오는 것은 중첩된 import로 인한 추가 네트워크 통신으로 인해 여전히 비효율적입니다(HTTP/2를 사용하더라도). 프로덕션 환경에서 최적의 로딩 성능을 얻으려면 트리 셰이킹, 지연 로딩 및 청크 파일 분할(더 나은 캐싱을 위해)을 이용하여 번들링 하는 것이 더 좋습니다.
개발 서버와 프로덕션 빌드 간에 최적의 출력과 동작 일관성을 보장하는 것은 쉽지 않습니다. 이것이 바로 Vite가 미리 설정된 빌드 커맨드를 이용하고, 빌드 퍼포먼스 최적화를 진행하는 이유입니다.
출처 : https://ko.vitejs.dev/guide/why.html
vite 설정
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
})
webpack 설정
const webpack = require('webpack');
const path = require('path');
const { HotModuleReplacementPlugin } = require('webpack');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
{
test: /.vue$/,
use: {
loader: 'vue-loader',
},
},
{
test: /.css$/,
use: ['vue-style-loader', 'css-loader'],
},
],
},
resolve: {
alias: {
vue: 'vue/dist/vue.js',
},
},
plugins: [
new HotModuleReplacementPlugin(),
new VueLoaderPlugin(),
]
};
Vite와 비교할 때 Webpack의 구성에는 수동 설정이 더 많이 포함.
Vite는 기본 제공되는 즉시 사용 가능한 개발 서버로 눈에 띄며 종종 광범위한 구성이 필요하지 않습니다.
이와 대조적으로 Webpack은 유연성을 제공하지만 추가 설정이 필요합니다. 개발자는 Webpack의 Watch Mode , webpack-dev-server
및 webpack-dev-middleware
변경 시 자동 코드 컴파일과 같은 옵션을 선택할 수 있습니다. 그러나 일반적으로 이러한 옵션을 설정하고 세부 조정하려면 구성이 필요합니다.
기존 번들러 기반 설정에는 적극적인 크롤링이 포함되며 서비스를 제공하기 전에 전체 애플리케이션을 구축해야 하므로 특히 복잡한 프로젝트에서 눈에 띄는 지연이 발생합니다.
Vite는 근본적으로 다른 접근 방식으로 콜드 스타트에 혁명을 일으켜 초기화 시간을 획기적으로 단축합니다.
효율적인 종속성 처리 : Vite는 고성능 Go 기반 번들러인 esbuild를 활용하여 일반 JavaScript 및 대규모 모듈을 포함한 종속성을 사전 번들링합니다. 예를 들어 lodash-es 에는 600개가 넘는 내부 모듈이 포함되어 있습니다. 전통적인 방법을 사용하고 와 같은 함수를 가져올 때 600개 이상의 HTTP 요청이 트리거됩니다. Vite의 솔루션은 단일 모듈로 사전 번들링하여 HTTP 요청을 하나로 줄이는 것입니다. 이렇게 요청이 크게 줄어들면 개발 서버의 페이지 로드 속도가 크게 향상됩니다.
ESM 기반 개발서버
주문형 소스 코드 로딩: Vite는 기본 ES 모듈을 활용하여 소스 코드를 제공하고 서버 로드 및 대기 시간을 최소화합니다. 브라우저 요청 시 소스 코드 변환 및 제공이 이루어지므로 효율성이 향상되고 대기 시간이 단축됩니다.
번들 기반 개발서버
반면 Webpack은 번들 기반 접근 방식을 채택하여 소스 코드와 종속성을 사전 번들링하여 개발 중에 서버 시작 시간을 연장합니다. Vite의 효율적인 초기화에 비해 Webpack의 서버 설정 시간은 본질적으로 더 깁니다.
그러나 Vite의 주문형 로딩 접근 방식은 사용자가 추가 데이터, CSS 및 자산이 필요한 경로로 이동할 때 약간의 지연을 초래할 수 있습니다. 이는 이러한 리소스가 추가 번들링 단계를 요구하는 경우 특히 두드러집니다. 반대로 Webpack의 전략은 모든 사이트 데이터를 사용할 수 있도록 보장하여 개발 서버 내의 새 페이지로의 브라우저 탐색을 더욱 빠르게 만듭니다.
Vite는 기본 ESM을 통해 HMR을 사용하여 일부 번들링 작업을 브라우저로 오프로드하여 서버 로드와 대기 시간을 줄입니다. 이는 전체 페이지를 다시 로드하지 않고도 빠른 업데이트를 보장하며, 이는 개발 중 실시간 피드백에 매우 중요합니다.
Webpack은 또한 HMR을 지원하여 실시간 업데이트를 가능하게 하고 개발 중에 애플리케이션 상태를 보존합니다. 그러나 기본 ES 모듈을 활용하는 데 있어 잠재적인 제한으로 인해 서버 로드 및 대기 시간이 높아질 수 있습니다.
캐싱은 저장된 자산을 재사용하여 웹 애플리케이션 성능을 향상하고 로드 및 빌드 시간을 줄이는 데 필수적입니다.
Vite의 캐싱은 파일 시스템 캐시 로 관리되며 package.json , lockfiles 및 vite.config.js 의 변경 사항에 따라 종속성을 업데이트합니다 . 해결된 종속성 요청을 캐시하여 페이지 다시 로드를 최적화합니다.
Webpack은 파일 시스템 캐싱 도 사용하여 감시 모드에서 수정된 파일을 지우고 비감시 모드에서 각 컴파일 전에 캐시를 제거하므로 최적의 캐싱을 위한 사용자 정의 구성이 필요합니다.
개발 서버 비교를 마무리하기 위해 Vite와 Webpack은 개발 서버에 대한 고유한 접근 방식을 제공합니다.
테스트 환경에는 다음이 포함됩니다.
번들링 시간을 비교하는 것부터 시작해 보겠습니다.
Vite [v4.4.11] | 웹팩 [v5.89.0] | |
---|---|---|
개발 첫 빌드 | 376ms | 6초 |
핫체인지 | 즉각적인 | 1.5초 |
프로덕션 빌드 | 2초 | 11초 |
Vite는 번들링 속도의 확실한 승자로 등장하여 빌드 시간을 대폭 단축합니다. Webpack은 구성 가능성과 강력한 개발 도구를 제공하지만 Vite에 비해 뒤떨어집니다.
Vite v4.4.11 | 웹팩 v5.89.0 | |
---|---|---|
제품 번들 크기 | 866kb | 934kb |
이 수치는 중간 정도의 종속성이 있는 중간 크기의 Vue.js 애플리케이션을 기반으로 합니다. 실제 번들 크기는 프로젝트 복잡성, 종속성 및 최적화 기술에 따라 달라질 수 있습니다.
Vite의 작은 번들 크기는 esbuild 및 기본 ES 모듈과의 효율적인 사전 번들링 때문입니다.
Webpack의 번들 크기는 다양한 구성 옵션과 플러그인을 통해 최적화될 수 있지만 일반적으로 포괄적인 번들링 프로세스로 인해 더 큰 번들을 생성합니다.
코드 분할은 코드를 더 작고 관리하기 쉬운 조각으로 나누어 필요할 때 정확하게 필요한 부분만 로드하는 데 사용되는 기본 기술입니다. 이렇게 하면 초기 로드 시간이 크게 줄어들고 리소스가 절약됩니다.
Vite가 Rollup을 사용하여 코드를 동적 로딩이나 다중 진입점과 같이 자동으로 별도의 청크로 분할하는 경우가 있으며,output.manualChunks 옵션을 통해 Rollup에 어떤 모듈을 별도의 청크로 분할할지 명시적으로 알려주는 방법이 있습니다 .
Vite의 사전 구성된 코드 분할 기능 외에도 Vite는 변수를 사용한 동적 가져오기도 지원합니다 .
const module = await import(`./dir/${kinsta}.js`)
Vite를 사용하면 개발자는 공식을 사용하여 공급업체 청크를 분할할 수도 있습니다 splitVendorChunkPlugin()
.
import { splitVendorChunkPlugin } from 'vite'
export default defineConfig({
plugins: [splitVendorChunkPlugin()],
})
모든 동적 가져오기 및 코드 분할로 인해 코드가 모듈 또는 청크로 구조화되는 것이 일반적이며 이러한 모듈 중 일부는 웹 애플리케이션의 여러 부분 간에 공유됩니다. Vite는 일반적인 청크를 인식하고 로딩 프로세스를 최적화합니다. 이를 더 잘 이해하기 위해 Vite 공식 문서 의 예를 살펴보겠습니다.
두 개의 비동기 청크에 필요한 공통 청크를 표시하는 그래프입니다. (이미지 출처 : VITE )
최적화 없이 사용자가 웹 애플리케이션의 섹션(공유 코드 Common Chunk C 를 사용하는 섹션 A 라고 함 )을 열면 브라우저는 섹션 A를 가져오는 것으로 시작합니다 . 섹션 A를 구문 분석하는 동안 Common Chunk C 가 필요하다는 것을 깨닫습니다 . 이를 위해서는 추가 네트워크 왕복이 필요하며 이로 인해 초기 페이지 로드 속도가 느려질 수 있습니다.
Entry (Section A) ---> async chunk A ---> common chunk C
반면에 Vite는 Async Chunk Loading Optimization 이라는 정교한 기능을 사용합니다 . 브라우저가 요구 사항을 발견할 때까지 기다리지 않습니다. 대신 적극적으로 대비합니다. 사용자가 섹션 A를 요청하면 Vite는 섹션 A 와 공통 청크 C를 동시에 보냅니다 . 필요한 청크를 병렬로 가져오면 로딩 프로세스 속도가 크게 향상됩니다.
Entry (Section A) ---> (async chunk A + common chunk C)
그러나 여기서 끝나지 않습니다. 공통 청크 C에는 자체 종속성이 있어 최적화되지 않은 시나리오에서 잠재적으로 추가 왕복이 발생할 수 있습니다. Vite는 이 측면을 간과하지 않습니다. 이러한 종속성을 엄격하게 분석하여 깊이에 관계없이 필요한 모든 것이 한 번에 효율적으로 로드되도록 합니다. 이는 추가 네트워크 왕복의 필요성을 없애고 응답성이 뛰어난 웹 애플리케이션을 보장합니다.
Vite의 비동기 청크 로딩 접근 방식은 필요한 모든 코드 청크를 병렬로 사전에 가져오고 제공함으로써 로딩 프로세스를 최적화합니다. 추가 네트워크 왕복을 제거하면 더 빠른 웹 경험이 가능해집니다. 이는 브라우저에 잘 준비된 여행 일정을 제공하여 불필요한 지연 없이 필요한 모든 리소스를 수신하도록 하는 것과 유사합니다.
Webpack의 경우 코드 분할에 사용할 수 있는 세 가지 일반적인 기술이 있습니다.
진입점:
이는 코드 조각을 분할하는 가장 쉬운 방법입니다. 구성 파일과 Webpack에 새 진입점을 정의하고 이를 별도의 청크로 추가할 수 있습니다.
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/separate-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
그러나 이 접근 방식에는 한계가 있습니다. 모듈을 다른 항목 청크로 가져오면 두 번들 모두에 포함되어 코드가 중복됩니다. 또한 필요할 때 핵심 애플리케이션 논리를 분할하는 데 적합하지 않습니다.
중복 방지entry[SplitChunksPlugin](https://webpack.js.org/plugins/split-chunks-plugin/)entry
: 또 다른 접근 방식은
종속성을 사용하거나
청크를 분할하여 중복성을 줄이는 데 도움이 됩니다. 다음은 종속성을 사용하여 코드 분할을 구성할 수 있는 방법의 예입니다
.
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: 'lodash',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
runtimeChunk: 'single',
},
};
: 마지막으로 Webpack은 주문형 코드 로딩에 유용한 기능인
동적 가져오기에 대한 ECMAScript 제안을 따르는 구문을 사용합니다. 이 방법은 더욱 유연하고 세분화되어 다양한 코드 분할 시나리오에 적합합니다.
const { default: _ } = await import('lodash');
또한 Webpack의 Magic Comments를 사용하여 청크의 이름을 설정하고, 지연 로드하고, 모듈 내보내기를 지정하고, 가져오기 우선순위를 설정할 수 있습니다.
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackExports: ["default", "named"] */
/* webpackFetchPriority: "high" */
'module'
);
트리 쉐이킹은 Vite와 Webpack 모두 JavaScript 번들의 크기를 줄이기 위해 사용하는 중요한 최적화 기술입니다.
Vite는 ES 모듈을 사용할 수 있을 뿐만 아니라 가져온 코드를 정적으로 분석하는 롤업을 활용합니다. 이는 Vite가 사용하지 않는 모듈의 모든 부분을 제외하여 번들 크기를 더 작게 만들 수 있음을 의미합니다. 예를 들어, 여러 기능이 있는 모듈이 있지만 그 중 하나만 사용하는 경우 Vite는 해당 기능만 번들에 포함합니다. 간단한 예는 다음과 같습니다.
ajax
에서 가져오려면 전체 파일을 가져와야 합니다.const utils = require('./utils');
const query = 'Kinsta';
// Use the 'ajax' method of the 'utils' object
utils.ajax(`https://api.example.com?search=${query}`).then(handleResponse);
importexport
Vite는 명시적 및 명령문을 사용하므로 사용되지 않는 코드를 감지하기 위해 자동화된 축소기에만 의존하지 않고도 매우 효과적인 트리 쉐이킹을 수행할 수 있습니다.import { ajax } from './utils';
const query = 'Kinsta';
// Call the 'ajax' function
ajax(`https://api.example.com?search=${query}`).then(handleResponse);
마지막으로 Vite의 경우 트리 쉐이킹을 위해 Rollup의 사전 구성된 옵션을 사용할 수 있습니다 .
Webpack은 트리 쉐이킹도 지원하지만 메커니즘이 다릅니다 . 코드의 종속성을 분석하고 번들링 프로세스 중에 사용되지 않는 부분을 제거합니다. 효과적이긴 하지만 특히 대규모 모듈이나 라이브러리를 처리할 때 Vite의 접근 방식만큼 철저하지 않을 수 있습니다.
또한 Webpack의 문서 에 따르면 . 프로덕션 환경에서 해당 파일에 의존하는 다른 코드가 있는 코드가 제거되지 않도록 파일을 부작용 없음 으로 표시해야 합니다 .
이를 수행하는 방법은 sideEffects
package.json 속성입니다.
{
"name": "kinsta-app",
"sideEffects": false
}
부작용을 정의하는 유사한 구성 옵션이 Vite의 롤업 옵션에도 존재한다는 점은 주목할 가치가 있습니다.
출처 : https://kinsta.com/blog/vite-vs-webpack/
Vite는...
1. 빠른 빌드! 빠른 ES Build 사용
2. 라이브러리 설치 직후 미리 bundle을 만들어 놓음
3. 소스코드 변경 시 필요한 것만 수정.
모듈 시스템 : https://www.youdad.kr/js-module-system/
마이그레이션 후기 : https://blog.seiker.kr/vite-migration-from-create-react-app/
마이그레이션 후기 2 : https://programmerplum.tistory.com/189
결국 vite 사용하는 이유는 ES build 이용하여 사전 번들링으로 빠른 빌드 가능, 개발서버 페이지 로드 속도 향상, 소스 코드 변경시 필요한 것만 수정이 가능. Vite의 단점은 브라우저가 ESM을 지원하지 않는 경우 위의 장점을 활용할 수 없다는 것이다.위의 단점은 IE의 지원 종료로 인해 해결되었다.
발표 중간에 런칭되어 있는 실무 React 프로젝트를 가져와서 직접 마이그레이션 해보았습니다. Cra에서 Vite로 넘어가는 것은 어렵지 않고, 생각보다 간단한 작업입니다.
yarn remove react-scripts @craco/craco webpack
yarn add vite @vitejs/plugin-react vite-tsconfig-paths vite-plugin-svgr
"start": "vite",
"build": "tsc && vite build",
<script type="module" src="/src/index.tsx"></script>
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import vitePluginSvgr from 'vite-plugin-svgr';
export default defineConfig({
base: '',
plugins: [react(), viteTsconfigPaths(), vitePluginSvgr()],
server: {
open: true,
port: 3000,
},
});
process.env.*
대신에 import.meta.env.*
를 사용합니다REACT_APP_*
대신에 VITE_APP_*
를 사용합니다."target": "ESNext",
"types": ["vite/client"],
<reference types="vite/client" />
ESM Native : ESM은 모듈화 문법인 import
, export
를 별도의 도구 없이 브라우저 자체에서 소화해 낼 수 있는 모듈 방식을 의미합니다. 만약 아래와 같은 코드를 웹팩과 같은 번들러 없이 브라우저에서 실행하면 에러가 발생합니다.
Tree Shaking : 임포트 되었지만 실제로는 사용되지 않는 코드를 분석하고 삭제하는 코드 최적화 기술.
Cold Start : 콜드 스타트는 최초로 실행되어 이전에 캐싱한 데이터가 없는 경우를 의미합니다.
Chunk : 하나의 번들 파일을 효과적으로 다루기 위해 여러가지의 파일로 다시 나누는 것
Crawling : 크롤링(crawling) 은 웹 페이지를 그대로 가져와서 거기서 데이터를 추출해 내는 행위다.
※ 해당 포스팅은, 상업적인 용도가 아닌 학습을 위한 내용입니다.
자료출처가 빠진 부분이 있을 수 있습니다. 코멘트 남겨주시면 추가하도록 하겠습니다.