esbuild와 vite (feat 번들러)

hour_2·2025년 11월 1일

프론트엔드

목록 보기
2/5

프론트엔드 개발 환경을 이해하기 위해서는 번들링이라는 개념을 빼놓을 수 없다.
esbuildvite 역시 이러한 번들링 과정을 효율적으로 개선하기 위해 등장한 도구들이기 때문이다.

따라서 본문에서는 두 도구를 살펴보기 전에 먼저 번들러의 개념과 등장 배경을 정리하고자 한다.

번들러란 무엇이고 왜 필요한가?

프론트엔드 내에서 번들러, 번들링이란 개념은 왜 생겼을까?

웹이 아주 단순하던 시절에는 번들링이 필요하지 않았다.

<script src="./index.js"></script>

위와 같은 한 줄의 코드면 브라우저가 ./index.js 하나의 파일을 받아서 실행했었다.

하지만 이건 웹이 단순했을 시절의 이야기이고, 현재는 이때와 다르다.

현재는 웹 서비스의 기능이 많아지고, 겹치는 공통 코드가 생기고, 여러 사람이 동시에 개발을 하고, 브라우저가 이해하지 못하는 TypeScript, jsx 등의 언어로 코드를 작성하고 있다.

그래서 우리는 자연스럽게 코드를 파일 단위로 쪼개고 모듈로 관리하기 시작했다. (import/export)
예를 들어 React 컴포넌트, util, API 클라이언트, hooks, 타입 정의 등 이런 것들을 전부 하나의 파일에 넣어두면 유지보수가 불가능하기에 기능별로 나눈 것이다.

하지만 최선의 해결 방법이 아니였다. 여기에서도 많은 문제들이 생겼다.


번들러가 등장하기 전에 존재했던 문제점

1. 파일이 너무 많아진다.

모듈을 잘게 나누는 것은 유지보수에 유리하지만 브라우저는 파일 하나를 받을 때마다 새로운 HTTP 요청을 수행해야 한다. 요청이 적으면 큰 문제가 되지 않지만 200개 이상의 모듈을 로드해야하는 상황이라면 성능 저하의 문제로 이어진다. 특히 HTTP/1.1 환경에서는 동시에 처리할 수 있는 요청의 수가 제한되어 있었기에 병목 현상이 심각했다.
이로 인해 개발은 모듈 단위로 / 배포는 묶어서 처리하는 도구가 필요했다.

2. 의존성 순서를 지켜야 한다

모듈 간의 의존성이 존재하는 경우, 불러오는 순서를 반드시 지켜야 한다.
예를 들어 A 모듈이 B를 사용하고 B가 C를 사용한다면, C-> B-> A 순서로 로드되어야 한다. 이러한 순서를 개발자가 <script> 태그를 통해 수동적으로 지정하는 것은 사실상 불가능에 가깝다.
따라서 의존 관계를 자동으로 분석하고 올바른 순서로 묶어주는 도구가 필요했다.

3. 브라우저가 모르는 문법을 쓰고 있다.

현제 프론트엔드 개발에서는 브라우저가 직접적으로 이해하지 못하는 다양한 문법과 언어가 사용된다.

  • 최신 ESNext 문법(예: optional chaining, async/await 등)
  • TypeScript
  • jsx
  • javaScript 코드 내에서 스타일을 불러오는 방식 (CSS-in-JS)
    이러한 코드들은 브라우저가 그대로 이해할 수 없기 때문에 누군가가 이를 미리 변환해야 한다.

위의 문제들을 해결하기 위해 등장한 것이 바로 번들러이다.

다시 말해 번들러는 웹서비스가 방대해지며 코드를 분리한 모듈로 관리를 하기 위한 도구이다.


번들러의 핵심 아이디어

간단하게 3단계로 설명한다면 아래와 같이 설명할 수 있다.

  • 내가 작성한 여러 개의 모듈 파일
  • 의존성 그래프를 따라가면서 하나씩 읽은 다음
  • 브라우저가 바로 실행할 수 있는 형태로 묶어서(bundle) 내보내는 도구

위의 3단계를 더 자세하게 설명하자면

  • 엔트리(entry) 파일을 정한다. (예시: src/main.tsx)

  • 그 파일 안에서 import ... from ... 한 걸 전부 따라가면서 현재 이 프로젝트가 실제로 쓰고 있는 파일 목록을 전부 찾아낸다. 이걸 의존성 그래프(dependency graph) 를 만든다고 한다.

  • 이후 브라우저가 이해할 수 있는 순서와 형식으로 재배치하고 하나(또는 여러 개)로 뭉친다.

  • 이때 그냥 이어붙이는 게 아닌 아래의 과정을 거친다.

    • 최신 문법을 구버전으로 변환(transpile)
    • 쓰지 않는 코드를 제거(tree-shaking)
    • 변수 이름을 최소화(minify)
    • 소스맵을 만들어서 디버깅 가능하도록
    • CSS나 이미지 같은 리소스도 같이 처리할 수 있도록

개발자 친화적인 코드 → 배포 친화적인 코드로 바꿔주는 변환 과정 전체를 수행한다.

지금은 Vite, Next.js, Remix 같은 프레임워크들이 이 번들 과정을 많이 감춰놓아서 우리가 매번 직접 webpack.config.js 를 쓰지 않아도 되지만 내부적으로는 여전히 의존성을 읽고, 변환하고, 묶어서 내보내는 작업이 돌아가고 있다. 이게 바로 번들러가 필요한 이유다.

번들러에는 여러가지 종류들이 있다.

나는 그 중에서 빠르고 최근에 사람들이 많이 사용 하는 esbuild를 알아볼 예정이다.

그 중 우리가 볼 번들러 esbuild

esbuild를 한 줄로 설명한다면 아래처럼 설명할 수 있다.

esbuild는 웹팩처럼 모든 것을 수행하는 거대한 플랫폼을 목표로 하는 게 아니라, JS/CSS를 빠르게 읽고 변환하고 묶는 그 핵심 부분을 최대한 빠르게 만든 도구다.

왜 esbuild가 빠른걸까?

esbuild의 공식문서에서는 각각의 요인은 단독으로는 미미한 성능 향상을 보이지만 이들이 모두 결합되었을 때 기존 번들러에 비해 수십 배에 달하는 속도 차이를 만들어낸다고 설명하고 있다.

그 중 주요하게 보면 좋을 4가지를 설명하겠다.

1. Go 언어로 작성된 esbuild

대부분의 기존 번들러Node.js 위에서 돌아가는 자바스크립트 프로그램이다. (ex=> webpack, Rollup, Parcel ...)
그러나 명령어 실행 후 종료되는 CLI 프로그램 특성상 JIT(Just-In-Time) 컴파일 방식을 사용하는 자바스크립트 언어는 이러한 환경에서 비효율적일 수밖에 없다. 번들러가 실행되는 순간 Node.js는 번들러의 자바스크립트 코드를 해석하는데 상당한 시간을 소비한다.

즉, 실제로 사용자의 코드를 번들링하기도 전에 런타임이 자신의 코드를 파싱하느라 리소스를 낭비하는 구조이다.
조금 더 쉽게 말하면 Node.js는 번들러 코드 자체를 해석하느라 시간이 많이 지체되느라 번들링이 시작도 안되고 있다는 이야기이다.

반면에 esbuild는 Go 언어로 작성되었기에 실행 가능한 바이너라 파일 형태로 컴파일되어 동작한다.

이로 인해 번들링을 시작하기 전 런타임이 자신의 코드를 해석하는 비용이 사실상 존재하지 않는다. (바이너리 파일 → 이미 기계어로 되어 있어서 운영체제가 프로그램 실행 명령을 내리는 순간 곧바로 CPU가 코드 수행)

즉 자바스크립트 기반 번들러는 매 실행 시 런타임 초기화 및 파싱 과정을 반복해야하지만, esbuild는 이러한 오버헤드를 Go 기반 네이티브 구조로 완전히 제거하는 것이다.


2. 병렬 처리를 위해 설계된 Go 언어

병렬 처리: 하나의 일을 여러 개의 CPU 코어가 동시에 나누어 처리하는 것

자바스크립트는 워커 스레드(CPU가 동시에 여러 일을 수행할 수 있도록 하는 작업 단위)를 통해 병렬 처리를 구현할 수는 있지만 esbuild가 사용하는 Go 언어의 벙렬 처리 모델은 근본적으로 구조가 다르다.

