모듈 시스템과 Webpack(2)

terry yoon·2022년 2월 10일
0
post-thumbnail
post-custom-banner

글을 작성한 이유

이전 포스팅이에서 모듈 시스템을 사용하기 위해 webpack 이 필요한 이유와 그 설정 방법에 대해 알아보았다. 이번에는 CSS를 웹팩환경에서 사용하는 방법과 기타 유용한 플러그인 및 설정에 대해 알아본다.

1️⃣ 웹팩 환경에서 CSS 사용하기

<link> 태그를 이용해서 HTML 파일에 정적으로 CSS를 로드해보자. 그리고 dev server를 구동해보자

// index.html 

...
<link rel="stylesheet" src="src/main.css" />
...

이럴 경우 이런 에러를 발견하게 된다.

Cannot GET src/main.css.. 

이 에러의 원인은 webpack.config.js에서 devSever 세팅 시, 정적 파일로 사용할 디렉토리를 public으로만 지정하였기 때문에 src에 있는 main.css를 읽지 못하는 것이다.

따라서 이 문제를 간단하게 해결하기 위해서는 static 배열에 ‘src’를 추가하면 되지만, 이럴 경우 devSever가 public 내에 번들링 된 파일 이외의 자원을 참조하는 문제가 발생한다.

devServer: {
  static: ['public'],
  port: 3000,
  compress: true,
  client: {
    logging: 'info',
    overlay: true,
  },
}

✅ style-loader, css-loader 사용하기

이런 문제를 해결하기 위해, Webpack은 CSS와 관련된 다음 2가지의 loade를 제공한다.

css-loader

css-loader | webpack

css-loader는 css 내의 @import 또는 url()로 된 문법을 import/require로 변환하거나, css 파일을 js 형태로 변환하는 역할을 한다. 따라서 해당 로더를 사용하면 js 파일에 css 파일을 import 하여 사용할 수 있다.

👉 사용방법

npm install --save-dev css-loader
// file.js

import css from "file.css";

...

이 덕분에 각 js파일과 css파일을 관심사에 따라 하나로 묶어서 관리하고, 해당 js 파일에 css 파일을 불러와 사용할 수 있다. 물론 css는 스코프가 없기 때문에, 클래스 네이밍 등에 신경써야 한다. (이는 css Module 또는 styled-components를 통해 해결할 수 있다)

style-loader

스타일 로더는 동적으로 CSS를 DOM에 삽입하여 CSS가 적용되도록 역할을 한다.즉, js 파일이 로드 되고 CSS import 구문을 만나면 <style> 태그 하위에 해당 css 코드를 동적으로 삽입한다.

물론 개발 시에 굉장히 편리하지만, CSS를 동적으로 삽입하는 것은 사용자 관점에서 굉장히 비효율적이고 느린 방법이다.

👉 사용방법

npm install --save-dev style-loader
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

이 때, style-loader가 먼저 오고 css-loader가 오는 순서에 주의하자

✅ miniCssExtractPlugin 사용하기

style-loader의 한계

스타일 로더는 개발 시 편리하지만 배포를 할 경우 성능 이슈가 발생한다. 위에서 언급했듯이 동적으로 CSS를 삽입하기 때문에 배포를 할 경우 정적인 CSS 파일을 생성할 필요가 있다. 이를 위한 플러그인이 miniCssExtractPlugin 이다.

miniCssExtractPlugin

MiniCssExtractPlugin | webpack

이 플러그인의 설명을 보면 CSS를 별도의 파일로 추출한다. 즉, CSS를 import 한 JS 파일 당 하나의 CSS파일을 생성한다.

👉 사용방법

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module: {
      rules: [
			 ...
        {
          test: /\.css$/i,
          use: [
						// 개발 시에는 style-loader를 사용하고 배포할 경우 해당 플러그인의 loader를 사용 
            development ? 'style-loader' : MiniCssExtractPlugin.loader,
            'css-loader',
          ],
        },
				...
			]
}
plugins: [
      new HtmlWebpackPlugin({
        template: path.resolve(ROOT_DIR, 'public/index.html'),
        title: '랜덤 카운트 업 - React Count Up',
      }),
			// 배포할 경우에만 해당 플러그인 사용 
      production &&
        new MiniCssExtractPlugin({
					// filename 옵션을 통해 빌드할 파일 이름과 위치를 지정
          filename: 'css/[name].css',
        }),
    ].filter(Boolean),

✅ CssMinimizerWebpackPlugin 사용하기

miniCssExtractPlugin의 한계

