Vite(빝) 쓰면 리액트 10배 빨라진다고?

임철종·2023년 2월 27일
1
post-thumbnail

Vite(빝)이 뭔데?

Vite 가이드에서는 이렇게 설명하고 있다.

Vite(프랑스어로 "빠르다(Quick)"를 의미하며, 발음은 "veet"와 비슷한 /vit/
입니다.)은 빠르고 간결한 모던 웹 프로젝트 개발 경험에 초점을 맞춰 탄생한 빌드 도구이며, 두 가지 컨셉을 중심으로 하고 있습니다.

  • 개발 시 네이티브 ES Module을 넘어 더욱 다양한 기능을 제공합니다. 가령, Hot Module Replacement (HMR)과 같은 것들 말이죠.
  • 번들링 시, Rollup 기반의 다양한 빌드 커맨드를 사용할 수 있습니다. 이는 높은 수준으로 최적화된 정적(Static) 리소스들을 배포할 수 있게끔 하며, 미리 정의된 설정(Pre-configured)을 제공합니다.

vite는 기본적으로 최적화 된 설정을 제공하지만, Plugin API 또는 JavaScript API를 이용할 수 있습니다. (물론 TypeScript 역시 지원하구요.)

Why?

예전에는 HTML, CSS, JS 파일을 모두 서버에 올리면 그것으로 배포가 끝이었다.
하지만 JS 라이브러리를 사용하기 시작하면서 편하게 사용하기 위해 npm(node package manager)이 생겼고 여러 라이브러리를 import하여 사용하기 시작했다.

문제

  1. 라이브러리가 담긴 node_modules 폴더가 너무 커져서 서버에 올리는 데에 많은 비용이 든다.
  2. import/require 문법은 브라우저 친화적이지 않다. (호환성이나 지원하지 않는 문법)

해결

모든 JS 파일을 하나로 합친다. (bundling) ex) webpack

  • 실제 필요한 코드만 찝어서 합쳐주기 때문에 용량이 절약된다.
  • 결과물은 JS 파일 하나이므로 import/require 문법을 사용하지 않는다.
  • npm과 webpack을 사용하면 실시간으로 번들링을 할 수 있다.

또 다른 문제

사용하는 npm 라이브러리가 많아질수록 webpack의 번들링이 오래걸린다.

2세대 번들링 툴 등장


Vite?

  1. Go 언어 기반으로 만든 매우 빠른 esbuild 라이브러리를 사용한다.
  2. 라이브러리를 설치하자마자 미리 bundle을 만들어 놓는다.
  3. 소스코드는 필요한 것만 건드린다.
  4. js 하나만 수정될 경우 변경사항만 반영한다. (Hot Module Replacement a.k.a HMR)

특징

vite는 depenendciessoure code 두가지 카테리로 나누어 개발 서버를 시작하도록 하여 서버 구동에 걸리는 시간을 단축했다.

  • Dependencies
    개발 시 그 내용이 바뀌지 않을 일반적인 JS 소스 코드
    기존 번들러로는 큰 디펜던시에 대한 번들링 과정이 매우 비효율적이고 많은 시간을 필요로 했다.
vite의 pre-bundling 기능은 esbuild를 사용하고 있다.
Go 기반으로 만든 esbuild는 기존 번들러인 Webpack, Parcel 대비 10-100배 빠른 번들링 속도를 보였다.
  • Sourece Code
    JSX, CSS 또는 Vue/Svelte 컴포넌트와 같이 컴파일링이 필요하고 수정도 잦은 소스 코드
vite는 native ESM을 이용해 소스코드를 제공하도록 한다.
브라우저가 곧 번들러라는 뜻
브라우저가 특정 모듈에 대한 소스 코드를 요청하면 이를 전달할 뿐이다.
조건부 동적 import 이후의 코든느 현재 화면에서 실제로 사용되어야만 처리한다.

vite 공식 홈페이지

느렸던 소스 코드 업데이트
기존의 번들러 기반으로는 소스 코드를 업데이트하면 전체 번들링을 다시 해야 했기 때문에 서비스가 커질수록 시간이 더 걸릴 수 밖에 없었다.

일부 번들러의 경우 메모리 상에서 이를 진행하여 실제로 갱신에 영향을 받는 파일들만을 새로이 번들링 하게끔 했으나, 결국 처음에는 모든 파일에 대한 번들링이 필요했다.

vite는 ESM을 이용한 HMR(Hot Module Replacement)을 지원한다. 어떤 모듈이 수정되면 vite는 수정된 모듈과 관련된 부분만 교체하고 브라우저에서 해당 모듈을 요청하면 교체된 모듈을 전달한다. 전 과정에서 ESM을 완벽하게 이용하므로 앱 사이즈가 커져도 갱신 시간에는 영향을 끼치지 않는다.

또한 vite는 HTTP 헤더를 이용해 소스코드는 304 Not Modified, 디펜던시는 Cache-Control: max-age=31536000,immutable을 이용해 캐시되도록 하여 한번으 요청이라도 줄여 퍼포먼스를 높였다.

지원하는 기능들

npm을 이용한 디펜던시 import와 사전 번들링

