Webpack 기초
현대 웹 개발, 특히 React 같은 SPA(Single Page Application)에서는 수많은 JavaScript 모듈, CSS, 이미지 에셋을 사용합니다. 이 파일들은 개발 단계에서는 분리되어 있지만, 브라우저 실행 단계에서는 다음 문제와 마주칩니다.
- HTTP 요청 수 증가: 수백 개의 파일을 각각 요청하면 네트워크 지연이 발생하고 성능이 저하됩니다.
- 모듈 의존성 관리 복잡성: 각 모듈이 다른 모듈에 의존하는 경우, 로딩 순서 등을 수동으로 관리하기 어렵습니다.
- 브라우저 호환성 문제: 최신 JavaScript(ES6+) 문법이나 TypeScript, Sass 등의 CSS 전처리기 등은 모든 브라우저에서 직접 지원되지 않습니다.
- 배포 최적화 어려움: 코드 압축, 이미지 최적화 등의 배포 전 최적화 작업을 수동으로 하기는 비효율적입니다.
이러한 문제들을 해결하고, 복잡한 웹 프로젝트의 효율적인 개발 및 배포를 돕기 위해 등장한 것이 바로 모듈 번들러(Module Bundler)입니다. 그중에서도 Webpack은 React, Vue 등 현대 프론트엔드 개발에서 가장 널리 사용되는 강력한 모듈 번들러입니다.
이번 장에서는 Webpack의 기본 개념과 동작 원리, 핵심 설정을 사용해 간단한 프로젝트를 빌드하는 방법을 학습합니다. Webpack은 모듈을 번들로 묶고, 로더와 플러그인을 통해 JavaScript 외 에셋과 빌드 결과물을 처리합니다.
모듈 번들러(Module Bundler)란?
모듈 번들러는 애플리케이션을 구성하는 여러 개의 모듈(JavaScript 파일, CSS 파일, 이미지 등)을 분석하고, 이들의 의존성을 파악하여 최종적으로 브라우저에서 실행 가능한 형태의 하나 또는 여러 개의 파일(번들)로 합쳐주는 도구입니다.
주요 역할- 모듈 의존성 해결:
import,require()등으로 연결된 모듈들을 찾아내 하나의 그래프를 만듭니다. - 코드 번들링: 수많은 모듈 파일을 웹 브라우저가 효율적으로 로드할 수 있는 몇 개의 파일로 묶습니다.
- 트랜스파일링(Transpiling): Babel과 같은 도구와 연동하여 ES6+ JavaScript 코드를 구형 브라우저가 이해할 수 있는 ES5 코드로 변환합니다.
- 에셋 처리: CSS, 이미지, 폰트 파일 등 JavaScript 코드가 아닌 에셋들도 모듈처럼 처리하여 번들에 포함시키거나 최적화합니다.
- 코드 최적화: 코드 압축(Minification), 난독화(Uglification), 트리 쉐이킹(Tree Shaking) 등을 통해 최종 번들 파일의 크기를 줄입니다.
- 개발 서버 제공: 개발 중 빠른 피드백을 위해 웹 서버를 제공하고, 코드 변경 시 자동으로 새로고침(Hot Module Replacement) 기능을 지원합니다.
Webpack 외에도 Vite, Parcel, Rollup 등 다양한 모듈 번들러가 있지만, Webpack은 가장 성숙하고 기능이 풍부하며 널리 사용됩니다.
Webpack 핵심 개념
Webpack을 이해하려면 몇 가지 핵심 개념을 알아야 합니다.
-
Entry (엔트리)
- Webpack이 번들링을 시작할 지점, 즉 애플리케이션의 메인 JavaScript 파일입니다.
- Webpack은 엔트리 파일을 시작으로 모든 의존성 모듈을 재귀적으로 따라가며 하나의 의존성 그래프를 만듭니다.
webpack.config.js파일에서entry속성으로 지정합니다.module.exports = { entry: './src/index.js', // 단일 엔트리 // entry: { app: './src/app.js', vendor: './src/vendor.js' }, // 다중 엔트리 };
-
Output (아웃풋)
- Webpack이 생성한 번들 파일을 저장할 경로와 파일 이름입니다.
output속성으로 지정합니다.const path = require('path'); module.exports = { output: { filename: 'bundle.js', // 번들 파일 이름 path: path.resolve(__dirname, 'dist'), // 번들 파일 저장 경로 clean: true, // 빌드 전 output 디렉토리 정리 }, };
-
Loader (로더)
- Webpack은 기본적으로 JavaScript와 JSON 파일만 이해합니다. 로더는 Webpack이 JavaScript가 아닌 파일(예: CSS, 이미지, 폰트)을 모듈로 처리할 수 있도록 변환(transform)하는 역할을 합니다.
- 개발자가 직접 작성하는 것이 아니라, 이미 만들어진 로더(예:
css-loader,style-loader,babel-loader,file-loader)를 설정에 추가하여 사용합니다. module.rules배열 안에 정의합니다.module.exports = { module: { rules: [ { test: /\.css$/, // .css 확장자를 가진 파일에 적용 use: ['style-loader', 'css-loader'], // 적용할 로더 배열 }, { test: /\.js$/, // .js 확장자를 가진 파일에 적용 exclude: /node_modules/, // node_modules 디렉토리 제외 use: { loader: 'babel-loader', // babel-loader 사용 (ES6+ -> ES5) options: { presets: ['@babel/preset-env', '@babel/preset-react'], }, }, }, ], }, };
-
Plugin (플러그인)
- 로더가 특정 파일을 변환하는 데 집중한다면, 플러그인은 번들링 과정 전반에 걸쳐 다양한 추가 기능을 수행합니다.
- 코드 최적화(번들 파일 압축), 에셋 관리(HTML 파일 생성, 이미지 최적화), 환경 변수 주입 등 로더로는 할 수 없는 광범위한 작업을 수행합니다.
plugins배열 안에 정의합니다.const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: './public/index.html', // HTML 템플릿 파일 지정 }), new MiniCssExtractPlugin({ filename: 'styles/[name].css', // CSS 파일을 별도 파일로 추출 }), ], };
-
Mode (모드)
development,production,none세 가지 모드를 제공합니다. 각 모드에 따라 Webpack의 내부 최적화 설정이 자동으로 적용됩니다.production모드: 코드 압축, 트리 쉐이킹 등 배포를 위한 최적화가 기본으로 활성화됩니다.development모드: 빠른 빌드 속도와 디버깅 편의성을 위해 최적화 기능이 비활성화됩니다.module.exports = { mode: 'development', // 'production' 또는 'none'으로 설정 가능 };
Webpack으로 React 앱 빌드하기 (간단 예제)
이제 간단한 React 프로젝트를 Webpack으로 빌드하는 과정을 통해 핵심 개념들을 실습해 보겠습니다.
mkdir my-webpack-app
cd my-webpack-app
npm init -y
# React 및 Webpack 관련 패키지 설치
npm install react react-dom
npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin style-loader css-loader babel-loader @babel/core @babel/preset-env @babel/preset-reactpublic/index.html 파일 생성<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Webpack React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>src/index.js 파일 생성 (엔트리 파일)// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App'; // App 컴포넌트 임포트
import './styles.css'; // CSS 파일 임포트
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);src/App.js 파일 생성 (React 컴포넌트)// src/App.js
import React, { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<h1>Hello, Webpack & React!</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(prev => prev + 1)}>Increment</button>
</div>
);
}
export default App;src/styles.css 파일 생성/* src/styles.css */
body {
font-family: sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.App {
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}.babelrc 파일 생성 (Babel 설정)// .babelrc
{
"presets": [
"@babel/preset-env", // 최신 JS 문법을 타겟 환경에 맞춰 변환
"@babel/preset-react" // JSX 문법을 JS로 변환
]
}webpack.config.js 파일 생성 (Webpack 설정)// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development', // 개발 모드 설정 (배포 시 'production'으로 변경)
entry: './src/index.js', // 엔트리 포인트 (Webpack이 빌드를 시작할 파일)
output: {
filename: 'bundle.js', // 빌드된 JavaScript 파일 이름
path: path.resolve(__dirname, 'dist'), // 빌드 결과물이 저장될 디렉토리 경로 (프로젝트 루트의 'dist' 폴더)
clean: true, // 빌드 시 이전 빌드 결과물 삭제
},
module: {
rules: [
// .css 파일 처리 규칙
{
test: /\.css$/, // .css 확장자를 가진 파일에 이 로더들을 적용
use: ['style-loader', 'css-loader'], // style-loader는 CSS를 <style> 태그로 주입, css-loader는 CSS 파일을 JS 모듈로 변환
},
// .js, .jsx 파일 처리 규칙 (Babel을 사용하여 ES6+ 및 JSX 변환)
{
test: /\.jsx?$/, // .js 또는 .jsx 확장자를 가진 파일에 적용
exclude: /node_modules/, // node_modules 디렉토리는 제외 (성능 향상)
use: {
loader: 'babel-loader', // Babel 로더 사용
options: {
presets: ['@babel/preset-env', '@babel/preset-react'], // .babelrc에 설정된 프리셋 사용
},
},
},
// 이미지 파일 처리 규칙 (Webpack 5부터 기본 제공)
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource', // 파일을 dist 폴더로 복사하고 URL을 반환
},
// 폰트 파일 처리 규칙
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html', // HTML 템플릿 파일 경로
filename: 'index.html', // 빌드될 HTML 파일 이름
}),
],
devServer: {
static: './dist', // 개발 서버가 정적 파일을 제공할 경로
port: 3000, // 개발 서버 포트
open: true, // 서버 시작 시 브라우저 자동 열기
hot: true, // HMR(Hot Module Replacement) 활성화
},
};package.json 스크립트 추가package.json 파일의 scripts 섹션을 다음과 같이 수정합니다.
"scripts": {
"start": "webpack serve --mode development", // 개발 서버 시작
"build": "webpack --mode production" // 프로덕션 빌드
},-
개발 서버 실행
npm start브라우저가 자동으로 열리고 React 앱이
http://localhost:3000에서 실행되는 것을 확인할 수 있습니다. 코드를 수정하면 자동으로 새로고침되거나 HMR이 적용됩니다. -
프로덕션 빌드
npm run builddist디렉토리에 최적화된index.html과bundle.js파일이 생성됩니다. 이 파일들을 웹 서버에 배포하면 됩니다.
Webpack 설정은 파일을 나열하는 일이 아니라, 빌드 목표에 맞춰 변환 책임과 최적화 책임을 나누는 작업입니다.
Webpack 학습 정리
이번 장에서는 모던 웹 개발의 핵심 도구인 Webpack의 기초를 학습했습니다.
이번 절에서는 모듈 번들러의 필요성과 Webpack의 Entry, Output, Loader, Plugin, Mode 역할을 구분했습니다. 로더는 JavaScript 외 에셋을 처리하고, 플러그인은 번들링 과정의 부가 작업과 최적화를 담당합니다.
또한, 직접 React 프로젝트를 설정하고 webpack.config.js 파일을 작성하여 JavaScript, CSS, 이미지 등을 번들링하고, 개발 서버를 실행하며, 최종 프로덕션 빌드를 수행하는 과정을 실습했습니다.
Webpack은 모듈 번들링, 로더, 플러그인, 빌드 모드의 역할을 이해한 뒤 프로젝트 요구에 맞게 적용해야 합니다. 다음 절에서는 통합 개발 환경(IDE), 디버깅 도구, 코드 품질 도구를 다룹니다.
Webpack 설정은 entry에서 dist까지 이어지는 빌드 파이프라인으로 읽을 때 문제 원인과 최적화 지점을 빠르게 나눌 수 있습니다.
Webpack 기초 절은 모듈 번들러란?, Webpack 핵심 개념, Webpack으로 React 앱을 구성하는 과정 중심으로 개념, 코드 흐름, 실전 판단을 연결합니다.
아래 다이어그램은 모듈 번들러(Module Bundler)란?을 개발 도구, 변경 추적, 배포 전 확인 순서로 정리합니다.
Webpack 기초를 학습할 때 흔들리기 쉬운 개념 경계와 적용 순서를 함께 정리했습니다.
Webpack 기초에서 다시 볼 기준과 확인 순서를 정리했습니다.