Go는 스레드 간 메모리 공유가 가능하며, 가비지 컬렉션 또한 단일 공유 힙에서 동작한다.
이로 인해 파싱, 코드 생성 등 CPU 집약적인 작업을 여러 스레드가 동시에 수행할 수 있다.

반면 자바스크립트는 워커 간 메모리를 공유하지 못하고 데이터를 직렬화하여 전달해야 하기 때문에 병렬성이 크게 제한된다.
즉, 병렬 처리를 지원하더라도 스레드 간 데이터 이동 비용이 발생하여 효율이 낮다.

esbuild는 이러한 Go의 구조적 장점을 최대한 활용한다. 전체 빌드 과정은 파싱 -> 링크 -> 코드 생성의 세 단계로 구분된다.

파싱코드 생성 단계는 병렬화가 용이하여 모든 CPU 코어를 적극적으로 활용한다.
링크 단계는 본질적으로 직렬 처리가 필요한 과정이므로 예외적으로 단일 스레드로 처리된다.

또한 여러 엔트리 포인트가 동일한 라이브러리를 import하는 경우에 esbuild는 텍스트메모리 공유를 통해 중복 파싱을 방지하고 캐시된 데이터를 재활용한다.


3. 모든 기능을 직접 구현

esbuild의 또 다른 특징은 외부 라이브러리에 의존하지 않고 내부 기능을 모두 자체적으로 구현했다는 점이다.

대부분의 번들러는 다음과 같은 구조를 따른다.

  • TypeScript 파서는 공식 타입스크립트 컴파일러에 의존
  • 이후 Babel을 통해 코드 변환 수행
  • 다양한 플러그인 체계를 거쳐 추가 변환 및 최적화 실행

이 과정에서 데이터 구조가 반복적으로 변경되고, 문자열과 AST(Abstract Syntax Tree) 간 변환이 여러 번 일어난다. 이러한 과정은 필연적으로 성능 저하로 이어진다.

반면 esbuild는 다음과 같은 주요 구성 요소를 모두 자체적으로 구현하였다.

  • JavaScript/TypeScript 파서
  • 코드 변환기
  • 번들러
  • 코드 생성기

이로 인해 esbuild는 한 번 생성한 AST를 여러 단계에서 재사용하며, 중간 표현 변환을 최소화한다. “이 데이터가 이후에도 재활용될 것”이라는 점을 고려한 구조적 설계를 통해 메모리 접근 횟수를 줄이고, CPU 캐시 효율을 높인다.

공식 문서에 따르면, esbuild는 전체 자바스크립트 AST를 단 세 번만 순회한다.

  1. 토큰화, 파싱, 스코프 구성, 심볼 선언
  2. 심볼 바인딩, 문법 최소화, JSX/TS → JS 변환, ESNext → ES2015 변환
  3. 식별자 및 공백 최소화, 코드 생성, 소스맵 생성

다른 번들러들은 이 과정을 여러 외부 라이브러리에 위임하여 문자열 ↔ AST 변환을 반복하기 때문에 상대적으로 느리다.

즉, esbuild는 일관된 내부 데이터 구조와 최소한의 AST 순회를 통해 뛰어난 성능을 달성한다.

4. 효율적인 메모리 사용

컴파일러 성능의 핵심은 방대한 데이터를 얼마나 적은 연산으로 처리하느냐에 달려 있다. esbuild는 데이터 표현 변환을 최소화하여 CPU 캐시 적중률을 높이고, 메모리 접근 횟수를 줄인다.

Go 언어는 구조체 필드를 메모리상에 밀집하게 배치할 수 있으며, 여러 불리언 값을 1바이트 단위로 압축하여 저장할 수 있다. 또한 값 타입을 직접 구조체 내부에 포함시킬 수 있어(embedding) 불필요한 동적 메모리 할당을 줄인다.

반면 자바스크립트는 이러한 메모리 최적화가 어렵다. JIT 히든 클래스, 박싱된 숫자 객체, 동적 프로퍼티 접근 등으로 인해 동일한 연산을 수행하더라도 더 많은 메모리와 연산 비용이 소모된다.