miniCssExtractPlugin 으로 생성된 CSS 정적 파일은 압축이나 불필요한 코드가 포함되어 있는 등 최적화가 되지 않았다. 따라서 빌드 시, 해당 CSS 파일을 다른 번들링 파일처럼 최적화할 수 있는 도구가 필요하다

CssMinimizerWebpackPlugin

CssMinimizerWebpackPlugin | webpack

👉 사용방법

npm install css-minimizer-webpack-plugin --save-dev

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

...
plugins: [
      new HtmlWebpackPlugin({
        template: path.resolve(ROOT_DIR, 'public/index.html'),
        title: '랜덤 카운트 업 - React Count Up',
      }),
      ...
      production && new CssMinimizerPlugin(),
		  ...
    ].filter(Boolean),
optimization: production && {
      minimize: true,
      minimizer: [
        new CssMinimizerPlugin({
          minimizerOptions: {
            preset: [
              'default',
              {
                discardComments: { removeAll: true },
              },
            ],
          },
        }),
      ],
 }
  • minimize : true development 모드에서도 최적화를 하기 위한 옵션
  • minimizerOptions (Link)
    • preset 설정에 대한 자세한 옵션 보기 (Link)
    • discardComments : 주석 제거 옵션

✅ TerserWebpackPlugin 사용하기

CssMinimizerWebpackPlugin의 문제점

CSS 압축을 위해서 CssMinimizerWebpackPlugin을 사용하였더니, 기존에 잘 압축되었던 JS 파일의 최적화가 되지 않는 문제가 발생한다. 원래는 JS 파일은 Webpack 최신 버전에서는 별도의 플러그인 없이 자동으로 최적화를 해주었지만, CssMinimizerWebpackPlugin을 사용하기 위해서는 별도의 JS 압축 플러그인이 필요하다

TerserWebpackPlugin

TerserWebpackPlugin | webpack

👉 사용방법

npm install terser-webpack-plugin --save-dev
const TerserPlugin = require("terser-webpack-plugin");

...

optimization: production && {
      minimize: true,
      minimizer: [
        new CssMinimizerPlugin({
          minimizerOptions: {
            preset: [
              'default',
              {
                discardComments: { removeAll: true },
              },
            ],
          },
        }),
			 new TerserPlugin()
      ],
 }

2️⃣ 웹팩 Plugin들

✅ CopyWebpackPlugin

CopyWebpackPlugin | webpack

빌드 디렉토리에 특정 파일 또는 폴더의 자원을 복사해주는 플러그인

필요한 상황

devServer의 static 옵션이 public 폴더일 경우, 해당 폴더 내의 index.html 및 icon 등에 필요한 image 자원이 위치해 있다. 따라서 개발 시에는 public 폴더로 bundle되어 문제가 되지 않지만, build 상황의 경우 entry로 설정한 src 하위 파일만 번들되어 build 폴더가 생성되므로 필요한 자원을 수동으로 일일히 build 폴더에 옮겨 주어야 한다.

이런 문제를 해결하기 위해 빌드 시, public 폴더 내의 있는 파일들을 build 폴더 내에 복사해주는 플러그인이 유용하게 사용된다.

👉 사용방법

const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        { from: "source", to: "dest" }, // from 위치에서 to로 복사할 경로를 지정
      ],
    }),
  ],
};

✅ Webpack Bundle Analyzer

https://github.com/webpack-contrib/webpack-bundle-analyzer

번들링 된 파일의 크기를 시각적으로 확인할 수 있는 도구

필요한 상황

번들링 된 파일의 크기를 확인하고 지나치게 큰 파일은 없는지 확인하여 적절히 코드 분할이나 최적화하는데 도움을 준다.

👉 사용방법

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

✅ Speed Measure Plugin

speed-measure-webpack-plugin

Webpack을 통해서 빌드할 경우 속도를 측정하여 CLI로 알려주는 도구

3️⃣ 유용한 설정

✅ 절대 경로 설정하기

// webpack.config.js 

	resolve: {
      extensions: ['.jsx', '.js', '.json', '.wasm'],
      alias: {
        '@': path.resolve(ROOT_DIR, 'src'), // alias를 통해 경로에 대한 별칭 지정 가능
      },
    },
// root에 jsconfig.json 생성 

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["**/*.js", "**/*.jsx"],
  "exclude": ["node_modules", "public"]
}
// test.js 

import { test } from '@/test.js' // src/test.js와 동일
profile
배운 것을 기록하는 FrontEnd Junior 입니다
post-custom-banner

0개의 댓글