import {method} from 'some-where'
이 코드는 네이티브 es에서 모듈의 위치를 찾을 수 없기 때문에 정상적으로 실행되지 않는다.
하지만 vite은 다음을 기준으로 모듈을 가져오기 때문에 정상적으로 실행된다.

  • Vite를 통해 ESM 스타일로 사전에 번들링 된 CommonJS 및 UMD(Universal Module Definition: CommonJS와 AMD 스타일의 모듈을 둘 다 지원하는 모듈 형태)모듈은 esbuild를 통해 이루어지며, JavaScript 기반의 다른 번들러보다 빠른 cold-start가 가능하다.
  • /node_modules/.vite/deps/my-dep.js?v=f2sf5t3ed와 같이 URL을 이용해 ESM을 지원하는 브라우저에서 모듈을 가져올 수 있도록 import 구문을 수정한다.

추가로 디펜던시는 반드시 캐싱된다.
vite는 HTTP 헤더를 이용해 요청한 디펜던시를 브라우저에서 캐시하도록 한다.

HMR (Hot Module Replacement)

vite에서는 기본적으로 ESM을 통해 HMR API를 제공한다. HMR 기능이 있는 프레임워크는 API를 활용하여 페이지를 다시 로드하거나 상태를 날려버리지 않고 즉각적이고 정확한 업데이트를 제공한다. vite는 Vue Single File Components, React Fast Refresh 또는 @prefresh/vite 와 같은 First-party HMR 모듈을 제공한다.

물론,create-vite에서 제공하는 템플릿에 기본적으로 HMR 모듈이 포함되어 있다.

TypeScript

.ts 파일에 대한 컴파일 및 import 역시 지원한다.

vite는 ts 파일에 대하여 트랜스파일링만 수행한다. (타입 검사는 IDE와 빌드 프로세스에서 수행된다.)

기본적으로 두 작업이 다르기 때문인데, 트랜스파일링은 파일 단위로 작동할 수 있으며, vite의 컴파일 모델과 일치한다. 하지만 타입 검사는 전체 모듈 그래프에 대한 탐색이 필요하므로 vite의 파이프라인에 타입 검사를 추가하게 되면 속도 이점이 사라지기 때문이다.

vite의 TypeScript 컴파일링은 esbuild를 이용하며, TypeScript 소스 코드를 JavaScript 소스 코드로 변환하는 작업에 대해 tsc 대비 약 20~30배 정도 빠른 퍼포먼스를 보이고 있다. (HMR은 50ms 미만)

ClientTypes
vite는 기본적으로 Node.js API 기반의 타입 시스템을 차용하고 있기 때문에 Client-side의 환경을 위해 Shim을 구성하고자 한다면 d.ts 선언 파일을 추가해야한다.
/// <reference types="vite/cleint" />
또는, tsconfigcompilerOptions.types 옵션에 vite/client를 명시해 줄 수도 있다.

  • .svg와 같은 에셋
  • vite를 통해 주입되는 import.meta.env에 명시된 환경 변수 타입들
  • import.meta.hot에 명시된 HMR API 타입들

ex

declare module '*.svg' {
  const content: React.FC<React.SVGProps<SVGElement>>
  export default content
}

/// <reference types="vite/client" />

Vue

vite는 기본적으로 vue를 지원하고 있다.

JSX

.jsx.tsx 사용 가능. 마찬가지로 esbuild를 통해 컴파일링한다.

CSS

.css파일을 import 할 때, 기본적으로 HMR을 위해 <style> 태그로 변환되어 불러온다.

css @import와 URL 재정의(Rebasing)
vite는 postcss-import를 이용해 css의 @import를 처리한다. 또한, CSS url()로 참조되는 모든 리소스에 대해 별다른 설정 없이 base를 맞춰주는 Rebasing 역시 진행한다.

별칭을 이용한 @import도 지원하며, 모든 것은 Sass나 Less에서도 사용이 가능하다.


PostCSS
만약 프로젝트에 PostCSS 설정 파일이 존재한다면 vite는 이를 이용해 모든 CSS 파일에 해당 설정을 적용한다.

CSS의 최소화는 PostCSS 이후에 진행되며, build.cssTarget 옵션을 이용해 설정할 수 있다.


CSS Modules
.module.css 확장자로 끝나는 모든 CSS 파일은 CSS 모듈 파일로 취급하고 일반적인 JS 모듈처럼 사용이 가능하다.

Static Asset

정적 에셋을 import 하는 경우, 이에 대한 Public URL이 반환된다.

import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl

URL 쿼리를 이용해 에셋을 가져올 때 어떻게 이를 가져올 것인지 명시할 수도 있다.

URL로 에셋 가져오기

import assetAsURL from './asset.js?url'

String 타입으로 에셋 가져오기

import assetAsString from './shader.glsl?raw'

JSON

JSON 파일은 바로 Import가 가능하다.

객체 형태로 가져오기

import json from './example.json'

필드를 지정해 가져오기

import { field } from './example.json'

Optimizations

CSS 코드 분리


마침

이번에 진행한 프로젝트에서 선임분께서 vite라는 번들링 툴을 사용한 것을 보고 어떠한 문제로 탄생하게 된 툴이며 어떤 해결책을 사용했는지 궁금하여 공식 가이드를 보며 정리해봤다.

어떤 문제가 있었고 그것을 해결하기 위해 어떤 방법을 사용했는지 아는 것은 늘 나를 설레게 한다.

비록 문제를 해결하기 위해 또 다른 문제가 생기기도 하고 해결해 나감으로 또 새롭게 알아야 할 것들이 너무나도 많아졌다지만, 우리는 언제나 그래왔듯 방법을 찾을 것이지 않은가?

profile
🌑🌘🌗🌖🌕

0개의 댓글