기본적으로 웹팩은 개발자가 작성한 파일들을 번들링한다. 번들링이란, 내가 작성한 모든 파일을 1개의 자바스크립트 파일로 합치는 행위를 말한다.
파일을 합칠 때는 파일을 합치는 시작점이 되는 파일이 필요하고 이러한 파일을 시작점 혹은 엔트리 포인트(Entry point)라고 한다.
이전의 예제로 보면, app.js
파일이 엔트리 포인트가 될 수 있고, math.js
파일은 app.js
가 사용하던 모듈로 인식되어 번들링 후에는 app.js
파일에 합쳐지게 될 것이다.
번들링 후에 탄생한 파일은 아웃풋(output)이라고 한다.
대략 위와 같이 그림을 그려볼 수 있다.
웹팩 사이트에서 이미지를 보면, 왼쪽을 Modules with dependencies
라고 한다. 모듈은 또 다른 모듈을 의존하여 실제 구조는 상당히 복잡한데, 이것을 번들링하여 보통 하나의 js로 만들어준다.
그림에서는 맨 왼쪽 최상단에 있는
.js
가 엔트리 포인트일 것이다.
"dependencies": {
"prettier": "^2.4.0",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
}
실습을 웹팩 4버전으로 할 거라, 직접 package.json
에 위와 같이 설치할 webpack
및 webpack-cli
버전을 입력했다.
아래 두가지 명령어 중 하나로 webpack-cli --help
명령어를 실행해볼 수 있다.
node_modules/.bin/webpack --help
npx webpack-cli --help
node_modules/.bin
밑에 해당 명령어가 존재한다는 것도 알 수 있고, npx
라는 명령어로 해당 위치를 찾아갈 필요 없이 cli
를 쉽게 호출할 수 있다는 것도 알 수 있다.
npx가 무엇인지 궁금하다면 npx란 무엇일까? 링크를 참조해보자.
간략히 설명하자면, 패키지 중에는 실행을 위한 패키지와 이용을 위한 패키지가 있다. 실행을 위한 패키지의 경우 이전에는global
옵션을 이용해서 설치했는데, 전역에서 이 패키지 저 패키지 설치하다보니 꼬이는 일이 부지기수였다. 그에 대한 해결책으로 나온 것이npx
이다.먼저 패키지가 설치되어 있는지 확인하고 없다면 설치하고 실행하는 간단한 구조이다.
--help
명령어를 입력하면 위의 사진처럼 webpack-cli
의 다양한 명령어를 구경할 수 있다.
--mode
: 환경에 따라 development
, production
, none
중 하나를 고를 수 있다.
--entry
: 이전에 말했던 엔트리 포인트를 지정할 수 있다.
--output
: 결과물이 위치할 경로를 지정할 수 있다.
이전에 모듈 방법에 대해 배울 때 AMD
방식으로 모듈을 이용한 app_amd.js
가 있었다.
import * as math from "./math_amd.js";
console.log("1+2 = ", math.sum(1, 2));
export function sum(a, b) {
return a + b;
}
npx webpack-cli --mode development --entry ./src/app_amd.js --output ./dist/app.js
위에서 배운 옵션 3가지 --mode
, --entry
, --output
을 이용하여 번들링해보았다.
위와 같은 화면이 뜨면서 번들링이 된다.
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./src/app_amd.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./src/app_amd.js":
/*!************************!*\
!*** ./src/app_amd.js ***!
\************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math_amd_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math_amd.js */ \"./src/math_amd.js\");\n\r\n\r\nconsole.log(\"1+2 = \", _math_amd_js__WEBPACK_IMPORTED_MODULE_0__[\"sum\"](1, 2));\r\n\n\n//# sourceURL=webpack:///./src/app_amd.js?");
/***/ }),
/***/ "./src/math_amd.js":
/*!*************************!*\
!*** ./src/math_amd.js ***!
\*************************/
/*! exports provided: sum */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return sum; });\nfunction sum(a, b) {\r\n return a + b;\r\n}\r\n\n\n//# sourceURL=webpack:///./src/math_amd.js?");
/***/ })
/******/ });
알수없는 괴물같은 js가 나타났다.
결과가 잘 나온다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>테스트용</title>
</head>
<body>
콘솔을 확인해봅시다.
<script src="dist/app.js"></script>
</body>
</html>
이제
type="module"
옵션은 필요 없다.
브라우저에서도 결과물을 잘 보여준다.
기존에는
npx webpack-cli --mode development --entry ./src/app_amd.js --output ./dist/app.js
위와 같이 긴 명령어를 이용해서 번들링을 해보았는데, 이렇게 긴 명령어를 치는 것도 귀찮고, 실수하기도 쉬우니 무언가 설정파일을 만들어서 자동화해보자.
기본: webpack.config.js or webpackfile.js
라고 되어있는 것을 볼 수 있다. 우리는 이 중 webpack.config.js
파일을 만들어보자.
// node.js의 CommonJS 를 사용한 모듈화
const path = require("path");
module.exports = {
mode: "development",
entry: {
main: "./src/app_amd.js",
},
output: {
path: path.resolve("./dist"),
// output 이름을 동적으로 나타낼 수 있는 효과가 있다.
// entry는 하나가 아니라 여러 개일 수도 있어서,
// entry가 여러 개라면 output도 여러 개다.
filename: "[name].js",
},
};
node js의 path
모듈을 사용하면 webpack.config.js
작성하기가 한결 쉬워서 path
모듈을 가져왔다.
module.exports
는 이전에 배웠듯 nodejs가 공식으로 지원하는 CommonJS에서 사용하는 모듈화 방법이다.
이전에 --mode
, --entry
, --output
으로 작성하던 부분을 json으로 나타내주었다.
path: path.resolve()
는 경로를 상대경로로 편리하게 나타내기 위함이다.filename: "[name].js"
는 이름을 동적으로 설정한다는 뜻이다. main
의 이름을 그대로 쓰게 만들 것이다. main.js
가 그대로 나온다. 이렇게 하는 이유는 entry point
가 여러 개 일 때 이름이 겹치지 않고 동적으로 잘 나타나게 하기 위해서이다. "scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
}
위와 같이 "build": "webpack"
처럼 등록하면 npm run build
를 입력했을 때 실행이 될 것이다.
웹팩을 실행하고 node
명령어로 실행도 해보았다.