본문으로 바로가기

Vite 정리

category JavaScript/Vite 2022. 8. 4. 17:43

Vite

  • 프랑스어로 '빠르다(Quick)'를 의미 
  • 발음은 'veet'와 비슷한 /vit/

  • 빠르고 간결한 모던 웹 프로젝트 개발 경험에 초점을 맞춰 탄생한 빌드 도구
  • 두 가지 컨셉을 중심으로 하고 있다.
    1. 개발 시 네이티브 ES Module을 넘어 더욱 다양한 기능을 제공합니다. 가령, Hot Module Replacement (HMR)과 같은 것들 말이죠.
    2. 번들링 시, Rollup 기반의 다양한 빌드 커맨드를 사용할 수 있습니다. 이는 높은 수준으로 최적화된 정적(Static) 리소스들을 배포할 수 있게끔 하며, 미리 정의된 설정(Pre-configured)을 제공합니다.
  • vite는 기본적으로 최적화된 설정을 제공하지만, Plugin API 또는 JavaScript API를 이용할 수 있습니다. (물론 TypeScript 역시 지원합니다.)

 

지원하는 브라우저

기본적으로 네이티브 ES 모듈 및 네이티브 ESM의 동적 Import를 지원하는 브라우저를 타깃으로 빌드를 수행합니다. 레거시 브라우저는 @vitejs/plugin-legacy 플러그인을 통해 지원이 가능합니다. 이와 관련해 좀 더 자세한 사항은 프로덕션 버전으로 빌드하기 섹션에서 다룹니다.

  • Chrome >= 87
  • Firefox >= 78
  • Safari >= 13
  • Edge >= 88

 

지원하는 템플릿

JavaScript TypeScript
vanilla vanilla-ts
vue vue-ts
react react-ts
preact preact-ts
lit lit-ts
svelte svelte-ts

 

Vite의 환경 변수

Vite는 import.meta.env 객체를 이용해 환경변수에 접근할 수 있도록 하고 있으며, 아래와 같은 환경 변수에 접근이 가능합니다

  • import.meta.env.MODE: {string} 현재 앱이 동작하고 있는 모드입니다.
  • import.meta.env.BASE_URL: {string} 앱이 제공되는 베이스 URL이며, 이 값은 base 설정에 의해 결정됩니다.
  • import.meta.env.PROD: {boolean} 앱이 프로덕션에서 실행 중인지 여부입니다.
  • import.meta.env.DEV: {boolean} 앱이 개발 환경에서 실행 중인지 여부이먀, 항상 import.meta.env.PROD와 반대되는 값을 가집니다.
  • import.meta.env.SSR: {boolean} 앱이 서버에서 실행중인지 여부입니다.

 

프로덕션에서의 환경 변수

프로덕션에서는 환경 변수가 모두 정적으로 교체됩니다.

따라서 항상 환경 변수는 정적으로 참조해야만 하며, import.meta.env[key]와 같은 동적 참조는 작동하지 않습니다.

 

JavaScript 문자열 및 Vue 템플릿에서도 마찬가지로 환경 변수는 모두 정적으로 교체됩니다.

이로 인해 환경 변수와 동일한 네이밍을 갖지만 실제로는 환경 변수가 아닌 경우, 의도하지 않은 동작이 발생될 수 있습니다.

가령 "process.env.NODE_ENV"가 "development": ''로 교체되는 경우, Missing Semicolon 또는 Unexpected token과 같은 오류가 표시됩니다.

  • JavaScript 문자열의 경우, 'import.meta\u200b.env.MODE'와 같이 너비가 0인 공백으로 문자열을 분리
  • Vue 템플릿 또는 JavaScript 문자열로 컴파일되는 HTML의 경우, <wbr> 태그를 이용(import.meta.<wbr>env.MODE)

.env 파일들

Vite는 dotenv를 이용해 환경 변수가 저장된 디렉터리 내 아래의 파일에서 환경 변수를 가져옵니다.

