Skip to content

May 9th, 2024

Vite Meetup SF

v4에서 마이그레이션하기

Node.js 지원

Vite는 더 이상 EOL에 도달한 Node.js 14 / 16 / 17 / 19를 지원하지 않습니다. Node.js 18 / 20+이 필요합니다.

Rollup 4

Vite는 이제 Rollup 4를 사용하며, 이 변경 사항은 특정 부분에 영향을 끼칠 수 있습니다.

  • Import assertion(assertions 속성)은 import attributes(attributes 속성)으로 표현을 바꾸었습니다.
  • Acorn 플러그인을 더 이상 지원하지 않습니다.
  • Vite 플러그인의 this.resolve skipSelf 옵션은 이제 기본적으로 true입니다.
  • Vite 플러그인의 this.parse는 현재 일시적으로 allowReturnOutsideFunction 옵션만을 지원합니다.

build.rollupOptions에서 빌드와 관련되어 바뀐 부분을 확인하려면, Rollup 4 릴리즈 노트의 변경 사항을 참고해 주세요.

TypeScript를 사용한다면, Rollup 4에서 요구하는 것과 같이 moduleResolution: 'bundler' (또는 node16/nodenext)를 설정해 주세요. 또는 skipLibCheck: true를 설정할 수도 있습니다.

CJS Node API 사용 중단

Vite의 CJS Node API는 더 이상 제공되지 않습니다. require('vite') 호출 시 경고가 나타나며, 이 대신 파일이나 프레임워크를 업데이트하여 Vite의 ESM 빌드를 가져오도록 해야 합니다.

표준 Vite 프로젝트에서 다음을 확인해 주세요:

  1. vite.config.js 파일에서 ESM 문법을 사용하고 있습니다.
  2. 가장 가까운 package.json 파일에 "type": "module"이 있거나 .mjs/.mts 확장자(예: vite.config.mjs 또는 vite.config.mts)를 사용하고 있습니다.

다른 프로젝트의 경우, 몇 가지 일반적인 접근 방식이 있습니다:

  • ESM을 기본값으로 설정하고, 필요한 경우 CJS를 사용: 프로젝트 package.json"type": "module"을 추가하세요. 이후 모든 *.js 파일은 ESM으로 해석되며 ESM 문법을 사용해야 합니다. 다만 확장자가 .cjs인 파일은 CJS로 해석됩니다.
  • CJS를 기본값으로 유지하고, 필요한 경우 ESM을 사용: 프로젝트 package.json"type": "module"이 없다면, 모든 *.js 파일은 CJS로 해석됩니다. 다만 확장자가 .mjs인 파일은 ESM으로 해석됩니다.
  • Vite를 동적으로 임포트: CJS를 계속 사용해야 하는 경우, import('vite')를 사용하여 Vite를 동적으로 임포트할 수 있습니다. 이를 위해 코드가 async 컨텍스트에서 작성되어야 하지만, Vite의 API가 대부분 비동기적이기 때문에 일반적으로 문제가 되지 않습니다.

자세한 내용은 트러블슈팅 가이드를 참조하세요.

defineimport.meta.env.* 치환 방식 변경

Vite 4에서 defineimport.meta.env.* 기능은 개발과 빌드 단계에서 서로 다른 치환 방식을 사용하고 있습니다:

  • 개발 단계에서는 두 기능 모두 globalThisimport.meta에 전역 변수로 주입됩니다.
  • 빌드 단계에서는 두 기능 모두 정규식을 사용하여 정적으로 치환됩니다.

이에 따라 변수 접근 시 개발과 빌드 단계에서 일관성이 없어지며, 때로는 빌드가 실패하기도 합니다. 예를 들어:

js
// vite.config.js
export default defineConfig({
  define: {
    __APP_VERSION__: JSON.stringify('1.0.0'),
  },
})
js
const data = { __APP_VERSION__ }
// dev: { __APP_VERSION__: "1.0.0" } ✅
// build: { "1.0.0" } ❌

const docs = 'I like import.meta.env.MODE'
// dev: "I like import.meta.env.MODE" ✅
// build: "I like "production"" ❌

Vite 5에서는 esbuild를 사용해 빌드 시 치환하는 방식으로 일관성을 유지하도록 하였습니다.

이 변경 사항은 대부분의 환경설정에는 영향을 미치지 않습니다. 이미 define 값은 esbuild의 문법을 따라야 한다는 사실이 문서화되어 있기 때문입니다:

esbuild와의 일관성을 유지하기 위해, 표현식은 JSON 객체(null, boolean, number, string, array, 또는 object)이거나 단일 식별자여야 합니다.

만약 값을 직접 정적으로 치환하는 것을 선호한다면, @rollup/plugin-replace 플러그인을 사용해 주세요.

