[Webpack] Loaders

2021. 6. 11. 21:32Frontend

 

위 포스팅은 이전 포스트의 내용을 이어서 진행합니다.

[Webpack] Config file & Asset Modules
[Webpack] Introduction & Setup

 

 

Loaders

로더(Loader)는 Webpack이 웹 애플리케이션을 번들링 할 때 자바스크립트 파일이 아닌 다른 리소스들 (Typescript, HTML, CSS, Font 등)을 변환할 수 있도록 도와줍니다. 공식문서의 설명을 인용하면 다음과 같습니다.

 

webpack은 기본적으로 JavaScript와 JSON 파일만 이해합니다. 로더를 사용하면 webpack이 다른 유형의 파일을 처리하거나, 그들을 유효한 모듈로 변환하여 애플리케이션에서 사용하거나 디펜던시 그래프에 추가합니다.

 

예를 들어서, 이전의 예시에서 레이아웃 구성을 위해 CSS를 다음과 같이 추가해보겠습니다. CSS파일은 Webpack이 기본적으로 지원하는 Javascript나 JSON형식이 아니므로 적절한 로더 없이 사용될 경우에는 다음과 같이 에러가 나게 됩니다.

 

button.css

button {
  outline: none;
  width: 300px;
  height: 100px;
  font-size: 20px;
  background-color: white;
  border: 1px solid black;
}

 

index.js

import './button.css';
import addImage from './add-image';

function addButton() {
  const button = document.createElement("button");
  button.innerHTML = "This is Button";
  const body = document.querySelector("body");
  body.appendChild(button);
}

addButton();
addImage();

 

css loader가 추가되지 않았기 때문에 번들링 시에 오류가 발생합니다.

 

 

따라서 CSS를 적절하게 변환해줄 수 있는 로더가 필요합니다. 

공식문서에서 권장하는 로더 설정방식은 다음과 같습니다. 

 

상위 수준에서 로더는 webpack 설정에 두 가지 속성을 가집니다.
변환이 필요한 파일(들)을 식별하는 test 속성 변환을 수행하는 데 사용되는 로더를 가리키는 use 속성

 

위 내용을 바탕으로 css를 변환시켜줄 로더인 'style-loader', 'css-loader'를 사용하도록 하겠습니다. 우선 해당 로더들은 webpack이 기본적으로 가지고 있는 모듈이 아니므로 dependency install을 해주어야 합니다.

yarn add -D style-loader css-loader

 

css-loader는 css파일을 자바스크립트로 변환해주는 역할을 합니다. 

style-loader는 자바스크립트로 변경된 스타일시트를 동적으로 DOM에 추가해줍니다. 

일반적으로 CSS번들링을 위해서는 위 두 로더를 같이 사용합니다.

 

여기서 중요한 것은 loader의 적용 순서인데, 웹팩은 여러 개의 로더를 적용할 때, 오른쪽에서 왼쪽 순서로 적용합니다. css파일을 자바스크립트로 먼저 변환한 이후에 해당 스타일시트를 동적으로 DOM에 추가해야 하므로 css-loader, style-loader의 순서대로 config.js파일에 추가해주어야 합니다.

 

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist'),
  },
  mode: 'none',
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
      {
        test: /\.(css)$/,
        use: [
          'style-loader', 'css-loader'
        ]
      }
    ]
  }
}

 

위 설정을 가지고 다시 한번 빌드하면 성공적으로 빌드가 되고, 해당 CSS가 잘 적용된 것을 확인할 수 있습니다.

 

 

Sass-Loader

브라우저가 이해하는 문법은 HTML, CSS, Javascript 뿐이지만, 실제로 개발할 때에는 조금 더 직관적이고 편리하게 개발하기 위해 여러 전처리 도구들을 사용합니다. CSS의 전처리기인 SCSS, SASS도 이에 해당합니다. 위에서 만들었던 button.css를 button.scss로 바꾸고 기존의 설정으로 webpack에서 빌드하게 되면 다음과 같이 에러가 납니다.

 

 

 

