specially thanks to 김정환 개발자님.
이 내용은 저의 개인 github 레포에서 정리중인 TIL에 기록된 내용을 정리하였습니다.
모듈 시스템이 없던 시절에서는 HTML 파일에 로직을 담당하는 함수가 담긴 스크립트 파일을 불러와서 사용해야 했다. 전역 공간에서 모든 함수가 노출될 수 있었다. 함수의 이름이 동일하다면 충돌이 이는 것은 당연했다. (브라우져의 콘솔에서도 함수를 사용할 수 있을정도로) 그래서 ES2015부터 모듈 구문이 사용되게 되었다.
const math = math || {};
(function () {
function sum(a, b) {
return a + b;
}
math.sum = sum; // math 객체에 메서드 형식으로 등록
})();
const = require
형태의 CommonJS가 유명하다. 서버 사이드 런타임인 node.js에서 사용된다. Express.js에서 주로 많이 사용했다.// math.js
exports function sum(a, b) { return a + b };
// app.js
const math = require('./math.js');
math.sum(1, 2) // 3
import { name } from './root'
형태로 불러와 사용할 수 있다.export default
키워드로 모듈을 내보내면 {}를 달지 않아도 된다. 웹팩의 등장에 핵심적으로 영향을 미친 것은 브라우져에서 모듈을 지원하는지에 대한 여부였다. IE와 같은 브라우져는 그 버전에 따라 지원이 너무 되지 않는 케이스가 많았다. 그래서 우리는 웹팩을 이용하게 되었다. 엔트리 포인트를 시작으로 (번들링이 시작되는 파일) 프로덕트의 코드를 모듈로 연결하여 하나의 결과물로 아웃풋 경로에 만드는 것이 웹팩이 하는 일이다. 웹팩의 도움으로 우리는 ES6의 모듈 시스템을 적극적으로 사용할 수 있다.
npm i -D webpack webpack-cli
npm을 통해서 웹팩 터미널 도구를 설치하면 node_modules/.bin 디렉토리에 webpack-cli에서 사용할 수 있는 명령어들이 보일 것이다. node_modules/.bin/webpack --help
를 입력하면 웹팩 상에서 사용할 수 있는 터미널 명령어가 나온다.
--mode
옵션은 웹팩이 번들링을 동작시키는 모드를 의미하는데, 개발에서 사용한다면 development를 지정해주면 된다.--entry
옵션은 말 그대로 번들링이 처음 시작될 하나의 시작점을 지정해주는 것이다. app.js에서 파일 번들링이 시작된다면 --entry <경로/app.js>
형태로 명령어를 작성하면 된다.-o, --output-path
옵션은 번들링 결과물이 저장될 위치를 말한다.node_modules/.bin/webpack --mode development --entry src/app.js --output dist/main.js
위의 명렁어를 실행하면 dist/main.js
경로에 번들링 결과물이 저장되는 것이다.
webpack.config.js 파일로 웹팩의 번들링 환경을 쉽게 구축해줄 수 있다. 설정 파일이 있다면 매번 노드 모듈 폴더의 웹팩 모듈을 찾지 않고, npm 스크립트 명령어로 번들링을 진행할 수 있다.
// output 절대 경로를 만들어주기 위해서 노드 내장 모듈인 path를 불러온다.
const path = require("path");
module.exports = {
mode: "development",
entry: {
main: "src/app.js",
},
output: {
filename: "[name].js",
path: path.resolve("./dist"),
},
};
[name].js
형태로 하여, entry 옵션에서 지정해준 키값 ‘main’을 받게 되는 형식이다."bundle" : "webpack"
로더는 말 그대로 불러오게 해주는 어떤 도구다. 프로젝트에 존재하는 파일을 모듈로 인식하게 해주는 것이 웹팩 로더의 역할이다. 그것이 CSS건 이미지 파일이건 모두 모듈로 인식해서 자바스크립트 파일에 불러와 사용하게 해준다. React 프로젝트를 할 때, 컴포넌트 파일에 css를 불러와 사용하는 것이 대표적인 예 일것이다. 역시나 불러올 때는 import
키워드를 사용하면 된다.
웹팩의 로더는 다른 언어로 작성된 파일을 자바스크립트 문법으로 변환해주거나 (예를 들면 타입스크립트로 작성된 코드를 자바스크립트로 변환한다던지), 적은 용량의 이미지는 html 태그의 인라인 data URL 형식으로 반환해주는 등의 역할을 한다.
webpack.config.js
설정 파일에서 로더의 동작을 module이라고 하는 객체 내부에서 정의해주면 된다.
module: {
rules: [
{
test: /\.js$/, // .js확장자로 끝나는 모든 파일에 이 로더를 적용한다는 의미 (정규표현식이 온다.)
use: ["로더 이름"], // 사용할 로더를 순서대로 반영한다.
loader: "로더 이름", // 단독으로 사용되는 로더 규칙을 정의 할 경우
option: {},
},
];
}
module.rules[0].use
에 정의된 로더 배열은 뒤에서부터 앞의 순서로 로더가 동작하게 된다. 그래서 특정 번들링 순서가 중요하다면 가장 먼저 적용되어야 할 로더를 맨 뒤에 요소로 넣어줘야 한다.loader: "로더이름"
형태로 정의해줄 수 있다.css-loader
style-loader
css-loader
가 변환해준 스타일 시트를 브라우저가 이해할 수 있도록 dom에 추가해주는 로더use: ["style-loader", "css-loader"]
file-loader
로더가 번들링할 파일들을 모듈화하여 번들링 시키는 동작을 도와주는 웹팩의 도구라면, 플러그인은 번들링된 파일에 특정 처리를 하는 도구다. 웹팩 플러그인을 활용하면 번들링이 된 main.js 파일을 한 줄로 만들어 난독화 시킬 수 있다. 또는 헤드의 타이틀에 개발중인지 아닌지를 표시할 수 도 있다. (html을 직접 건들지 않고)
로더가 함수의 형태였다면, 플로그인은 인스턴스를 만드는 클래스로 제작된다. new 키워드로 플러그인 인스턴스 생성자 함수를 부르면 된다. webpack.config.js
설정 파일에서 plugins : [ new 플러그인 ]
형태로 사용해주면 된다.
웹팩 플러그인을 직접 만들어서 사용하는 경우는 극히 드물다. 웹팩 내장 플러그인을 불러와서 붙이거나 서드파티 플러그인을 설치해서 사용한다. 웹팩 내장 플러그인의 문서는 여기에서 볼 수 있다.
BannerPlugin
// webpack.config.js
const webpack = require("webpack");
const childProcess = require("child_process");
module.exports = {
plugins: [
new webpack.BannerPlugin({
banner: `
Build Date: ${new Date().toLocaleString()}
Commit Version: ${childProcess.execSync("git rev-parse --short HEAD")}
Author: ${childProcess.execSync("git config user.name")}
`,
}),
],
};
DefinePlugin
process.env.NODE_ENV
값이 디폴트이다. 지정되지 않았다면 웹팩 mode 옵션의 환경이 기준이 된다.module.exports = {
plugins: [
new webpack.DefinePlugin({
"api.domain":
process.env.NODE_ENV === "development"
? JSON.stringify("http://dev.api.domain")
: JSON.stringify("http://product.api.domain"),
}),
],
};
HtmlWebpackPlugin
npm i -D html-webpack-plugin
const HtmlWebpackPlugin = require("html-webpack-plugin");
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
templateParameters: {
env: process.env.NODE_ENV === "development" ? "(개발중)" : "",
},
minify:
process.env.NODE_ENV === "production"
? {
collapseWhitespace: true, // 프로덕션 환경이면 공백을 날린다.
removeComments: true, // 프로덕션 환경이면 주석을 날린다.
}
: false,
}),
];
CleanWebpackPlugin
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
plugins: [new CleanWebpackPlugin()];