본문 바로가기
Frontend/Webpack

[Webpack] 누군가 해둔 웹팩 설정을 뜯어보자.

by 지구 2021. 6. 25.

지금 유지보수하고 있는 모니터링 시스템은 운영중인 사이트와 다르게 vue.js (+webpack) 설정으로 구성되어 있다.

다른 사람이 설정해놓은 웹팩을 가져다 쓰기 보다는 이해하고 사용하고 싶어서, 하나씩 분석해보기로 했다.

(참고: webpack 은 3.x 버전이다)


우선, 각 환경별로 설정되어 있는 webpack config 에서 공통으로 사용하는 webpack.base.conf.js

'use strict';
const webpack = require('webpack');
const path = require('path');
const utils = require('./utils');
const config = require('../config');
const vueLoaderConfig = require('./vue-loader.conf');

module.exports = {
  // 복잡한 상대경로를 쉽게 사용하도록 하는 option
  context: path.resolve(__dirname, '../'),
  
  // 번들 프로세스를 시작할 지점
  entry: './src/main.js',
  
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js', // 청크 출력 이름을 별도로 지정 (df. [id].js)
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  
  resolve: {
    // 동일한 이름의 다른 확장자를 가진 파일이라면 배열에 정의한 순서대로 해결하도록 하는 option (해결한 뒤 건너뜀)
    extensions: ['.js', '.vue', '.json'],
    // 특정 디렉토리를 특정 문자열로 치환해서 사용하기 위한 option
    alias: {
      '@': path.join(__dirname, '..', 'src'),
    }
  },
  
  module: {
    // 모듈이 생성될 때 요청에 일치하는 규칙 배열로 모듈이 생성되는 방식을 수정할 수 있는 option
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [
          path.join(__dirname, '..', 'src'),
          path.join(__dirname, '..', 'test'),
          path.join(__dirname, '..', 'node_modules/webpack-dev-server/client')
        ]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  
  node: {
    setImmediate: false,
    // @hajs webpack5 에서는 automatic node.js polifills 가 제거되어 필요없습니다.
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  },
  
  plugins: [
    // 웹팩으로 빌드한 결과물로 HTML 파일을 생성해주는 플러그인 (HTML 생성의 단순화)
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: config.build.index,
      inject: true,
      minify: process.env.NODE_ENV === 'production' ? {
        removeComments: true,       // 주석 제거
        collapseWhitespace: true,   // 빈칸 제거
        removeAttributeQuotes: true // 따옴표의 중복인 경우 따옴표 생략 (HTML 은 항상 따옴표를 사용하도록 권장하긴 함..)
      } : false
    }),
    
    // 특정 폴더에 있는 자원들을 빌드할 때 내가 선언한 위치로 옮겨주는(복붙해주는) 플러그인
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: process.env.NODE_ENV === 'production'
          ? config.build.assetsSubDirectory
          : config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ]),
    
    // 환경별로 자원을 별도로 관리하도록 지원해주는 플러그인
    new webpack.DefinePlugin({
      'process.env': require(`../config/${process.env.NODE_ENV_SHORT}.env`)
    }),
    
    // 빌드 이전의 결과물을 제거하는 플러그인
    new CleanWebpackPlugin(),
  ]
};

 

그 다음으로는 개발환경에서 사용할 webpack.dev.conf.js

'use strict';
const webpack = require('webpack');
const merge = require('webpack-merge');
const base = require('./webpack.base.conf');
const utils = require('./utils');
const config = require('../config');
const path = require('path');
const portfinder = require('portfinder');

const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');

