JavaScript 번들러의 이해 — (3) 번들러 개론

권세규
네이버 플레이스 개발 블로그
7 min readJun 17, 2022

--

이 글은 JavaScript 번들러의 이해 — (2) TypeScript 모듈에 이어지는 글로, 번들로러서의 Webpack을 알아봅니다. 만약 JavaScript / TypeScript에서 모듈이 어떻게 처리되는지 모르시는 분들은 이전 편을 참고하시면 되겠습니다.

번들러가 뭔지 잘 몰라도 이름은 들어봤을 정도로, 이제는 사실상 업계 표준이 된 Webpack. 그런데 사실 Webpack이 왕좌를 차지한 지는 얼마 되지 않았습니다. 점유율 1위를 탈환한 것은 2020년이 처음이었거든요. 그리고 얼마 지나지 않아 Vite를 비롯한 루키들에게 그 자리를 위협받고 있습니다.

이렇게 자주 변하는 프론트엔드 생태계를 이해하려면, 번들러의 근본을 이해하고 어떤 기능들이 있는지 알 필요가 있습니다.

1편에서 언급했듯 번들러는 JavaScript 모듈을 한 파일로 연결합니다. 이를 위해서는 의존성 분석이 필수적이지요. 과거부터 BrowserifyGulpJS가 이 기능을 충실히 수행하고 있었습니다.

그런데 React.js, sass, babel 등 다양한 도구가 등장하면서, 단순히 JavaScript 파일을 연결(Concatenate)해주는 것만으로는 부족한 시대가 열렸습니다. 이런 시대의 흐름 속에서 번들러는 좀 더 종합적인 웹 개발의 중심이 되기 시작했습니다.

번들러와 관련있는 기능들

번들링(Bundling)

번들링은 번들러의 존재 의의이자 가장 중요한 핵심입니다. 복잡한 의존성을 가진 js 파일들을 알아서 예쁘게 정렬하고 포장합니다.

  • 브라우저에서 사용할 수 없는 CommonJS 등의 모듈을 지원
  • 파일을 하나로 합침으로서 HTTP 요청 감소

2022년 기준으로 굉장히 다양한 번들러가 난립하고 있으며, 역사적인 IE 지원 종료와 함께 생태계에 새로운 지각 변동이 예상됩니다. 이들에 대해서는 4편에서 다루도록 하겠습니다.

Various bundlers which include browserfiy, gul, webpack, rollup, parcel, esbuild, vite and snowpack.

트랜스파일(Transpile)

트랜스파일이란, 어떤 언어를 추상화 단계가 비슷한 언어로 변환하는 것을 말하며, 컴파일(Compile) 개념의 일부로도 볼 수 있습니다.

모던 웹 어플리케이션은 HTML, CSS, JavaScript만을 사용해서 개발하기엔 너무나 복잡해져버렸고, 이를 보완하고자 다양한 슈퍼셋(Superset, 기존 집합을 포함하는 더 큰 집합)이 등장했습니다. 이들을 기존 생태계에서 사용하려면 트랜스파일이 필수였지요. 아래와 같은 사례들은 모두 트랜스파일로 볼 수 있습니다.

  • TypeScript → JavaScript
  • SASS → CSS
  • ES2021 JavaScript → ES2014 JavaScript
  • JSX → JavaScript

각 도구별로 CLI 트랜스파일러를 제공하지만, 이것들은 추상화 수준이 낮은 로우API라서 직접 쓰기엔 매우 불편합니다. 마치 C언어 프로그램을 만들 때 make 파일 없이 일일이 파일 하나씩 컴파일을 하는 것과 같지요.

번들러는 번들링 과정에서 트랜스파일러를 수용하여, JavaScript 이외의 도구들도 포용하는 형태로 진화하고 있습니다.

폴리필(Polyfill)

폴리필은 어떤 언어 집합 내에서 기본적으로 지원하지 않는 기능을 구현해주는 것입니다. 예를 들어 ES6에 추가된 Set, Map은 새로운 문법이 아니라 ‘객체’에 불과합니다. 단지 그것이 ES5에선 정의되지 않은 객체일 뿐이지요.

폴리필은 문법의 문제가 아니므로, 문법을 변환하는 트랜스파일러의 소관이 아닙니다. 이 때문에 트랜스파일러를 붙인다고 무조건 IE 지원이 되지는 않았던 것입니다.

일반적으로 폴리필은 즉시실행함수(IIFE)로 구성된 모듈 배포를 합니다. 웹 클라이언트 용으로 널리 쓰이는 폴리필인 polyfill.io소스코드를 보시면 이해가 빠르게 될 것입니다.

번들러가 폴리필에 직접 관여하지는 않습니다. 그저 수많은 JavaScript 의존성 중 하나라고 생각할 뿐이지요. 어느 도구를 쓰느냐, 무엇을 폴리필하느냐에 따라 방법은 천차만별입니다.

코드 분할(Code Splitting)

번들링 전략은 1편에서 언급했던 문제들을 해결했지만, React.js, Angular, vue.js를 필두로 한 SPA 개발이 유행하면서 또 다른 문제를 마주하게 됩니다. 물리적으로 하나의 HTML 페이지에서 논리적인 여러 페이지를 서빙하게 되면서, 당장 필요하지 않은 페이지의 소스도 번들링을 해야한다는 점이지요.

이는 결국 번들 파일의 크기를 늘리고 사용자 경험을 저해하게 됩니다. 따라서 필요에 따라 번들을 나눠야 하는 요구사항이 생겼습니다. 마침 ES6에서 동적 import가 추가되면서 이것이 좀 더 쉬워졌지요.

코드 분할의 필요성은 요구사항에 따라 달라지기 때문에, 번들러가 직접 분할 지점을 결정을 하여 나눠주지는 못합니다. 대신 사용자가 좀 더 편하게 번들링 단위를 통제할 수 있는 기능을 제공합니다. Next.js는 이러한 기능을 잘 활용한 예시로 볼 수 있습니다.

트리 쉐이킹(Tree Shaking)

코드 분할이 요구사항에 맞는 번들 분할로 파일 크기를 줄이는 전략이라면, 트리 쉐이킹은 불필요한 코드를 삭제하는 전략입니다. “무엇이 사용되지 않는 코드인가”라는 문제를 풀기 위해서는 정적 분석이 필수적이며, 번들러 별로 알고리즘이나 접근 방식, 설정 인터페이스가 달라 매우 큰 차이가 나는 부분이기도 합니다.

Webpack의 경우 ES6 모듈에 한해서 파일 단위로만 트리 쉐이킹을 수행합니다. 반면 Rollup은 좀 더 일반적인 경우에도 잘 처리를 합니다.

소스압축(Minification)

소스압축(Minification)은 소스코드의 식별자(변수명, 함수명 등)를 a, b 같은 짧은 이름으로 바꾸고, 주석과 공백 문자를 삭제하는 것입니다. 보안 증진과 소스 파일 감소라는 두 가지 목적이 있습니다.

좀 더 고도화된 난독화 도구들(Obfuscator)은 한 발짝 나아가서 동일 동작을 하는 다른 소스코드를 만들기도 하며, 전산언어학적인 연구 과제로 다루기도 합니다.

하지만 웹 개발에서는 중요 로직을 클라이언트에 넣지 않는 것을 지향하므로, 보안보다는 번들 압축에 초점을 맞춥니다. 성능 문제가 생길 수 있는 난독화는 특별한 이유가 없다면 하지 않습니다.

소스압축도 번들러가 직접 하는 것이 아니라, 전문 서드파티 도구(ex: uglify)를 번들링 과정에서 집어넣어서 수행합니다. 또한 압축된 번들을 배포할 때에는 파일 이름에 .min.js 를 붙이는 관례가 있습니다.

소스맵(Sourcemap)

번들링과 소스압축을 거치면 원형이 거의 남아있지 않은데, 이는 디버깅을 어렵게 만들 수 있습니다. 이를 해결하기 위해, 번들러는 소스맵(Sourcemap)을 생성하는 기능을 제공합니다.

소스맵은 브라우저에서 디버깅을 용이하게 하기 위하여, 변형 전의 소스 정보를 보존한 메타 파일입니다. 소스맵을 어떻게 생성하느냐에 따라서 빌드 시간이 크게 차이 날 수 있습니다.

소스맵은 주로 개발 과정에서 사용합니다. 프로덕션에서도 빠른 문제 대응을 하기 위해 생성할 것을 권장하는 주장도 있지만, Sentry 같은 로그 도구로 어느 정도 대체할 수도 있기 때문에 상황에 맞게 판단하시기 바랍니다.

빠른 모듈 교체(Hot Module Replacement)

웹 개발 특성 상, 개발 과정에서 소스코드를 살짝 수정한 뒤 동작을 확인할 일이 굉장히 많습니다. 그런데 매번 전수 빌드를 돌리면 너무 시간이 오래 걸리므로 현실적이지 않습니다.

수정된 부분 및 그 관련된 코드만 교체하고, 서버를 재부팅하거나 페이지를 새로고치지 않아도 상태를 변경해주는 기능을 빠른 모듈 교체(Hot Module Replacement, HMR)이라고 합니다.

번들러에 따라 HMR을 어느 정도까지 어떻게 구현하느냐가 차이가 있습니다. 웹소켓이나 캐싱을 쓰는 방식도 있고, 후기에 나온 Vite나 Snowpack처럼 개발 중에 번들링을 배제해버리는 기법을 사용하기도 합니다.

고도화된 HMR 도구들은 React 상태까지 보존을 하기도 하지만, 오히려 HMR이 일관성없는(inconsistent) 동작을 한다는 이유로, 고급 기능은 비활성화하고 새로고침하며 개발하는 경우도 있습니다. 반면 CSS 작업을 할 때는 오히려 이런 기능이 끊김없는 경험을 제공합니다.

다음 편에는 2022년 기준 6월 기준 왕좌에 앉은 Webpack에 대해 심층적으로 알아보도록 하겠습니다.

이전 편 읽기

(1) JavaScript 모듈
(2) TypeScript 모듈

다음 편 읽기

(4) Webpack 및 다른 번들러들

--

--