안녕하세요 이번에는 미루고 미뤘던 번들러에 대해서 공부를 해보고 글을 써보려고 합니다.
번들러가 뭘까요 ?
"Bundle" 은 무슨 말일까요 ?
번역기의 힘을 빌려 한국어 뜻을 확인해 보니 묶음이라는 의미군요.
번들러란 이름에서도 유추할 수 있듯 어플리케이션에 필요한 여러 모듈들을 하나로 묶는 도구입니다.
왜 번들러를 사용해야 하나요 ?
그러게요
왜 모듈들을 묶어야 할까요 ? 그냥 없이 개발하면 안될까요 ?
한번 번들러 없이 만들어보겠습니다 !
번들러 없이 만들어보기

이 서비스는 덧셈, 뺄셈 기능이 있고 각각 input으로 받을 값을 계산하는 계산기입니다.
index.html
<html>
<head>
<meta charset="UTF-8" />
<title>계산기</title>
</head>
<body>
<header>
<h1>계산기</h1>
</header>
<main>
<form id="addForm">
<h2>덧셈</h2>
<input id="addFirstInput" />
<input id="addSecondInput" />
<button id="addButton">버튼</button>
</form>
<form id="minusForm">
<h2>뺄셈</h2>
<input id="minusFirstInput" />
<input id="minusSecondInput" />
<button id="minusButton">버튼</button>
</form>
</main>
</body>
</html>
음 좋습니다.
이제 기능을 더해보겠습니다.
add.js
const fristInput = document.querySelector("#addFirstInput");
const secondInput = document.querySelector("#addSecondInput");
const form = document.querySelector("#addForm");
const handleSubmit = (e) => {
e.preventDefault();
const num1 = Number(fristInput.value ?? 0);
const num2 = Number(secondInput.value ?? 0);
alert(num1 + num2);
};
form.addEventListener("submit", handleSubmit);
이제 add 기능을 테스트해 보겠습니다.

굳 잘 동작하네요 !
이제 minus 동작을 추가해 보겠습니다.
minus.js
const fristInput = document.querySelector("#minusFirstInput");
const secondInput = document.querySelector("#minusSecondInput");
const form = document.querySelector("#minusForm");
const handleSubmit = (e) => {
e.preventDefault();
const num1 = Number(fristInput.value ?? 0);
const num2 = Number(secondInput.value ?? 0);
alert(num1 - num2);
};
form.addEventListener("submit", handleSubmit);
이제 한번 실행시켜볼까요 ?
흠 에러가 발생했네요.
에러를 한번 확인해 보겠습니다.
문제 1 전역 변수
Uncaught SyntaxError: Identifier 'fristInput' has already been declared (at minus.js:1:1)
fristInput이 이미 선언되었다고 하네요...
음 분명 minus.js 파일에는 fristInput라는 변수가 하나밖에 없는데 왜 이런 문제가 발생할까요 ?
원인을 분석해 보면 add.js에 fristInput라는 변수가 있어서 저런 에러가 발생했네요.
즉시 실행 함수
즉시 실행 함수를 통해서 문제를 해결할 수 있을 거 같습니다.
(() => {
const fristInput = document.querySelector("#minusFirstInput");
const secondInput = document.querySelector("#minusSecondInput");
const form = document.querySelector("#minusForm");
const handleSubmit = (e) => {
e.preventDefault();
const num1 = Number(fristInput.value ?? 0);
const num2 = Number(secondInput.value ?? 0);
alert(num1 - num2);
};
form.addEventListener("submit", handleSubmit);
})();
이제 확인해 볼까요 ?
에러가 사라졌네요 좋습니다.
즉시 실행 함수로 스코프를 하나 만들어 전역스코프에 변수가 선언 안 되게 막아서 중복 선언 문제가 해결됐네요 !
다른 해결 방법은 없을까요 ?
type="module"
<html>
<head>
<meta charset="UTF-8" />
<title>계산기</title>
</head>
<body>
<header>
<h1>계산기</h1>
</header>
<main>
<form id="addForm">
<h2>덧셈</h2>
<input id="addFirstInput" />
<input id="addSecondInput" />
<button id="addButton">버튼</button>
</form>
<form id="minusForm">
<h2>뺄셈</h2>
<input id="minusFirstInput" />
<input id="minusSecondInput" />
<button id="minusButton">버튼</button>
</form>
</main>
<script type="module" src="./add.js"></script>
<script type="module" src="./minus.js"></script>
</body>
</html>
이렇게 type="module"로 모듈로 바꾸면 됩니다.
모듈은 각각 독립된 스코프를 갖고 있기 때문이죠.
이제 이러면 정말 끝일까요 ? 다른 문제는 없을까요 ?
서비스를 다시 확인해 보겠습니다 !
문제 2 여러 번의 네트워크 요청