결과적으로 esbuild는 “작업을 최소화하는 것이 곧 성능 향상으로 이어진다”는 단순한 원칙을 철저히 구현한 번들러이다.
AST는 세 번만 순회하며, 불필요한 표현 변환을 최소화하고, 일관된 데이터 구조를 유지한다. 이러한 설계적 일관성이 누적되어, 결과적으로 기존 번들러보다 수십 배 빠른 성능을 제공한다.


실제 벤치마크 결과

공식 문서에서 제시한 벤치마크 결과에 따르면, esbuild의 성능은 다른 주요 번들러와 비교했을 때 압도적으로 빠르다.

테스트 환경: 6코어 MacBook Pro, macOS, 16GB RAM

JavaScript 대규모 프로젝트 시뮬레이션

  • esbuild: 0.39초 (기준 1배)
  • Parcel 2: 14.91초 (약 38배 느림)
  • Rollup 4 + Terser: 34.10초 (약 87배 느림)
  • Webpack 5: 41.21초 (약 106배 느림)

TypeScript 대규모 프로젝트 시뮬레이션

  • esbuild: 0.10초
  • Parcel 2: 6.91초 (약 69배 느림)
  • Webpack 5: 16.69초 (약 167배 느림)

이 결과는 다른 번들러의 설계가 잘못되었다는 의미가 아니라 esbuild가 전혀 다른 구조적 접근을 취했기 때문에 가능한 성능 차이임을 보여준다.

많은 번들러는 TypeScript 컴파일러를 그대로 내부에 포함해 사용하는데 이는 본래 정확한 타입 분석을 목표로 만들어졌기 때문에 성능을 우선하지 않는다. 이러한 구조는 메가모픽 객체 사용, 불필요한 동적 프로퍼티 접근 등으로 인해 오버헤드를 유발한다.
반면 esbuild는 TypeScript 파싱 및 트랜스파일 과정을 자체적으로 구현하여 이러한 병목을 근본적으로 제거하였다.


esbuild의 철학: 해주는 것과 하지 않는 것

esbuild의 개발자는 명확한 철학을 가지고 도구를 설계했다.

그는 esbuild를 프론트엔드 개발의 모든 요구를 해결하는 만능 도구로 만들 생각이 없다고 밝힌다.

따라서 다음과 같은 기능들은 의도적으로 제공하지 않는다.

  • Elm, Svelte, Vue, Angular 등 타 프레임워크 전용 언어 직접 지원
  • TypeScript 타입 검사 기능 (tsc로 분리)
  • 커스텀 AST 조작 API
  • Hot Module Reloading(HMR)
  • Module Federation

그 이유는 명확하다. 지나치게 많은 기능을 수용하면 도구의 복잡도가 증가하고 성능 최적화에 일관성을 유지하기 어렵다.

이러한 이유로 esbuild는 자신을 웹을 위한 링커(linker)로 정의한다.
즉 JavaScript와 CSS를 빠르고 안정적으로 묶어주는 역할에 집중하고 그 외 영역은 다른 도구와 결합하여 사용하는 것을 권장한다.

이러한 철학 덕분에 esbuild의 코어는 가볍고 단순하며, vite와 같은 다른 툴에서 내부 빌드 엔진으로 통합하기에 적합하다.
실제로 vite는 TypeScript 변환 과정에서 esbuild를 사용하고 있다.

생태계 내 활용 사례

esbuild는 아직 1.0.0 버전에 도달하지 않았지만 이미 다양한 프로덕션 환경에서 핵심 빌드 도구로 사용되고 있다.

대표적인 예시는 다음과 같다.

  • Vite: TypeScript를 JavaScript로 변환하는 단계에 esbuild를 사용
  • AWS CDK: 코드 번들링에 esbuild를 채택
  • Phoenix(Elixir 프레임워크): 프론트엔드 빌드 도구로 esbuild를 지원

esbuild는 단독으로 사용되지 않더라도 이미 여러 툴체인 내부에서 필수 구성 요소로 작동하고 있다.


그렇다면 이제 esbuild를 핵심 빌드 도구로 사용하고 있는 도구들 중 하나인 vite에 대해서 자세하게 알아보겠다.




Vite란 정확히 무엇인가?

기존에는 webpack처럼 모든 코드를 번들링한 뒤 브라우저에 제공하는 구조가 일반적이였지만 프로젝트가 커질수록 이 번들링 시간이 너무 오래 걸렸고 HMR(Hot Module Replacement, 코드 수정 후 새로고침 없이 반영)도 느려졌다. 이러한 성능을 해결하기 위해 등장한 것이 vite이다.

vite의 핵심 아이디어는 브라우저가 이미 지원하는 ESM(ES Modules)을 적극적으로 활용하고, 개발 시점과 배포 시점을 다른 전략으로 처리하는 것이다.
개발 중에는 번들링을 하지 않고도 빠른 응답을 제공하며 배포 시에는 여전히 번들링을 통해 최적화된 산출물을 생성하는 방식을 취한다.

이처럼 Vite는 이 문제를 개발 단계배포 단계를 완전히 분리함으로써 해결했다.

Vite를 사용해야 하는 이유

1. 기존 번들러가 갖고 있던 구조적 한계

브라우저가 ESM을 네이티브로 지원하기 전까지는 자바스크립트 모듈을 브라우저에서 직접 불러와 실행하는 것이 사실상 불가능했다. 이 때문에 프론트엔드 개발자들은 여러 개의 소스 모듈을 하나의 파일 또는 적은 수의 파일로 묶어주는(bundling) 방식을 사용해야 했다. webpack, Rollup, Parcel과 같은 도구는 이 과정을 자동화하여 개발자의 생산성을 크게 높였다.

그러나 애플리케이션의 규모가 커지고 모듈 수가 수백, 수천 개로 증가하면서 문제가 드러났다. 기존의 번들러는 “개발 서버를 띄우기 전에” 프로젝트 전체를 한 번 훑고 의존성을 분석하고 빌드해야 했다. 모듈 수가 많아질수록 이 초기 단계는 점점 길어졌고, 변화가 잦은 개발 환경에서는 비합리적인 대기 시간으로 이어졌다. 또한 HMR을 지원하더라도, 내부적으로는 여전히 번들 단위로 재처리가 필요했기 때문에 변경 사항이 브라우저에 반영되기까지 수 초가 걸리는 경우도 드물지 않았다. 이런 느린 피드백 루프는 곧바로 개발자의 생산성 저하로 이어졌다.

Vite는 이 문제를 “처음부터 다 번들링하지 않아도 되는 구조”를 도입하는 방식으로 해결하고자 한다. 즉, 번들링이 필요한 시점에만 번들링을 한다는 방향으로 아키텍처를 재구성했다.


2. 모듈을 의존성과 소스 코드로 분리해 다룬다

Vite가 가진 중요한 설계 포인트는 애플리케이션을 다음 두 가지 범주로 나누어 처리한다는 점이다.

  1. 의존성(dependencies)

    • 예: Vue, React, UI 라이브러리, 유틸성 패키지 등
    • 개발 중에는 거의 변경되지 않는 코드
    • 대체로 순수한 JavaScript로 되어 있고, 변환 비용이 크지 않다
    • 그러나 모듈 수가 많을 경우(수백 개 이상) 초기 번들링 비용이 매우 크다

    Vite는 이 의존성들을 사전 번들링(pre-bundling) 한다. 이 작업에는 Go로 작성된 esbuild를 사용한다.
    esbuild는 기존 자바스크립트 기반 번들러 대비 10~100배에 달하는 속도를 제공하므로, 대규모 의존성 번들링에서 병목이 발생하지 않는다.

  2. 소스 코드(source code)

    • 예: JSX, TSX, Vue SFC, Svelte 컴포넌트, CSS 등
    • 변환이 필요하고, 개발 중에 자주 변경되는 코드
    • 전체를 한 번에 처리할 필요가 없는 코드

    Vite는 이 소스 코드를 네이티브 ESM 으로 제공한다.
    즉, 브라우저가 실제로 요청하는 모듈만 그때그때 변환해서 전달한다. 조건부 import()로 분기되는 코드나 현재 화면에서 사용하지 않는 라우트의 코드는 실제로 접근할 때까지 변환되지 않는다.
    이로 인해 초기 구동 속도가 크게 향상된다.

    이 구조를 통해 Vite는 “처음부터 전체 애플리케이션을 번들링해야만 서버를 시작할 수 있는” 기존 번들러의 제약을 제거한다.


3. 서버 구동 속도 개선

기존 번들러 기반 개발 서버는 콜드 스타트 시 프로젝트 전체를 한 번 빌드한 뒤에야 페이지를 제공할 수 있었다. 이는 프로젝트 규모가 커질수록 선형적으로 느려지는 구조였다.

