본문으로 건너뛰기
Previous
Next
Webpack Complete Guide | Internals· Loaders

Webpack Complete Guide | Internals· Loaders

Webpack Complete Guide | Internals· Loaders

이 글의 핵심

This guide explains how Webpack 5 actually builds bundles?�not only entry/output and loaders, but the module graph, Tapable plugin hooks, loader pitch vs. normal phases, splitChunks strategies, tree shaking with sideEffects, and filesystem caching for fast production builds.

What Webpack Does (At a Glance)

Entry file(s)
  ??resolve & parse imports ??build a module dependency graph
  ??run loaders per rule (transform TS, CSS, assets??
  ??plugins hook into compilation (optimize, emit HTML, define env??
  ??emit chunks + assets to disk
ConceptRole
EntryStarting module(s) for the dependency graph
OutputFilenames and publicPath for emitted chunks/assets
LoaderPer-module transform (chainable)
PluginSubscribes to Tapable hooks on the compiler/compilation
Modedevelopment vs production defaults (minify, tree shaking, devtool)
ChunkA group of modules emitted as one or more files

Real-World Adoption

Webpack remains the most widely-used bundler in production:

Enterprise Usage:

  • Facebook: Powers Meta’s massive frontend infrastructure serving billions
  • Airbnb: Bundles their entire web platform with custom Webpack configurations
  • Microsoft: Uses Webpack for Office 365, Teams, and Azure Portal
  • Netflix: Built their UI platform on Webpack for streaming across 190+ countries
  • Alibaba: Handles millions of daily transactions with Webpack-based builds

Market Position:

  • 20+ million weekly npm downloads (April 2026) - the most downloaded bundler
  • 64,000+ GitHub stars - decade of production battle-testing
  • Used in 8+ million repositories - industry standard
  • Created by Tobias Koppers in 2012, now maintained by 600+ contributors

Why Webpack Dominates:

  • Module Federation: Only bundler with true micro-frontend federation (Webpack 5)
  • Plugin ecosystem: 15,000+ plugins and loaders for every use case
  • Enterprise migration: Gradual migration path from legacy codebases
  • Configuration power: Can handle any build requirement through plugins
  • Production proven: 10+ years of production use at massive scale

Key Innovations:

  • Code splitting (2014): First mainstream bundler with dynamic import()
  • Tree shaking (2016): Dead code elimination through static analysis
  • Module Federation (2020): Share code between separately deployed apps
  • Persistent caching (Webpack 5): 95% faster subsequent builds

Adoption Statistics:

  • 85% of Fortune 500 use Webpack in production
  • Create React App: Webpack-based (40M+ projects created)
  • Vue CLI: Default Webpack configuration
  • Angular CLI: Built on Webpack

Modern Alternatives:

  • Vite: 10-100x faster dev server (uses esbuild)
  • Turbopack: Next.js bundler (Rust-based)
  • esbuild: Fastest transpiler (Go-based)

Despite newer tools, Webpack’s maturity, Module Federation, and migration support keep it essential for large enterprises in 2026.


1. Dependency Graph and Bundling Algorithm

1.1 From entry to module graph

Webpack does not ?�concatenate files in folder order.??It starts from each entry module, parses static import / import() / require() (and webpack-specific APIs), and recursively discovers dependent modules. The result is a directed module graph: nodes are modules; edges are dependency relationships.

  • Resolution: For each request string (./foo, lodash, url?resource), resolve options (alias, extensions, conditionNames) determine which file on disk becomes the next module.
  • Parsing: The parser records dependencies. Dynamic import() adds an async dependency (a future chunk boundary), not just another synchronous edge.

Cycles in the graph are allowed; webpack tracks visited modules so each module is built once per compilation (with nuances for HMR and context modules).

1.2 Compilation phases (simplified)

Conceptually, a production build walks phases like:

  1. Make ??traverse from entries, create module objects, apply loaders, attach dependencies.
  2. Finish modules ??dependencies resolved, some optimizations run per module.
  3. Seal ??form chunks from the module graph (entries + split points), assign IDs, run chunk graph optimizations.
  4. Emit ??render chunk assets (JS/CSS), run asset plugins, write to output.path.

Plugins tap into hooks before/after each phase?�e.g. Compilation.hooks.optimizeModules, optimizeChunks, processAssets.

1.3 Chunk graph vs module graph

  • Module graph: which source files depend on which.
  • Chunk graph: how modules are partitioned into output files. An entry chunk contains (directly or by reference) modules reachable from an entry without crossing an async boundary. Async chunks are loaded when a dynamic import() runs.

Understanding this split is key: code splitting is ?�cutting the module graph along async boundaries and shared-module rules,??not a separate magic step.


2. Loader and Plugin Architecture

2.1 Loaders: pipeline and order

Loaders are functions (or async functions) that transform one resource at a time. In a rule, use: ['a', 'b', 'c'] means:

  • Execution order for the normal phase: c ??b ??a (right to left, bottom to top in the array).
  • Each loader receives the output of the previous one; the last loader should return JavaScript (or a module string) unless the type is asset/raw.

Pitch loaders: If a loader exports pitch, that runs before passing control down the chain, in left-to-right order. A pitch can short-circuit the rest of the chain. This is used for things like style-loader injecting HMR without running css-loader unnecessarily in some cases.

Loaders run in Node with a limited this context (e.g. this.async() for async work, this.resourcePath). They should stay stateless across invocations; caching belongs in cache-loader / webpack 5 cache, not globals.

2.2 Plugins: Tapable and hooks

Webpack?�s core uses Tapable: a mini hook system. Compiler (one per run) exposes long-lived hooks; Compilation (per compilation) exposes hooks for module/chunk/asset work.

Common patterns:

  • Compiler.hooks.thisCompilation ??add plugins to the Compilation.
  • Compilation.hooks.processAssets ??emit or transform assets (Webpack 5 API).
  • HtmlWebpackPlugin, DefinePlugin, MiniCssExtractPlugin all subscribe to specific hooks to run at the right time.

Loader vs plugin: use a loader when the transform is per file and synchronous with module source. Use a plugin when you need global behavior (multiple chunks, alternate entries, custom output files, replacing bootstrap code).


3. Code Splitting Strategies

StrategyMechanismTypical use
Multi-entryMultiple entry pointsLegacy MPA, admin vs public shell
Dynamic import()Async chunk per split pointRoute-level lazy loading
splitChunksExtract shared modulesVendor bundle, common UI
runtimeChunk: 'single'Separate webpack runtimeLong-term caching of app code without runtime invalidation

3.1 splitChunks mental model

optimization.splitChunks runs after the module graph exists. It merges or splits chunks based on:

  • chunks: 'async' (only async chunks), 'initial' (sync), 'all' (both).
  • cacheGroups: named rules with test, priority, minChunks, enforce, etc.
  • maxInitialRequests / maxAsyncRequests: guardrails so you do not create hundreds of tiny files.

Higher priority wins when a module matches multiple groups. reuseExistingChunk: true avoids duplicating vendors already emitted.

3.2 Magic comments

// Named async chunk (debuggable filenames)
const Chart = React.lazy(() =>
  import(/* webpackChunkName: "chart" */ './Chart')
);

// Hint: prefetch during idle time (likely next navigation)
import(/* webpackPrefetch: true */ './HeavyDialog');

// Hint: high priority parallel load
import(/* webpackPreload: true */ './CriticalSubfeature');

webpackChunkName affects the chunk?�s logical name; output filename still follows output.chunkFilename and [contenthash] in production.


4. Tree Shaking Mechanism

Tree shaking is dead-code elimination based on ES module static structure:

  1. Parse import { x } from './lib' ??webpack knows only x is needed from that module (if the export is static).
  2. optimization.usedExports ??mark which exports are referenced (production mode enables aggressive behavior).
  3. Minifier (Terser by default) removes unreferenced function bodies and exports.

Requirements:

  • Prefer ESM import/export in application code. require and module.exports are harder to analyze.
  • "sideEffects": false in package.json (or a precise list like ["*.css", "./register.ts"]) tells webpack it can drop whole files if no imports reach them.
  • Avoid patterns that defeat analysis: default-exported object with many methods, re-export barrels that pull entire packages, assigning require to variables.
{
  "sideEffects": ["*.css", "./src/polyfills.ts"]
}

If a package incorrectly marks sideEffects: false, you can break runtime behavior??verify* with your bundle analyzer.


5. Production Webpack Patterns

  • [contenthash] in filename / chunkFilename ??cache-friendly file names; only changes when content changes.
  • runtimeChunk: 'single' ??isolates webpack?�s small runtime so application chunk hashes stay stable when only app code changes.
  • splitChunks ??shared vendor code and deduplication across entries.
  • optimization.minimize: true ??Terser + scope analysis for smaller output.
  • cache: { type: 'filesystem' } ??persist incremental work between CI runs (configure cacheDirectory and buildDependencies).
  • devtool: 'source-map' (or hidden-source-map if you upload maps only to error tracking).

Analyze before optimizing: webpack-bundle-analyzer or Rspack/webpack stats JSON?�optimize the top few KB offenders first (duplicate lodash, full icon packs, moment locales).


6. Installation

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

7. Full webpack.config.ts Example

import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

const isDev = process.env.NODE_ENV !== 'production';

const config: webpack.Configuration = {
  mode: isDev ? 'development' : 'production',

  entry: {
    main: './src/index.tsx',
  },

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: isDev ? '[name].js' : '[name].[contenthash:8].js',
    chunkFilename: isDev ? '[name].chunk.js' : '[name].[contenthash:8].chunk.js',
    assetModuleFilename: 'assets/[name].[contenthash:8][ext]',
    publicPath: '/',
    clean: true,
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },

  devtool: isDev ? 'eval-source-map' : 'source-map',

  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.module\.css$/,
        use: [
          isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: { modules: { localIdentName: '[name]__[local]--[hash:base64:5]' } },
          },
        ],
      },
      {
        test: /\.css$/,
        exclude: /\.module\.css$/,
        use: [isDev ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader'],
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg|webp)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: { maxSize: 8 * 1024 },
        },
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
    ],
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      favicon: './public/favicon.ico',
    }),
    ...(isDev
      ? []
      : [
          new MiniCssExtractPlugin({
            filename: 'css/[name].[contenthash:8].css',
          }),
        ]),
    new webpack.DefinePlugin({
      'process.env.API_URL': JSON.stringify(process.env.API_URL),
    }),
    ...(process.env.ANALYZE ? [new BundleAnalyzerPlugin()] : []),
  ],

  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          chunks: 'all',
          priority: 10,
        },
      },
    },
    runtimeChunk: 'single',
  },

  devServer: {
    port: 3000,
    hot: true,
    historyApiFallback: true,
    proxy: {
      '/api': { target: 'http://localhost:8000', changeOrigin: true },
    },
  },
};