.env                # 모든 상황에서 사용될 환경 변수
.env.local          # 모든 상황에서 사용되나, 로컬 개발 환경에서만 사용될(Git에 의해 무시될) 환경 변수
.env.[mode]         # 특정 모드에서만 사용될 환경 변수
.env.[mode].local   # 특정 모드에서만 사용되나, 로컬 개발 환경에서만 사용될(Git에 의해 무시될) 환경 변수
환경 변수 우선순위
.env.production과 같이 특정 모드에 대한 환경 변수는 일반적인 환경 변수(.env)보다 높은 우선순위를 갖습니다.
또한 Vite가 실행될 때 이미 존재하던 환경 변수는 가장 높은 우선 순위를 가지며, .env 파일로 인해 덮어씌워지지 않습니다.
가령 VITE_SOME_KEY=132 vite build 와 같이 말이죠
.env 파일은 Vite가 시작될 때 가져와집니다. 따라서 파일을 변경했다면 서버를 재시작해주세요.

 

마찬가지로 이렇게 설정된 환경 변수는 import.meta.env 객체를 통해 문자열 형태로 접근이 가능합니다.

참고로, Vite에서 접근 가능한 환경 변수는 일반 환경 변수와 구분을 위해 VITE_ 라는 접두사를 붙여 나타냅니다. 가령, 아래와 같이 환경 변수를 정의했다면:

VITE_SOME_KEY=123
DB_PASSWORD=foobar

VITE_SOME_KEY 변수만이 import.meta.env.VITE_SOME_KEY로 접근이 가능합니다. (DB_PASSWORD는 노출되지 않습니다.)

console.log(import.meta.env.VITE_SOME_KEY) // 123
console.log(import.meta.env.DB_PASSWORD) // undefined

만약 환경 변수에 대한 접미사(Prefix)를 커스터마이즈 하고자 한다면, envPrefix 옵션을 참고해주세요.

 

보안 권고 사항
1) .env.*.local 파일은 오로지 로컬에서만 접근이 가능한 파일이며, 데이터베이스 비밀번호와 같은 민감한 정보를 이 곳에 저장하도록 합니다. 또한 .gitignore 파일에 *.local 파일을 명시해 Git에 체크인되는 것을 방지하도록 합니다.
2) Vite 소스 코드에 노출되는 모든 환경 변수는 번들링 시 포함되게 됩니다. 따라서, VITE_* 환경 변수에는 민감한 정보들이 포함되어서는 안됩니다.

 

Vite의 모드

기본적으로, dev 명령으로 실행되는 개발 서버는 development 모드로 동작하고, build 명령으로 실행되는 경우에는 production 모드로 동작합니다.

다시말해 vite build 명령을 실행하게 되면 .env.production에 정의된 환경 변수를 불러오게 됩니다.

# .env.production
VITE_APP_TITLE=My App

앱 내에서는 My App 이라는 문자열이 import.meta.env.VITE_APP_TITLE 환경 변수를 통해 나타나지게 됩니다.

 모드 라는 개념은 단지 개발(development) 모드나 프로덕션(production) 모드 뿐만 아니라 더 넓은 개념을 다루고 있습니다. 가령, 프로덕션 모드와 비슷한(그러나 일부 다른 환경 변수를 갖는) "staging" 이라는 모드가 필요하다면 어떻게 해야 할까요?

방법은 간단하게도, 그저 --mode 옵션을 전달해 사용할 모드를 지정하면 됩니다. 예를 들어 "staging" 모드로 서버를 동작(배포)하고 싶다면 아래와 같이 명령을 실행해주면 됩니다.

vite build --mode staging

"staging" 모드에서 사용될 환경 변수는 .env.staging 파일에 정의합니다.

# .env.staging
NODE_ENV=production
VITE_APP_TITLE=My App (staging)

위와 같이 환경 변수를 설정하게 되면 "staging" 모드에서는 My App (staging) 이라는 문자열이 import.meta.env.VITE_APP_TITLE 환경 변수를 통해 나타나지게 됩니다.

 

* Vite를 사용해야하는 이유

기존의 문제점들

브라우저에서 ESM(ES Modules)을 지원하기 전까지는, JavaScript 모듈화를 네이티브 레벨에서 진행할 수 없었습니다.

따라서 개발자들은 "번들링(Bundling)*"이라는 우회적인 방법을 사용할 수 밖에 없었습니다. (* 번들링: 모듈화된 소스 코드를 브라우저에서 실행할 수 있는 파일로 한데 묶어 연결해주는 작업)

Webpack, Rollup 그리고 Parcel과 같은 도구는 이런 번들링 작업을 진행해줌으로써 프런트엔드 개발자의 생산성을 크게 향상시켰습니다.