// webpack.base.conf.js 내용을 가져와서 병합하는 방식
const devWebpackConfig = merge(base, {
  // @hajs mode 옵션은 webpack4+ 버전부터 지원합니다.
  // mode: 'developement',
  
  // 디버깅 프로세스를 향상시킬 소스 매핑 스타일 (cheap-module-eval-source-map : 고품질 운영 환경에 적합)
  // todo @hajs webpack5 부터는 `eval-cheap-module-source-map` 를 사용해야 합니다.
  devtool: config.dev.devtool,
  
  plugins: [
    // HMR 을 활성화 시키는 방법, devServer.hot 옵션까지 써줘야 브라우저가 자동으로 reload 해줌 (옵션을 안써주면 webpack 작업만 자동으로 실행됨)
    new webpack.HotModuleReplacementPlugin(),

    // 번들링된 웹팩 파일 로그에 상세 경로를 함께 출력해주는 플러그인
    new webpack.NamedModulesPlugin(),

    // 컴파일 도중 오류가 발생했을 때 오류가 발생한 리소스는 제외하고 번들링해주는 플러그인
    new webpack.NoEmitOnErrorsPlugin(),
  ],
  
  devServer: {
    // inline 모드를 사용할 때 DevTools 콘솔에서 표시될 level (df.info)
    // todo @hajs webpack6 이후부터는 제거될 옵션이라고 안내하고 있다.
    clientLogLevel: 'warning',
    
    // History API 또는 react-router 등을 사용하는 경우 새로고침시 404 에러를 해결해주는 option
    historyApiFallback: true,
    
    // HMR(핫 모듈 교체) 기능 활성화 여부
    // HMR : 내용이 변경된 모듈을 페이지 새로고침 없이 런타임에서 업데이트하고, 업데이트에 실패할 경우 새로고침을 수행
    hot: true,
    
    // 번들링된 자원을 서비스할 위치를 지정, CopyWebpackPlugin 을 사용하기 떼문에 필요하지 않아서 비활성화 시킨다.
    contentBase: false,
    
    // 제공되는 모든 항목에 gzip 압축 사용여부
    compress: true,
    
    // 사용할 호스트 지정
    host: process.env.HOST || config.dev.host,
    
    // 요청을 수신할 포트 지정
    port: (process.env.PORT && Number(process.env.PORT)) || config.dev.port,
    
    // 서버가 시작되면 브라우저를 열도록 하는 옵션 (df.true)
    open: config.dev.autoOpenBrowser,
    
    // 컴파일러 오류 또는 경고가 있을 때 브라우저 전체 화면에 오버레이를 표시하는 옵션 (df.false)
    overlay: {
      warnings: true,
      errors: true,
    },
      
    // devServer 에서는 번들된 결과물을 메모리에 저장하므로 별도의 선언이 필요하고, 보통 output 에 적어준 publicPath 와 동일하게 선언하면 된다.
    publicPath: config.dev.assetsPublicPath,
    
    // proxy 서버를 띄워서 브라우저->서버가 아닌 서버->서버로 요청을 보내는 option. CORS 에러를 피할 수 있다.
    proxy: config.dev.proxyTable,
    
    // 초기 시작 정보 외에 콘솔에 아무것도 기록하지 않는 옵션, FriendlyErrorsPlugin 를 사용하기 때문에 필요하지 않아서 비활성화 시킨다.
    quiet: true,
    
    // 웹팩은 파일 시스템을 사용해서 파일 변경 사항을 알리는데 NFS(Network File System) 을 사용하는 경우 작동하지 않아서 이런 경우 polling 을 사용한다.
    watchOptions: {
      poll: config.dev.poll,
    }
  },
});

// 실행 가능한 포트를 찾아서 devServer 에 연결
module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port;
  console.log("*** "+portfinder.basePort); // 8088
  portfinder.getPort((err, port) => {
    if (err) {
      console.log("portFinder eror!");
      reject(err);
    } else {
      console.log("portFinder success! port:"+port);
      process.env.PORT = port;
      devWebpackConfig.devServer.port = port;

      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }));

      resolve(devWebpackConfig);
    }
  })
});

module.exports = devWebpackConfig;

정리하면서..

  1. 환경 구성은 common, dev, prd 3개로 나눠서 관리하면 편하다.
  2. 웹팩은 기본적으로 javascript 만 알고 있기 때문에 css, html 을 읽고 합쳐주는 style-loader, css-loader 가 필요하다.
    (vue 는 공식홈페이지에서 style-loader 와 css-loader 를 함께 사용하는 것을 권장했다.)

 

참고 사이트

https://webpack.js.org/configuration/entry-context/#context

https://github.com/jantimon/html-webpack-plugin

https://joshua1988.github.io/webpack-guide/devtools/source-map.html#%EC%86%8C%EC%8A%A4-%EB%A7%B5

https://agal.tistory.com/71

 

반응형

'Frontend > Webpack' 카테고리의 다른 글

[Webpack] webpack plugins 정리  (0) 2021.06.25
[Webpack] devServer option 정리  (0) 2021.06.25
[Webpack] webpack option 정리  (0) 2021.06.25

댓글