오늘도 SSAFY 수업을 잘 끝내고 밥 먹고 조금 쉬다가 공부를 시작한다!흠!
시작해보자!
빌드 도구를 활용하면 작성한 코드를 애플리케이션을 실행할 수 있는 최소 단위의 파일로 묶고, 편리하게 배포할 수 있는 준비를 할 수 있다.
Webpack의 역할
Webpack 빌드도구의 핵심은 작성한 자바스크립트의 파일을 최적화하여 적은 수의 파일로 사용자 요청을 처리하는 것이다.
사이트의 서버 부담을 줄이고, 사용자가 페이지를 불러오는 시간도 줄일 수 있다.
자바스크립트는 재사용이 편리한 모듈로 작성이 되지만, 다른 모듈에 의존하거나, 의존한 모듈이 다시 다른 모듈을 의존하기도 한다. 이런 구조에서 불러올 모듈을 파악하기 위해 의존 관계를 추적하는 작업을 직접 하는 건 매우 귀찮은 작업이 될 것이다.
예를 들어서, myUtil이라는 유틸리티 모듈을 accounts.jsx, transactions.jsx 등 여러 React 컴포넌트에 사용하고 있다고 생각해보자.
Webpack 같은 도구를 사용하지 않으면 개별 컴포넌트를 사용할 때마다 수동으로 myUtil을 의존성으로 추가해줘야 한다. 또한, myUtil을 의존하고 있는 다른 컴포넌트에서 이미 myUtil을 불러온 후에 불필요하게 두세 번씩 불러와야할 수도 있다.
Webpack은 3가지 자바스크립트 모듈을 지원한다.
1. CommonJS
2. AMD
3. ES6 모듈
위 3가지를 모두 지원한다.
그렇기에 여러 가지 모듈이 순서없이 뒤섞여도 걱정할 필요는 없다.
Webpack은 프로젝트 내 모든 자바스크립트의 의존성을 분석하고 아래와 같은 작업을 수행한다.
모든 의존 모듈을 올바른 순서로 불러온다.
모든 의존 모듈을 한 번씩만 불러온다.
자바스크립트 파일이 가능한 한 적은 파일로 묶여지게 한다 => '정적 자원'이라 한다.
Webpack은 '코드 분리'와 '정적 자원에 대한 해시 적용 기능'도 지원하기에 특정 상황에서만 필요한 블록도 정의할 수 있다. 그리고 이렇게 분리한 코드 블록은 다른 자원과 함께 묶이지 않고 필요한 시점에 불러올 수도 있다. 이러한 기능들을 선택적으로 적용할 수 있기에 자바스크립트와 배포를 좀 더 최적화할 수 있게 되는 것이다.
사실, Webpack은 자바스크립트만을 위한 도구는 아니다. '로더(loader)'를 이용하면 다른 정적 자원에 대한 전처리도 가능하다. 예를 들어보면, 번들링을 수행하기 전에 아래와 같은 작업을 할 수도 있다.
JSX, Jade, CoffeeScript 파일을 자바스크립트 파일로 변환
ES6 미지원 브라우저를 대응하기 위해 ES6+ 코드를 ES5 코드로 변환
Sass나 Compass로 작성된 스타일 파일을 CSS로 변환
스프라이트 이미지를 하나의 PNG 파일이나 JPG 파일 또는 인라인 데이터 URI 이미지로 최적화
물론 여러 종류의 파일 형식을 위한 다양한 로더를 사용할 수도 있다. Webpack의 작동을 변경할 수 있는 플러그인도 홈페이지에서 찾아볼 수 있고, 만약 필요한 기능을 찾을 수 없으면 문서의 설명을 따라서 직접 플러그인을 만들 수도 있다.
이 글에서는 그 중에서 아래와 같은 작업에 Webpack을 적용해보는 것에 대해서 서술하고자 한다.
npm 의존 모듈을 관리하고 번들링하여 일일이 인터넷에서 내려받거나 HTML의 < script>태그에 추가하지 않아도 사용할 수 있게 한다.
JSX를 일반적인 자바스크립트로 변환하고 디버깅 편의를 위해 소스맵을 제공
스타일을 관리
핫 모듈 대체를 적용
개발용 웹 서버(webpack-dev-server)를 실행
또한 webpack.config.js 파일을 이용하면 Webpack의 로드, 전처리, 번들링 과정을 설정할 수도 있다.
※ '번들러(bundler)'는 '번들(bundle)'을 만드는 도구를 의미한다. Webpack 같은 도구는 다수의 자바스크립트 파일과 정적 자원을 모아 소수의 파일로 만들기 때문에 완료된 결과물을 '번들'이라고 할 수 있다. 그리고 이렇게 번들을 만드는 과정을 '번들링(bundling)'이라고 한다.
Webpack 적용
Webpack을 사용하려면 몇 가지 의존 모듈을 추가해야 한다.
Webpack : 번들러 도구(npm 패키지 이름은 webpack)
로더 : 스타일, CSS, 핫 모듈 대체, Babel 및 JSX 전처리기(npm 패키지 이름은 style-loader, css-loader, react-hot-loader, babel-loader,babel-core,babel-preset-react)
Webpack 개발 서버: Express 기반의 개발 서버로 HMR 기능을 사용할 수 있게 한다(NPM 패키지 이름 : webpack-dev-server)
버전이나 그런건 자기가 알아서 설정하는 것인데, 예를 들면 아래와 같이 설정할 수 있다.
{
"name": "reactprac9_webpack_velog",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "./node_modules/.bin/webpack -w",
"wds-cli": "./node_modules/.bin/webpack-dev-server --inline --hot --module-bind 'css=style-loader!css-loader' --module-bind 'jsx=react-hot-loader!babel-loader' --config webpack.dev-cli.config.js",
"wds": "./node_modules/.bin/webpack-dev-server --config webpack.dev.config.js"
},
"author": "Azat Mardan",
"license": "MIT",
"babel": {
"presets": [
"react"
]
},
"dependencies": {},
"devDependencies": {
"babel-core": "6.13.2",
"babel-loader": "6.4.1",
"babel-preset-react": "6.5.0",
"css-loader": "0.23.1",
"react": "15.5.4",
"react-dom": "15.5.4",
"react-hot-loader": "1.3.1",
"style-loader": "0.13.1",
"webpack": "2.4.1",
"webpack-dev-server": "2.4.2"
}
}
Webpack을 실행하고 싶다면 npm run build 명령 또는 ./node_modules/.bin/webpack -w 를 직접 실행할 수도 있다. '-w 플래그'는 '감시'를 의미한다. 즉, 소스 코드가 변경되는지 계속해서 감시하고 변경이 있을 때 번들을 다시 빌드하는 기능이다. Webpack이 자동으로 변경을 반영하기 위해 계속해서 실행횐다. 그리고 필요한 모든 모듈은 npm i 명령으로 설치해야 한다.
webpack -w 명령은 기본적으로 webpack.config.js 파일을 기준으로 실행된다. 이 파일 없이는 Webpack은 실행 불가 이다.
※ 참고로 Node.js에서 '모듈'과 '패키지'는 동의어이다.
Webpack이 번들링을 하기 위해서는 처리할 대상(소스 코드)과 로더를 이용한 처리 방법을 전달 받아야 한다. 그리고 이런 역할을 하는 것이 프로젝트 최상위에 있는 webpack.config.js 파일이다.
무조건 이렇게 해야 하는 것이 아니고, 프로젝트 마다 다르겠지만, 예시를 보여주면, 나는 이번 프로젝트에서 아래와 같은 것을 Webpack으로 처리하려고 한다.
JSX 파일을 자바스크립트 파일로 변환
css-loader를 이용해 CSS를 < style> 태그로 삽입
모든 자바스크립트 파일을 bundle.js 파일이라는 하나의 파일로 묶기
소스맵을 통해 개발자 도구에서 적절하게 소스 코드의 행을 확인할 수 있게 하기
Webpack을 사용하기 위해서 자체적인 설정 파일이 필요한데, 위와 같은 일을 처리하기 위해 나는 아래와 같이 설정할 수 있었다.
module.exports = {
entry: './jsx/app.jsx',
output: {
path: __dirname + '/js/',
filename: 'bundle.js'
},
devtool: '#sourcemap',
stats: {
colors: true,
reasons: true
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{
test: /\.jsx?$/,
exclude: /(node_modules)/,
loaders: ['babel-loader']
}
]
}
}
'devtool' 속성으로 소스맵을 생성하도록 지정하면, 원본 소스의 줄번호를 확인할 수 있어 개발 단계에서 유용하다.
※ 필요하면 설정 파일을 여러 개 사용할 수도 있다. 개발 환경, 실제 환경, 테스트 환경 또는 다른 목적의 빌드에 따라 설정할 수 있다. 파일 이름도 스스로나 동료가 이해할 수 있는 이름이면 어떤 것도 괜찮다. 파일 이름을 Webpack에 전달할 때는 --config 옵션을 이용한다.
코드 모듈화
간단한 프로젝트는 상관없지만 규모가 있는 프로젝트를 만들 때는 전역을 사용하면 이름이 충돌하거나 여러 개의 < script> 태그가 중복으로 포함되지 않도록 관리해야 하는 귀찮음 문제가 발생할 수 있다. 이런 경우에 CommonJS 문법을 사용해 Webpack에 의존성 관리를 맡길 수 있다. Webpack을 이용하면 webpack.config.js의 설정에 따라 반드시 필요한 의존 모듈만 추가하고, bundle.js 파일 하나로 묶어준다.
이러한 모듈화를 통해 코드를 정리하는 것은 매우 바람직한 행동이다. Browserify, SystemJS와 같은 다른 번들러나 모듈 로더를 사용하는 경우에도 CommonJS/Node.js 문법(require, module.exports)을 사용할 수 있다. 코드를 전역에서 분리해서 리팩토링하면 다른 시스템에도 옮길 수 있다!
import문을 사용하는 ES6 모듈을 사용하려면 추가적인 Webpack 설정이 필요하다. import 문법은 CommonJS의 require와 modeule.exports 문법을 대체하는 동일한 기능이 아니라면 다르게 작동하게 된다. 따라서 한 가지 방법으로, HTML < script>와 전역 객체를 사용하는 대신, require()와 module.exports를 사용하는 방법으로 리팩토링 할 수 있다. 또한, style-loader를 사용하여 CSS 파일도 require로 불러올 수 있고, Babel 로더를 사용하여 JSX 파일도 require로 불러올 수 있다.
아래와 같이 말이다.
const React = require('react')
const ReactDOM = require('react-dom')
class Content extends React.Component {
constructor(props) {
~~~
}
submit(event) {
~~~
}
render() {
~~~
}
}
module.exports = Content
index.html 파일에서는 Webpack이 생성하는 bundle.js 파일을 불러온다. 파일 이름은 webpack.config.js에 설정되 있어 같은 이름으로 추가한다. bundle.js 파일은 npm run build 명령을 통해 실행해야 생성된다.
핫 모듈 대체(HMR : Hot Module Replacement)
'핫 모듈 대체'는 Webpack과 React를 사용할 때 많은 개발자들이 칭찬하는 기능 중 하나이다. 심지어 모든 기능이 없어도 핫 모듈 대체 기능만 있다면 React를 쓰겠다고 할 정도로 말이다.
핫 모듈 대체 기능을 적용하면 코드를 작성하여 브라우저에 변경 사항을 반영하는 과정에 앱의 상태를 유지한 상태로 변경을 적용할 수 있어 빠르게 변경 사항을 확인할 수 있게 한다.
예를 들어, 작업 중인 화면까지 가는데 10번의 클릭을 해야 한다고 하자. 사이트에 새로운 코드를 추가하기 위해서 브라우저를 새로고침하고 10번을 클릭해야 해당 페이지에 들어갈 수 있을 것이다. 하지만 HMR을 사용하면 새로고침을 하지 않고도 페이지에 변경 사항이 반영된다.
HMR의 가장 큰 강점은, 변경 사항이 있을 때 앱의 상태를 저장하여 코드 작성과 확인을 반복하는 과정을 매우 빠르게 해준다는 것이다.
HMR의 작동 과정의 핵심은 공식문서(https://goo.gl/azXzyr)을 참고하길!
HRM의 작동 순서를 보면 아래와 같다.
1. 앱의 소스 코드가 Webpakc 개발 서버에 전달된다.
2. 컴파일된 앱 코드가 브라우저에 전달된다.
3. 웹소켓이 변경 사항을 가져오기 위해 서버를 감시한다.
4. 브라우저에서 앱이 실행된다.
5. 개발자가 브라우저에서 앱을 조작한다(상태 변경)
6. 개발자가 소스 코드를 수정한다.
7. 갱신된 소스 코드가 서버에 전달된다.
8. 갱신 => 자바스크립트 코드 뭉치와 JSON형식의 매니페스트가 전달되고, 기존의 상태 변경은 유지된다.
HMR이 완벽하지는 않다. 갱신이 되지 않거나, 특정 상황에서는 실패하기도 한다. 문제가 발생하면 WDS는 페이지를 실시간으로 새로고침한다. 그리고 이런 동작은 webpack/hot/dev-server에 의해 제어된다. 만약 페이지를 수동으로 새로고침하고 싶다면 webpack/hot/only-dev-server를 사용하면 된다.
Webpack을 사용하고 설정하는 또 다른 방법도 있다.
명령줄 옵션을 사용할 수 있다.
하지만 설정 파일로 하면 내용이 짧지만 명령줄로 하면 코드가 길어지고 만약 길어진 코드에 오류가 났을 경우에는 굉장히 머리 아플 수 있고 발견하기도 어렵다. 따라서 설정 파일을 추천한다.
Webpack은 React와 함께 사용하면 번들링을 간소화하고 개선할 수 있는 좋은 도구이다. 배포를 위해 코드, 이미지, 스타일 및 다른 정적 자원을 최적화할 수 있을 뿐 아니라, 개발 과정에서도 WDS와 HMR을 사용하는 훌륭한 도구이다.
※ webpack.config.js 파일은 Webpack 설정을 위한 기본 파일이다. 이 파일은 반드시 CommonJS 모듈 형식으로 작성된 Node.js 자바스크립트 파일이어야 하고, 객체 리터럴로 작성한 설정을 내보낸다(객체에 JSON처럼 쌍다옴표를 사용할 수 있기는 하다!)