icon
14장 : 개발도구와 워크플로우

Webpack 기초


현대 웹 개발, 특히 React와 같은 SPA(Single Page Application) 개발에서는 수많은 JavaScript 파일(모듈), CSS, 이미지 등의 에셋(Asset)을 사용합니다. 이러한 파일들은 각각 독립적인 모듈로 개발되지만, 실제 웹 브라우저에서 실행될 때는 다음과 같은 문제에 직면합니다.

  • HTTP 요청 수 증가: 수백 개의 파일을 각각 요청하면 네트워크 지연이 발생하고 성능이 저하됩니다.
  • 모듈 의존성 관리 복잡성: 각 모듈이 다른 모듈에 의존하는 경우, 로딩 순서 등을 수동으로 관리하기 어렵습니다.
  • 브라우저 호환성 문제: 최신 JavaScript(ES6+) 문법이나 TypeScript, Sass 등의 CSS 전처리기 등은 모든 브라우저에서 직접 지원되지 않습니다.
  • 배포 최적화 어려움: 코드 압축, 이미지 최적화 등의 배포 전 최적화 작업을 수동으로 하기는 비효율적입니다.

이러한 문제들을 해결하고, 복잡한 웹 프로젝트의 효율적인 개발 및 배포를 돕기 위해 등장한 것이 바로 모듈 번들러(Module Bundler) 입니다. 그중에서도 Webpack은 React, Vue 등 현대 프론트엔드 개발에서 가장 널리 사용되는 강력한 모듈 번들러입니다.

이번 장에서는 Webpack의 기본 개념과 동작 원리를 이해하고, 핵심 설정들을 사용하여 간단한 프로젝트를 빌드하는 방법을 학습할 것입니다. Webpack을 통해 여러분은 수많은 파일을 하나의 번들로 묶고, 최신 문법을 구형 브라우저에서도 동작하게 변환하며, 에셋들을 효율적으로 처리하여 웹 애플리케이션의 성능을 최적화할 수 있게 될 것입니다.


모듈 번들러(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으로 빌드하는 과정을 통해 핵심 개념들을 실습해 보겠습니다.

1. 프로젝트 초기화 및 기본 패키지 설치

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-react

2. public/index.html 파일 생성

public/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>

3. src/index.js 파일 생성 (엔트리 파일)

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>
);

4. src/App.js 파일 생성 (React 컴포넌트)

src/App.js
// 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;

5. src/styles.css 파일 생성

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;
}

6. .babelrc 파일 생성 (Babel 설정)

.babelrc
// .babelrc
{
  "presets": [
    "@babel/preset-env",  // 최신 JS 문법을 타겟 환경에 맞춰 변환
    "@babel/preset-react" // JSX 문법을 JS로 변환
  ]
}

7. webpack.config.js 파일 생성 (Webpack 설정)

webpack.config.js
// 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) 활성화
  },
};

8. package.json 스크립트 추가

package.json 파일의 scripts 섹션을 다음과 같이 수정합니다.

package.json
  "scripts": {
    "start": "webpack serve --mode development", // 개발 서버 시작
    "build": "webpack --mode production" // 프로덕션 빌드
  },

9. 개발 서버 실행 및 빌드

  • 개발 서버 실행

    npm start

    브라우저가 자동으로 열리고 React 앱이 http://localhost:3000에서 실행되는 것을 확인할 수 있습니다. 코드를 수정하면 자동으로 새로고침되거나 HMR이 적용됩니다.

  • 프로덕션 빌드

    npm run build

    dist 디렉토리에 최적화된 index.htmlbundle.js 파일이 생성됩니다. 이 파일들을 웹 서버에 배포하면 됩니다.


마무리하며

이번 장에서는 모던 웹 개발의 핵심 도구인 Webpack의 기초를 학습했습니다.

여러분은 모듈 번들러가 왜 필요하고 어떤 역할을 하는지 이해했으며, Webpack의 핵심 개념인 Entry, Output, Loader, Plugin, Mode의 역할을 명확히 구분했습니다. 특히, 로더가 JavaScript가 아닌 에셋들을 처리하고, 플러그인이 번들링 과정 전반에 걸쳐 다양한 최적화 작업을 수행한다는 것을 배웠습니다.

또한, 직접 React 프로젝트를 설정하고 webpack.config.js 파일을 작성하여 JavaScript, CSS, 이미지 등을 번들링하고, 개발 서버를 실행하며, 최종 프로덕션 빌드를 수행하는 과정을 실습했습니다.

Webpack은 처음 접할 때 다소 복잡하게 느껴질 수 있지만, 웹 애플리케이션의 성능 최적화와 효율적인 개발 워크플로우를 위해 반드시 숙달해야 할 강력한 도구입니다. 이 장에서 배운 기초 지식을 바탕으로 여러분의 프로젝트에 Webpack을 적용하고, 더 나아가 다양한 로더와 플러그인을 활용하여 빌드 프로세스를 최적화하는 역량을 키우시길 바랍니다. 다음 장에서는 개발 생산성을 더욱 높여주는 통합 개발 환경(IDE)과 디버깅 도구, 그리고 코드 품질 도구들에 대해 알아보겠습니다.