Vite는 다음과 같은 방식으로 이를 개선한다.

  • 의존성은 esbuild로 미리 빠르게 묶는다.
  • 애플리케이션 소스는 브라우저가 요청하는 시점에만 변환하여 전달한다.
  • 브라우저가 ESM을 지원하므로, 의존성 그래프를 브라우저가 직접 따라가게 한다.

이 구조에서는 서버가 “모든 모듈이 준비될 때까지 기다린 뒤 응답”하는 것이 아니라, “응답 가능한 것부터 즉시 제공”하는 방향으로 바뀐다. 그 결과 개발 서버의 시작 시간이 매우 짧아진다.


4. 코드 갱신 속도(HMR)의 일관성

전통적인 번들러 환경에서 파일을 수정하면, 번들러는 변경된 파일뿐 아니라 그 파일과 연결된 번들도 다시 생성해야 했다. 애플리케이션이 커질수록 이 시간은 길어졌고, HMR을 사용하더라도 결국 내부에서 번들 재생성이 일어났기 때문에 대규모 프로젝트에서는 빠른 피드백을 제공하지 못했다.

Vite는 개발 서버에서 번들을 만들지 않는다. 대신 각 모듈을 ESM 단위로 브라우저에 제공한다. 이 구조에서는 어떤 모듈이 변경되었을 때 그 모듈과 그 모듈을 직접 참조하는 모듈만 교체하면 된다. 브라우저는 요청한 모듈만 다시 받으면 되므로, 애플리케이션 크기가 커져도 HMR 속도가 느려지지 않는다.

또한 Vite는 HTTP 캐시를 적극적으로 활용한다.

  • 소스 코드는 변경 여부를 판단해 304 Not Modified로 응답하고
  • 변하지 않는 의존성은 Cache-Control: max-age=31536000, immutable로 강하게 캐시한다.

이로 인해 네트워크 요청 수가 줄어들고, 결과적으로 전체 페이지 로딩 성능이 개선된다.


배포 시에도 번들링이 필요한 이유

현대 브라우저가 ESM을 지원한다고 해서, 프로덕션 환경에서 “번들링을 전혀 하지 않은” ESM을 그대로 배포하는 것이 항상 최선인 것은 아니다. 이유는 다음과 같다.

  1. 중첩된 import가 많을수록 네트워크 요청 수도 함께 증가한다. HTTP/2 환경이라 하더라도 다량의 작은 요청은 초기 로딩에 부담을 줄 수 있다.
  2. 트리 셰이킹(tree-shaking), 코드 스플리팅(code splitting), 지연 로딩(lazy loading), 캐시 효율을 위한 청크 분할 등 프로덕션 수준의 최적화는 여전히 번들링 과정에서 수행하는 것이 가장 효과적이다.
  3. 개발 서버와 빌드 결과가 다르면 디버깅이 어렵다. 개발 중에 보이던 동작과 배포 후 동작이 달라지지 않도록, Vite는 미리 정해진 빌드 명령과 동일한 파이프라인을 제공한다.

따라서 Vite는 개발 시점에는 번들링을 하지 않지만, 프로덕션 빌드 시점에는 여전히 번들링을 수행한다. 이때 사용하는 번들러는 esbuild가 아니라 Rollup이다.


왜 프로덕션 번들링에는 esbuild를 사용하지 않는가

Vite는 개발 시 “사전 번들링” 단계에서 esbuild를 사용한다. 이 단계에서는 속도가 최우선이므로 esbuild의 선택이 매우 타당하다. 그러나 프로덕션 빌드 전체를 esbuild로 처리하지는 않는다. 이유는 다음과 같다.

  1. 플러그인 생태계와의 호환성

    Vite는 Rollup의 플러그인 인프라를 적극적으로 채택하고 있다. 이 인프라는 이미 생태계가 크고, 다양한 프레임워크·언어·리소스에 대응하는 플러그인이 존재한다. esbuild 기반으로 완전히 전환할 경우 이런 유연성이 줄어든다.

  2. 유연성과 성능의 트레이드오프

    esbuild는 매우 빠르지만, 현재 시점에서 빌드 파이프라인 전체를 대체할 만큼 유연하지는 않다. Vite는 “현실적인 프로덕션 환경에서 필요한 변환과 최적화를 안정적으로 수행할 수 있는가”를 더 중요하게 보며, 이 지점에서 Rollup이 더 균형 잡힌 선택이다.

  3. 향후 대체 가능성

    Rollup은 v4에서 파서를 SWC로 전환하는 등 성능 개선을 계속하고 있으며, Rust로 포팅된 Rolldown 프로젝트도 진행 중이다. 이 흐름이 성숙하면, Vite는 개발과 빌드 사이의 불일치를 더 줄인 형태로 진화할 수 있다.