음 네트워크 요청을 여러 번 하네요.
기능이 점점 많아지면 여러 개의 파일이 생길거고 요청하는 파일의 수가 점점 많아질 거 같습니다.
그러면 다 한 파일로 짜면 되지 않을까요 ?
한파일로 짜기
const addFristInput = document.querySelector("#addFirstInput");
const addSecondInput = document.querySelector("#addSecondInput");
const addForm = document.querySelector("#addForm");
const handleAddSubmit = (e) => {
e.preventDefault();
const num1 = Number(addFristInput.value ?? 0);
const num2 = Number(addSecondInput.value ?? 0);
alert(num1 + num2);
};
addForm.addEventListener("submit", handleAddSubmit);
const minusFristInput = document.querySelector("#minusFirstInput");
const minusSecondInput = document.querySelector("#minusSecondInput");
const minusForm = document.querySelector("#minusForm");
const handleSubmit = (e) => {
e.preventDefault();
const num1 = Number(minusFristInput.value ?? 0);
const num2 = Number(minusSecondInput.value ?? 0);
alert(num1 - num2);
};
minusForm.addEventListener("submit", handleSubmit);
지금은 간단한 기능 2개밖에 없어서 별 문제가 없지만 나중에 서비스가 커질수록 관리가 힘들어질 거에요.
번들러 적용시켜보기
이러한 문제들을 번들러로 해결할 수 있을까요 ?
한번 최소한의 세팅만 해서 확인해 보겠습니다 !
webpack.config.js
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
mode: "none",
};
이제 결과를 확인해 볼까요 ?
dist/bundle.js
/******/ (() => {
// webpackBootstrap
/******/ var __webpack_modules__ = [
,
/* 0 */ /* 1 */
/***/ () => {
const fristInput = document.querySelector("#addFirstInput");
const secondInput = document.querySelector("#addSecondInput");
const form = document.querySelector("#addForm");
const handleSubmit = (e) => {
e.preventDefault();
const num1 = Number(fristInput.value ?? 0);
const num2 = Number(secondInput.value ?? 0);
alert(num1 + num2);
};
form.addEventListener("submit", handleSubmit);
/***/
},
/* 2 */
/***/ () => {
const fristInput = document.querySelector("#minusFirstInput");
const secondInput = document.querySelector("#minusSecondInput");
const form = document.querySelector("#minusForm");
const handleSubmit = (e) => {
e.preventDefault();
const num1 = Number(fristInput.value ?? 0);
const num2 = Number(secondInput.value ?? 0);
alert(num1 - num2);
};
form.addEventListener("submit", handleSubmit);
/***/
},
/******/
];
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/
}
/******/ // Create a new module (and put it into the cache)
/******/ var module = (__webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {},
/******/
});
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](
module,
module.exports,
__webpack_require__
);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/
}
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter =
module && module.__esModule
? /******/ () => module["default"]
: /******/ () => module;
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/
};
/******/
})();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for (var key in definition) {
/******/ if (
__webpack_require__.o(definition, key) &&
!__webpack_require__.o(exports, key)
) {
/******/ Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key],
});
/******/
}
/******/
}
/******/
};
/******/
})();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
/******/
})();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, {
value: "Module",
});
/******/
}
/******/ Object.defineProperty(exports, "__esModule", { value: true });
/******/
};
/******/
})();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
(() => {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _add__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(1);
/* harmony import */ var _add__WEBPACK_IMPORTED_MODULE_0___default =
/*#__PURE__*/ __webpack_require__.n(_add__WEBPACK_IMPORTED_MODULE_0__);
/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};
/* harmony reexport (unknown) */ for (const __WEBPACK_IMPORT_KEY__ in _add__WEBPACK_IMPORTED_MODULE_0__)
if (__WEBPACK_IMPORT_KEY__ !== "default")
__WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () =>
_add__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__];
/* harmony reexport (unknown) */ __webpack_require__.d(
__webpack_exports__,
__WEBPACK_REEXPORT_OBJECT__
);
/* harmony import */ var _minus__WEBPACK_IMPORTED_MODULE_1__ =
__webpack_require__(2);
/* harmony import */ var _minus__WEBPACK_IMPORTED_MODULE_1___default =
/*#__PURE__*/ __webpack_require__.n(_minus__WEBPACK_IMPORTED_MODULE_1__);
/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};
/* harmony reexport (unknown) */ for (const __WEBPACK_IMPORT_KEY__ in _minus__WEBPACK_IMPORTED_MODULE_1__)
if (__WEBPACK_IMPORT_KEY__ !== "default")
__WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () =>
_minus__WEBPACK_IMPORTED_MODULE_1__[__WEBPACK_IMPORT_KEY__];
/* harmony reexport (unknown) */ __webpack_require__.d(
__webpack_exports__,
__WEBPACK_REEXPORT_OBJECT__
);
})();
/******/
})();
음 뭐가 엄청 많아졌지만 add.js, minus.js가 하나의 파일로 잘 합쳐졌다는것은 확실하게 보이네요.
브라우저 콘솔창도 확인해 볼까요 ?
처음에 발생했던 전역 변수 문제는 발생하지 않네요 !
번들 결과물에서 각각 파일에 따른 지역 스코프를 자동으로 생성을 해줍니다.