css를 js로 빌드하여 동적으로 DOM에 적용하는 것은 되어 있지만, Webpack이 scss를 css로 변환시키는 방법을 알지 못하기 때문입니다. 따라서 적절한 로더인 sass-loader를 적용해주면 이 문제를 해결할 수 있습니다. 위에서 언급했듯, scss를 css로 변환하는 것이 가장 먼저 일어나야 하는 작업이기 때문에 sass-loader가 제일 우측에 와야 합니다.

 

yarn add -D sass sass-loader

 

package.json

{
  "name": "webpack-test",
  "version": "1.0.0",
  "description": "webpack-test",
  "main": "index.js",
  "author": "yeoul",
  "license": "MIT",
  "devDependencies": {
    "css-loader": "^5.2.6",
    "sass": "^1.34.1",
    "sass-loader": "^12.1.0",
    "style-loader": "^2.0.0",
    "webpack": "^5.38.1",
    "webpack-cli": "^4.7.2"
  }
}

 

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist'),
  },
  mode: 'none',
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
      {
        test: /\.(css)$/,
        use: [
          'style-loader', 'css-loader'
        ]
      },
      {
        test: /\.(scss)$/,
        use: [
          'style-loader', 'css-loader', 'sass-loader'
        ]
      }
    ]
  }
}

위와 같이 적용하면 성공적으로 번들링이 되는 것을 확인할 수 있습니다.

 

 

Babel Loader

크롬, 사파리, 파이어폭스 등의 에버그린 브라우저 (사용자가 명시적으로 업데이트 하지 않아도 브라우저가 자동으로 업데이트 수행)의 경우에는 대부분의 ES6 (Ecma Script Version 6) 문법을 지원합니다. 하지만 IE(MS에서 공식적으로 지원 종료를 선언했지만?) 등의 오래된 브라우저들의 경우 여전히 ES6에 대한 지원을 하고 있지 않습니다. 따라서 ES6로 작성된 문법을 지원하기 위해서는 빌드 타임에 이를 오래된 브라우저도 이해할 수 있는 ES5 문법으로 트랜스파일(transpile) 해주어야 합니다. 모듈 번들러인 webpack에 트랜스파일링 도구인 babel을 사용하여 이를 구현할 수 있습니다. 

 

먼저 대표적인 ES6문법인 Class문법을 적용하도록 이전의 코드를 수정해보겠습니다. 

add-image.js

import Student from "./student.png";

class AddImage {
  render() {
    const img = document.createElement("img");
    img.alt = "student";
    img.width = 300;
    img.src = Student;

    const body = document.querySelector("body");
    body.appendChild(img);
  }
}

export default AddImage;

 

index.js

import "./button.scss";
import AddImage from "./add-image";

function addButton() {
  const button = document.createElement("button");
  button.innerHTML = "This is Button";
  const body = document.querySelector("body");
  body.appendChild(button);
}

const addImage = new AddImage();

addButton();
addImage.render();

 

webpack 공식 문서에 따르면 webpack5 이후부터는 es5뿐만 아니라 es6형식으로의 output이 가능하기 때문에 위의 코드를 그대로 빌드해도 빌드는 제대로 되는 것을 확인할 수 있습니다. 다만 es6에서만 지원하는 class문법이 따로 Transpile되지 않은 채로 빌드되기 때문에 class문법이 그대로 빌드 결과물에 들어가있게 되며, 따라서 es6문법을 지원하지 않는 IE등의 브라우저에서는 제대로 화면이 출력되지 않게 됩니다. 

 

webpack 4 used to only emit ES5 code. webpack 5 can generate both ES5 and ES6/ES2015 code now.

 

 

bundle.js (babel적용하기 이전)

