모듈 번들러가 뭐에오

골두·2024년 6월 17일
0

Frontend

목록 보기
3/30
post-thumbnail

모듈이란 하나의 큰 JS 코드 베이스를 모듈이라는 여러 단위로 분할해 사용할 수 있게 만드는 구조를 말한다. CommonJS와 ESM이 나오면서 부터 적극적으로 사용되기 시작했는데 이 모듈 시스템을 적극적으로 활용하면서 JS로 개발할 때는 모듈없이 개발하기 힘들게 되었다.
하지만 모든 브라우저에서 모듈 기능을 활용할 수 있는 것은 아니기에 브라우저에서 이 모듈을 사용하려면 의존 관계를 미리 해결하고 브라우저에서 인식이 가능한 JS 코드로 변환하는 작업이 별도로 필요하고 이 역할을 모듈 번들러가 해준다.

모듈 번들러 역할

모듈 번들러의 역할은 어찌되었든 의존 관계를 분석 후 브라우저가 인식 가능한 JS코드로 변환한다. 더 구체적으로는 어떤 모듈을 읽고 브라우저에서도 인식이 가능한 일반 함수로 변환하고 그 함수의 인수로 다른 모듈을 import하기 위한 함수와 모듈의 export하는 값을 저장하기 위한 객체를 전달해준다.

// AS-IS
import { test } from "testM";

export const value = test + 30;

