About Vite (feat. 느려진 개발 서버 해결하기)

강은비·2025년 2월 1일
0
post-thumbnail

Vite는 프론트엔드 개발을 위한 차세대 빌드 도구이며, 다음과 같은 기능을 제공한다.

의존성 사전 번들링

의존성은 주로 변경되지 않는 JS 소스 코드로, Vite는 esbuild를 사용하여 이를 사전에 번들링한다. esbuild는 기존 JS 기반 번들러보다 빠른 성능을 제공하여 빠른 콜드 스타트를 가능하게 한다.

여러 의존성이 존재하는 ESM 모듈을 하나의 모듈로 변환하여 페이지 로드 퍼포먼스를 향상시킨다.

Ex) loadah-es: 600개의 모듈을 갖고 있어 lodash-es 모듈을 가져올 때 600개의 HTTP 요청을 전송해야 한다. Vite는 lodash-es 모듈를 하나의 모듈로 번들링하여 한 번의 HTTP 요청을 전송하도록 한다.

Vite는 사전 번들링된 의존성을 node_modules/.vite 디텍터리 내에 캐시한다. 다시 번들링하는 경우는 다음과 같다.

  • 패키지 매니저의 lock 파일 변경
  • 폴더의 수정 시간 패치
  • vite.config.js 와 관련된 필드 변경
  • NODE_ENV 값 변경

개발 서버 실행 시 504(Outdated Optimize Dep) 에러가 발생한다면 파일 시스템 캐시를 의심해봐라

HMR (Hot Module Replacement)

Vite는 Native ESM을 활용해 HMR을 제공한다. 기존 번들러는 소스 코드 업데이트 시 번들링 과정을 다시 수행해야 하지만, Vite는 수정된 모듈과 관련된 부분만 교체하고 브라우저에게 전달한다. 특히, 애플리케이션 크기가 커져도 HMR 갱신 시간에는 영향을 미치지 않아, 대규모 프로젝트에서도 뛰어난 개발 효율성을 제공한다.

Rollup 기반의 프로덕션 빌드

Vite는 Rollup을 기반으로 프로덕션 빌드를 수행하며, 정적 리소스를 고도로 최적화한다. Rollup의 강력한 플러그인 생태계를 활용할 수 있으며, Vite의 플러그인 API를 통해 추가적인 기능 확장이 가능하다.

Remix ❤️ Vite

2024년부터 Remix가 Vite 플러그인을 지원하여 Vite의 장점을 Remix 프로젝트에서도 활용할 수 있다. Remix는 더 이상 자체 컴파일러를 사용하지 않고 Vite를 사용한다고 발표했다.

Next.js도 Vite 플러그인 지원해줬으면...

Server-only module

.server 모듈을 사용해 서버에서만 실행되는 코드를 분리함으로써 클라이언트 번들 사이즈를 줄일 수 있다.

Presets

Remix Vite 플러그인은 다양한 도구와 호스팅 제공업체와의 통합을 지원하기 위해 presets 옵션을 제공한다. 예를 들어, Vercel과 같은 특정 호스팅 환경에 맞춰 Remix와 Vite의 빌드 출력을 조정할 수 있어, 특정 플랫폼에 종속되지 않고 유연한 배포 환경을 제공할 수 있다. 이를 통해 Vendor Lock-in 문제를 완화할 수 있다.

Plugin Ecosystem

개발자가 원하는 대로 Vite 플러그인을 구성할 수 있다. 또한, 커스텀 플러그인을 만들거나 다양한 플러그인을 추가함으로써 추가적인 기능 확장이 가능하다.

참고: https://github.com/vitejs/awesome-vite#plugins

이외에도 Native ESM을 이용한 빠른 HMR 등 기존 Vite가 갖고 있는 장점을 활용할 수 있다.

느려진 Vite 개발 서버...?

Remix 프로젝트에서 Vite를 사용한 이후 어느 시점부터 개발 서버가 느려지는 상황을 겪었고, 아래 두 방법을 통해 개발 서버 성능 문제를 해결할 수 있었다.

Import 경로 식별 작업 줄이기

  • 확장자가 생략된 import 경로를 식별하는 것은 최악의 경우 많은 비용이 드는 작업이 될 수 있다.
  • resolve.extensions 의 기본값: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json']

예시

import { Component } from './Component';

/*
1. `./Component`가 존재하는지 확인, 없음
2. `./Component.mjs`가 존재하는지 확인, 없음
3. `./Component.js`가 존재하는지 확인, 없음
4. `./Component.mts`가 존재하는지 확인, 없음
5. `./Component.ts`가 존재하는지 확인, 없음
6. `./Component.jsx`가 존재하는지 확인, 있음!
*/

⚠️ import 경로를 식별하기 위해 총 6번의 파일 시스템 작업이 필요하다. 즉, 암시적인 import가 많이질수록 경로를 식별하는데 더 많은 시간이 필요하게 된다.

해결방법

  • import 경로에 확장자를 포함해 명시적으로 지정하자. TypeScript를 사용한다면, tsconfig.json의 compilerOptions에 "allowImportingTsExtensions": true를 추가해 .ts와 .tsx 확장자를 코드에서 바로 사용할 수 있다.
  • resolve.extensions의 목록을 좁혀 전반적인 파일 시스템 작업을 줄인다. 이 경우 node_modules 내의 파일에 대해서도 잘 수행되는지 확인해야 한다.

배럴 파일 지양하기

  • Barrel export는 circular dependency를 발생시킬 가능성이 높아 HMR 성능을 저하시킬 수 있다.
  • madge 와 같은 도구를 사용해 circular dependency를 검사할 수 있다.
  • 배럴 파일을 통해 개별 모듈만 임포트하는 경우에도 해당 배럴 파일의 모든 파일이 변환되기 때문에, 페이지 로드 속도가 느려지게 된다.

References

0개의 댓글