Webpack Study part 1. (webpack concept읽기)

marong61
marong61
Jul 27, 2017 · 18 min read

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-loader

css-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’ 등등..

multiple

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 define and require
  • @import in 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가지 종류의 코드가 있다.

  1. 당신(당신의 팀)이 작성한 코드.
  2. third-party library or “vendor”
  3. 모든 모듈과 상호작용하는 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 틀린부분이 있을 수 있으니 주의하세요.. 그리고 알려주세요

marong61

Written by

marong61

FrontEnd Developer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade