티스토리 뷰




이전 강좌의 코드를 활용합니다.


핫 모듈 리플레이스먼트(Hot Module Replacement - HMR)는 웹팩이 제공하는 가장 유용한 기능 중 하나입니다. 전체 새로고침 없이 모든 종류의 모듈들을 런타임 시점에 업데이트 되게 해줍니다. 이 가이드에서는 구현에 초점을 두고 설명합니다. 어떻게 동작하는지, 왜 유용한지에 대한 더 자세한 내용은 concept page 를 확인하십시오.


HMR 은 프로덕션에서 사용하기 위한 것이 아닙니다. 개발에서만 사용하십시오. 자세한 내용은 building for porduction guide를 참고하십시오. 


HMR 사용

이 기능은 생산력 향상에 도움을 줍니다. 우리가 해야할 일은 webpack-dev-server 구성을 업데이트하고, 웹팩에 내장된 HMR 플러그인을 사용하는 것 뿐입니다. 또한 index.js 모듈을 통해 사용될 print.js 엔트리 포인트도 제거 합니다.


만약 webpack-dev-server 대신 webpack-dev-middleware 라우트를 선택했다면, 커스텀 서버 또는 어플리케이션에서 HMR을 사용할 수 있도록 webpack-hot-middleware 패키지를 사용하십시오.


webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const webpack = require('webpack');

  module.exports = {
    entry: {
-      app: './src/index.js',
-      print: './src/print.js'
+      app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
+     hot: true
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
+     new webpack.HotModuleReplacementPlugin()
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };


webpack-dev-server 구성을 수정하기 위해 CLI를 사용할 수 있습니다. 다음 명령어를 입력하세요 :

webpack-dev-server —hotOnly


이제 index.js 파일을 업데이트 합니다. 그래서 print.js 내부의 변화가 감지될때, 웹팩에게 업데이트 된 모듈을 수용하도록 알려줍니다.


index.js

  import _ from 'lodash';
  import printMe from './print.js';

  function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;

    element.appendChild(btn);

    return element;
  }

  document.body.appendChild(component());
+
+ if (module.hot) {
+   module.hot.accept('./print.js', function() {
+     console.log('Accepting the updated printMe module!');
+     printMe();
+   })
+ }


print.js 의 console.log 구문을 수정 하십시오. 그러면 브라우저 콘솔창을 통해 이런 결과를 볼 수 있을 것입니다.


print.js

  export default function printMe() {
-   console.log('I get called from print.js!');
+   console.log('Updating print.js...')
  }

console

[HMR] Waiting for update signal from WDS...
main.js:4395 [WDS] Hot Module Replacement enabled.
+ 2main.js:4395 [WDS] App updated. Recompiling...
+ main.js:4395 [WDS] App hot update...
+ main.js:4330 [HMR] Checking for updates on the server...
+ main.js:10024 Accepting the updated printMe module!
+ 0.4b8ee77….hot-update.js:10 Updating print.js...
+ main.js:4330 [HMR] Updated modules:
+ main.js:4330 [HMR]  - 20


Node.js Api를 통해 Via the Node.js API

Webpack Dev Server를 Node.js API와 함께 사용 할때, dev 서버 옵션을 웹팩 config 객체에 두지 마십시오. 대신 두번째 파라미터로 생성자에게 전달합니다. 예 :


new WebpackDevServer(compiler, options)


HMR을 사용하기 위해, 웹팩 구성 객체 또한 HMR 엔트리 포인트를 포함하도록 변경 시켜야 합니다. 이것을 위해 webpack-dev-server 패키지는 addDevServerEntrypoints라는 메소드를 포함하고 있습니다. 다음은 이것을 나타내는 간단한 예제입니다.


dev-server.js

const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');

const config = require('./webpack.config.js');
const options = {
  contentBase: './dist',
  hot: true,
  host: 'localhost'
};

webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);

server.listen(5000, 'localhost', () => {
  console.log('dev server listening on port 5000');
});


만약 webpack-dev-middleware를 사용하고 있다면, 사용자 개발 서버에서 HMR을 사용하기 위해 webpack-hot-middleware 패키지를 확인 하십시오.


깨달음

