본문 바로가기

기술 스택 선정 고민 : 번들러

기술 스택 선정 고민 : 번들러

부제 : 스스로 불러들인 재앙

  • 발단 : 본격적으로 메인 프로젝트에 들어가기 전에 프론트엔드 팀원들과 스택 선정에 대해 심도깊은 논의를 해보고 싶었다.  
  • 전개 : '일단 저지르고 보는 파'인 나는 팀원들에게 아래와 같은 제안을 하였다. 

 

  • 절정 : 역시 사랑스러운 우리 팀원들은 흔쾌히 오케이! 를 외쳐주셨다. 하지만, 5번과 6번의 개념은 나와 같이 너무나 생소한 분야라고 하시는 것 같아 잽싸게 '제가 할게요' 찬스를 썼다.

 

  • 결말 : 따라서 머리를 싸매며 '번들러'와 '빌드' 등과의 개념과 열심히 맞서 싸우며 정보의 바다를 헤엄치는 중이다.
    • 하지만, 결론적으로는 프로젝트에 들어가기 전에 꼭 필요한 개념이다!

 

빌드란?

 

빌드는 배포를 하기위한 선행과정으로 작성한 코드와 애플리케이션이 사용하는 이미지, css등을 모두 모아서 패키징을 하는 작업이다.

  • 즉, 웹 애플리케이션의 소스 코드와 리소스를 가공하여 실행 가능한 형태로 만드는 과정이다.
  • 빌드 단계에서는 다음과 같은 작업들이 수행된다.
    1. 트랜스파일링: 예를 들면 ES6으로 작성된 코드를 모든 브라우저에서 호환 가능한 ES5로 변환한다. (Babel)
    2. 번들링: 웹 앱에서 사용되는 여러 개의 파일을 하나의 번들 파일로 묶어 준다. (Webpack, Parcel, Rollup)
    3. 압축: 공백 문자, 주석 등을 제거하는 등 빌드된 파일을 압축하여 파일 크기를 최소화한다. (UglifyJS, Terser)
    4. 정적 파일 처리: HTML, CSS, 이미지, 폰트 등 정적 파일을 처리한다. (이미지 최적화, CSS 전처리기 컴파일 등)
    5. 환경 변수 설정: 개발 환경에서는 디버그 모드를 활성화, 프로덕션 환경에서는 최적화가 적용된 버전을 생성할 수 있다.
  • 즉, 일반적으로 빌드 과정 안에 번들링이 포함되어 있다.
  • 빌드와 번들링 도구는 종종 함께 사용되어 프론트엔드 애플리케이션의 전체 빌드 프로세스를 구성한다.
  • Webpack은 빌드 도구이자 번들링 도구이기도 하다. 

 

번들러란?

 

초기 웹 서비스는 간단한 형태였기때문에 이를 구성하는 HTML이나 자바스크립트 파일의 크기도 상대적으로 작았다.

  • 즉, HTML에서 JavaScript 소스를 제공하고 (<script> 삽입) 브라우저에서 이것을 순서대로 로드하는 방식이었다. 
  • 문제는 스크립트를 로드한 전역 컨텍스트에서 불러온 각 모듈간의 충돌이 발생한다는 것이다. (동일한 변수명 등)
  • 또한 웹 애플리케이션를 구성하는 파일의 양이 많다면, 사용자의 요청에 응답하는 시간이 길어지게 된다.
  • 하나의 파일안에 모든 스크립트를 작성한다면 이러한 문제를 해결 할 수 있겠지만 유지보수 측면에서 봤을 때에는 상당히 좋지 못한 방법이다.
  • 이러한 배경으로부터 번들러가 나오게 되었다.

 

번들러는 웹 애플리케이션을 구성하는 여러 개의 JavaScript 파일, CSS 파일, 이미지 등과 같은 리소스들을 하나로 묶어주는 도구이다.

  • 웹 애플리케이션에서는 일반적으로 여러 개의 파일이 사용되는데, 이 파일들은 웹 페이지 로딩 시에 개별적으로 요청되며 너무 많은 파일 요청은 네트워크 지연과 오버헤드를 초래할 수 있다.
  • 번들러는 이러한 문제를 해결하기 위해 여러 개의 파일을 하나의 번들(bundle)로 묶어주는 역할을 한다.
  • 이렇게 번들된 파일은 한 번의 요청으로 다운로드되며, 웹 페이지 로딩 속도를 향상시키고 성능을 개선시킬 수 있다.
  • 또한 모듈 단위의 코드를 작성할 수 있게 되어 코드의 가독성이 향상되며 유지 보수가 편하다는 장점이 있다.

 

번들러가 처음 만들어진 이유는 JavaScript 모듈을 브라우저에서 실행할 수 있는 단일 JavaScript 파일로 번들링하기 위함이었지만, 단순히 번들하는 것을 넘어서서 사용하지 않는 코드를 제거하는 등의 최적화 작업에 대한 필요성도 높아졌고, 이에 구글에서는 2009년 Closure Compiler라는 전문적인 JavaScript 최적화 도구를 만들기도 했다.

  • 하지만 최근에는 번들러 자체에서 개발과 빌드, 최적화를 위한 각종 플러그인을 제공하고 있기 때문에 굳이 별도의 태스크 러너나 최적화 도구를 쓰지 않아도 되게 되었다. 

번들러는 흩어져있던 코드들을 차곡 차곡 하나로 압축시켜주는 것 !

 

번들러 비교 

 

주요한 번들러로는 Webpack, Parcel, Rollup, ESbuild 등이 있다.

  • 이러한 번들러들은 각자의 특징과 설정 방식을 가지고 있으며, 개발자는 프로젝트의 요구 사항에 맞는 번들러를 선택하여 사용할 수 있다.
  • 아래와 같이 모듈 & 번들러로 인한 개발자의 고뇌도 엿볼 수 있다. '번들러'의 필요성에 대해 깊이 이해하려면 그 시작인 '모듈'이라는 개념부터 알아야 하는데, 출처 레퍼런스를 보는 것을 강력 추천한다! ( 출처:  자바스크립트 모듈 & 번들러로 본 조선시대 붕당의 이해 )

 

메인 프로젝트에서 사용할 번들러 성능 비교는 아래의 5가지의 장/단 분석으로 이루어질 예정이다.

  1. Webpack
  2. ESbuild
  3. Vite
  4. Parcel
  5. Turbopack (nextjs 13 내장 번들러) / Rspack

 

* Rollup에 대한 이야기가 많이 나오는데, 라이브러리나 패키지 개발에 적합한 번들러로 프로젝트에서는 고려하지 않기로 했다. 
특히 아래의 경우 webpack 사용을 추천한다고 한다. 
- code spliting이 필요 / static asset이 많음 / 안정성을 추구해야 함 / CommonJS 종속성이 있는 코드를 번들링할 경우

 

📌 webpack

 

장점

  • 오랫동안 사용되어 다양한 레퍼런스 등 생태계가 풍부하고 안정성이 뛰어나다.
  • 특히, 서드파티 라이브러리 관리나 CSS 전처리, 이미지 에셋 관리에 있어 다른 번들러보다 강점을 보인다.
  • 코드 스플리팅이 활성화된 단계에서 빌드 시간이 Rollup, Parcel과 비교했을 때 웹팩이 가장 빠르다.
    • 초기에 구동될 필요가 없는 코드를 분리하여 lazy loading을 통해 페이지 초기 로딩속도를 개선할 수 있다.
  • 개발 중에 변경사항을 자동으로 새로고침 해주는 라이브 리로딩 기능, 새로고침 없이 런타임에 브라우저의 모듈을 업데이트 하는 핫 모듈 교체(HMR) 등 개발 서버가 다른 번들러에 비해 뛰어나다.
    • Rollup과 Parcel은 플러그인을 설치하면 되긴 하지만 설정을 추가해주고, 특정 상황에서는 동작하지 않는 경우가 있다.

 

