돌아가기 빌드 도구
## main - vite.config.js

```javascript
import path from 'path'
import vue from '@vitejs/plugin-vue'
import vueI18n from '@intlify/unplugin-vue-i18n/vite'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
import { visualizer } from 'rollup-plugin-visualizer'
import { defineConfig, loadEnv } from 'vite'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '')
  const isProduction = mode === 'production'

  return {
    plugins: [
      wasm(),
      topLevelAwait(),
      vue(),
      vueI18n({
        include: [path.resolve(__dirname, './src/locales/**')]
      }),
      visualizer({
        filename: 'stats.html',
        open: false,
        gzipSize: true,
        brotliSize: true
      })
    ],
    resolve: {
      alias: {
        '@': path.resolve(__dirname, 'src'),
        '@aa': path.resolve(__dirname, 'src/app/annotator/annotators/audio-annotator'),
        '@app': path.resolve(__dirname, 'src/app'),
        '@assets': path.resolve(__dirname, 'src/assets'),
        '@ia': path.resolve(__dirname, 'src/app/annotator/annotators/image-annotator'),
        '@pa': path.resolve(__dirname, 'src/app/annotator/annotators/pcd-annotator'),
        '@pta': path.resolve(__dirname, 'src/app/annotator/annotators/prompt-annotator'),
        '@services': path.resolve(__dirname, 'src/app/annotator/services'),
        '@shared': path.resolve(__dirname, 'src/app/shared'),
        '@ta': path.resolve(__dirname, 'src/app/annotator/annotators/text-annotator'),
        '@va': path.resolve(__dirname, 'src/app/annotator/annotators/video-annotator')
      },
      extensions: ['.ts', '.js', '.vue', '.json'],
      dedupe: ['vue']
    },
    define: {
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false,
      __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
    },
    server: {
      host: true,
      port: 8080,
      strictPort: false,
      cors: true,
      hmr: { overlay: true }
    },
    preview: { port: 4173 },
    build: {
      target: 'es2020',
      outDir: 'dist',
      assetsDir: 'assets',
      sourcemap: isProduction ? 'hidden' : true,
      minify: isProduction ? 'terser' : false,
      chunkSizeWarningLimit: 1500,
      rollupOptions: {
        output: {
          entryFileNames: 'assets/[name].[hash].js',
          chunkFileNames: 'assets/[name].[hash].js',
          assetFileNames: 'assets/[name].[hash][extname]',
          manualChunks(id) {
            if (id.includes('node_modules/three') || id.includes('three-mesh-bvh'))
              return 'vendor-three'
            if (
              id.includes('node_modules/video.js') ||
              id.includes('node_modules/@videojs') ||
              id.includes('node_modules/videojs-')
            )
              return 'vendor-videojs'
            if (id.includes('node_modules/jsoneditor') || id.includes('node_modules/ajv'))
              return 'vendor-jsoneditor'
            if (id.includes('@mathjax') || id.includes('mathjax')) return 'vendor-mathjax'
            if (
              id.includes('node_modules/vue/') ||
              id.includes('node_modules/vuex/') ||
              id.includes('node_modules/vue-router/') ||
              id.includes('node_modules/vue-i18n/') ||
              id.includes('node_modules/vue-draggable-plus') ||
              id.includes('node_modules/sortablejs')
            )
              return 'vendor-vue'
            if (id.includes('node_modules/lodash')) return 'vendor-lodash'
            if (id.includes('node_modules/fabric')) return 'vendor-fabric'
            if (id.includes('node_modules/@fancyapps') || id.includes('fancybox'))
              return 'vendor-fancybox'
          }
        }
      },
      terserOptions: isProduction
        ? { compress: { drop_console: true, drop_debugger: true } }
        : undefined
    },
    css: {
      devSourcemap: true,
      preprocessorOptions: {
        scss: { quietDeps: true, silenceDeprecations: ['import', 'legacy-js-api'] }
      }
    },
    optimizeDeps: {
      include: [
        'vue',
        'vuex',
        'vue-router',
        'vue-i18n',
        'axios',
        'lodash',
        'dayjs',
        'chroma-js',
        'nanoid'
      ],
      exclude: ['annotator-util', 'image-annotator-util', 'wasm-utils']
    },
    worker: { format: 'es' },
    esbuild: { supported: { 'top-level-await': true } }
  }
})
```

## staging - webpack.config.cjs

```javascript
const path = require('path')
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  entry: './src/main.js',
  mode: 'development',
  devServer: {
    historyApiFallback: true,
    hot: true,
    liveReload: true,
    allowedHosts: 'all'
  },
  experiments: {
    syncWebAssembly: true,
    topLevelAwait: true
  },
  module: {
    rules: [
      // ts-loader (Vue 파일 포함)
      // vue-loader (v15 - Vue 2용)
      // babel-loader
      // css/sass/postcss loaders
      // worker-loader
      // asset/resource
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[fullhash].bundle.js'
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@app': path.resolve(__dirname, 'src/app'),
      '@assets': path.resolve(__dirname, 'src/assets'),
      '@ia': path.resolve(__dirname, 'src/app/annotator/annotators/image-annotator'),
      '@pa': path.resolve(__dirname, 'src/app/annotator/annotators/pcd-annotator'),
      '@pta': path.resolve(__dirname, 'src/app/annotator/annotators/prompt-annotator'),
      '@ta': path.resolve(__dirname, 'src/app/annotator/annotators/text-annotator'),
      '@va': path.resolve(__dirname, 'src/app/annotator/annotators/video-annotator')
    },
    extensions: ['.ts', '.js', '.vue', '.json']
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({ template: 'public/index.html' }),
    new CopyWebpackPlugin({ patterns: [{ from: 'public', to: '.' }] }),
    new webpack.DefinePlugin({
      /* process.env 주입 */
    })
  ],
  cache: { type: 'filesystem' }
}
```