파일이 하나니 네트워크 요청도 한 번만 발생하네요 !
번들러의 기능들
이제 번들러의 기능에 대해서 좀 더 알아보겠습니다 !
Resolution
Resolution은 의존성을 분석할 때 경로와 확장자를 정해주는 기능입니다. 쉽게 말하면 파일 위치를 정확하게 찾게 해주는 기능입니다.
예를 들어
import { useState } from 'react'
이런 코드가 있다면react라는 라이브러리, react.tsx, react.ts, react.js 어디서 가져오는지 정해주는 기능을 합니다.
만약 이 계산기를 개발할 때 계산 로직이 있는 파일들은 .calc.js라는 확장자로 써야 한다면
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
mode: "none",
resolve: {
extensions: [".calc.js"],
},
};
이런 식으로 설정할 수 있습니다.
Load
Load는 무슨 기능일까요 ?
이 기능에 대해서 알아보기 전에 javascript로 작성한 코드를 typescript로 바꿔보겠습니다!
그리고 webpack.config.js를 수정해 보겠습니다.
const path = require("path");
module.exports = {
entry: "./index.ts",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
mode: "none",
resolve: {
extensions: [".ts", ".js"],
},
};
자 이제 빌드를 해볼까요 !

???
빌드에 실패했네요.
에러 메시지를 살펴볼까요 ?
Module parse failed: Unexpected token (1:60)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
"구문 분석에 실패했고 이 파일 형식을 처리하려면 로더가 필요하다."라네요.
로더는 Javascript가 아닌 다른 파일도 처리할 수 있게 해주는 기능입니다.
위에서 빌드 할 때 에러가 났던 원인은 Javascript가 아닌 Typescript를 처리하려고 해서 발생했습니다.
Typescript를 처리할 수 있게 Loader 세팅을 해볼까요 ?
module: {
rules: [{ test: /\.ts$/, use: "ts-loader" }],
},
이 코드를 webpack.config.js에 추가 후 빌드를 다시 해봤습니다.

이제 잘 동작하네요 !
Optimization
번들러에는 어떤 최적화 기법이 있을까요 ?
우선 현재 번들링 된 결과물을 살펴볼게요
dist/bundle.js
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ([
/* 0 */
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
__exportStar(__webpack_require__(1), exports);
__exportStar(__webpack_require__(2), exports);
/***/ }),
/* 1 */
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
const fristInput = document.querySelector("#addFirstInput");
const secondInput = document.querySelector("#addSecondInput");
const form = document.querySelector("#addForm");
const handleSubmit = (e) => {
var _a, _b;
e.preventDefault();
const num1 = Number((_a = fristInput.value) !== null && _a !== void 0 ? _a : 0);
const num2 = Number((_b = secondInput.value) !== null && _b !== void 0 ? _b : 0);
alert(num1 + num2);
};
form.addEventListener("submit", handleSubmit);
/***/ }),
/* 2 */
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
const fristInput = document.querySelector("#minusFirstInput");
const secondInput = document.querySelector("#minusSecondInput");
const form = document.querySelector("#minusForm");
const handleSubmit = (e) => {
var _a, _b;
e.preventDefault();
const num1 = Number((_a = fristInput.value) !== null && _a !== void 0 ? _a : 0);
const num2 = Number((_b = secondInput.value) !== null && _b !== void 0 ? _b : 0);
alert(num1 - num2);
};
form.addEventListener("submit", handleSubmit);
/***/ })
/******/ ]);
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__(0);
/******/
/******/ })()
;