하지만 애플리케이션이 점점 더 발전함에 따라 처리해야 하는 JavaScript 모듈의 개수도 극적으로 증가하고 있습니다.

심지어 수천 개의 모듈이 존재하는 것도 대규모 프로젝트에서는 그리 드믄 일이 아닙니다.

이러한 상황에서 JavaScript 기반의 도구는 성능 병목 현상이 발생되었고, 종종 개발 서버를 가동하는 데 비합리적으로 오랜 시간을 기다려야 한다거나 HMR을 사용하더라도 변경된 파일이 적용될 때 까지 수 초 이상 소요되곤 했습니다.

이와 같은 느린 피드백 루프는 개발자의 생산성과 행복에 적지 않은 영향을 줄 수 있습니다.

Vite는 이러한 것에 초점을 맞춰, 브라우저에서 지원하는 ES Modules(ESM) 및 네이티브 언어로 작성된 JavaScript 도구 등을 활용해 문제를 해결하고자 합니다.

지루할 정도로 길었던 서버 구동

콜드-스타트 방식으로 개발 서버를 구동할 때, 번들러 기반의 도구의 경우 애플리케이션 내 모든 소스 코드에 대해 크롤링 및 빌드 작업을 마쳐야지만이 실제 페이지를 제공할 수 있습니다. (* 콜드-스타트: 최초로 실행되어 이전에 캐싱한 데이터가 없는 경우를 의미)

vite는 이 문제를 dependencies 그리고 source code 두 가지 카테고리로 나누어 개발 서버를 시작하도록 함으로써 해결했습니다.

  • Dependencies: 개발 시 그 내용이 바뀌지 않을 일반적인(Plain) JavaScript 소스 코드입니다. 기존 번들러로는 컴포넌트 라이브러리와 같이 몇 백 개의 JavaScript 모듈을 갖고 있는 매우 큰 디펜던시에 대한 번들링 과정이 매우 비효율적이었고 많은 시간을 필요로 했습니다.
  • Vite의 사전 번들링 기능은 Esbuild를 사용하고 있습니다. Go로 작성된 Esbuild는 Webpack, Parcel과 같은 기존의 번들러 대비 10-100배 빠른 번들링 속도를 보였습니다.
  • Source code: JSX, CSS 또는 Vue/Svelte 컴포넌트와 같이 컴파일링이 필요하고, 수정 또한 매우 잦은 Non-plain JavaScript 소스 코드는 어떻게 할까요? (물론 이들 역시 특정 시점에서 모두 불러올 필요는 없습니다.)
  • vite는 Native ESM을 이용해 소스 코드를 제공하도록 하고 있습니다. 다시말해, 브라우저가 곧 번들러라는 말입니다. vite는 그저 브라우저의 판단 아래 특정 모듈에 대한 소스 코드를 요청하면 이를 전달할 뿐입니다.

느렸던 소스 코드 갱신

기존의 번들러 기반으로 개발을 진행할 때, 소스 코드를 업데이트 하게 되면 번들링 과정을 다시 거쳐야 했었습니다.

따라서 서비스가 커질수록 소스 코드 갱신 시간 또한 선형적으로 증가할 수 밖에 없었죠.

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

"모든 파일"을 번들링 하고, 이를 다시 웹 페이지에서 불러오는 것이 얼마나 비효율적인 것인지 느껴지시나요? 이러한 이슈를 우회하고자 HMR(Hot Module Replacement)* 이라는 대안이 나왔으나, 이 역시 명확한 해답은 아니었습니다. (* HMR: 앱을 종료하지 않고 갱신된 파일만을 교체하는(Replacement) 방식. 다만 마찬가지로 앱 사이즈가 커질수록 선형적으로 갱신에 필요한 시간이 증가한다.)

 

물론, vite는 HMR을 지원합니다. 번들러가 아닌 ESM을 이용해서 말이죠.

어떤 모듈이 수정되면 vite는 그저 수정된 모듈과 관련된 부분만을 교체할 뿐이고, 브라우저에서 해당 모듈을 요청하면 교체된 모듈을 전달할 뿐입니다.

전 과정에서 완벽하게 ESM을 이용하기에, 앱 사이즈가 커져도 HMR을 포함한 갱신 시간에는 영향을 끼치지 않습니다.

 

또한 vite는 HTTP 헤더를 이용해 퍼포먼스를 한 단계 높였습니다.

필요에 따라 소스 코드는 304 Not Modified로, 디펜던시는 Cache-Control: max-age=31536000,immutable을 이용해 캐시되도록 함으로써, 한 번의 요청이라도 덜 하도록 말이죠.

 

이렇게나 빠른 Vite를 사용하지 않을 이유가 있나요?

배포 시 번들링 과정이 필요한 이유

이렇게나 다양한 이점을 가진 ESM 이지만, 비교적 최근에야 지원되기 시작했기에 실제 배포 시 사용이 불가능한 브라우저 환경이 있을 수 있습니다.

또한 네트워크를 통해 필요한 시점에 해당 모듈을 요청하기에, HTTP/2를 이용하더라도 오버헤드가 발생될 수 있구요.

따라서, 모든 사용자 경험을 충족시키기 위해서라도 배포 시에는 번들링 과정이 필수적입니다. 이것이 바로 vite가 미리 설정된 빌드 커맨드를 이용하고, 빌드 퍼포먼스 최적화를 진행하는 이유입니다.

왜 번들링 시에는 Esbuild를 사용하지 않나요?

Esbuild는 굉장히 빠른 속도로 번들링이 가능하다는 장점이 있으나, 번들링에 필수적으로 요구되는 기능인 코드 분할(Code-splitting) 및 CSS와 관련된 처리가 아직 미비합니다.

Vite에서 사용중인 Rollup은 이에 대해 조금 더 검증되었고 유연한 처리가 가능하게끔 구현되어 있기에 현재로서는 이를 사용하고 있으며, 향후 Esbuild가 안정화 되었을 때 어떤 프로덕션 번들링 도구가 적절할 것인지 다시 논의할 예정입니다.

 

* Vite와 다른 도구의 차이점

WMR

Preact 팀의 WMR은 유사한 기능 세트를 제공하며, Rollup의 플러그인 인터페이스에 대한 Vite 2.0의 지원은 그것에 영감을 받았습니다.

WMR은 주로 Preact 프로젝트를 위해 설계되었으며, 사전 렌더링과 같은 보다 통합된 기능을 제공합니다. 범위 측면에서는 Preact 자체와 같이 컴팩트한 크기를 강조하는 Preact 메타 프레임워크에 더 가깝습니다. Preact를 사용한다면, WMR은 보다 더 미세하게 조정된 경험을 제공할 것입니다. 그러나 WMR이 다른 프레임워크에 대한 지원 우선순위를 정할 것 같지는 않습니다.

@web/dev-server

@web/dev-server (이전 es-dev-server)는 훌륭한 프로젝트이며, 이는 Vite 1.0의 Koa 기반 서버 설정에 영감을 주었습니다.

@web/dev-server는 범위 측면에서 약간 낮은 수준입니다. 공식 프레임워크 통합을 제공하지 않으며 프로덕션 빌드를 위한 Rollup 구성을 수동으로 설정해야 합니다.

전반적으로 Vite는 더 독창적인 작업 흐름를 제공하는 것을 목표로 하는, 보다 독단적이고 높은 수준의 도구입니다. 즉, @web 총합 프로젝트에는 Vite 사용자에게도 도움을 줄 수 있는 다른 많은 훌륭한 도구가 포함되어 있습니다.

Snowpack

Snowpack은 Vite와 유사하게 번들링을 하지 않는 네이티브 ESM 개발 서버입니다. 다만 이 프로젝트는 더 이상 관리되지 않으며, Snowpack 팀은 현재 Vite를 통해 제공되는 정적 사이트 빌더인 Astro를 만들고 있습니다. Astro 팀은 Vite 생태계에서 활발하게 활동하고 있으며, Vite를 개선하는 데 도움을 주고 있습니다.

다른 세부 구현 사항을 제외하고, 두 프로젝트는 기존 도구에 비해 기술적 이점 측면에서 많은 부분을 공유했습니다. Snowpack v1(현재는 esinstall)에서 Vite의 디펜던시 사전 번들링에 대한 영감을 얻기도 했습니다. 두 프로젝트 간 주요한 차이점은 다른 빌드 도구와의 차이점 (Vite v2)를 참고해주세요.

 

 

 

 

참고: https://vitejs-kr.github.io/

반응형

'JavaScript > Vite' 카테고리의 다른 글

Vite에 jsconfig 설정하기  (0) 2022.08.05
Vite 프로젝트 생성해보기(with Tailwind CSS)  (0) 2022.08.05