Front-end/개발환경

Babel과 Webpack을 이용한 ES6 환경 구축 | PoiemaWeb

AGAL 2021. 3. 5. 15:22
반응형

 

Babel는 최신 사양의 자바스크립트 코드를 IE나 구형 브라우저에서도 동작하는 ES5 이하의 코드로 변환(트랜스파일링)할 수 있다. 

 

 

Babel CLI 설치

# package.json 생성
$ npm init -y

# babel-core, babel-cli, babel-preset 설치
$ npm install --save-dev @babel/core @babel/cli @babel/preset-env

 

Babel을 사용하려면 Babel 프리셋을 설치해야 한다. @babel/preset-env은 함께 사용되어야 하는 Babel 플러그인을 모아 둔 것으로, Babel이 제공하는 공식 Babel 프리셋(Official Preset)은 아래와 같다.

 

package.json 파일에서 정상적으로 설치됐는 지를 확인한다. (버전은 다를 수 있다)

{
  ...
  
  "devDependencies": {
    "@babel/cli": "^7.13.0",
    "@babel/core": "^7.13.8",
    "@babel/preset-env": "^7.13.9"
  }
}

 

설치가 완료되었으면 프로젝트 루트에 .babelrc 파일을 생성하고 아래와 같이 작성한다. 지금 설치한 @babel/preset-env를 사용하겠다는 의미이다.

{
  "presets": ["@babel/preset-env"]
}

 

 

트랜스파일링

Babel을 사용하여 ES6+ 코드를 ES5 이하의 코드로 트랜스파일링하기 위해 Babel CLI 명령어를 사용할 수도 있으나 npm script를 사용하여 트랜스파일링하는 방법에 대해 알아보도록 하자.

package.json 파일에 scripts를 추가한다. 완성된 package.json 파일은 아래와 같다.

{
  ...
  
  "scripts": {
    "build": "babel src/js -w -d dist/js"
  },
  "devDependencies": {
  	...
  }
}

 

위 npm script는 src/js 폴더(타깃 폴더)에 있는 모든 ES6+ 파일들을 트랜스파일링한 후, 그 결과물을 dist/js 폴더에 저장한다. 사용한 옵션의 의미는 아래와 같다.

  • -w : 타깃 폴더에 있는 모든 파일들의 변경을 감지하여 자동으로 트랜스파일한다. (--watch 옵션의 축약형)
  • -d : 트랜스파일링된 결과물이 저장될 폴더를 지정한다. (--out-dir 옵션의 축약형)

 

프로젝트 루트에  src/js 폴더를 생성하고 ES6+ 자바스크립트 파일을 작성한다.

터미널에서 아래 명령으로 트랜스파일링을 실행한다.

$ npm run build

 

 

Babel 플러그인

2019년 11월 현재 TC39 프로세스의 stage 3(candidate) 단계에 있는 Class field declarations proposal에서 에러가 발생하였다.

 

@babel/preset-env는 현재 제안 단계에 있는 사양에 대한 플러그인을 지원하지 않기 때문에 발생한 에러이다. 현재 제안 단계에 있는 사양을 지원하려면 별도의 플러그인을 설치하여야 한다.

 

설치가 필요한 플러그인은 Babel 홈페이지에서 검색할 수 있다. 상단 메뉴의 Search에 제안(프로포절)의 이름을 입력하면 해당 플러그인을 검색할 수 있다. 클래스 필드 정의 제안 플러그인을 검색하기 위해 “Class field”를 입력해보자.

 

검색한 플러그인 @babel/plugin-proposal-class-properties를 설치해보자.

$ npm install --save-dev @babel/plugin-proposal-class-properties

 

설치한 플러그인은 .babelrc 파일에 추가해 주어야 한다.

{
  ...
  "plugins": ["@babel/plugin-proposal-class-properties"]
}

 

 

브라우저에서 모듈 로딩 테스트

ES6+에서 새롭게 추가된 기능은 물론 현재 제안 상태에 있는 “클래스 필드 정의 제안”도 ES5로 트랜스파일링되었고 ES6의 모듈의 import와 export 키워드도 트랜스파일링되어 모듈 기능도 정상적으로 동작하는 것을 확인하였다. 하지만 모듈 기능은 node.js 환경에서 동작한 것이고 Babel이 모듈을 트랜스파일링한 것도 node.js가 기본 지원하는 CommonJS 방식의 module loading system에 따른 것이다. 

 

브라우저는 CommonJS 방식의 module loading system(require 함수)을 지원하지 않으므로 위에서 트랜스파일링된 결과를 그대로 브라우저에서 실행하면 에러가 발생한다. 브라우저의 ES6 모듈 기능을 사용하도록 Babel을 설정할 수도 있으나 앞서 설명한 바와 같이 브라우저의 ES6 모듈 기능을 사용하는 것은 문제가 있다.

Webpack을 통해 이러한 문제를 해결해보도록 하자.

 

 

Webpack 설치

Webpack은 의존 관계에 있는 모듈들을 하나의 자바스크립트 파일로 번들링하는 모듈 번들러이다. Webpack을 사용하면 의존 모듈이 하나의 파일로 번들링되므로 별도의 모듈 로더가 필요없다. 그리고 다수의 자바스크립트 파일을 하나의 파일로 번들링하므로 html 파일에서 script 태그로 다수의 자바스크립트 파일을 로드해야 하는 번거로움도 사라진다.

 

Webpack이 자바스크립트 파일을 번들링하기 전에 Babel을 로드하여 ES6+ 코드를 ES5 코드로 트랜스파일링하는 작업을 실행하도록 설정할 것이다. 그리고 Sass를 사용하는 경우, Sass 트랜스파일링도 Webpack에서 관리하도록 할 것이다.

 

Webpack을 설치한다.

# Webpack V4는 webpack-cli를 요구한다
$ npm install --save-dev webpack webpack-cli

 

 

babel-loader

Webpack이 모듈을 번들링할 때 Babel을 사용하여 ES6+ 코드를 ES5 코드로 트랜스파일링하도록 babel-loader를 설치한다.

# babel-loader 설치
$ npm install --save-dev babel-loader

 

이제 npm script를 변경하여 Babel 대신 Webpack을 실행하도록 수정하자. 아래와 같이 package.json 파일의 scripts를 변경한다. 

{
  ...
  "scripts": {
    "build": "webpack -w"
  },
  "devDependencies": {
    ...
  }
}

 

 

webpack.config.js

webpack.config.js은 Webpack이 실행될 때 참조하는 설정 파일이다. 프로젝트 루트에 webpack.config.js 파일을 생성하고 아래와 같이 작성한다.

const path = require('path');

module.exports = {
  // enntry file
  entry: './src/js/main.js',
  // 컴파일 + 번들링된 js 파일이 저장될 경로와 이름 지정
  output: {
    path: path.resolve(__dirname, 'dist/js'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [
          path.resolve(__dirname, 'src/js')
        ],
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-proposal-class-properties']
          }
        }
      }
    ]
  },
  devtool: 'source-map',
  // https://webpack.js.org/concepts/mode/#mode-development
  mode: 'development'
};

 

이제 Webpack을 실행하여 트랜스파일링 및 번들링을 실행한다.

$ npm run build

 

트랜스파일링은 Babel이 실행하고 번들링은 Webpack이 실행한다. 만약 이전에 실행시킨 빌드 명령이 실행 중인 상태라면 중지시키고 다시 아래 명령을 실행한다.

 

 

babel-polyfill

Babel을 사용하여 ES6+ 코드를 ES5 이하로 트랜스파일링하여도 브라우저가 지원하지 않는 코드가 남아 있을 수 있다. 예를 들어, ES6에서 추가된 Promise, Object.assign, Array.from 등은 ES5 이하로 트랜스파일링하여도 대체할 ES5 기능이 없기 때문에 그대로 남아 있다.

오래된 브라우저에서도 ES6+에서 새롭게 추가된 객체나 메소드를 사용하기 위해서는 @babel/polyfill을 설치해야 한다.

$ npm install @babel/polyfill

 

babel-polyfill은 개발 환경에서만 사용하는 것이 아니라 실제 환경에서도 사용하여야 하므로 --save-dev 옵션으로 개발 설치를 하지 않도록 한다.

 

ES6의 import를 사용하는 경우에는 진입점의 선두에서 먼저 폴리필을 로드하도록 한다.

// enntry file 에서 (ex. /src/js/main.js)
import "@babel/polyfill";

 

webpack을 사용하는 경우에는 위 방법을 대신 폴리필을 webpack.config.js 파일의 entry 배열에 추가한다.

// webpack.config.js
const path = require('path');

module.exports = {
  // entry files
  entry: ['@babel/polyfill', './src/js/main.js'],
  ...

 

 

Sass 컴파일

Sass를 컴파일한 결과물인 css를 bundle.js 파일에 포함시키는 방법과 별도의 css 파일로 분리하는 방법이 있다.

 

컴파일된 css를 bundle.js 파일에 포함시키는 방법

필요한 패키지를 설치하자. node-sass는 node.js 환경에서 사용할 수 있는 Sass 라이브러리이다.

실제로 Sass를 css로 컴파일하는 것은 node-sass이다. style-loader, css-loader, sass-loader는 Webpack 플러그인이다.

$ npm install node-sass style-loader css-loader sass-loader --save-dev

 

webpack.config.js 파일을 아래와 같이 수정한다.

const path = require('path');

module.exports = {
  // entry files
  entry: ['@babel/polyfill', './src/js/main.js', './src/sass/main.scss'],
  // 컴파일 + 번들링된 js 파일이 저장될 경로와 이름 지정
  output: {
    path: path.resolve(__dirname, 'dist/js'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [
          path.resolve(__dirname, 'src/js')
        ],
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-proposal-class-properties']
          }
        }
      },
      {
        test: /\.scss$/,
        use: [
          "style-loader", // creates style nodes from JS strings
          "css-loader",   // translates CSS into CommonJS
          "sass-loader"   // compiles Sass to CSS, using Node Sass by default
        ],
        exclude: /node_modules/
      }
    ]
  },
  devtool: 'source-map',
  // https://webpack.js.org/concepts/mode/#mode-development
  mode: 'development'
};

 

빌드 명령이 실행 중인 상태라면 중지시키고 다시 아래 명령을 실행한다.

$ npm run build

 

컴파일된 CSS는 bundle.js에 포함되어 있다.

 

컴파일된 CSS를 별도의 CSS 파일로 분리하는 방법

Sass 파일이 방대해지면 자바스크립트 파일에서 분리하는 것이 효율적일 수 있다. bundle.js 파일에 컴파일된 css를 포함시키지 말고 별도의 css 파일로 분리해서 하나의 파일로 번들링해보자. 이때 사용하는 플러그인은 mini-css-extract-plugin이다.

 

mini-css-extract-plugin을 설치하자.

$ npm install --save-dev mini-css-extract-plugin

 

webpack.config.js 파일을 아래와 같이 수정한다.

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  // entry files
  entry: ['@babel/polyfill', './src/js/main.js', './src/sass/main.scss'],
  // 컴파일 + 번들링된 js 파일이 저장될 경로와 이름 지정
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/bundle.js'
  },
  plugins: [
    // 컴파일 + 번들링 CSS 파일이 저장될 경로와 이름 지정
    new MiniCssExtractPlugin({ filename: 'css/style.css' })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [
          path.resolve(__dirname, 'src/js')
        ],
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-proposal-class-properties']
          }
        },
        exclude: /node_modules/
      },
      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ],
        exclude: /node_modules/
      }
    ]
  },
  devtool: 'source-map',
  // https://webpack.js.org/concepts/mode/#mode-development
  mode: 'development'
};

 

빌드 명령이 실행 중인 상태라면 중지시키고 다시 아래 명령을 실행한다.

$ npm run build
반응형