Skip to content

Webpack

A bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows to load parts for the application on demand. Through "loaders," modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.

Categories

Plugins

TypeScript - Type Declarations Plugins

Webpack을 왜 사용하나요?

여기에 Webpack을 사용해야 할 몇 가지 현실적인 이유가 있습니다.

  • 하나의 파일로 js 파일을 번들할 수 있습니다.
  • 프론트엔드 코드에 npm 패키지를 사용할 수 있습니다.
  • ES6/ES7 자바스크립트 코드를 작성할 수 있습니다. (Babel을 이용하여)
  • 코드를 압축 또는 최적화할 수 있습니다.
  • LESS/SCSS를 CSS로 돌릴 수 있습니다.
  • HMR(Hot Module Replacement)을 사용할 수 있습니다.
  • 자바스크립트로 모든 유형의 파일을 포함할 수 있습니다.
  • 이 글에서 다루지 못한 아주 많은 고급기능이 있습니다.

왜 이러한 기능이 필요한가요?

  • js 파일 번들 - 자바스크립트를 모듈로 작성할 수 있습니다, 그래서 각각의 파일에 대해서<script>

    태그를 별도로 작성할 필요가 없습니다. (상황에 따라서 둘 이상의 js 파일이 필요한 경우 구성 가능함)

  • npm 패키지 사용 - npm은 인터넷상에서 오픈소스 코드의 커다란 생태계입니다. npm 코드를 저장할 기회가 주어지며, 원하는 프론트엔드 패키지를 가져다 쓸 수 있습니다.
  • ES6/ES7 - 많은 기능을 추가되어 더 강력하고 더 쉽게 자바스크립트를 작성할 수 있습니다. 여기에 소개하는 글이 있습니다.
  • 코드 압축/최적화 - 배포되는 파일의 크기를 줄입니다. 페이지 로딩이 빨라지는 등의 장점을 포함합니다.
  • LESS/SCSS를 CSS로 돌리기 - CSS를 작성하는 더 좋은 방법입니다. 여기에 소개하는 글이 있습니다.
  • HMR 사용 - 생산성이 향상됩니다. 코드를 저장할 때 마다 페이지의 리프레시가 자동으로 이루어집니다. 코드를 작성하는 동안 페이지의 상태를 최신으로 유지해야 하는 경우 정말 편리합니다.
  • 자바스크립트로 모든 유형의 파일을 포함 - 추가적인 빌드 도구의 수를 줄일 수 있고, 프로그램적으로 파일을 사용 및 수정할 수 있습니다.

How to install

Webpack의 모든 기능을 사용하려면 전역으로 설치해야 합니다:

$ npm install -g webpack

그러나 Webpack의 일부 기능이나 최적화 플러그인 정도만 필요한 경우라면 로컬에 설치합니다:

$ npm install --save-dev webpack

webpack-cli

설정파일 생성과 같은 편리한 기능은 CLI버전을 별도로 설치:

$ npm install -g webpack-cli

사용방법은:

$ npx webpack-cli init

WARNING

설치 도중 @webpack-cli/generators 패키지를 설치하는데, github 에서 보안에러가 출력되므로 init 이후 제거하자.

How to use

Webpack을 실행하려면:

$ webpack

Webpack에서 파일의 상태가 변경되면 자동으로 빌드하려는 경우:

$ webpack --watch

특정한 이름의 사용자가 정의한 Webpack 설정 파일을 사용하려면:

$ webpack --config myconfig.js

Modules

import 문과 export 문은 ES2015에서 표준화되었습니다. 현재는 대부분의 브라우저에서 지원되지만, 몇몇 브라우저에서는 새 구문을 인식하지 못합니다. 하지만 webpack은 바로 사용할 수 있도록 지원해주니 걱정하지 마세요.

보이지않는 곳에서 webpack이 실제로 코드를 "트랜스파일" 하여 이전 브라우저에서도 실행 할 수 있도록 합니다. dist/main.js을 보면 webpack이 어떻게 트랜스파일 하는지 볼 수 있을 것입니다. 매우 독창적입니다! importexport 외에도 webpack은 다양한 모듈 구문을 지원합니다. 자세한 내용은 Module API에서 볼 수 있습니다.

webpack은 importexport 문 이외는 코드를 변경하지 않습니다. 다른 ES2015 기능을 사용한다면 webpack의 로더 시스템인 Babel을 트랜스파일러로 사용해야 합니다.

자세한 내용은 JavaScript Module System 항목 참조. (CommonJS, AMD, UMD 차이점 등 정리)

Library Project

webpack.config.js 파일의 예시:

{
  entry: {
    'my-lib': './src/index.ts',
    'my-lib.min': './src/index.ts'
  },
  output: {
    path: path.resolve(__dirname, '_bundles'),
    filename: '[name].js',
    libraryTarget: 'umd',
    library: 'MyLib',
    umdNamedDefine: true
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js']
  },
  devtool: 'source-map',
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      minimize: true,
      sourceMap: true,
      include: /\.min\.js$/,
    })
  ],
  module: {
    loaders: [{
      test: /\.tsx?$/,
      loader: 'awesome-typescript-loader',
      exclude: /node_modules/,
      query: {
        declaration: false,
      }
    }]
  }
}

.output.library로 지정한 라이브러리 이름으로 Export 된다. 따라서 HTML5/JavaScript에서 직접 사용할 경우:

<script src="./dist/main.js"></script>
<script type="text/javascript">
    let mylib;
    window.onload = () => {
        mylib = new MyLib.MyClass(document.getElementById('root'));
        // USE `graph`
    }
</script>

Loaders

Loader Features

로더는 다음과 같은 특징을 갖습니다.

  • 로더는 연결할 수 있습니다. 체인의 각 로더는 처리된 리소스에 변환을 적용합니다. 체인은 역순으로 실행됩니다. 첫 번째 로더는 결과(변환이 적용된 리소스)를 다음 로더로 전달하며, 전달은 다음 로더로 계속됩니다. 마지막으로, webpack은 체인의 마지막 로더가 JavaScript를 반환할 것으로 예상합니다.
  • 로더는 동기식 또는 비동기식일 수 있습니다.
  • 로더는 Node.js에서 실행되며 Node.js에서 가능한 모든 작업을 수행 할 수 있습니다.
  • 로더는 options 객체로 구성 할 수 있습니다. (여전히 query 파라미터를 사용하여 옵션을 설정할 수 있지만 권장하지 않음)
  • 일반 모듈은 loader 필드가 있는 package.json을 통해 main 및 로더를 내보낼 수 있습니다.
  • 플러그인은 로더에 더 많은 기능을 제공 할 수 있습니다.
  • 로더는 추가로 임의의 파일을 생성할 수 있습니다.

로더는 전처리 기능을 통해 결과물을 커스터마이즈 할 수 있도록 합니다. 이제 사용자는 압축, 패키징, 언어 번역 뿐만 아니라 더 많은 세밀한 로직을 유연하게 추가할 수 있습니다.

로더 실행 순서

위의 #Loader Features 에서도 설명 되었지만 체인은 역순으로 실행됩니다.

{
    test: /\.css$/,
    loaders: ['style'],
},
{
    test: /\.css$/,
    loaders: ['css'],
},

and

{
    test: /\.css$/,
    loaders: ['style', 'css'],
},

위 두 구문은 기능적으로 동등하다. style(css(file))로 동작한다. 로더는 오른쪽에서 왼쪽으로 동작한다.

Importing Other Assets

custom.d.ts와 같이 *.d.ts로 끝나는, 타입을 적는 파일에 다음과 같이 작성한다:

declare module '*.svg' {
  const content: any;
  export default content;
}

Plusings

DefinePlugin
컴파일할 코드에서 특정 문자열을 설정한 값으로 치환.
UglifyJsPlugin
난독화 및 Minify 적용.
CopyWebpackPlugin
Copies individual files or entire directories, which already exist, to the build directory.
Workbox
https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin

Troubleshooting

caniuse-lite is outdated

Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
npx browserslist@latest --update-db
yarn add -W caniuse-lite

Module not found

다음과 같이 모듈을 찾을 수 없는 에러가 출력될 경우:

ERROR in ./src/main.js
Module not found: Error: Can't resolve 'components/DoISuportIt' in '[absolute path to my repo]/src'
resolve 'components/DoISuportIt' in '[absolute path to my repo]/src'
  Parsed request is a module
  using description file: [absolute path to my repo]/package.json (relative path: ./src)
    Field 'browser' doesn't contain a valid alias configuration
    aliased with mapping 'components': '[absolute path to my repo]/src/components' to '[absolute path to my repo]/src/components/DoISuportIt'
      using description file: [absolute path to my repo]/package.json (relative path: ./src)
        Field 'browser' doesn't contain a valid alias configuration
      after using description file: [absolute path to my repo]/package.json (relative path: ./src)
        using description file: [absolute path to my repo]/package.json (relative path: ./src/components/DoISuportIt)
          as directory
            [absolute path to my repo]/src/components/DoISuportIt doesn't exist
          no extension
            Field 'browser' doesn't contain a valid alias configuration
            [absolute path to my repo]/src/components/DoISuportIt doesn't exist
          .js
            Field 'browser' doesn't contain a valid alias configuration
            [absolute path to my repo]/src/components/DoISuportIt.js doesn't exist
          .jsx
            Field 'browser' doesn't contain a valid alias configuration
            [absolute path to my repo]/src/components/DoISuportIt.jsx doesn't exist