일반 변경 사항

이제 SSR 외부화 모듈은 프로덕션과 일치

Vite 4에서는 상호 운용성을 위해 SSR 외부화 모듈이 .default.__esModule로 접근하도록 래핑 되었으나, 이는 런타임 환경(예: Note.js)에서 로드될 때 프로덕션에서의 동작과 일치하지 않았기에, 일관성 측면에서 파악하기 어려운 문제를 발생시켰습니다. 참고로 기본적으로 링크되지 않은 모든 프로젝트 디펜던시는 SSR 외부화됩니다.

Vite 5에서는 프로덕션과 일치하도록 .default.__esModule 처리를 제거했습니다. 일반적으로 올바르게 패키징된 디펜던시에는 영향을 미치지 않으나, 모듈 로드에 문제가 발생하는 경우 다음 방법을 시도해 볼 수 있습니다:

js
// Before:
import { foo } from 'bar'

// After:
import _bar from 'bar'
const { foo } = _bar
js
// Before:
import foo from 'bar'

// After:
import * as _foo from 'bar'
const foo = _foo.default

이 변경 사항은 Node.js 동작과 일치하므로, Node.js에서 임포트해 테스트할 수도 있습니다. 만약 이전 동작을 유지하고자 한다면, legacy.proxySsrExternalModulestrue로 설정하세요.

worker.plugins 옵션은 이제 함수를 전달받음

Vite 4에서의 worker.plugins 옵션은 플러그인의 배열((Plugin | Plugin[])[])을 전달받았습니다. Vite 5에서는 플러그인의 배열을 반환하는 함수(() => (Plugin | Plugin[])[])를 전달해야 합니다. 병렬 워커 빌드를 보다 일관되고 예측 가능하게 실행하기 위해 이와 같이 변경되었습니다.

.를 포함하는 경로가 index.html로 폴백되도록 허용

Vite 4에서는 appType이 기본값인 spa로 설정되어 있더라도 .을 포함하는 경로에 접근하면 index.html로 폴백되지 않았습니다. Vite 5에서는 index.html로 폴백됩니다.

이미지 경로를 존재하지 않는 파일(예: <img src="./file-does-not-exist.png">)로 지정해도 더 이상 브라우저에서 404 에러 메시지를 콘솔에 표시하지 않습니다.

개발 및 프리뷰 단계에서의 HTML 동작 일치

Vite 4에서는 디렉터리 구조와 끝 슬래시에 따라 개발 서버와 프리뷰 서버가 HTML을 다르게 제공합니다. 이는 빌드된 앱을 테스트할 때 일관성을 떨어뜨리기 때문에, Vite 5에서는 하나의 동작으로 리팩토링 되었습니다. 예를 들어 다음과 같은 파일 구조가 있다고 가정했을 때, 아래 표와 같이 동작합니다:

├── index.html
├── file.html
└── dir
    └── index.html
요청이전 (개발)이전 (프리뷰)이후 (개발 & 프리뷰)
/dir/index.html/dir/index.html/dir/index.html/dir/index.html
/dir/index.html (SPA 폴백)/dir/index.html/index.html (SPA 폴백)
/dir//dir/index.html/dir/index.html/dir/index.html
/file.html/file.html/file.html/file.html
/file/index.html (SPA 폴백)/file.html/file.html
/file//index.html (SPA 폴백)/file.html/index.html (SPA 폴백)

매니페스트 파일은 이제 기본적으로 .vite 디렉터리에 생성됨

Vite 4에서는 매니페스트 파일(build.manifestbuild.ssrManifest)이 기본적으로 build.outDir의 루트에 생성되었습니다.

Vite 5에서는 build.outDir.vite 디렉터리에 생성됩니다. 이 변경 사항은 동일한 매니페스트 파일 이름을 가진 public 파일이 build.outDir로 복사될 때 충돌을 방지하는 데 도움이 됩니다.

매니페스트 파일의 최상위 항목으로 CSS 파일이 존재하지 않음

Vite 4에서는 JavaScript 엔트리 포인트에 해당하는 CSS 파일이 매니페스트 파일(build.manifest)의 최상위 항목으로도 존재했습니다. 이는 의도한 것이 아니며, 구조가 단순한 경우에만 정상적으로 동작합니다.

Vite 5에서 해당 CSS 파일은 JavaScript 엔트리 파일 섹션 내에서만 찾을 수 있습니다(이해를 돕기 위해 동작하는 예시를 함께 첨부합니다. Vite 4 / Vite 5 - 옮긴이). 또한 JS 파일을 추가할 때, 관련된 CSS 파일도 함께 추가되어야 합니다. 만약 CSS 파일을 별도로 추가하는 경우, 별도의 엔트리 포인트를 이용해 주세요.

