[JS] Webpack 세팅

게코젤리·2023년 6월 15일

리액트를 처음 배울 때 간략하게 접하고 이건 다신 볼일 없겠지 하고 지나갔던 웹팩이 다시 돌아왔다. 바닐라 자바스크립트로 개발을 하려니까 필요해서...

CRA 이전의 웹팩의 기본 형태, 바닐라 자바스크립트로 웹팩을 빌드하는 방법에 대해 알아보자. webpack5 버전을 사용하고 CSS loader, Dev Server, Babel 등의 설정 방법을 다뤄볼 것이다.

0. 웹팩 왜 씀?


위와 같이 module을 사용하면 여러 개의 js파일을 하나로 모을 수 있다.

문제는 모든 JavaScript 파일을 개별 모듈로 불러올 경우, 파일 하나당 하나의 HTTP 요청이 발생한다. 이는 네트워크 지연 시간을 증가시키고, 웹 페이지의 로딩 시간이 늘어나게 한다.

웹팩을 사용하면 이러한 모듈을 하나의 번들로 합쳐서 HTTP 요청을 최소화할 수 있다.

1. Webpack 설치

  1. 터미널에 명령어를 입력해서 webpack, webpack-cli를 설치한다.
    • -D 옵션은 (--save-dev와 같다) 개발 환경에서만 필요한 종속성을 프로젝트에 추가하는데 사용한다.
npm init -y
npm i -D webpack webpack-cli
  1. 루트 디렉토리에 webpack.config.js를 생성하고 다음과 같이 설정한다.
// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development', // 배포할땐 production
  entry: {
    app: './src/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',// [name]은 entry 객체의 key를 참조
    path: path.resolve(__dirname, 'dist'),
  },
};

filename에 [contenthash]을 붙이는 이유
웹 브라우저는 서버로부터 받은 자원(HTML, CSS, JavaScript 파일 등)을 캐시(임시 저장소)에 저장하고, 동일한 자원을 요청할 때 캐시에 저장된 자원을 재사용한다. 그러나 이러한 캐시 기능 때문에 개발자가 서버의 자원을 업데이트 했을 때 문제가 생길 수 있다. 이미 캐시에 저장된 자원이 있으면 브라우저는 서버에 요청하지 않고 기존 캐시의 자원을 사용하기 때문.
이를 해결하기 위해 filename에 해시값을 추가해 캐시가 무효화 되도록 한다. 파일 내용이 바뀌면 해시값도 바뀌고, 해시값이 바뀌어서 파일명이 바뀌면 브라우저는 이를 새로운 자원으로 인식하고 서버에 새 자원을 요청한다.

  1. package.json에 webpack 명령어를 추가한다.
    터미널에 npm run build를 입력하면 dist폴더에 app.[해시값].js파일이 생긴 것을 볼 수 있다.
// package.json
{
  "scripts": {
    "build": "webpack"
 },
}

2. html-webpack-plugin

  1. html-webpack-plugin, clean-webpack-plugin 설치
npm i -D html-webpack-plugin clean-webpack-plugin
  1. webpack.config.js 설정
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    app: './src/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'test',
      filename: 'index.html',
      template: './src/index.html', 
      // template 파일을 참조해서 output path에 index.html을 생성한다
    }),
    new CleanWebpackPlugin(),// build할 때마다 dist 폴더를 초기화
  ],
};

3. CSS, 이미지 파일

  1. style, css 로더 설치
npm i -D style-loader css-loader
  1. webpack.config.js 설정
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    app: './src/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    assetModuleFilename: 'images/[hash][ext][query]',
    // 옵션_ 이미지 파일이 많을 경우 위와 같이 dist에 폴더를 생성하고 저장할 수 있음.
  },
  module: {
    rules: [
      {// css 파일을 js에 import해서 쓸 수 있음.
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {// Webpack의 asset module 기능을 이용하여 이미지 파일들을 처리
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'test',
      filename: 'index.html',
      template: './src/index.html',
    }),
    new CleanWebpackPlugin(),
  ],
};

4. Babel

리액트도 아닌데 Babel이 필요해?
비록 JSX 문법이 포함되지 않는 프로젝트라도, Babel을 Webpack 환경에 포함시키면 중요한 이점을 얻을 수 있다. Babel은 고급 JavaScript 기능을 이전 버전의 JavaScript로 변환함으로써, 어떠한 브라우저 호환성 문제 없이 최신 언어 기능들을 활용할 수 있게 해주기 때문!

  1. 바벨 설치
npm i -D babel-loader @babel/core @babel/preset-env
  • babel-loader: Webpack이 JavaScript 파일들을 번들링하기 전에 Babel을 사용하여 변환할 수 있게 해주는 로더. 이를 통해 최신 JavaScript 문법을 이용해 코드를 작성하더라도, 이전 버전의 JavaScript를 이해하는 브라우저에서도 동작하게 할 수 있음.
  • @babel/core: Babel의 핵심 의존성. 코드 변환 엔진을 포함하며, JavaScript 파일을 읽어 들이고, 코드를 파싱하고, 변환하고, 다시 출력하는 기능을 수행.
  • @babel/preset-env: 개발자가 설정한 환경에 맞게 Babel이 필요한 플러그인들을 자동으로 결정해주는 Babel 프리셋.
  1. webpack.config.js 설정
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    app: './src/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    assetModuleFilename: 'images/[hash][ext][query]',
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'test',
      filename: 'index.html',
      template: './src/index.html',
    }),
    new CleanWebpackPlugin(),
  ],
};

5. dev-server

devServer는 파일 시스템의 변경을 감지하고 자동으로 번들을 재생성하고, 브라우저를 새로고침한다. 그러나 중요한 점은, devServer가 실시간으로 반영하는 이런 변경 사항들은 실제 'dist' 디렉토리의 번들 파일에는 적용되지 않는다. 즉, devServer는 개발 과정에서만 실시간 변경을 반영하며, 최종적인 빌드 결과물인 번들 파일에는 영향을 미치지 않는다. 따라서 실제 배포를 위한 최종 번들을 생성하려면 별도로 npm run build와 같은 명령을 이용하여 번들링 과정을 진행해야한다.

  1. devserver 설치
npm i -D webpack-dev-server
  1. webpack.config.js 설정
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    app: './src/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    assetModuleFilename: 'images/[hash][ext][query]',
  },
  devServer: {
    static: {
      directory: path.resolve(__dirname, 'dist'),
    },
    port: 3000,
    open: true, // 서버가 시작될 때 브라우저 자동열림
    hot: true, // Hot Module Replacement(HMR) 활성화, 새로고침 없이 변경된 모듈 재로드
    compress: true, // 모든 항목을 gzip 압축으로 제공, 네트워크 전송 시간 단축
    
    // historyApiFallback: true, // SPA(Single Page Application)에서 중요. true로 설정하면, 404 응답 대신 index.html 페이지를 제공하여 클라이언트 측 라우팅이 가능. 이를 통해 브라우저에서 경로가 변경되어도 항상 루트 인덱스 페이지를 제공할 수 있음.
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'test',
      filename: 'index.html',
      template: './src/index.html',
    }),
    new CleanWebpackPlugin(),
  ],
};
  1. package.json에 명령어 추가
{
"scripts": {
    "build": "webpack",
    "start": "webpack serve"
  },
}

https://www.youtube.com/watch?v=IZGNcSuwBZs
https://webpack.kr/concepts/

0개의 댓글