참고 자료

esbuild 공식 문서: https://esbuild.github.io/faq/#top-level-var
vite 공식 문서: https://ko.vite.dev/guide/why.html

6개의 댓글

comment-user-thumbnail
2025년 11월 2일

아티클을 읽고 esbuild가 왜 그렇게 빠른지에 대해서 자세하게 알게 되었습니다.
그리고 Vite를 이해하기 위해 번들러의 필요성부터 esbuild의 내부 동작 원리까지 깊이 있게 다룬 점이 좋았습니다.

  1. 배운 점
    esbuild가 빠른 이유 4가지

1) Go 언어 : Webpack 같은 JS 기반 툴과 달리, Go로 작성되어 컴파일된다.

2) 병렬 처리: Go 언어는 스레드 간 메모리 공유가 가능해 파싱, 코드 생성 등 무거운 작업을 CPU 코어가 동시에 나눠 처리합니다. -> 훨씬 효율적

3) 모든 기능 자체 구현: TypeScript 파서, 코드 변환기, 번들러를 모두 직접 구현했습니다. 외부 라이브러리를 거치며 발생하는 코드 구조 변환 오버헤드가 없습니다.

4) 효율적인 메모리 사용: Go 언어의 특징을 활용해 데이터를 밀도있게 배치하고 불필요한 메모리 할당을 줄여 CPU 캐시 효율을 극대화합니다.

번들러의 근본적인 필요성: 단순히 파일을 합치는 것을 넘어 HTTP 문제 해결, import 순서 자동 분석, TS, JSX 변환이라는 3가지 문제를 해결하기 위해 등장했다는 것을 명확히 알게 되었습니다.

esbuild는 타입 검사, HMR, 커스텀 AST 조작 등 복잡한 기능은 의도적으로 제외하여 핵심 속도에만 집중했다는 점을 배웠습니다.

  1. 더 궁금한 점
    esbuild는 타입 검사 없이 TypeScript를 JavaScript로 변환만 합니다. 그렇다면 Vite 환경에서 개발할 때, 타입 에러는 언제 어떻게 잡히는지 궁금해서 찾아봤습니다.

개발 중 (npm run dev):
Vite(esbuild)는 타입 검사 없이 TS를 JS로 변환해서 브라우저로 보내줌니다.
타입 에러는 IDE의 빨간 줄을 보고 개발자가 실시간으로 수정.

배포 전 (npm run build):
tsc 명령어가 먼저 실행되어 프로젝트 전체의 타입 무결성을 검사합니다.
여기서 에러가 나면 빌드가 실패합니다. 검사를 통과하면, Vite 빌드 파일을 생성!!

  1. 인상깊은 점
  • Webpack 대비 JavaScript는 106배, TypeScript는 167배 빠르다는 결과는 esbuild의 성능이 가장 좋다는 것을 직관적으로 알 수 있었습니다.

  • 코드 구조를 단 3번만 순회한다: 다른 툴들이 여러 라이브러리를 거치며 데이터를 변환하고 또 변환할 때, esbuild는 모든 것을 내부에서 처리하며 AST 순회를 최소화한다는 점이 기억에 남습니다.

답글 달기
comment-user-thumbnail
2025년 11월 2일

esbuild, vite와 관련된 지식들을 잘 정리되어 있어서 관련 개념들을 잘 정리할 수 있었어요. Vite를 사용해보기는 했지만, Vite의 동작 원리와 기존 번들러의 구조적 한계, 그로 인해 vite를 사용해야 하는 이유 등 배경 지식을 알 수 있어서 좋았어요.

[아티클 요약]
https://long-floss-5d9.notion.site/1-29f9c7f359638053b67ec61d51153cf0?pvs=74