Minification
Minification은 코드를 최소화하는 최적화 기법이에요.
webpack에서는 mode 값을 production으로 활성화시킬 수 있습니다.
webpack.config.js
const path = require("path");
module.exports = {
entry: "./index.ts",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
mode: "production",
resolve: {
extensions: [".ts", ".js"],
},
module: {
rules: [{ test: /\.ts$/, use: "ts-loader" }],
},
};
이제 결과를 확인해 볼까요 ?
dist/bundle.js
(()=>{"use strict";var e={618:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0});const r=document.querySelector("#addFirstInput"),n=document.querySelector("#addSecondInput");document.querySelector("#addForm").addEventListener("submit",(e=>{var t,u;e.preventDefault();const o=Number(null!==(t=r.value)&&void 0!==t?t:0),i=Number(null!==(u=n.value)&&void 0!==u?u:0);alert(o+i)}))},277:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var u=Object.getOwnPropertyDescriptor(t,r);u&&!("get"in u?!t.__esModule:u.writable||u.configurable)||(u={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,u)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),u=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),u(r(618),t),u(r(345),t)},345:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0});const r=document.querySelector("#minusFirstInput"),n=document.querySelector("#minusSecondInput");document.querySelector("#minusForm").addEventListener("submit",(e=>{var t,u;e.preventDefault();const o=Number(null!==(t=r.value)&&void 0!==t?t:0),i=Number(null!==(u=n.value)&&void 0!==u?u:0);alert(o-i)}))}},t={};!function r(n){var u=t[n];if(void 0!==u)return u.exports;var o=t[n]={exports:{}};return e[n].call(o.exports,o,o.exports,r),o.exports}(277)})();
오 코드가 엄청 읽기 어려워졌네요.
불필요한 띄어쓰기, 줄바꿈이 사라졌네요.
안에 코드들도 확인해볼까요 ?
확인이 어려워서 prettier로 읽기 좋게 바꾸고 내용을 확인해 보겠습니다.
dist/bundle.js(prettier 적용 후)
(() => {
"use strict";
var e = {
618: (e, t) => {
Object.defineProperty(t, "__esModule", { value: !0 });
const r = document.querySelector("#addFirstInput"),
n = document.querySelector("#addSecondInput");
document.querySelector("#addForm").addEventListener("submit", (e) => {
var t, u;
e.preventDefault();
const o = Number(null !== (t = r.value) && void 0 !== t ? t : 0),
i = Number(null !== (u = n.value) && void 0 !== u ? u : 0);
alert(o + i);
});
},
277: function (e, t, r) {
var n =
(this && this.__createBinding) ||
(Object.create
? function (e, t, r, n) {
void 0 === n && (n = r);
var u = Object.getOwnPropertyDescriptor(t, r);
(u &&
!("get" in u
? !t.__esModule
: u.writable || u.configurable)) ||
(u = {
enumerable: !0,
get: function () {
return t[r];
},
}),
Object.defineProperty(e, n, u);
}
: function (e, t, r, n) {
void 0 === n && (n = r), (e[n] = t[r]);
}),
u =
(this && this.__exportStar) ||
function (e, t) {
for (var r in e)
"default" === r ||
Object.prototype.hasOwnProperty.call(t, r) ||
n(t, e, r);
};
Object.defineProperty(t, "__esModule", { value: !0 }),
u(r(618), t),
u(r(345), t);
},
345: (e, t) => {
Object.defineProperty(t, "__esModule", { value: !0 });
const r = document.querySelector("#minusFirstInput"),
n = document.querySelector("#minusSecondInput");
document.querySelector("#minusForm").addEventListener("submit", (e) => {
var t, u;
e.preventDefault();
const o = Number(null !== (t = r.value) && void 0 !== t ? t : 0),
i = Number(null !== (u = n.value) && void 0 !== u ? u : 0);
alert(o - i);
});
},
},
t = {};
!(function r(n) {
var u = t[n];
if (void 0 !== u) return u.exports;
var o = (t[n] = { exports: {} });
return e[n].call(o.exports, o, o.exports, r), o.exports;
})(277);
})();
좀 더 자세하게 확인해 볼까요 ?
Before
const fristInput = document.querySelector("#addFirstInput");
const secondInput = document.querySelector("#addSecondInput");
const form = document.querySelector("#addForm");
const handleSubmit = (e) => {
var _a, _b;
e.preventDefault();
const num1 = Number(
(_a = fristInput.value) !== null && _a !== void 0 ? _a : 0
);
const num2 = Number(
(_b = secondInput.value) !== null && _b !== void 0 ? _b : 0
);
alert(num1 + num2);
};
form.addEventListener("submit", handleSubmit);
기존에는 이렇게 직접 만들었던 변수 이름이 그대로 보존됩니다.
After
const r = document.querySelector("#addFirstInput"),
n = document.querySelector("#addSecondInput");
document.querySelector("#addForm").addEventListener("submit", (e) => {
var t, u;
e.preventDefault();
const o = Number(null !== (t = r.value) && void 0 !== t ? t : 0),
i = Number(null !== (u = n.value) && void 0 !== u ? u : 0);
alert(o + i);
});
적용 후에는 변수 이름이 엄청 짧아졌네요. 또한 form같이 다른곳에서 사용 안 하는 변수는 사라지는 현상도 보여집니다.
전체적인 구조가 e라는 객체 안에 숫자의 key에 함수가 담기고 277함수에서 다른 모듈들을 불러오는 역할을 하고 즉시 실행 함수 마지막 부분에서 277함수를 실행시키는 구조입니다.
파일 크기도 확인해 볼까요 ?

