Webpack Study part 1. (webpack concept읽기)
Webpack을 공부하기 위해 webpack.js.org 에 있는 Concept부터 보기로했다.
webpack 은 애플리케이션에 필요한 모든 dependancies(js module, css, image, etc..)를 하나의 그래프로 만든다.
그 그래프의 꼭지점, 최상점이 되는 위치가 entry 이다. (You can think of your application’s entry point as the contextual root or the first file to kick off your app.)
entry 는 webpack.config.js 에서 정의된 entry property 에 따라서 정의된다.
webpack 은 기능이 워낙 많기때문에 config.js 가 복잡한것같다 배울것도 많고 하지만 많이 알아서 나쁠것은 없는듯하다. 그리고 영문도 워낙 잘 적혀있어서 배우기도 쉽다.
1–1. Single Entry Syntax
// usage entry: string|Array<string> const config = {
entry: './path/to/my/entry.js',
entry: { main: './path/to/my/entry.js', }
}; module.exports = config;
1–2. Object Entry Syntax
// entry: {[entryChunkName: string]: string|Array<string>}
const config = {
entry: { app: './src/app.js', vendors: './src/vendors.js' }
}; module.exports = config;
webpack homepage 에선 이 방법은 scalable 한 방법이라고 설명하고있다. 다른 configuration 과 merge 도 가능하고, 재사용, combinded도 용이한 방법이라고 설명한다.
그리고 빌드 환경과 빌드 타겟에 따라 다른 환경을 적용하기에 용이한 문법 이라고 설명하고 있다.!
또한 CommonsChunkPlugin 을 이용할 수 있도록 한다고 한다. 이 CommonsChunkPlugin 은 codeSplitting 에서 다시나오니 그때 자세히 다루도록 하자.
대충 설명되있는 것으로(눈치로) 볼때 우리의 bundle.js 에서 vendor 에 포함되어 있는 vender 를 뽑아주는 것 같다. 즉 중복성을 제거해 주는것이다.
1–3. Multi Page Application
const config = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
},
};위와같이 한다면 3가지의 서로다른 그래프를 만들도록 설정하는 것이다.
CommonsChunkPlugin 를 이용한다면 모두가 공유하는 공통의 번들을 만들 수 있다고한다.
CommonsChunkPlugin 이놈이 codeSplitting 부터 여러가지 일을 하는듯하다!
2. OutPut
output 은 컴파일된 코드뭉치(번들)이 디스크에 어떻게, 어디에 저장될지를 결정하는 configuration이다.
2–1. Usage
output 속성에 필요한 최소조건은 filename, path 이다.
const config = {
output: {
filename: 'bundle.js',
path: '/home/public',
},
};
// /home/public/bundle.js 에 저장된다.filename 은 번들링된 파일을 사용하게될때 그 파일의 이름이다. path 은 그 파일이 저장될 위치이다.
2–2. Multiple Entry Points
만약 번들파일이 하나가 아닌 여러개를 만들도록 entry 가 설정이 되어있다면 substitutions 를 사용하여 unique 한 filename 을 만들어 줄 수 있다.
const config = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
},
};2–3. Advanced
이런식의 output property 도 가능하다.
const config = {
entry: { ... },
output: {
path: "/home/proj/cdn/assets/[hash]",
publicPath: "http://cdn.example.com/assets/[hash]/"
},
};path 은 ‘번들링된 파일이 어디에 저장되느냐’ 를 결정한다고 했다. 그렇다면 publicPath 은 무엇일까? publicPath 은 webpack을 이용한 웹 어플리케이션이 브라우저에서 실행될때, 즉 웹서버에 올라가 running 될 때, 브라우저가 asset file 들을 서버 어디에서 가져와야 하는지? 그것을 결정하는 속성이다.
path, publicPath에 관한 stackoverflow question
3. Loaders
loaders 는 다른 빌드툴들에서 사용되는 task 와 의미가 비슷하다. 코드내에 ‘import’ 되거나 ‘load’ 되는 파일들을 ‘전처리’ 해주는 모듈이다. 이 loaders 를 사용하면 css도 javascript 안에서 import 될 수 있다!
예제를 보자. 일단 설치하자.
npm install --save-dev css-loader npm install --save-dev ts-loadercss-loader 는 모든 css 파일에 적용된다. ts-loader 는 모든 typeScript 파일에 적용된다.
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
],
},
};3–1. Configuration
module.rules 를 이용해 여러 loader modules 를 설정할 수 있다. 그리고 더욱 세세한 설정도 이 rules 를 통해서 가능하다.
3–2. Resolve loader
loader module 은 모듈의 standard 협의를 따른다. 이 말은 즉 특별한 설정을 하지 않는 이상 node_modules 에서 가져온다.
4. Plugins
Plugins 은 webpack configuration 에서 사용되는 일종의 모듈이다. plugins 은 loaders 가 하지 못하는 것들을 할 수 있고, 또 다른 ‘어떤것’도 할 수 있다.
4–1. Anatomy
plugin 은 js object 이며, apply property 를 가지고 있으며 이 apply 를 이용하여 webpack compolier 는 plugin 을 적용시킨다.
function ConsoleLogOnBuildWebpackPlugin() { }; // webpack compiler 가 plugin 을 추가할때 실행할 코드 ConsoleLogOnBuildWebpackPlugin.prototype.apply = function(compiler) {
compiler.plugin('run', function(compiler, callback) {
console.log('The webpack build process is starting!!!');
callback();
});
};
4–2. Usage
plugins 은 인자를 받을 수 있기 때문에 plugins property 에 new instance 로 들어가야 한다.
const HtmlWebpackPlugin = require('html-webpack-plugin'); // installed via npm const webpack = require('webpack');
// to access built-in plugins const path = require('path'); const config = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [ { test: /\.(js|jsx)$/, use: 'babel-loader' } ]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
],
}; module.exports = config;
5. Configuration
webpack config file 은 대부분 비슷비슷해 보인다. 왜냐하면 webpack config 는 기본적으로 js object를 exports 하기 때문이다.
webpack config 는 기본적으로 node commonJS module 이기 때문에
- require 이용 다른 file import 가능
- npm module을 `require` `import` 가능
- js control flow expressions 가능
[DEV] ? wow : wowow - 자주 사용하는 value 를 constant 나 variables 로 지정해서 사용가능.
- 함수를 만들거나 실행시켜서 webpack config 의 일정부분을 생성할 수 있다.
등등의 문법으로 사용가능하다.
다음은 피해야할 configuration style 이다.
- CLI arguments 사용하는것 보다는 — env 를 통해 arguments 를 넘기는것이 더 좋다. 혹은 자신만의 CLI 를 이용하거나.
- Export non-deterministic values
- 설정파일을 길게 작성하는것을 피해라. 설정파일이 길어지면 설정파일을 나누는것이 더 좋다.
5–1. Mutiple target
webpack 을 이용하여 만든 library or bundle 은 다양한 target 에 대응되도록 output 을 설정할 수 있다.
webpack 과 같은 AMD 용 이면
libraryTarget: ‘amd’,
script tag에 들어갈
libraryTarget: ‘umd’,
node 에서 사용될
libraryTarget: ‘commonjs’ 등등..
6. Module
module 프로그래밍은 개발자로 하여금 프로그램을 여러가지 기능별 파일로 쪼개는 것이다. 이 쪼개진 파일들을 우리는 모듈이라고 부른다.
잘 짜여진 모듈은 재사용이 쉽고, 높은 추상화를 가졌으며 다른 모듈의 네임스페이스를 침범하지않는 캡슐화를 갖고있다.
Node.js 는 javascript 의 module programing 을 지원한다. 그러나 웹 에선 모듈프로그래밍이 가능하기 까지는 굉장히 오랜 시간이 걸렸다. 다양한 툴들이 웹에서의 모듈프로그래밍을 지원하고, 또 각각 장점과 한계를 가지고있다.
webpack 은 이러한 시스템들을 교제로 삼아 공부하였다. 그래서 ‘프로젝트 안의 모든것들을 모듈로’ 라는 컨셉을 가지고 webpack 을 만들었다.
6–1. What is webpack module?
Node modules 와는 대조적으로 webpack module의 의존성을 표현하는 방법, import 하는 방법은 다양하다.
- ES2015 style
import - commonJs style
require - AMD style
defineandrequire @importin css/scss/less- stylesheet 파일에서
url()
7. Module Resolution
다양한 방법으로 module 를 resolve 시킬 수 있다.
webpack 이 사용하는 resolver는 webpack 이 bundling 하는 파일안의 require import 같이 모듈을 주입하는 문장을 만날때 webpack 으로 하여금 그 모듈들을 잘 찾도록 도와준다. 그리고 webpack 은 enhanced-resolve을 이용하여 모듈의 경로를 다양하게 지원한다.
7–1. Resolving Rules in webpack
enhanced-resolve 를 이용하면 3가지 방법으로 모듈의 경로를 정의할 수 있다.
1.Absolute Path
import "/home/me/file";
import "C:\\Users\\me\\file";2.Relative paths
import "../src/file1"; import "./file2";3.Module paths
import "module"; import "module/lib/file";기본적으로 module 은 node_modules 를 찾는데 configuration 에서 resolve.modules 를 변경하여 다른 모듈 루트 경로를 추가할 수 도 있다. 또한 resolve.alias 를 통하여 자신이 직접만든 모듈이나 외부 다른 모듈들도 추가시킬 수 있다.
자세한건 RESOLVE
resolve.modules & resolve.alias 를 통해 모듈의 directory path 를 알고나면 경로의 마지막에 있는 path point을 검사한다.
존재한다면 그대로 확장자를 이용, 모듈 import 확장자가 없다면?? resolve.extensions을 이용 그 경로파일에 resolve.extensions 확장자가 존재하는지 검사.
있으면 import!
folder 라면 첫째로….
그 폴더의 package.json 파일이 존재하는가?? 존재한다면 resolve.mainFields 에 정의된 filed 를 package.json 에서 찾아 그에 따른 파일을 import 한다.
package.json 이 없거나 resolve.mainFields 에 정의된 filed 가 package.json 에 존재하지 않는다면 resolve.mainFiles 에 정의된 특정 파일을 찾아보고
있으면 import 한다!
webpack은 프로젝트에 따라 합리적인 default resolve options 를 제공하고있다.
7–2. Resolving Loaders
loader 를 resolution 하는것도 module 의 룰과 동일하다. 다만, resolve 대신 resolveLoader 가 사용된다~
8. Dependancy Graph
webpack 은 어떤 한 파일이(A) 다른 파일에(B) 의존성이 있을때(A 가 B 가 없으면 구동이 되지 않을때), 이것을 dependancy로 다룬다. webpack 이 모든것을 dependancy 로 관리하기 때문에 image, fonts 같은 non-code assets 을 일종의 depandancies 로 제공할 수 있는것이다. (기존 프로그래밍에서 depandancy 를 의미하는 것과 비슷하다고 생각됨)
webpack application 이 시작되면 entry point 를 진입점으로 dependancy graph(tree) 를 그려나간다.
entry point 를 시작으로 재귀적으로 그래프를 그려나가면서 그 그래프에 어플리케이션에 필요한 모든 모듈들을 포함시킨다. 이것들은 패키지화 되어 소수의 작은 파일들로 혹은 단 하나의 파일로 컴파일 되어 브라우저에 올라가게 된다.
이렇게 웹 프로그래밍을 하는데 node와 같은 모듈프로그래밍을 가능케 하며, 필요한 모듈만 알아서 주입시키고, 사용자에게 빠른 응답성을 제공하고자 중복도 제거하고, 뒤에나오겠지만 codeSplitting 으로 불필요한 코드의 로딩도 제한한다.
이런 webpack 의 아름다운 기능과 편리한 개발을 가능하게 하는 concept 덕분에 많은 개발자들이 webpack 을 사용하나보당..
공부를 하면할수록 끝판왕인듯… 역시 좋은 철학과 높은 확장성을 갖고있으면 좋은 툴 이 될 수 밖에 없는듯.. 비단 progrmaing tool 뿐만 아니라 서비스 또한 그런듯(slack도 그렇고, zeplin도 그렇고 기타 등등..)
9. The Manifest
일반적으로 webpack을 이용한 어플리케이션에는 3가지 종류의 코드가 있다.
- 당신(당신의 팀)이 작성한 코드.
- third-party library or “vendor”
- 모든 모듈과 상호작용하는 webpack runtime 과 manifest (webpack runtime 이 어떤 파일인지 잘 모르겠다)
9–1. Runtime
Runtime 은 manifest 와 함께 브라우저에서 webpack 이 구동될때 모듈화된 코드와 연결하기 위해 필요한 모든 코드를 말한다.
이 코드들은 애플리케이션에 이용되는 모듈들과 통신하기위한 loading, resolving logic 을 포함하고 있고 그 모듈들과 상호작용한다.
또한 이미 로드된 모듈들과 연결하며, 아직 로드되지 않은 lazy load 예정인 모듈과도 연결된다. (뭔말인지 잘 모르겠지만 웹팩이 번들링할 때 이용하는 코드와 번들링된 웹팩 코드들을 말하는듯)
9–2. Manifest
webpack 으로 만들어진 웹애플리케이션에 브라우저가 접근하면 index.html 이 불리게 될것이다(보통의 경우에..)
그 index.html 안에, webpack 으로 manage 된 번들 파일들과 다양한 종류의 assets 들이 어떻게 보이는가??
webpack은 어떻게 /src 를 날려버리고 우리의 모듈들의 서로 상호작용하는것을 어떻게 관리할까?
그 해답은 manifest data 에게 있다.
컴파일러가 우리의 애플리케이션을 enters, resolves, mapping 할때 애플리케이션 모듈에 관한 자세한 기록을 유지한다. 그것을 Manifest 라고 부른다. 그리고 이 Mainfest 를 runtime 이 이용하여 모듈을 번들링하고 browser 에 올린다. require, import 등 어떠한 module syntax 를 사용하던 runtime 은 module resolve 하는 문장을 webpack_require 로 변경한다. mainfest data 를 이용하여 runtime 은 모듈을 식별하고 가져올 수 있다.
9–3. The Problem
브라우저에 접근했을때, runtime 이 manifest 를 이용하여 여러 모듈들과 assets들을 아름답게 연결하는 과정을 우리가 꼭 알 필요가 있을까???
물론 일반적인 경우에 중요하지않다. 그러나 우리가 브라우저의 캐싱을 이용하여 웹 프로젝트의 performance 를 증가시키기로 마음먹었다면! 이 눈깜작할 사이에 일어나는 runtime이 하는 일을 잘 이해할 필요가 있다.
bundle file name 에 hash 를 사용하여 이용한다면 우리는 브라우저에게 그 파일이 변경되었으니 브라우저가 가지고있는 캐시는 유효하지 않다고 알려줄 수 있다. 어떤 hash들은 그 파일이 변경되지 않았음에도 hash 가 변경될 수 도 있다. 이 경우에는 runtime 과 manifest 가 모든 build change 마다 injection 되기 때문이다.
10. Targets
javascript 는 server, browser 에 둘다 사용되기 때문에 webpack 은 두가지 경우에 모두 사용될 수 있도록 다중 개발 target 을 지원한다.
10–1. Usage
module.exports = { target: 'node' };위처럼 환경설정하면 node 환경에서 webpack 이 구동된다. 자세한 target 설정은 -> target
webpack 은 target 에 다중설정을 지원하지 않는다. 따라서 다중 타겟을 구현하기 위해서는 설정 자체를 다중 설정으로 분할해야한다.
var path = require('path');
var serverConfig = {
target: 'node',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.node.js'
},
//… }; var clientConfig = {
target: 'web', // <=== can be omitted as default is 'web'
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.js'
},
//… }; module.exports = [ serverConfig, clientConfig ];
다음은 GUIDES 를 보고 공부하자.
러닝커브가 조금 있다고 했는데 문서가 워낙 잘 나와있고, 역시나 .org 라 그런지 문법도 굉장히 간결해서 해석도 잘된다. (물론 나의 융통성있는 의역덕분 일 수도… 아니면 3.3 버전이라 그런가??)
여하튼 안 쓸 이유가 없는 툴같다.
medium 에 markdown 을 import 하는 기능이 있어서 나의 study git 에 있는 글을 옮겼는데.. 생각과는 다르게 import 된다. 그래서 수정이 필요하다. 역시나 미디엄의 코드 작성칸? 은 굉장히 구리다.
p.s 틀린부분이 있을 수 있으니 주의하세요.. 그리고 알려주세요
