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
| Concept | Role |
|---|---|
| Entry | Starting module(s) for the dependency graph |
| Output | Filenames and publicPath for emitted chunks/assets |
| Loader | Per-module transform (chainable) |
| Plugin | Subscribes to Tapable hooks on the compiler/compilation |
| Mode | development vs production defaults (minify, tree shaking, devtool) |
| Chunk | A 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),resolveoptions (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:
- Make ??traverse from entries, create module objects, apply loaders, attach dependencies.
- Finish modules ??dependencies resolved, some optimizations run per module.
- Seal ??form chunks from the module graph (entries + split points), assign IDs, run chunk graph optimizations.
- 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 theCompilation.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
| Strategy | Mechanism | Typical use |
|---|---|---|
| Multi-entry | Multiple entry points | Legacy MPA, admin vs public shell |
Dynamic import() | Async chunk per split point | Route-level lazy loading |
splitChunks | Extract shared modules | Vendor bundle, common UI |
runtimeChunk: 'single' | Separate webpack runtime | Long-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 withtest,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:
- Parse
import { x } from './lib'??webpack knows onlyxis needed from that module (if the export is static). optimization.usedExports??mark which exports are referenced (production mode enables aggressive behavior).- Minifier (Terser by default) removes unreferenced function bodies and exports.
Requirements:
- Prefer ESM
import/exportin application code.requireandmodule.exportsare harder to analyze. "sideEffects": falseinpackage.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
requireto 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]infilename/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 (configurecacheDirectoryandbuildDependencies).devtool: 'source-map'(orhidden-source-mapif 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,
},
};
Related Posts
- [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?� ?�당 ?�이브러�?공식 문서�?참고?�세?? 글 말�???참고 ?�료 링크???�용?�면 좋습?�다.
같이 보면 좋�? 글 (?��? 링크)
??주제?� ?�결?�는 ?�른 글?�니??
- [Vite ?�벽 가?�드 | ESM 개발 ?�버·HMR·esbuild·Rollup ?��?·Plugins·최적?�·실??(/blog/vite-complete-guide/)
- TypeScript 5 ?�벽 가?�드 | Decorators·const Type Parameters
- GitHub Actions ?�벽 가?�드 | CI/CD·?�동?�·Workflow·배포·?�전 ?�용
??글?�서 ?�루???�워??(관??검?�어)
Webpack, Build Tool, JavaScript, TypeScript, Frontend, Performance, Optimization, Bundler ?�으�?검?�하?�면 ??글???��????�니??