0w0

webpack 메모 (concepts)

webpack 사이트를 읽으며 느낀 점을 적는 곳. 계속 추가될 수도 있다.

concepts

왜 webpack인가?

번들러 사용 이전

브라우저에서 JS 동작하기 위해서는

이렇게 2가지 방법이 있었다.

IIFE

그래서 IIFE를 사용해서 파일을 안전히 연결/결합할 수 있었다.

IIFE를 사용은 Make, Gulp, Grunt태스크러너들이 모든 프로젝트 파일을 연결하여 사용하기에 재사용하기 쉽지만, 빌드 최적화를 더 어렵게 만들며, 하나를 바꿔도 전체를 빌드해야 했다.

Node.js의 탄생

Node.js가 탄생되었을 때는, 추가할 수 있는 스크립트 태그, html 파일이 없었지만 require을 도인한 CommonJS로 인해 현재 파일에서 모듈을 불러오고 사용할 수 있게 되었다. 이는 필요한 곳에 모듈을 가져와 유효범위 문제도 해결했다.

npm + Node.js + modules

JS는 이곳 저곳에 사용되고 있었지만, 브라우저의 CommonJS 지원이 없었다. Node.js 프로젝트에서는 훌륭했지만 브라우저에서는 모듈을 지원하지 않았으므로 Browserify, RequireJS, SystemJS 같은 번들러 도구들이 만들어졌다.

ESM

모듈이 ECMAScript 표준에서 공식 기능이 되었다. 그러나 여전히 브라우저에서는 불완전하고 번들링이 더 빠르므로 권장되는 방법이다.

Automatic Dependency Collection

태스크 러너, Google Closure Compiler 조차 모든 의존성을 수동으로 선언해야한다. webpack 같은 번들러는 자동으로 빌드하고, 여러 설정으로 디펜던시 그래프를 추론한다. 이는 플러그인, 로더와 함께 큰 역할을 한다.

그래서...

이것이 webpack존재하는 이유이다. JS 앱을 번들로 묶을 수 있는(ESM, CommonJS 둘 다!) 도구이며, 다양한 에셋을 지원하도록 확장도 가능하다.

webpack은 성능/로딩 시간을 중요히 여긴다.

webpack 안에는...

입력 - 출력 사이에는 모듈, 엔트리포인트, 청크 등등 많은 것이 있다.

main

// webpack.config.js

module.exports = {
  entry: './index.js',
};

다른 예제

// webpack.config.js
module.exports = {
  entry: {
    home: '.home.js',
    about: './about.js',
  },
};

청크 그룹에는 하나 이상의 청크가 있을 때도 있다.

청크

청크는 2가지 형태로 제공된다.

출력

출력 파일 명은 설정에서 두 필드에서 영향을 받는다.

이 필드는 플레이스 홀더를 제공한다.

Concepts

webpack@4.0.0 이후로는 설정 파일을 필요로하지 않는다. 그러나 멋지게 사용하기 위해서는 필요하다.

핵심개념

Entry

엔트리 포인트디펜던시 그래프를 만들기 위해 사용하는 모듈이다. webpack은 엔트리 포인트가 의존하는 다른 모듈과 라이브러리를 찾아낸다.

기본적으로

// webpack.config.js
module.exports = {
  entry: {
    main: 'file.js',
  },
};

또한 엔트리 포인트는 하나 이상을 지정할 수 있다.

// webpack.config.js
module.exports = {
  entry: ['file1.js', 'file2.js'],
  output: {
    filename: 'bundle.js',
  },
};

객체 구문도 가능하다.

// webpack.config.js
module.exports = {
  entry: {
    app: './src/app.js',
    adminApp: '.src/adminApp.js',
  },
};

엔트리 포인트의 속성들

여러 페이지일 때

// webpack.config.js
module.exports = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js',
  },
};

이는 3개의 개별 디펜던시 그래프를 원한다는 뜻이다.

왜 굳이 이럴까?

optimization.splitChunks 사용하여, 각 페이지 간 공유되는 앱 코드 번들을 만드는 것이다.

// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js',
    another: './src/another-module.js',
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },

  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
};

이렇게 적으면 중복 의존성이 제거된다.

참조

Output

생성된 번들을 내보낼 위치와 이름을 지정하는 방법을 담당한다. 기본값으로 지정된 것은

엔트리 포인트여러 개일 수 있으나, 출력은 하나여야 한다.

설정할 때는 최소한 output.filename은 있어야 한다.

// webpack.config.js
module.exports = {
  output: {
    filename: 'bundle.js',
  },
};