CLI 단축키는 Enter를 눌러 실행해야 함

CLI 단축키(예: 개발 서버를 재시작하는 r)는 이제 명시적으로 Enter를 눌러야 실행됩니다. 예를 들어, 개발 서버를 재시작하려면 r + Enter를 누르면 됩니다.

이 변경으로 Vite가 OS별 단축키를 무시하고 제어하는 것을 방지하며, Vite 개발 서버를 다른 프로세스와 결합할 때 더 나은 호환성을 제공할 수 있게 되고, 이전의 주의 사항을 피할 수 있습니다.

experimentalDecoratorsuseDefineForClassFields TypeScript 동작 변경

Vite 5는 esbuild 0.19를 사용하며, esbuild 0.18의 호환성 계층도 제거해 experimentalDecoratorsuseDefineForClassFields가 처리되는 방식이 변경되었습니다.

  • experimentalDecorators는 기본적으로 활성화되지 않습니다.

    데코레이터를 사용하려면 tsconfig.jsoncompilerOptions.experimentalDecoratorstrue로 설정해야 합니다.

  • useDefineForClassFields의 기본값은 TypeScript target 값에 따라 달라집니다.

    targetESNext 또는 ES2022 이상이 아니거나, tsconfig.json 파일이 존재하지 않는 경우, useDefineForClassFields는 기본적으로 false로 설정되는데, 이를 esbuild.target의 기본값인 esnext와 함께 사용할 경우 문제가 발생할 수 있습니다. 이는 정적 초기화 블록으로 트랜스파일링되어 브라우저에서 지원되지 않을 수 있기 때문입니다.

    따라서, targetESNext 또는 ES2022 이상으로 설정하거나, tsconfig.json을 구성할 때 useDefineForClassFields를 명시적으로 true로 설정하는 것을 권장합니다.

jsonc
{
  "compilerOptions": {
    // 데코레이터를 사용하는 경우 true로 설정
    "experimentalDecorators": true,
    // 브라우저에서 구문 분석 오류가 발생하는 경우 true로 설정
    "useDefineForClassFields": true,
  },
}

--https 플래그 및 https: true 설정 제거

--https 플래그는 내부적으로 server.https: truepreview.https: true를 설정합니다. 이 설정은 Vite 3에서 삭제되었던 HTTPS 인증서 자동 생성 기능과 함께 사용하기 위해 만들어졌는데, 이를 적용해도 Vite는 인증서 없이 HTTPS 서버를 시작하므로, 더 이상 의미가 없습니다.

@vitejs/plugin-basic-ssl 또는 vite-plugin-mkcert를 사용하고 있다면, 이미 내부적으로 https 설정이 되어 있으므로, --https, server.https: true, preview.https: true를 제거해도 됩니다.

resolvePackageEntryresolvePackageData API 제거

resolvePackageEntryresolvePackageData API는 Vite의 내부를 노출해 Vite 4.3의 최적화를 잠재적으로 가로막았기에 제거되었습니다. 이 API는 다음과 같은 서드파티 패키지로 대체할 수 있습니다:

  • resolvePackageEntry: import.meta.resolve 또는 import-meta-resolve 패키지.
  • resolvePackageData: 위와 동일하며, 패키지 디렉터리를 크롤링하여 루트의 package.json을 가져옵니다. vitefu 커뮤니티 패키지를 사용할 수도 있습니다.
js
import { resolve } from 'import-meta-env'
import { findDepPkgJsonPath } from 'vitefu'
import fs from 'node:fs'

const pkg = 'my-lib'
const basedir = process.cwd()

// `resolvePackageEntry`:
const packageEntry = resolve(pkg, basedir)

// `resolvePackageData`:
const packageJsonPath = findDepPkgJsonPath(pkg, basedir)
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))

사용되지 않는 API 제거

  • CSS 파일의 기본 내보내기 (예: import style from './foo.css'): 이 대신 ?inline 쿼리를 사용
  • import.meta.globEager: 이 대신 import.meta.glob('*', { eager: true })를 사용
  • ssr.format: 'cjs' 및 legacy.buildSsrCjsExternalHeuristics (#13816)
  • server.middlewareMode: 'ssr'server.middlewareMode: 'html': 이 대신 appType + server.middlewareMode: true 를 사용 (#8452)

고급

플러그인/도구 개발자에게만 영향을 미치는 변경 사항입니다.

이 외로, 몇몇 사용자에게만 영향을 미치는 변경 사항도 있습니다.

v3에서 마이그레이션하기

먼저 v3에서 마이그레이션하기 가이드를 확인하여 앱을 Vite v4로 이전하는 데 필요한 변경 사항을 확인하고, 그다음 이 페이지의 변경 사항을 진행하세요.

Released under the MIT License.