답글 달기
comment-user-thumbnail
2025년 11월 2일

esbuild 빠른 이유

  • 번역없이 바로 실행되는 Go언어로 만들어져, 미리 기계어로 번역돼서 나오기에 바로 번들링을해 시간을 단축시킨다.
  • 병렬처리에 강한 Go언어를 사용해일을 동시에 나누어 처리한다.
  • 기존 번들러는 외부 도구를 사용했는데 esbuild는 읽고 변환하고 묶는 것까지 자체구현했다.
  • 낭비없는 메모리 사용

vite가 빠른 이유

  • 의존성: 개발중에 거의 변경되지 않는 코드, ui라이브러리 등을 사전 번들링을 한다.
  • 소스코드: 변환이 필요하고 개발중에 변경되는 파일 등을 번들링없이 요청할때만 변환한다.
    이전 번들러들은 처음부터 전체 애플리케이션을 번들링해야만 서버를 시작할 수 있는 번들러 밖에 없었는데 vite규칠을 깨고 필요한 파일만 그때그때 변환해서 속도가 빠르다.

vite특징

  • 브라우저가 필요한 파일만 그때그때 변환해준다. 즉 기존 번들러와 같이 전체를 빌드할때까지 기다리지 않아 빠르다.
  • HMR로 코드 수정하고 새로고침을 하지 않아도 바로 반영이 된다.
  • 배포할때는 Rollup을 사용한다.

모듈마다 파일을 분리해 유지보수를 쉽게 한 것처럼 서로 다른 목표를 가진 작업(개발, 배포)도 분리해야 가장 좋은 결과를 얻을 수 있음을 깨달았습니다.

답글 달기
comment-user-thumbnail
2025년 11월 2일

혜린님의 아티클을 읽고 번들링에 대한 개념과 빌드 툴인 esbuild, Vite의 역할에 대해 좀 더 명확히 이해할 수 있었습니다!

깨달은 점

  1. 웹 애플리케이션의 복잡도가 증가하면서, 코드의 관리와 성능 최적화를 위해 모듈화가 필수적이었지만 모듈을 나누면 HTTP 요청이 많아져 성능 문제가 생길 수 있고, 이를 해결하기 위해 번들러가 등장한 이 배경을 명확하게 알게되었습니다.

  2. esbuild의 빠른 성능을 결정짓는 중요한 요소 중 하나는 Go 언어 -> esbuild는 Go로 작성되었기 때문에 바이너리 형태로 실행되며 초기화 오버헤드가 없어 빠른 성능을 낼 수 있고 또한 병렬 처리와 효율적인 메모리 사용 덕분에 CPU와 메모리를 효율적으로 활용할 수 있다는 걸 알게되었습니다.

  3. 웹 애플리케이션이 대규모화 되는 시대에서 번들링 과정은 단순히 코드가 빠르게 실행되게 만드는 것뿐만 아니라, 코드 유지보수성, 성능 최적화, 개발 효율성을 크게 향상시킬 수 있는 중요한 요소라는 점을 깨닫게 되었습니다 !!

  4. 대부분이 사용하는 빌드 툴인 Vite에 대해서 명확한 이해없이 여태 사용했던 것같은데 이 아티클로 Vite의 장점에 대해 알 수 있었고 왜 사용하는지 알게되었습니다

1개의 답글
comment-user-thumbnail
2025년 11월 2일

혜린님의 아티클로 번들러의 등장 배경과 번들러의 핵심을 알게 되었어요. 제가 발표한 webpack과 rollup이 아닌 요즘 개발에 사용되고 있는 esbuild와 rollup에 대해 알게 되는 계기가 되었어요.
esbuild는 go 언어로 작성되어 런타임 오버헤드가 적다는 점, 메모리 접근 최소화로 CPU 캐시 효율을 높였다는 점, esbuild의 전체 빌드 과정 등을 알게 되었어요. CS적인 부분도 작성해주셔서 좋았습니다.

Vite 또한 개념과 등장 배경, 개발과 배포의 분리, 캐싱 등의 내용을 잘 설명해주신것 같아요. 개발 단계에서는 esbuild를 사용하고, 빌드 단계에서는 Rollup을 사용한다는 점 또한 새롭게 알게 되었어요.
고생 많으셨어요!!

답글 달기