// TO-BE
function (require, module, exports) {
    var { test } = require('testM);
    exports.y = test + 30;
}

분석해 변환된 함수는 CommonJS 형식으로 되어있는데 ESM을 CommonJS로 트랜스파일링 시켜 브라우저에서도 사용하기 편하게 한다.

모듈 번들러 변화

이전에는 모듈 번들러를 사용하기에 종류가 너무 적었다. 그나마 괜찮은 모듈 번들러는 Webpack이였는데 그 외에 마땅히 있지 않았고 CRA가 없는 시대도 있었기 때문에 Webpack의 configuration을 직접 설정하면서 사용하고 있었고 별 다른 선택지가 없었다.

하지만 이제는 ESM을 브라우저에서 지원(<script type="module" />)을 시작했고 에버그린 브라우저(별도의 재설치가 없어도 업데이트가 가능한 브라우저)가 아닌 브라우저들을 지원하지 않는 비율이 급격하게 늘어나게 되었다.

loader의 확장

로더란 JS를 트랜스파일링 하기위해 사용하는 파일 전처리 기능으로, JS 트랜스파일링에는 일반적으로 babel-loader를 이용해 ES5 함수로 트랜스파일링하였으나 최근에 여러개의 loader들이 나오게 되었다.

웹팩에서 번들링 동작 방식에는 오버헤드가 존재하는데, 프로젝트 소스 코드상 사소한 한 부분이라도 수정하게 되면 Webpack에서 설정상의 entry 부터 시작해 다시 의존성 그래프를 구성하고 프로젝트를 리빌딩해 빌드하기 때문에 프로젝트의 모든 JS 코드 또한 loader의 전처리 과정을 수행할 수 밖에 없으니 더 좋은 loader들이 등장하게 된 이유기도 한 것 같다.

babel-loader

최신 JS 문법이더라도 ES5 문법까지 트랜스파일링을 진행해준다. 개발자들이 최신 문법을 이용해 소스코드를 짜도 최신 문법을 지원하지 않는 브라우저를 대비해 ES5 문법으로 트랜스파일링을 해 격차를 없애주고 개발자의 생산성을 증대시켜주는 역할을 수행한다. (var로 코드 짜는 일은 없다)

esbuild-loader

오픈소스 링크: [링크]

esbuild란 Go언어로 작성된 JS 번들러로 ESNext 및 Typescript 변환을 매우 빠른속도로 처리해주고 JS 압축 기능을 도와준다.

JS는 인터프리터 언어기에 한줄한줄 인터프리터에서 기계어로 번환하는 작업을 실행하기에 느릴 수 밖에 없으나 Go의 경우 실행 전 컴파일 단계에서 미리 소스코드를 기계어로 변환해두기 때문에 불필요한 작업이 생략되고 JS는 싱글스레드라 순차적인 진행이 필요하나 Go의 경우 멀티 스레드 기반으로 동작이 가능해 여러 파일이 동시적으로 번들링, 트랜스파일링이 가능하다는 이점 또한 존재한다.

SWC

SWC는 loader는 아니지만 babel-loader, esbuild-loader를 통해 트랜스파일링해주는 것과 연관이 있어 첨부하게 되었다.

SWC(Speedy Web Compiler) 는 JS 프로젝트의 컴파일, 번들링 모두 사용 가능한 Rust라는 언어로 제작된 빌드 툴로 기존 빌드에 활용하던 babel과 Terser(TODO: 추후 더 조사해보기, 대충 코드 경량화를 위한 툴)를 대체할 수 있다.

Rust라는 언어는 병렬 처리가 가능한 (고려되어 설계된) 언어이기 때문에 병렬처리가 가능한 언어이다.

그렇기 때문에 Rust 언어로 작성된 SWC는 의존성이 없는 파일들을 동시에 변환할 수 있는데, 약 20배나 빠른 벤치마크 결과까지 있기 때문에 매우 속도가 빨라졌다라고 볼 수 있다.

bundler 종류의 확장

모듈 번들러에는 새로운 번들러들이 많이 나왔지만 지금 TIL에서는 webpack과 Vite 2개에만 초점을 둔다. (추후 turbopack과 다른 모듈 번들러도 조사해보면 좋다.)

Webpack

이전에 대중화된 대표적인 모듈 번들러로 JS, CSS 및 이미지 번들링을 포함해 복잡한 프로젝트 설정을 처리할 수 있는 도구이나 프로젝트가 커지면 커질수록 빌드 속도가 느리고 설정이 복잡하다는 단점 또한 존재한다.

Vite

ES 모듈 사용 전에 개발자는 모듈화된 방식으로 JS를 작성하기 위한 기본 매커니즘이 없었기에 번들링(JS에서 빌드) 이라는 개념이 익숙한 이유다. 더 큰 애플리케이션을 만들기 위해 JS의 양 또한 많이 증가하게 되면서 JS 기반 도구의 성능 병목 현상까지 발생하게 되었고, 개발 서버 가동 시 오랜 시간이 걸리고 HMR(핫 모듈 교체)를 쓰더라도 파일 편집에 시간이 걸릴 수 있다. (브라우저 반영되는 핫 리로드까지 몇 초 정도 걸릴 수 있음)

이러한 문제를 해결하기 위해 Vite가 나오게 되었다. Vite는 브라우저에서 지원하는 ESM 및 네이티브 언어로 작성된 JS 도구 등을 활용해 문제를 해결하려고 노력 중이다.

콜드 스타트

콜드 스타트의 정의는 최초로 실행되어 이전에 캐싱한 데이터가 없는 경우를 말함

개발 서버 구동 시 번들러 기반의 도구는 애플리케이션 내 모든 소스 코드에 대해 크롤링 및 빌드(번들링) 작업을 마쳐야만 실제 페이지를 제공했다.

vite는 애플리케이션 모듈을 dependencies와 source code 2개의 카테고리로 나뉘어 개발 서버의 시작 시간을 개선한다.

dependencies

내용이 바뀌지 않을 일반적인 JS코드(주로 dependency)로 기존 번들러에서는 이런 수십 수백개의 라이브러리여도 dependency에 대해 번들링 과정이 필요했고 이것은 불필요하고, 많은 시간을 소요하는 원인 중 하나였기에 vite는 esbuild를 사용한 사전 번들링(이런 코드들을 미리 번들링 해둔 것) 기능을 활용하고 있다. Go언어를 쓰는 esbuild기에 기존 번들러보다 10 ~ 100배 까지의 빠른 속도를 제공할 수 있다.

sources

소스코드도 처음부터 다 불러오지 않는다. Native ESM을 이용해 vite에서는 소스코드를 제공하기에 import 경로를 계속 따라가면서 만들어진 인스턴스 트리 기반으로 필요한 파일들만 불러와서 처리하기에 처음부터 번들링 하는 기존 모듈 번들러보다는 속도가

번들링

이제는 대부분의 브라우저가 기본적으로 ESM을 지원하지만 그럼에도 번들링을 사용하는 이유는 최적의 로딩 성능을 위해 불필요한 파일들을 쳐내는 트리 세이킹, 지연 로딩, 청크 파일 분할 등의 작업을 위해서는 번들링을 하는 것이 좋다.

쉬운 설정

webpack의 경우 React와 관련된 기본 설정만 해도 100 ~ 200 줄이 넘지만 vite의 경우 plugin에 react() 함수 하나만 추가해도 모든 기본설정이 완료되는 템플릿 화가 되어있기 때문에 복잡한 설정을 무시하기 매우 쉽다

profile
나 볼려고 만든 블로그 (블로그 이전: https://goldfrosch.tistory.com/)

0개의 댓글