[[absolute path to my repo]/src/components/DoISuportIt]
[[absolute path to my repo]/src/components/DoISuportIt]
[[absolute path to my repo]/src/components/DoISuportIt.js]
[[absolute path to my repo]/src/components/DoISuportIt.jsx]

webpack.config.js에 resolve.alias 속성을 설정하면 된다:

const path = require("path");
// ...

module.exports = {
  //...
   resolve: {
              extensions: [".ts", ".tsx", ".js", ".jsx"],
              modules: [path.resolve(__dirname, "src"), "node_modules"],

              // path alias를 설정하는 부분
              alias: {
                  "@Commons": path.resolve(__dirname, "ClientApp/Commons"),
                  "@Template": path.resolve(__dirname, "ClientApp/Views/Template"),
                  "@Images": path.resolve(__dirname, "ClientApp/Images"),
                  "@Config": path.resolve(__dirname, "ClientApp/Config"),
                  "@Contexts": path.resolve(__dirname, "ClientApp/Contexts"),
              }
          },
  //...
}

또다른 양상

다음과 같이 node_modules에 있는 파일 전역에 걸쳐, 에러가 출력될 수 있다:

ERROR in ./node_modules/crypto-js/index.js 4:805-831
Module not found: Error: Can't resolve './rabbit-legacy' in '/home/your/Project/answer-dev/api/node_modules/crypto-js'
 @ ./lib/crypto.ts 1:0-35 3:9-15
 @ ./lib/reccApiBase.ts 14:0-37 364:13-22 369:13-22
 @ ./lib/reccApi.ts 23:0-40 72:2-13
 @ ./lib/index.ts 1:0-32 2:15-22

35 errors have detailed information that is not shown.
Use 'stats.errorDetails: true' resp. '--stats-error-details' to show it.

webpack 5.72.0 compiled with 35 errors in 4469 ms
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

이 경우 webpack.config.js의 resolve.extensions를 확인하자. 이것은 import할때 확장자를 붙이지 않아도 되도록 하는 역할을 한다.

ERR_PACKAGE_PATH_NOT_EXPORTED

nextjs 에서 다음과 같은 에러 발생.

- wait compiling...
- error ./app/globals.scss.webpack[javascript/auto]!=!./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[3].oneOf[13].use[2]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[13].use[3]!./node_modules/next/dist/build/webpack/loaders/resolve-url-loader/index.js??ruleSet[1].rules[3].oneOf[13].use[4]!./node_modules/next/dist/compiled/sass-loader/cjs.js??ruleSet[1].rules[3].oneOf[13].use[5]!./app/globals.scss
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './tailwind.plugin.yourui' is not defined by "exports" in /home/your/Project/your-web/node_modules/your-ui/package.json
Import trace for requested module:
./app/globals.scss.webpack[javascript/auto]!=!./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[3].oneOf[13].use[2]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[13].use[3]!./node_modules/next/dist/build/webpack/loaders/resolve-url-loader/index.js??ruleSet[1].rules[3].oneOf[13].use[4]!./node_modules/next/dist/compiled/sass-loader/cjs.js??ruleSet[1].rules[3].oneOf[13].use[5]!./app/globals.scss
./app/globals.scss

중요한 점은 다음 줄 이다: Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './tailwind.plugin.yourui' is not defined by "exports" in /home/your/Project/your-web/node_modules/your-ui/package.json

해당 package.json 파일의 exports 에 해당 subpath 를 추가하면 된다. 이 경우는 다음과 같이 수정했다:

{
  // ...
  "exports": {
    ".": {
      "import": "./dist/your-ui.es.js",
      "require": "./dist/your-ui.umd.js"
    },
    "./tailwind.plugin": {
      "require": "./tailwind.plugin.yourui.js"
    }
  },
  // ...
}

See also

Favorite site

Guide

Typescript

Compare

References


  1. Create_the_latest_JavaScript_web_frontend_development_environment_with_Webpack_and_Babel.pdf