단점

  • 웹팩은 웹 애플리케이션에서 사용하는 CSS나 이미지 같은 에셋들을 JavaScript 코드로 변환하고, 이를 분석해서 번들하는 방식을 사용한다. 그렇기 때문에 다른 번들러에 비해 웹팩의 구성은 설정할 게 많고 많이 복잡한 편이다.
  • 코드 스플리팅에 있어선 Rollup과 Parcel이 성능적으로 더 뛰어나다. (하지만 활성화 단계에서는 Webpack이 뛰어남)
  • 자바스크립트가 아닌 파일들은 로더(번들 되기 전 파일 단위를 처리)나 플러그인(번들된 결과물을 추가로 후처리) 등으로 변경해주어야 한다.
  • 트리 쉐이킹이 지원되기는 하지만 CommonJs 방식으로 모듈을 로드한 부분을 ES6 문법으로 교체해야 하고 Terser 등 별도의 플러그를 설치해야 한다.
  • ES6 모듈 형태로 빌드 결과물을 출력할 수 없다.
  • 복잡한 문서가 진입장벽을 높인다. 
  • 퍼포먼스 문제로 Vite로 마이그레이션하여 수십 초 이상 걸리던 개발 서버 구동을 1초 이하로 단축하는 등 사례가 보인다. (하지만, Vite로 마이그레이션했다가 빌드가 안정적이지 않아 골치를 앓았다는 포스팅도 보인다.)

 

ESbuild

 

장점

  • 내부적으로 JavaScript로 작성된 다른 번들러와 달리(Webpack) ESBuild는 Go로 작성되었고, JS 기반의 번들러보다 10배~100배 빠른 퍼포먼스를 보여준다.
    • JavaScript는 인터프리터 언어이기 때문에 한줄씩 기계어로 변환하지만(+싱글 스레드), Go는 컴파일 단계에서 미리 소스 코드를 기계어로 변환해둔다.(+멀티 스레드)
    • 네이티브 코드 방식 사용, 병력처리 최적화, 메모리 사용 최적화 등 

단점

  • 메이저 버전이 릴리즈되지 않았다.
  • 설정이 wepack 등 처럼 유연하지 못하고 안전성 관련 이슈가 있다. (es5이하 문법을 100% 지원하지 않는 등)
  • 빌드만 할 수 있는 도구로 트랜스파일링, 코드 스플리팅, HMR 등 여러 기능을 종합적으로 제공해주는 webpack같은 도구가 여전히 필요하다. 

 

📌 vite

 

Vite는 ESBuild의 단점을 보완시킨 라이브러리로, 번들러이자 빌드 도구이다.

 

장점 (공식 문서 : Vite를 사용해야 하는 이유)

  • Rollup, Parcel 등 여러 개발 도구를 통합하여 단점을 개선하는 방식으로 개발됐다.
  • 개발 서버 구동 시간이 0에 가깝다.
  • 모든 CommonJS 및 UMD 파일을 ESM으로 불러올 수 있도록 변환한다.
  • 별도의 설정 없이 다양한 리소스의 import가 가능하다.
  • Direct import 구문에 대해 Preload하도록 함으로써 네트워크 비용을 줄이는 등 CSS 빌드가 최적화되어있다.
  • TypeScript, CSS 로더, HMR을 제공하면서도 복잡한 설정이 필요없다.

 

단점

  • ES6 타겟으로 번들을 생성하므로, ES5 이하로 타겟을 잡으려면 별도로 polyfill을 다뤄야 한다.
    • @vitejs/plugin-legacy 플러그를 제공해준다.
  • 기본적으로 <root>/index.html이 빌드를 위한 진입점이므로, 순수한 JS 번들을 생성하기 위해서는 라이브러리 모드를 설정해야 한다. 
  • SSR 지원 폭이 아직까지는 좁다.
  • ESM을 프로덕션에 배포하기에는 코드 스플리팅 등 최적화 기능을 제공하지 않기에 기존 번들 과정이 필요하다.
    • 즉, 개발 환경에서는 ESBuild, ESM을 사용하여 쾌적한 개발 경험을 제공하지만, 프로덕션 환경에서는 Rollup으로 번들링하기 때문에 개발 환경과 프로덕션 환경이 다르다는 것을 주의해야 한다. 

 

parcel

 

장점

  • Parcel은 Webpack과 달리 애플리케이션 진입을 위한 HTML 파일 자체를 읽기 때문에 별도의 설정 파일 없이, 설치만 하면 별도의 설정 파일 없이 빌드 명령어를 입력해 바로 사용이 가능하다. (Webpack은 에셋을 읽기 위해 JavaScript로 변환하기 위한 플러그인과 로더 등을 깔아야 함)
  • ES6 및 CommonJS 모듈 모두에 대해 트리 쉐이킹을 지원한다.
  • 트랜스파일에 대한 기본 제공을 지원하여 트랜스파일러도 간편하게 설정할 수 있다.
    • Webpack과 Rollup은 트랜스파일에 필요한 파일 유형을 일일이 설정해야 한다.
    • Parcel은 .babelrc, posthtml 등 설정파일들을 프로젝트 루트 디렉토리에 만들기만 하면 자동으로 파일을 읽어와 세팅을 해준다. 
    • 사용자가 설정한 트랜스파일 외에도 모든 모듈에서 Babel을 사용하여 최신 JavaScript를 브라우저에서 지원하는 형식으로 컴파일한다.

 

단점

  •  Webpack에 비해 생태계가 좁고 안정적이지 않다. (깃헙의 이슈)
  • 공식 문서가 잘 작성되어 있지만, 커스텀한 설정이 필요하다면 설정 파일을 다시 작성해야 한다.

 

Turbopack

 

Turbopack은 Webpack과 Next.js 개발자가 만든 자바스크립트와 타입스크립트 번들러이다. Next.js 13에는 기본으로 Webpack 대신 Turbopack이 번들러로 내장되어 있다.

장점

  • Webpack의 후속이라 할 수 있으며, JavaScript 대신 Rust로 개발되어 애플리케이션 규모가 클 수록 유리하다.
    • 동일한 작업을 두 번 수행하지 않는 캐싱과 메미이제이션을 활용한 아키텍쳐 덕분
    • webpack보다 700배 빠른 업데이트 속도 등
  • Vite와 비교하면 leaf 컴포넌트에서는 Turbopack이 약 68% 빠르긴 하지만, root 컴포넌트라면 거의 비슷하다. 

 

단점

  • 현재 알파 버전이고 많은 기능을 제공하지 않기에 프로덕션 용도로 사용하기는 적합하지 않다.
  • 알파 버전은 next dev만 지원하고 next build는 지원하지 않는다. (공식 문서 참고)

점 하나 찍혀있다.

Rspack

이것이 바로 프론트엔드의 세계인가... Turbopack에 뒤이은 Rspack이 나왔다.

장점

  • Rust 기반으로 재구성한 Webpack 호환 번들러로 Rust로 재작성된 로더와 플러그인을 내장하고 있다.
  • SWC를 내장해서 코드 분석 등에 사용하고 있기 때문에 Babel 등 트랜스파일링을 위한 로더가 따로 필요하지는 않다.
  • Webpack + SWC와 비교해 cold start(최초기동)은 약 8배, HMR은 약 3배의 속도 개선이 보인다. (벤치마크 테이블)
  • Turbopack과 달리, Webpack API 호환성을 어느 정도 유지하므로 unplugin 툴 등을 사용하면 ESBuild나 Vite 프로젝트까지 Rust 기반 툴링을 사용할 수 있다.

 

단점

  • 아직 프로덕션 용도로 사용하기에는 시기상조이다.

 

요약

 

구관이 명관인가? 라는 생각이 들면서, 대규모 프로젝트인 경우 '안정성'에 가장 큰 우선순위를 두고 신중하게 결정해야 한다는 생각이 들었다.

  • 풍부한 레퍼런스와 많은 서드파티를 필요로 하는 복잡한 어플리케이션이라면 Webpack
  • 최소한의 서드파티로 라이브러리를 만들고 싶다면 Rollup (+ 트리 쉐이킹과 같이 효율성을 고려해야 하는 프로젝트라면)
  • 복잡한 설정을 피하고 비교적 간단한 어플리케이션을 만들고 싶다면 Parcel
  • CRA를 대체하고 쾌적한 개발 환경이 필요하다면 Vite
  • 시간이 지나 프로덕션으로 사용이 가능해지고 안정성이 보장된다면, 성능적으로는 Turbopack / Rspack

 

 

 

레퍼런스 

- [JavaScript] 번들러(Bundler)란?

- 자바스크립트 모듈 & 번들러로 본 조선시대 붕당의 이해 

- Webpack, Rollup, ESBuild, Vite 비교

 - Webpack, Rollup, ESBuild, Vite, Parcel 비교 및 사용법 

- Turbopack이 Vite보다 정말 10배 빠를까?

 

 

728x90
⬆︎