export default config;

8. Loaders in Practice

TypeScript with Babel (faster builds, no type-check in webpack)

npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-typescript @babel/preset-react
{
  test: /\.(ts|tsx)$/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: [
        '@babel/preset-env',
        ['@babel/preset-react', { runtime: 'automatic' }],
        '@babel/preset-typescript',
      ],
    },
  },
},

Run tsc --noEmit in CI for type safety.

SVG as React components

npm install -D @svgr/webpack
{
  test: /\.svg$/,
  use: [{ loader: '@svgr/webpack', options: { icon: true } }],
},

9. Persistent Caching (Webpack 5)

export default {
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
    cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
  },
} satisfies webpack.Configuration;

Invalidate the cache when dependencies or config change??buildDependencies.config` ties invalidation to your config file list.


10. Environment-Specific Configs

// webpack.common.ts
export const commonConfig = { /* shared */ };

// webpack.dev.ts
import { merge } from 'webpack-merge';
import { commonConfig } from './webpack.common';

export default merge(commonConfig, {
  mode: 'development',
  devtool: 'eval-source-map',
  devServer: { hot: true, port: 3000 },
});

// webpack.prod.ts
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';

export default merge(commonConfig, {
  mode: 'production',
  devtool: 'source-map',
  optimization: {
    minimizer: ['...', new CssMinimizerPlugin()],
  },
});

11. Module Federation (Micro-Frontends)

import { ModuleFederationPlugin } from 'webpack/container';

new ModuleFederationPlugin({
  name: 'host',
  remotes: {
    userApp: 'userApp@https://user.example.com/remoteEntry.js',
  },
  shared: {
    react: { singleton: true, requiredVersion: '^18.0.0' },
    'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
  },
}),

Remote app exposes exposes and shares compatible library versions so duplicates are not loaded twice.


12. Minimal Production Optimization Snippet

const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  mode: 'production',
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: { drop_console: true },
        },
      }),
      new CssMinimizerPlugin(),
    ],
    usedExports: true,
  },
};

  • [Vite Complete Guide](/en/blog/vite-complete-guide/
  • [TypeScript 5 Complete Guide](/en/blog/typescript-5-complete-guide/
  • [GitHub Actions CI/CD Guide](/en/blog/github-actions-complete-guide/

?�주 묻는 질문 (FAQ)

Q. ???�용???�무?�서 ?�제 ?�나??

A. Webpack 5 deep dive: dependency graph, Tapable hooks, loader pipeline, code splitting, tree shaking, and production cach???�무?�서????본문???�제?� ?�택 가?�드�?참고???�용?�면 ?�니??

Q. ?�행?�로 ?�으�?좋�? 글?�?

A. �?글 ?�단???�전 글 ?�는 관??글 링크�??�라가�??�서?��?배울 ???�습?�다. C++ ?�리�?목차?�서 ?�체 ?�름???�인?????�습?�다.

Q. ??깊이 공�??�려�?

A. cppreference?� ?�당 ?�이브러�?공식 문서�?참고?�세?? 글 말�???참고 ?�료 링크???�용?�면 좋습?�다.


같이 보면 좋�? 글 (?��? 링크)

??주제?� ?�결?�는 ?�른 글?�니??


??글?�서 ?�루???�워??(관??검?�어)

Webpack, Build Tool, JavaScript, TypeScript, Frontend, Performance, Optimization, Bundler ?�으�?검?�하?�면 ??글???��????�니??