html plugin 설치
index.html을 build 폴더에 복사하고 컴파일된 main.js 화일을 index.html에 자동으로 주입이 되도록 설정한다.
먼저 html plugin을 설치한다.
1 |
npm install --save-dev html-webpack-plugin |
webpack.config.js에 plugin을 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new HtmlWebpackPlugin() ] }; |
build 폴더에 생성된 파일을 지우고 다시 컴파일하면 index.html을 별도로 복사하지 않더라도 복사후에 main.js 화일이 inject 된것을 확인할 수 있다.
하지만, 빌드된 index.html을 열어보면 title 등이 임의로 생성한 화일이라는걸 알수 있을 것이다. 우리가 만든 index.html을 빌드된 곳으로 복사하기 위해서는 template 옵션을 추가하면 된다.
1 2 3 4 5 6 7 8 |
... plugins: [ new HtmlWebpackPlugin({ template: "./index.html" }) ] }; |
webpack-dev-server
파일이 변경될 때마다 컴파일하고 확인하는 작업은 굉장히 번거롭다. 수정된 내용을 바로 확인하는 HMR (Hot Module Replacement) 기능을 개발할 때는 갖춰야 한다.
webpack-dev-server 모듈을 설치한다.
1 |
npm install --save-dev webpack-dev-server |
package.json에 실행스크립트를 한줄 추가한다.
webpack 4.x에서는 webpack-dev-server 였는데 webpack 5.x 에서 부터 webpack server 로 명령어가 바뀌었다.
1 2 3 4 |
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack server" }, |
cmd 창에서 실행해보자
1 |
npm run start |
실행이 종료되지 않고 watch 모드로 계속 실행이 되어있는것을 알수 있다. cmd창에서 보이는 http://localhost:8080 으로 접속해보자. 동일한 화면을 확인할수 있다.
index.js에 콘솔 로그를 아래와 같이 한 줄 추가하고 저장하자. 자동으로 컴파일이 되고 화면이 갱신되는것을 확인할 수 있다.
1 |
console.log('hello webpack dev server'); |
css loader
css 파일을 만들고 index.html에 inject 해보자. style.css 화일을 추가하고 아래와 같이 몇줄 추가한다.
1 2 3 4 |
body { font-size: 24px; color: blue; } |
css를 합치서 CommonJS 모듈로 만들어 주는 css-loader 모듈과 String으로 부터 꺼내서 style 태그를 붙여서 스타일 태그로 만들어주는 style-loader 두 개의 모듈을 추가한다.
1 |
npm install --save-dev style-loader css-loader |
webpack에 3가지 중요한 요소가 시작점을 지정하는 entry point, 결과를 저장하는 output, 그리고 나머지가 모듈 로딩 기능이다. webpack은 javascript를 컴파일할 수 있지만 css 를 제어할 수 없다. 이것을 가능하게 해주는기능이 module 기능이다. module에 위에서 추가한 두가지 로더를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ "style-loader", "css-loader" ] } ] }, plugins: [ new HtmlWebpackPlugin() ] }; |
test에는 정규표현식이 들어가고 true, false를 판단하는 predicate 역할을 한다. npm start로 개발버전으로 실행도 해보고 build해서 프로덕션버전으로 테스트해보자.
index.js 상단에 css 를 import 하고 스타일이 적용되는지 테스트하자
1 2 3 |
import _ from 'lodash'; import './style.css'; ... |
sass 로딩
sass를 로딩하기 위해서는 sass 를 css로 컴파일해주는 로더가 필요하다. 다음 두가지 sass 로더를 추가한다.
1 |
npm install --save-dev sass-loader node-sass |
hello.scss 화일을 추가한다. body 태그와 div 태그를 나란히 쓰지 않고 네스팅 구조로 사용한것은 sass 문법중 하나이다.
1 2 3 4 5 6 7 8 9 |
body { background: #bbbbbb; div { margin: 2rem; padding: 1rem; border: 1px solid red; } } |
webpack에 sass 로더를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ "style-loader", "css-loader" ] }, { test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] } ] }, plugins: [ new HtmlWebpackPlugin() ] }; |
index.js에 scss 화일을 import 하고 제대로 적용되는지 테스트 하자.
1 2 3 4 |
import _ from 'lodash'; import './style.css'; import './hello.scss'; ... |
CSS 플러그인
빌드된 결과물을 보면 js만 있지 css 는 보이지 않는다. 추가된 css 는 어디에 있을까? 한번 찾아보자.
main.js 화일을 열고 body 태그를 검색해보면 자바스크립트 스트링 변수로 들어가 있을 것이다. 이걸 해주는게 css-loader가 하는 역할이다.
그리고, style을 검색해보면 style 태그를 createElement로 만들어주는 자바스크립트 코드를 찾을수 있을것이다. 이것이 style-loader가 해주는 역할이다.
이와같이 Javascript로 CSS를 주입하는 기술을 CSS-in-JS 라고 한다. 리액트에서 사용되는 styled component도 이런 기술중의 하나이다.
mini-css-extract-plugin 플러그인을 사용해서 별도의 css 화일로 분리해보자.
먼저 플러그인을 설치한다.
1 |
npm install --save-dev mini-css-extract-plugin |
먼저, css와 scss를 정규표현식 .(sa|sc|c)ss 을 사용하여 하나로 합친다.
플러그인에 main.css로 만들도록 추가한다. 그리고, loader에 추가시 style-loader를 빼고 그 자리에 해당 플러그인을 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"] } ] }, plugins: [ new HtmlWebpackPlugin(), new MiniCssExtractPlugin({ filename: "main.css" }) ] }; |
빌드후에 결과를 확인해보자. main.css 화일이 생겼고 index.html에도 링크가 자동으로 생성된것을 알 수 있다.
Post-CSS 설정
앞서 SCSS 같은 전처리기를 사용하였다면 후처리기를 사용하여 여러가지 번거로운 일을 한번에 할수 있다.
PostCSS에는 많은 플러그인들이 있다. https://github.com/postcss/postcss/blob/master/docs/plugins.md
많은 플러그인 중 autoprefixer는 자동으로 vendor 별 prefix를 붙여주는 플러그인이다.
예를 들어, CSS3에 도입된 border-radius 속성은 표준속성이며 각 브라우저에서는 다음과 같이 해야 한다.
- -webkit-border-radius : 사파리, 크롬
- -moz-border-radius: 파이어폭스
- -o-border-radius: 오페라
- -ms-border-radius: 인터넷 익스플로러
- border-radius: 표준 속성
autoprefixer를 사용하게되면 표준속성만 사용하더라도 이 플러그인이 자동으로 브라우저 호환 속성들을 추가해주게 된다.
먼저 모듈을 추가한다.
1 |
npm install --save-dev postcss-loader autoprefixer |
postcss.config.js 를 추가하고 어떤 브라우저를 지원하는지를 명시한다.
1 2 3 4 5 6 7 |
module.exports = { plugins: [ require('autoprefixer')({ 'browsers': ['>1%', 'last 2 versions'] }) ] } |
webpack 환경설정에서는 postcss-loader를 추가한다.
1 2 3 4 5 6 7 8 9 10 |
... module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "sass-loader"] } ] }, ... |
hello.scss 에는 테스트 코드를 한줄 삽입한다.
1 2 3 4 |
... .box { display: flex; } |
빌드 후 main.css 를 살펴보자. 브라우저 호환성 코드가 들어간 것을 확인할 수 있을것이다.
하지만, 빌드하게 되면 browsers 부분을 package.json이나 .browserlistrc 화일로 별도로 분리하라는 경고가 나올것이다.
이 브라우저 호환성 화일은 autoprefixer 뿐만 아니라 다음과 같은 여러군데서 사용하기 때문이다.
- Autoprefixer
- babel-preset-env
- postcss-preset-env
- eslint-plugin-compat
- stylelint-no-unsupported-browser-feature
>%1 의 의미는 상위 1% 이상 선택된 브라우저를 의미
last 2 versions 는 각 브라우저의 최신 버전 2개까지 지원한다는 의미
not ie <= 8 은 ie 8이하 버전은 호환하지 않는다는 의미
package.json에 추가하면 다음과 같다.
1 2 3 4 5 6 7 8 |
... }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] } |
그리고 postcss.config.json에는 browsers를 삭제한다.
1 2 3 4 5 |
module.exports = { plugins: [ require('autoprefixer')() ] } |