이렇게하면 bundle.jsdist 폴더에 두둥등장.

엔트리 포인트가 여럿일 때는

// webpack.config.js
module.exports = {
  entry: {
    app: './src/app.js',
    search: './src/search.js',
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist',
  },
};

다른 용례

CDN해시를 사용하는 예제

// webpack.config.js
module.exports = {
  // ...
  output: {
    path: '/home/proj/cdn/assets/[fullhash]',
    publicPath: 'https://cdn.example.com/assets/[fullhash]/',
  },
};

publicPath를 잘 모를 경우

Loader

webpack은 JavaScript와 JSON만 이해한다. 로더를 사용해서 다른 것도 처리해보자. 이는 모듈로 변환해서, 앱에서 사용하거나 디펜던시 그래프에 추가한다.

Loader는 두 가지 속성을 가진다.

간단한 예시 : TypeScript를 JavaScript로 변환 이미지를 데이터 URL로 로드

# 모든 .css 파일에 css-loader 적용, .ts 파일에는 ts-loader 적용한다는 지시를 해보자.
npm install --save-dev css-loader ts-loader
// webpack.config.js
module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: 'css-loader' },
      { test: /\.ts$/, use: 'ts-loader' },
    ],
  },
};

사용 방법

import Styles from 'styles-loader!css-loader?modules!./styles.css';

참조

설정

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // [style-loader](/loaders/style-loader)
          { loader: 'style-loader' },
          // [css-loader](/loaders/css-loader)
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
          // [sass-loader](/loaders/sass-loader)
          { loader: 'sass-loader' },
        ],
      },
    ],
  },
};

로더는 전처리 기능을 통해 결과물을 커스터마이즈하며, 사용자는 압축 , 패키징, 번역 등등 세밀한 로직을 추가할 수 있다.

Plugin

로더는 특정 유형의 모듈 변환을 담당 플러그인은 번들 최적화, 에셋관리, 환경 변수 주입 등 광범위한 작업을 담당

require()를 통해서 요청하며, plugin 배열에 추가한다.

의문점, 그럼 import/export 로는 안되나?

// webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin'); //npm 으로 설치됨
const webpack = require('webpack'); //빌트인 플러그인에 접근하기 위함
const path = require('path');

module.exports = {
  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.ProgressPlugin(),
    new HtmlWebpackPlugin({ template: './src/index.html' }),
  ],
};

Mode

development, production, none 설정이 가능하며 기본 값은 production이다.

브라우저호환성

webpack은 ES5 호환되는 모든 브라우저를 지원한다. import(), require.ensure()을 위한 Promise를 요구한다.

설정

webpack 설정이 정확히 동일할 경우는 거의 없다. 이는 결국 webpack 설정파일도 JavaScript 파일이기 때문

이런 일을 할 수 있다.

이런 행위는 피하자.

다중 타겟

단일 설정 객체, 함수, promise를 내보낼 수 있는 것 처럼 다중 설정도 할 수 있다.

다중 설정 내보내기

사용 가능한 언어들

반드시 JavaScript로 할 필요는 없다. TypeScript, CoffeeScirpt, Babel/JSX 등으로도 가능

Module

모듈형 프로그래밍에서 개발자는 개별 기능을 프로그램(모듈)로 나눈다.

일관성 있는 설계와 명확한 목적을 알 수 있다.

webpack module

webpack은 다양하게 의존성 표시 가능하다

의문점 : 혼합해서 사용가능하나?

지원하는 모듈타입

또한 webpack은 여러 언어로 작성된 모듈과 로더를 통해 다양한 전처리기를 지원한다. 또한 로더는 네이티브가 아닌 모듈을 어떻게 처리하며 의존성을 번들에 포함할지 정의한다.

가능목록

Module Resolution

리졸버는 절대 경로로 모듈을 찾는데 도움이 되는 라이브러리이다.

절대경로

import '/home/me/file';
import 'C:\\Users\\me\\file';

절대 경로이므로 굳이 건들일 것이 없다.

상대경로

import '../src/file1';
import './file2';

import, require가 발생하는 리소스 파일 폴더를 현재 폴더로 간주한다. import/require 지정된 상대 경로는 이 경로에 결합되어 모듈에 대한 절대 경로를 생성한다.

모듈경로

import 'module';
import 'module/lib/file';

캐싱

Module Federation

추후 추가

실제로 사용해보자!

지금까지 기본적인 개념 같아 보이는 것을 메모했다. 이제부터는 실제로 해보자.


inspired by 역시 webpack이 뭔지 모르겠다.