React + Typescript + Webpack (esbuild-loader) 설정

1. React와 TypeScript 설치

먼저, React와 TypeScript를 설치하겠습니다. TypeScript 의존성은 .ts 파일을 컴파일하는 데 사용됩니다.

npm i react react-dom
npm i -D typescript

2. Webpack 설치

다음으로, Webpack을 설치합니다. Webpack은 모듈 번들러로, 개발 중과 빌드 과정에서 유용하게 사용됩니다.

npm install -D webpack webpack-cli webpack-dev-server

3. 로더 설치

CSS 스타일을 적용하기 위해 style-loader와 css-loader를 설치합니다. 또한, 빠른 번들링을 위해 esbuild-loader를 사용할 것입니다.

npm i -D style-loader css-loader esbuild-loader

각 로더의 역할은 다음과 같습니다:

  1. css-loader: CSS를 JavaScript 파일에서 import할 수 있게 해줍니다.
  2. style-loader: 스타일을 HTML 파일에 적용시키기 위한 로더입니다. CSS의 Hot Module Replacement(HMR)를 사용하기 위해 필요합니다.
  3. esbuild-loader: 빠른 번들링을 제공합니다. 이는 babel-loader, ts-loader보다 성능이 뛰어납니다.

4. Webpack Plugin 설치

추가적으로 필요한 Webpack Plugin을 설치합니다. html-webpack-plugin, fork-ts-checker-webpack-plugin, dotenv-webpack을 사용할 것입니다.

npm i -D html-webpack-plugin fork-ts-checker-webpack-plugin dotenv-webpack

각 플러그인의 역할은 다음과 같습니다:

  1. html-webpack-plugin: HTML 파일을 생성하고, 빌드된 자산을 해당 HTML 파일에 자동으로 포함시켜줍니다.
  2. fork-ts-checker-webpack-plugin: TypeScript 타입 검사 및 린팅 작업을 Webpack 빌드와 병렬로 수행합니다.
  3. dotenv-webpack: 빌드 과정에서 .env 파일의 환경 변수를 로드하여 함께 번들링합니다.

5. Webpack 설정 파일 작성

이제 Webpack 설정 파일을 작성해보겠습니다.

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const Dotenv = require('dotenv-webpack');

module.exports = {
  entry: './src/main.tsx',
  module: {
    rules: [
      // type: 'asset'을 지정해줌으로서, file-loader, url-loader, @svgr/webpack의 설치가 필요없어졌습니다.
      // <https://webpack.kr/guides/asset-modules/#general-asset-type>
      {
        test: /\\.(png|svg|jpe?g|gif|webp)$/i,
        type: 'asset',
        parser: {
          // maxSize의 용량값보다 작은 경우 인라인(Base64 형식으로 인코딩)으로, 그 이상일 경우 별도 파일로 분리합니다.
          dataUrlCondition: {
            maxSize: 4 * 1024, // 4KiB를 기준으로 합니다.
          },
        },
        generator: {
          filename: 'assets/images/[name]_[contenthash:8][ext]',
        },
      },
      // esbuild-loader 세팅
      {
        test: /\\.(js|jsx|ts|tsx|mjs)$/,
        exclude: /node_modules/,
        loader: 'esbuild-loader',
        options: {
          target: 'es2020',
        },
      },
      // 스타일 관련 loader 세팅
      {
        test: /\\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  // 절대 경로 세팅
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs'],
    alias: {
      '@': path.resolve(__dirname, 'src/'),
      '@components': path.resolve(__dirname, 'src/components/'),
      '@assets': path.resolve(__dirname, 'src/assets/'),
      'styled-system': path.resolve(__dirname, 'src/styled-system/'),
    },
  },
  // 번들링 세팅
  output: {
    filename: '[name]_[chunkhash:8].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true, // webpack5의 기능으로, 빌드시마다 자동으로 dist의 파일을 삭제하고 새로 빌드합니다.
  },
  // 플러그인 세팅
  plugins: [
    // 번들링시 html 파일을 생성합니다.
    new HtmlWebpackPlugin({
      template: './index.html',
      filename: 'index.html',
    }),
    // esbuild-loader는 ts-loader와 다르게 컴파일시 타입 체킹이 되지 않습니다. 따라서 아래 플러그인을 통해서 체킹해줍니다.
    new ForkTsCheckerWebpackPlugin({
      async: false,
      typescript: {
        configFile: path.resolve(__dirname, 'tsconfig.json'),
      },
    }),
    // 빌드 과정에서 환경변수를 로드합니다.
    new Dotenv(),
  ],
};

6. Webpack 설정 파일 분리

개발과 프로덕션 환경을 구분하여 설정 파일을 분리합니다.

package.json

{
	//...
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --config webpack.dev.js --open --hot"
  }
  //...
}

webpack.dev.js

// webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.config.js');

module.exports = merge(common, {
  mode: 'development',
  // <https://webpack.kr/configuration/devtool/#root>
  devtool: 'eval-source-map',
  // webpack-dev-server 설정입니다.
  devServer: {
    /*
      HTML5HistoryAPI를 사용하기 위해선 이 설정을 true로 하여야 합니다.
      React와 같은 SPA의 경우, HTML5HistoryAPI를 사용하여 라우트를 구현합니다.
      따라서 이 설정을 활성화 하지 않는 경우 라우트 에러가 나타날 수 있습니다.
    */
    historyApiFallback: true,
    port: 3000,
    hot: true, // Hot Module Replacement를 사용합니다.
  },
});

webpack.prod.js

// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.config.js');

module.exports = merge(common, {
  mode: 'production',
  devtool: 'hidden-source-map', // 에러 보고 목적으로 소스맵을 사용할 때 선택합니다.
});

이렇게 React와 TypeScript 환경을 구축하기 위한 모든 설정이 완료되었습니다. 이제 개발 환경과 프로덕션 환경 모두에서 효율적으로 작업을 진행할 수 있게 되었습니다!