핫 모듈 리플레이스먼트(교체)는 까다로울 수 있습니다. 이전 예제를 살펴보면 확인할 수 있습니다. 예제 페이지로 돌아가서 버튼을 클릭해보면, console은 예전 printMe 함수를 표시하는것을 볼 수 있습니다. 


이 현상은 버튼의 onclick 이벤트가 여전히 예전 printMe 함수에 바인딩 되어 있기 때문입니다. 이것을 HMR과 함께 동작하게 해주려면 그 바인딩을 module.hot.accept를 사용하는 새로운 printMe 함수로 업데이트 해주어야 합니다. 


index.js

  import _ from 'lodash';
  import printMe from './print.js';

  function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;  // onclick event is bind to the original printMe function

    element.appendChild(btn);

    return element;
  }

- document.body.appendChild(component());
+ let element = component(); // Store the element to re-render on print.js changes
+ document.body.appendChild(element);

  if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updated printMe module!');
-     printMe();
+     document.body.removeChild(element);
+     element = component(); // Re-render the "component" to update the click handler
+     document.body.appendChild(element);
    })
  }


이것은 단지 하나의 예시일 뿐입니다. 다른 방법들도 많이 있습니다. 다행스럽게도, HMR을 쉽게 만들어 주는 많은 로더가 존재합니다. (그중 일부는 아래에 언급합니다.)


스타일시트와 사용하는 HMR

사실 CSS와 함께 사용하는 HMR은 style-loader의 도움과 함께 꽤 쉽습니다. 이 로더는 CSS의 의존성이 업데이트 될때 뒤에서 module.hot.accept 를 사용, <style> 태그를 패치합니다.


우선 다음 명령어로 두개의 로더를 설치합니다. 

npm install --save-dev style-loader css-loader


이제 로더를 사용할 수 있도록 config 파일을 수정합니다.


webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
  const webpack = require('webpack');

  module.exports = {
    entry: {
      app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
      hot: true
    },
+   module: {
+     rules: [
+       {
+         test: /\.css$/,
+         use: ['style-loader', 'css-loader']
+       }
+     ]
+   },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
      new webpack.HotModuleReplacementPlugin()
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };


핫 로딩 스타일시트는 모듈로 가져오는 것 만큼 쉽습니다.


project

  webpack-demo
  | - package.json
  | - webpack.config.js
  | - /dist
    | - bundle.js
  | - /src
    | - index.js
    | - print.js
+   | - styles.css

styles.css

body {
  background: blue;
}

index.js

  import _ from 'lodash';
  import printMe from './print.js';
+ import './styles.css';

  function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;  // onclick event is bind to the original printMe function

    element.appendChild(btn);

    return element;
  }

  let element = component();
  document.body.appendChild(element);

  if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updated printMe module!');
      document.body.removeChild(element);
      element = component(); // Re-render the "component" to update the click handler
      document.body.appendChild(element);
    })
  }


body의 background : red 로 바꾸고 페이지를 보면, 새로고침 없이 즉시 배경색이 바뀌는 걸 확인할 수 있습니다.


styles.css

  body {
-   background: blue;
+   background: red;
  }


기타 코드 및 프레임워크

커뮤니티에는 HMR의 동작을 자연스럽게 만들게 하기 위한 많은 로더와 예제가 다양한 라이브러리 및 프레임워크와 함께 존재합니다.


커뮤니티에는 HMR이 다양한 라이브러리 및 프레임워크와 원활하게 동작할 수 있도록 해주는 많은 로더와 예제가 존재합니다.


  • React Hot Loader : 실시간 react 컴포넌트 변경
  • Vue Loader : vue 컴포넌트를 위한 로더
  • Elm Hot Loader : Elm 프로그래밍 언어를 위한 로더
  • Angular HMR : 로더 필요없음! HMR API를 완벽하게 제어하기 위해서 메인 NgModule 파일의 변경만 있으면 됩니다.


Hot Module Replacement를 지원해주거나 향상시킬 수 있는 또 다른 로더 및 플러그인을 알고 계신다면, 이 리스트에 추가할 수 있도록 pull request를 보내주세요. 


더 읽을 거리


Concepts - Hot Module Replacement

API - Hot Module Replacement





출처 : https://webpack.js.org/







댓글
Kakao 다음웹툰 / 웹표준 및 ft기술, 웹접근성 / 세아이 아빠, 제주 거주 중..
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday