webpack이란?

최근 몇 년동안 웹 개발은 소수의 애셋과 약간의 자바스크립트가 포함된 페이지에서 복잡한 자바스크립트와 대규모 의존성 트리(여러 파일이 여러 다른 파일에 의존하는)가 포함된 다양한 기능을 갖춘 웹 애플리케이션으로 발전했습니다.

개발자 커뮤니티에는 이러한 복잡성에 대응하기 위해 다음과 같은 방법을 고안했습니다.

  • 한 프로그램을 여러 파일로 분할하고 구성할 수 있는 자바스크립트 모듈
  • 향후 자바스크립트에서 제공될 기능을 미리 이용할 수 있게 해주는 자바스크립트 전처리기와 자바스크립트로 컴파일되는 언어(예: 커피스크립트)

이러한 방법은 아주 유용하지만 파일을 브라우저가 이해할 수 있도록 번들로 묶고 변환(트랜스파일 및 컴파일)하는 이전에는 없던 추가 단계를 거쳐야 이용할 수 있습니다. 이 때문에 webpack과 같은 도구가 필요해졌습니다.

webpack 은 프로젝트의 구조를 분석하고 자바스크립트 모듈을 비롯한 애셋을 찾은 다음 이를 브라우저에서 이용할 수 있는 번들로 묶고 패킹하는 모듈 번들러입니다.

grunt나 gulp와 같은 빌드 도구와의 차이점

webpack은 빌드 툴이 아니므로 grunt나 gulp 등의 태스크 러너나 빌드 시스템과는 다르지만 이러한 툴을 대체하면서 장점을 제공할 수 있습니다.

grunt나 gulp 같은 빌드 툴은 정의한 경로에서 구성과 일치하는 파일을 찾습니다. 따라서 구성 파일에서 이러한 파일을 변환, 조합 및/또는 축소minify하는 작업이나 단계를 지정해야 합니다.

  1. 소스 폴드(자바스크립트 ES6)
  2. 폴더에서 모든 파일을 읽음
  3. 첫 번째 작업(플러그인)을 통해 파일을 처리
  4. JS(ES5)로 트랜스파일
  5. 파일을 저장하거나 다음 작업으로 전달
  6. JS 모듈을 번들로 묶음
  7. 파일 저장
  8. 번들로 묶은 자바스크립트 파일

이와 달리 webpack은 프로젝트 전체를 한 단위로 분석합니다. 즉, 지정한 메인 파일에서 시작해 자바스크립트의 require와 import 문을 참고해 프로젝트의 모든 의존성을 조사하고 로더를 이용해 처리한 후 묶은 자바스크립트 파일을 생성합니다.

webpack-flow

webpack의 방식이 더 빠르고 직관적입니다. 또한 다른 파일 형식까지 번들로 묶을 수 있는 방법을 제공합니다.

로더

webpack의 가장 흥미로운 기능 중 하나인 로더를 이용하면 외부 스크립트와 도구를 통해 소스 파일을 전처리하고 다양한 변경과 변환을 적용할 수 있습니다. 이러한 변환은 JSON 파일을 일반 자바스크립트로 구문 분석하거나, 차세대 자바스크립트 코드를 현재 브라우저가 이해할 수 있는 일반 자바스크립트로 변환해 먼저 이용할 수 있게 하는 등 다양한 상황에서 유용합니다. 로더는 리액트의 JSX를 일반 자바스크립트로 변환하는 데도 이용할 수 있으므로 리액트 개발에도 중요합니다.

로더는 별도로 설치해야 하며, webpack.config.js의 “modules” 키에서 구성해야 합니다. 로더 구성 설정에는 다음과 같은 항목이 있습니다.

  • test: 이 로더로 처리하기 위해 일치해야 하는 파일 확장자를 비교하는 정규 표현식(필수)
  • loader: 로더의 이름(필수)
  • include/exclude: 로더가 명시적으로 추가하거나 무시할 폴더와 파일을 수동으로 지정하는 옵션 설정
  • query: 로더로 추가 옵션을 전다하는 데 이용되는 쿼리 설정

이번에는 인사말 텍스트를 별도의 JSON 구성 파일로 옮겨보겠습니다. 먼저 웹팩의 JSON 로더 모듈을 설치합니다.

npm install --save json-loader
module.exports = {
    devtool: 'eval-source-map',
    entry: __dirname + "/app/main.js",
    output: {...},

    module: {
        loaders: [
            {
                test: /\.json$/,
                loader: "json"
            }
        ]
    },
    devServer: {
        contentBase: "./public",
        colors: true,
        historyApiFallback: true,
        inline: true
    }
}

마지막으로 config.json 파일을 만들고 Greeter에서 이 파일을 가져오게 합니다.

// config.json
{
    "greetText": "Hi there and greetings from JSON!"
}

// greeter.js
var config = require('./config.json');

module.exports = function() {
    var greet = document.createElement('div');
    greet.textContent = config.greetText;
    return greet;
}

바벨

바벨은 자바스크립트 컴파일과 도구 지원을 위한 플랫폼으로서 다음과 같은 기능을 제공합니다.

  • 아직 일부 브라우저에서 지원되지 않은 자바스크립트의 버전(ES6, ES6 등)을 이용할 수 있게 해줍니다.
  • React의 JSX와 같은 자바스크립트 구문 확장을 이용할 수 있게 해줍니다.

바벨은 독립 실행형 도구지만 로더로 이용할 수 있으며, 웹팩과 아주 잘 어울립니다.

설치와 구성

바벨은 모듈형 구조를 띠며, 다양한 npm 모듈로 배포됩니다. 핵심 기능은 ‘babelcore’ npm 패키지로 제공되며, 웹팩과의 통합은 ‘babel-loader’ npm 패키지로 제공됩니다. 또한 코드에서 이용하려는 기능과 확장 유형마다 각기 다른 패키지를 설치해야 합니다. 많이 사용되는 패키지로는 ES6 컴파일을 위한 babel-preset-es2015와 React JSX 지원을 위한 babel-preset-react가 있습니다.

이러한 패키지를 모두 개발 의존성으로 설치하려면 다음 명령을 이용할 수 있습니다.

npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

다른 웹팩 로더와 마찬가지로 바벨도 웹팩 구성 파일의 모듈 섹션에서 구성할 수 있습니다.

// webpack.config.js
module.exports = {
    devtool: 'eval-source-map',
    entry: __dirname + "/app/main.js",
    output: {...},
    module: {
        loaders: [
            {
                test: /\.json$/,
                loader: "json"
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {
                    presets: ['es2015', 'react']
                }
            }
        ]
    },
    devServer: {
        contentBase: "./public",
        colors: true,
        historyApiFallback: true,
        inline: true
    }
}

이제 바벨을 이용해서 ES6모듈과 구문을 이용할 수 있으니 JSX까지 이용해서 예제 코드를 작성해 보겠습니다. 바벨을 설치 및 구성하고 React와 React-DOM을 설치합니다.

npm install --save react react-dom
// Greeter.js
import React, {Component} from 'react';
import config from './config.json';

class Greeter extends Component {
    render() {
        return (
            <div>
                {config.greetText}
            </div>
        );
    }
}

export default Greeter

다음으로 ES6 모듈 정의를 이용하고 Greeter 리액트 컴포넌트를 렌더링하도록 main.js 파일을 업데이트합니다.

// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';

render(<Greeter />, document.getElementById('root'));

바벨 구성 파일

바벨을 webpack.config.js에서 완전히 구성할 수도 있지만 구성 설정과 옵션, 조합이 매우 다양하기 때문에 동일한 파일 안에서 모든 구성을 처리하려면 지나치게 복잡해질 수 있습니다. 이 때문에 ‘.babelrc’라는 별도의 바벨 리소스 구성을 만드는 경우가 많습니다.

지금까지 사용한 바벨 전용 구성은 프리셋 정의뿐이므로 이 항목만 포함하는 바벨 전용 구성 파일을 따로 만드는 것이 과하게 보일 수 있습니다. 그러나 바벨 전용 구성을 만들어 놓으면 사용면에서 편리합니다. 먼저 프리셋 구성을 webpack.config.js 파일에서 분리해 기본 로더 설정만 남겨놓습니다.

// 바벨 프리셋 정의를 제거한 웹팩 구성
module.exports = {
    devtool: 'eval-source-map',
    entry: __dirname + "/app/main.js",
    output: {...},
    module: {
        loaders: [
            {
                test: /\.json$/,
                loader: "json"
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel'
            }
        ]
    },
    devServer: {...}
}

이어서 바벨의 프리셋 구성을 포함하는 ‘.babelrc’라는 파일을 만듭니다.

{
    "presets": ["react", "es2015"]
}