... other codes
class AddImage {
  render() {
    const img = document.createElement("img");
    img.alt = "student";
    img.width = 300;
    img.src = _student_png__WEBPACK_IMPORTED_MODULE_0__;

    const body = document.querySelector("body");
    body.appendChild(img);
  }
}

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (AddImage);
... other codes

 

따라서 babel 설정을 통해 해당 코드가 es5 문법으로 적절하게 트랜스파일링 되도록 설정해주어야 합니다. 

yarn add -D @babel/core @babel/preset-env babel-loader

 

@babel/core

babel을 사용하기 위해서 반드시 설치해야 하는 핵심 모듈입니다. 웹팩에서 babel을 사용하기 위한 로더인 babel-loader도 이 @babel/core를 사용해서 babel을 실행합니다. 

 

 

@babel/preset-env

함께 사용되어야 하는 babel 플러그인을 모아둔 것입니다. @babel/preset-react, @babel/preset-typescript등이 있으나 위 프로젝트는 react, typescript가 사용되지 않으므로 preset-env만 추가해주면 됩니다. 중요한 것은 @babel/core를 설치했다고 하더라도 preset을 설치하지 않으면 babel은 그 자체로는 아무것도 하지 않는다는 것입니다. 따라서 preset을 설치해서 어떤 문법들을 트랜스파일링할 것인지를 명시해야 하며 preset-env는 babel에서 지원하는 공식 preset으로, es6 -> es5로 트랜스파일링하기 위해 필요한 플러그인들을 포함하고 있기 때문에 이를 사용하면 es6문법이 성공적으로 트랜스파일링됩니다.

 

 

babel-loader

webpack에서 babel을 실행하기 위한 로더입니다. 

 

webpack.config.js

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle2.js",
    path: path.resolve(__dirname, "./dist"),
  },
  mode: "none",
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
      {
        test: /\.(css)$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.(scss)$/,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
      {
        test: /\.(js$)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/env"],
          },
        },
      },
    ],
  },
};

 

babel을 적용하여 es6문법을 트랜스파일링하도록 설정해주었습니다. 이 설정대로 빌드하게 되면 빌드 결과물은 다음과 같이 성공적으로 es5 문법으로 트랜스파일링된 것을 알 수 있습니다.

 

bundle.js (after transpile)

... some codes
var AddImage = /*#__PURE__*/function () {
  function AddImage() {
    _classCallCheck(this, AddImage);

    _defineProperty(this, "temp", function () {
      console.log("hello world!");
    });
  }

  _createClass(AddImage, [{
    key: "render",
    value: function render() {
      var img = document.createElement("img");
      img.alt = "student";
      img.width = 300;
      img.src = _student_png__WEBPACK_IMPORTED_MODULE_0__;
      this.temp();
      console.log("a" || 0);
      var body = document.querySelector("body");
      body.appendChild(img);
    }
  }]);

  return AddImage;
}();
...some codes

 

 

 

Reference

https://webpack.kr/concepts/#loaders

 

Concepts | 웹팩

웹팩은 모듈 번들러입니다. 주요 목적은 브라우저에서 사용할 수 있도록 JavaScript 파일을 번들로 묶는 것이지만, 리소스나 애셋을 변환하고 번들링 또는 패키징할 수도 있습니다.

webpack.kr

https://poiemaweb.com/es6-babel-webpack-1

 

Babel | PoiemaWeb

현재 브라우저는 ES6를 완전하게 지원하지 않는다. ES6+(ES6 이상의 버전)를 사용하여 프로젝트를 진행하려면 ES6+로 작성된 코드를 IE를 포함한 모든 브라우저에서 문제 없이 동작시키기 위한 개발

poiemaweb.com

 

 

반응형

'Frontend' 카테고리의 다른 글

[Webpack] Code Splitting  (2) 2021.06.17
[Webpack] Plugins  (0) 2021.06.12
[Webpack] Config file & Asset Modules  (0) 2021.06.11
[Webpack] Introduction & Setup  (0) 2021.06.09
[Optimization] using JSX props  (0) 2021.04.04