4KB => 1KB로 많이 크기가 많이 감소했네요 !
Tree Shaking
Tree Shaking은 사용하지 않는 코드를 제거하는 최적화 기법입니다.
기능을 확인해 보기 위해서 utils.ts 파일을 추가하고 적용해 보겠습니다.
utils.ts
export const stringToNumber = (str: string) => {
console.log(`${str}을 숫자로 변환`);
return Number(str);
};
export const numberToString = (num: number) => {
console.log(`${num}을 문자열로 변환`);
return String(num);
};
add.ts
import { stringToNumber } from "./utils";
const fristInput = document.querySelector("#addFirstInput") as HTMLInputElement;
const secondInput = document.querySelector(
"#addSecondInput"
) as HTMLInputElement;
const form = document.querySelector("#addForm");
const handleSubmit = (e: Event) => {
e.preventDefault();
const num2 = stringToNumber(secondInput.value ?? "0");
const num1 = stringToNumber(fristInput.value ?? "0");
alert(num1 + num2);
};
form.addEventListener("submit", handleSubmit);
export {};
minus.ts 파일도 적용시켰습니다.
이제 빌드를 하고 결과물을 확인해 볼까요 ?
dist/bundle.js의 일부
748: (e, t) => {
Object.defineProperty(t, "__esModule", { value: !0 }),
(t.numberToString = t.stringToNumber = void 0),
(t.stringToNumber = function (e) {
return console.log("".concat(e, "을 숫자로 변환")), Number(e);
}),
(t.numberToString = function (e) {
return console.log("".concat(e, "을 문자열로 변환")), String(e);
});
},
음 분명 stringToNumber이 함수만 사용하는데 왜 번들 결과에 numberToString 함수도 포함되어 있을까요 ?
원인은 Common js 때문입니다.
Common js는 런타임에 모듈을 동적으로 로드하고 해석합니다. 즉 모듈의 어떤 부분이 사용될지 빌드 할 때 결정할 수 없고 번들러가 트리쉐이킹하기 어렵습니다.
ESM으로 전환을 하면 문제가 해결될까요 ? 한번 전환해서 확인해 보겠습니다. tsconfig.json파일을
before
"module": "CommonJS",
after
"module": "ESNext",
이렇게 바꾸면 됩니다.
이제 바꿨으니 결과를 확인해 볼까요 ?
dist/bundle.js
(() => {
"use strict";
var e = function (e) {
return console.log("".concat(e, "을 숫자로 변환")), Number(e);
},
t = document.querySelector("#addFirstInput"),
u = document.querySelector("#addSecondInput");
document.querySelector("#addForm").addEventListener("submit", function (n) {
var r, o;
n.preventDefault();
var l = e(null !== (r = u.value) && void 0 !== r ? r : "0"),
d = e(null !== (o = t.value) && void 0 !== o ? o : "0");
alert(d + l);
});
var n = document.querySelector("#minusFirstInput"),
r = document.querySelector("#minusSecondInput");
document.querySelector("#minusForm").addEventListener("submit", function (t) {
var u, o;
t.preventDefault();
var l = e(null !== (u = r.value) && void 0 !== u ? u : "0"),
d = e(null !== (o = n.value) && void 0 !== o ? o : "0");
alert(d - l);
});
})();
결과를 보면
stringToNumber함수는 이렇게 잘 남아있고
var e = function (e) {
return console.log("".concat(e, "을 숫자로 변환")), Number(e);
},
사용되지 않는 numberToString는 결과에 포함되지 않습니다 !
이번에는 번들러에 매우 기본적인 내용에 대해서 공부해 봤는데요. 이 내용뿐만 아니라 더 많은 기능들도 있고 여러 번들러들도 있습니다.
혹시 잘못된 내용이 있거나 부족한 부분이 있으면 댓글로 알려주세요 !
공부했던 자료들 출처
https://youtu.be/QfU5REp8sjQ?si=ixqWAuex1edJlhQx
https://www.heropy.dev/p/x8iedW
https://blog.leehov.in/24
https://webpack.kr/
'frontend' 카테고리의 다른 글
| 🚀 "우당탕탕 도서관" 프론트엔드 개발자 글쓰기 커뮤니티 2기 모집 안내 🚀 (0) | 2024.05.04 |
|---|---|
| ??? : 추상화 잘합니다. (대충 할 말 빙빙 돌려 말한다는 뜻) (0) | 2024.04.01 |
| 폼 미쳤다... React Hook Form (0) | 2024.03.25 |
| React Suspense 알아보기 (1) | 2024.03.25 |
| SSR 진짜 CSR보다 빠를까 ? (1) | 2024.03.25 |