Vite가 왜 빠르다고 하는지 알고 계시나요?

dodog·2024년 9월 8일
post-thumbnail

지난번엔 모듈에 대해 깊게 파보는 시간을 가지게 되었는데
드디어 모듈에 대해 이해하고 Vite에 대해 깊게 공부해보는 시간을 가지게 되었다.

내가 React를 처음 공부하던 22년도만 해도 CRA가 vite보다 앞섰던 것 같은데
작년부터는 완전히 Vite가 리액트 프로젝트를 시작할 때 표준 도구가 되었다.

Vite와 CRA의 차이점

Vite는 CRA와 같은 리액트 프로젝트 빌드 도구인데, CRA보다 뭐가 좋아서 다들 Vite로 넘어가게 된걸까?

CRA는 빌드를 위한 번들러와 트랜스파일러로 webpack과 babel을 사용하는 반면, Vite는 rollup 과 esbuild를 사용하고, CRA는 webpack, babel 설정 파일이 아예 숨겨져 있지만, Vite는 vite.config.js 파일을 통해 번들링와 트랜스파일링을 설정할 수 있다.

대충 이러한 표면적인 차이가 있긴한데, 그래서 그런 표면적인 부분에서의 차이가 실제로 어떤 경험적인 차이를 만들어내는걸까?

Vite의 압도적으로 빠른 개발 서버

vite는 개발 서버가 webpack에 비해 훨씬 빠른 것으로 매우 잘 알려져있다.
조금만 찾아보면 vite가 production 빌드에서는 rollup을 번들러로 사용하고 dev 빌드에서는 esbuild를 사용한다는 것을 알 수 있는데, 이렇게만 이해하면 그냥 esbuild가 webpack보다 빠른거 아닌가? 라는 단순한 이해에 그칠 수 밖에 없게 된다.

실제로 vite의 개발서버가 왜 webpack보다 압도적으로 빠른지 이해하기 위해서는, 지난번에 열심히 공부해보았던 webpack 그리고 CommonJS와 ESModule에 대한 깊은 이해가 반드시 선행되어야한다.

Vite의 사전 번들링

Vite를 조금 공부해본 사람은 사전 번들링 때문에 Vite의 개발 서버가 빠르다는 것을 안다.
하지만 거기서 그치지 않고 사전 번들링은 왜 빠른걸까? 라는 의문을 품게 되면 조금 복잡해진다.

개인적으로 이번에 공부해보고 느낀거지만 '사전 번들링' 이라는 단어는 '의존성을 ESM으로 사전 번들링' 이라는 표현이 과도하게 생략된 단어라고 생각한다.
지금부터 그 이유에 대해서 설명해보려고 한다.

Vite는 개발 서버에 한정해서 프로젝트의 모든 의존성, 즉 node_modules를 esbuild를 통해서 전부 esm 방식으로 변경시키는 사전 번들링을 진행한다. 이런 사전 번들링 과정을 거치고 나면, 개발 서버에서는 당연하게도 더이상 번들링 자체가 불필요해진다.

왜냐면 ESModule의 가장 큰 강점은 JS 네이티브 모듈이라서 그냥 그대로 브라우저에서 가져와서 쓸 수 있다는 점인데, 의존성(node_modules)은 이미 ESM으로 사전 번들링을 완료했고, 프로젝트 코드베이스는 애초에 esm으로 동작하므로 모든 코드가 ESM이니까 번들링 없이 그냥 바로 가져가서 사용할 수 있기 때문이다. (당연히 TS, JSX로 작성한 경우 트랜스파일링은 해야함)

그래서 ESM으로 동작하는 개발 서버가 빠른 것이고, 특히 개발 서버에서는 GO로 만들어진 esbuild를 활용해서 번들링과 트랜스파일링을 모두 진행하기 때문에 더욱 더 빨라지는 것이다.

Vite는 왜 프로덕션과 개발에서 다른 번들러를 사용하나?

정말 자연스럽게도 이런 의문이 들 것이다. 프로덕션에서는 Rollup으로 번들링하고 개발에서는 esbuild로 번들링한다. 그럼 그 이유는 대체 무엇일까?

Vite 공식문서에서 캡쳐해온 내용인데 속도를 조금 포기하더라도 안정적으로 번들링을 진행하는 Rollup을 선택했다고 한다.

게다가 프로덕션과 개발 빌드에서의 번들러가 달라짐으로 인해 발생하는 최종 번들 결과물의 불일치 문제에 대한 해결책까지 적어두었다.

본인들도 이 문제를 인지하고 있어서인지 Rollup을 Rust로 포팅한 Rolldown을 만들어서 프로덕션과 개발에서 동일한 빌드 도구를 사용해서 불일치를 제거할 예정이라고 한다.

Q: 프로덕션이나 Webpack에서는 왜 이 좋은 사전 번들링을 안 하나요?

Vite를 제대로 이해하기 전에는 나도 이런 의문을 가졌다. '사전 번들링'이라는 단어가 괜히 더 혼란스럽게 만든 것 같기도 하다. Webpack과 비교해보면 좀 더 쉽게 이해할 수 있다.

Webpack은 CJS를 중심으로 돌아가기 때문에, 코드를 하나 수정할 때마다 전체 프로젝트 코드베이스를 브라우저가 이해할 수 있는 모듈로 바꿔주는 번들링 과정이 필요하다. 그래서 애초에 구조적으로 사전 번들링이 불가능한 것이다.
(의존성은 이전 번들링 결과를 캐싱하여 다시 번들링 하지 않아도 될 수 있다)

Vite 프로덕션 빌드도 사전 번들링이라는 개념 자체가 없다. 왜냐면 프로덕션 빌드는 의존성 일부만 사전 번들링 하는게 아니라 전체 코드를 ESM으로 번들링을 하고 또 여러가지 최적화 작업까지 처리해야하기 때문이다.

또 Webpack에서도 개발서버는 수정될 때마다 매번 번들링을 해줘야 하지만 프로덕션은 번들링을 한번만 진행하면 된다는 차이를 생각해본다면 프로덕션에서는 사전 번들링 자체가 생각할 필요없는 것임을 알 수 있다.

요약하자면, 프로덕션 빌드에는 사전 번들링이라는 개념 자체가 불필요하고 의미없으며, Webpack 개발서버는 CJS 중심이기 때문에 구조적으로 ESM으로의 사전 번들링이 불가능하다는 것이다.

마무리

Vite의 동작에 대해서 이해하기까지 Webpack, Babel 그리고 CJS, ESM 등의 모듈에 대한 공부를 거친 정말 긴 여정이였다.

정말 신기하게도 Vite에 대한 탐구를 진행하면서 반대로 Webpack과 CJS 그리고 ESM에 대한 이해까지 늘어났다.

내가 아무런 생각 없이 누리고 있던 것들이 실제로 어떻게 돌아가는지 이해하는 과정은 웹개발을 처음 공부하게 되었을 때, 웹사이트가 사실은 벽돌처럼 html 태그를 한땀한땀 쌓아올려 만든 것임을 깨닫게 된 것처럼 흥미로웠다.

다음에는 Rollup과 esbuild에 대해서도 공부해보고 Vite가 왜 이 둘을 사용하는지에 대해서도 한번 알아보면 좋을 것 같다.

profile
심리학, 사회문제해결에 관심이 많은 프론트엔드 개발자

0개의 댓글