ES6 In Depth: Babel과 Broccoli로 지금 당장 ES6 이용하기

ES6 In Depth는 ECMAScript 표준의 6번째 에디션(줄여서 ES6)을 맞아 JavaScript에 새로 추가된 요소들을 살펴보는 시리즈입니다.

ES6 가 여기 있습니다. 그리고 사람들은 벌써 ES7 을 말하고 있습니다. 사람들은 예정된 미래에 대해, 그리고 새로운 표준의 빛나는 기능에 대해 이야기하고 있습니다. 웹 개발자로서, 우리는 이 모든 것들을 어떻게 활용해야 할까요? 지금까지 ES6 In Depth 시리즈를 통해 당장 ES6 코딩을 시작하라고 여러 번 권했습니다. 재밌는 도구들을 사용하면 가능한 일입니다. 지금까지 이렇게 권했습니다.

만약 이 문법을 지금 당장 웹에서 사용하고 싶다면, Babel 또는 구글의 Traceur 를 사용해서 ES6 코드를 웹 친화적인 ES5 코드로 변환해서 사용하세요.

오늘 바로 이 방법을 차근차근 살펴볼 것입니다. 위에 언급한 도구들을 트랜스파일러(transpiler) 라고 부릅니다. 트랜스파일러는 소스-투-소스 컴파일러(source-to-source compiler) 라고도 알려져 있습니다. 즉 어떤 프로그래밍 랭귀지를 다른 랭귀지로 변환시키는 컴파일러입니다. 트랜스파일러를 이용하면 ES6 문법으로 코드를 작성하더라도 작성된 코드가 모든 브라우저 위에서 실행되는 것을 보장할 수 있습니다.

트랜스파일러, 우리의 구원

트랜스파일러는 사용하기 아주 쉽습니다. 트랜스파일러의 동작은 2단계로 요약됩니다.

1. ES6 문법에 따라 코드를 작성합니다.

let q = 99;
let myVariable = `${q} bottles of beer on the wall, ${q} bottles of beer.`;

2. 우리가 작성한 코드를 입력으로 넣고 트랜스파일러를 실행시킵니다. 그러면 트랜스파일러는 다음과 같은 결과를 만듭니다.

"use strict";

var q = 99;
var myVariable = "" + q + " bottles of beer on the wall, " + q + " bottles of beer."

우리가 익히 잘 알고 있는 전통적인 JavaScript 코드입니다. 이 코드는 모든 브라우저에서 사용 가능합니다.

트랜스파일러(transpiler) 가 입력 데이터를 출력 산출물로 변환시키는 내부 동작 방식은 무척 복잡하며 본 글의 범위를 벗어납니다. 우리가 자동차의 내부 동작 방식을 모르고도 운전할 수 있는 것처럼, 오늘 우리는 트랜스파일러를 코드 처리에 사용하는 블랙박스로 남겨둘 것입니다.

Babel, 실제로 사용하기

어떤 프로젝트에 Babel 을 사용하는 2가지 다른 방식이 있습니다. 우선 커맨드라인 도구를 사용하는 방법이 있습니다. 당신은 다음 커맨드로 Babel 을 사용할 수 있습니다.

babel script.js --out-file script-compiled.js

브라우저 안에서 사용하는 것도 가능합니다. Babel 을 통상적인 JS 라이브러리로 포함시키고 ES6 코드를 "text/babel" 타입 script 태그에 정의합니다.

<script src="node_modules/babel-core/browser.js"></script>
<script type="text/babel">
// Your ES6 code
</script>

당신이 만드는 코드가 점차 커져서 코드를 여러개의 파일과 디렉토리로 나누게 될 경우, 위의 방법들은 적용되지 않습니다. 그런 시점이 되면, 빌드 도구를 사용해서 Babel 을 빌드 파이프라인에 통합시키는 방법을 사용해야 합니다.

다음 섹션에서 우리는 Babel 을 Broccoli.js 라는 빌드 도구에 통합시킬 것입니다. 우리는 한쌍의 ES6 예제 코드를 작성하고 실행시킬 것입니다. 문제가 발생할 경우 GitHub 레파지토리에 있는 broccoli-babel-examples 소스 코드를 참조하세요. GitHub 레파지토리에는 3개의 프로젝트가 담겨 있습니다.

  1. es6-fruits
  2. es6-website
  3. es6-modules

GitHub 의 각 프로젝트는 위의 예제들을 빌드하기 위한 것입니다. 우리는 아주 작은 솔루션에서 시작해서 멋진 프로젝트의 시작점으로 쓰일 수 있을 정도로 범용적인 솔루션으로 발전시켜 나갈 것입니다. 이번 글에서는 처음 2개 예제를 깊게 다룰 것입니다. 이 글이 끝나고 나면, 당신은 3번째 예제 코드를 스스로 읽고 이해할 수 있을 것입니다.

만약 “나는 모든 브라우저들이 새로운 기능을 지원할 때까지 기다리겠다.” 라고 생각한다면 당신은 뒤쳐지는 겁니다. 완전한 합의란, 설사 그런 일이 정말 일어난다고 해도, 아주 오랜 시간을 필요로 합니다. 트랜스파일러가 필요한 이유는 이겁니다. 이제 새로운 ECMAScript 표준이 매년 제정될 계획입니다. 우리는 새로운 표준이 출시되는 것을 새로운 통합 브라우저 플랫폼이 출시되는 것보다 자주 보게 될 것입니다. 지금 당장 뛰어들어서 새로운 기능이 제공하는 장점을 누리는 것이 좋습니다.

첫 번째 Broccoli & Babel 프로젝트

Broccoli 는 프로젝트를 가능한 빠른 속도로 빌드하기 위해 만들어진 도구입니다. 특히 Broccoli 플러그인을 이용해서 파일들을 난독화(uglify) 하거나 최소화(minify) 할 수 있습니다. Broccoli 는 프로젝트를 수정할 때마다 매번 파일, 디렉토리, 명령어들을 다뤄야 하는 부담을 덜어줍니다. Broccoli 를 다음과 같이 생각하세요.

좁은 범위에서 Rails 의 어셋 파이프라인(asset pipeline) 이라고 생각하세요. 비록 Broccoli 가 Node 위에서 실행되고 백엔드(backend) 와는 상관 없는 솔루션이지만 말이지요.

프로젝트 셋업

Node

이미 알고 있겠지만, Node 를 설치해야 합니다. Node 0.11 또는 그이상의 버전이 필요합니다.

유닉스 시스템을 사용하고 있다면, 패키지 매니저(apt, yum)로 설치하는 것을 피하세요. 왜냐하면 그렇게 설치하려면 루트(root) 권한을 이용하게 되기 때문입니다. 현재 사용자 계정에서 위의 링크가 제공하는 바이너리 파일들을 수동으로 설치하는 것이 최선의 방법입니다. 왜 루트 권한 사용을 피해야 하는지 NPM을 sudo 명령으로 실행하지 마세요(Do not sudo npm) 기사를 읽어 보면 알 수 있습니다. 그 기사에서 당신은 또다른 설치법들도 볼 수 있을 것입니다.

Broccoli

우선 다음과 같은 명령으로 우리의 첫번째 Broccoli 프로젝트를 만들어 봅시다

mkdir es6-fruits
cd es6-fruits
npm init
# Create an empty file called Brocfile.js
touch Brocfile.js

이제 broccolibroccoli-cli 를 인스톨합니다.

# the broccoli library
npm install --save-dev broccoli
# command line tool
npm install -g broccoli-cli

간단한 ES6 코드

우리는 src 폴더를 만들고 그 안에 fruits.js 파일을 넣을 것입니다.

mkdir src
vim src/fruits.js

새로 만든 파일에 ES6 문법에 따라 간단한 스크립트를 써넣읍시다.

let fruits = [
  {id: 100, name: 'strawberry'},
  {id: 101, name: 'grapefruit'},
  {id: 102, name: 'plum'}
];

for (let fruit of fruits) {
  let message = `ID: ${fruit.id} Name: ${fruit.name}`;

  console.log(message);
}

console.log(`List total: ${fruits.length}`);

이 코드는 ES6 기능을 3가지 사용합니다.

  1. let 을 이용한 로컬 스코프 변수 선언 (나중에 별도의 글을 통해 설명할 것입니다.)
  2. for-of 루프
  3. 템플릿 문자열

파일을 저장하고 실행시켜 보세요.

node src/fruits.js

아직 동작하지 않을 것입니다. 하지만 이제 우리는 이 코드를 Node와 모든 브라우저에서 실행 가능하도록 만들 것입니다.

let fruits = [
    ^^^^^^
SyntaxError: Unexpected identifier

트랜스파일러를 쓸 시간

이제 우리는 Broccoli 를 써서 우리가 작성한 코드를 읽어들여서 Babel 에 전달할 것입니다. 이를 위해 Brocfile.js 파일을 만들고 다음과 같은 코드를 추가합니다.

// import the babel plugin
var babel = require('broccoli-babel-transpiler');

// grab the source and transpile it in 1 step
fruits = babel('src'); // src/*.js

module.exports = fruits;

우리가 broccoli-babel-transpiler 를 필요로 하는 것에 주의합시다. 이것은 Babel 라이브러리를 사용하게 해주는 Broccoli 플러그인입니다. 따라서 다음과 같은 명령으로 반드시 설치해야 합니다.

npm install --save-dev broccoli-babel-transpiler

이제 프로젝트를 빌드해서 다음과 같은 명령으로 실행시킬 수 있습니다.

broccoli build dist # compile
node dist/fruits.js # execute ES5

실행 결과는 다음과 같습니다.

ID: 100 Name: strawberry
ID: 101 Name: grapefruit
ID: 102 Name: plum
List total: 3

쉽습니다! dist/fruits.js 파일을 열어서 변환(transpile)된 코드가 어떻게 생겼는지 살펴보세요. Babel 의 장점은 읽기 편한 코드를 만들어 준다는 점입니다.

웹사이트를 위한 ES6 코드

이제 2번째 예제를 맞아 좀 더 수준을 높여봅시다. 우선 es6-fruits 폴더를 벗어나서 새로운 es6-website 디렉토리를 만드세요. 디렉토리를 만들고 설정하는 과정은 앞서의 프로젝트 셋업 과정과 동일합니다.

src 폴더에 3개의 파일을 만들 것입니다.

src/index.html

<!DOCTYPE html>
<html>
  <head>
    <title>ES6 Today</title>
  </head>
  <style>
    body {
      border: 2px solid #9a9a9a;
      border-radius: 10px;
      padding: 6px;
      font-family: monospace;
      text-align: center;
    }
    .color {
      padding: 1rem;
      color: #fff;
    }
  </style>
  <body>
    <h1>ES6 Today</h1>
    <div id="info"></div>
    <hr>
    <div id="content"></div>

    <script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
    <script src="js/my-app.js"></script>
  </body>
</html>

src/print-info.js

function printInfo() {
  $('#info')
  .append('<p>minimal website example with' +
          'Broccoli and Babel</p>');
}

$(printInfo);

src/print-colors.js

// ES6 Generator
function* hexRange(start, stop, step) {
  for (var i = start; i < stop; i += step) {
    yield i;
  }
}

function printColors() {
  var content$ = $('#content');

  // contrived example
  for ( var hex of hexRange(900, 999, 10) ) {
    var newDiv = $('<div>')
      .attr('class', 'color')
      .css({ 'background-color': `#${hex}` })
      .append(`hex code: #${hex}`);
    content$.append(newDiv);
  }
}

$(printColors);

function* hexRange 코드가 눈에 띌 겁니다. 그렇습니다. 바로 ES6 제너레이터입니다. 아직 이 기능을 지원하지 않는 브라우저들이 있습니다. 이 기능을 이용하려면, 폴리필(polyfill)이 필요합니다. Babel 은 이 폴리필을 제공합니다. 우리는 이제 곧 이 폴리필을 사용할 것입니다.

다음 단계는 모든 JS 파일들을 합쳐서(merge) 웹사이트 안에서 사용하는 것입니다. 가장 어려운 부분은 우리가 사용할 Brocfile 을 작성하는 것입니다. 이번에 우리는 4개의 플러그인을 설치합니다.

npm install --save-dev broccoli-babel-transpiler
npm install --save-dev broccoli-funnel
npm install --save-dev broccoli-concat
npm install --save-dev broccoli-merge-trees

이제 플러그인들을 사용해봅시다.

// Babel transpiler
var babel = require('broccoli-babel-transpiler');
// filter trees (subsets of files)
var funnel = require('broccoli-funnel');
// concatenate trees
var concat = require('broccoli-concat');
// merge trees
var mergeTrees = require('broccoli-merge-trees');

// Transpile the source files
var appJs = babel('src');

// Grab the polyfill file provided by the Babel library
var babelPath = require.resolve('broccoli-babel-transpiler');
babelPath = babelPath.replace(/\/index.js$/, '');
babelPath += '/node_modules/babel-core';
var browserPolyfill = funnel(babelPath, {
  files: ['browser-polyfill.js']
});

// Add the Babel polyfill to the tree of transpiled files
appJs = mergeTrees([browserPolyfill, appJs]);

// Concatenate all the JS files into a single file
appJs = concat(appJs, {
  // we specify a concatenation order
  inputFiles: ['browser-polyfill.js', '**/*.js'],
  outputFile: '/js/my-app.js'
});

// Grab the index file
var index = funnel('src', {files: ['index.html']});

// Grab all our trees and
// export them as a single and final tree
module.exports = mergeTrees([index, appJs]);

이제 코드를 빌드하고 실행해볼 시간입니다.

broccoli build dist

dist 폴더 구조가 다음과 같이 생성됐어야 합니다.

$> tree dist/
dist/
├── index.html
└── js
    └── my-app.js

이것은 코드가 정상적으로 동작하는지 검증하기 위해 어떤 웹서버로도 실행시켜 볼 수 있는 정적인 웹사이트입니다. 예를 들어 다음처럼 테스트해 볼 수 있습니다.

cd dist/
python -m SimpleHTTPServer
# visit http://localhost:8000/

그러면 다음과 같은 화면을 볼 수 있어야 합니다.

simple ES6 website

Babel 과 Broccoli 를 더 재밌게 쓰기

위의 2번째 예제를 보면 Babel 로 할 수 있는 많은 일들을 상상할 수 있습니다. 잠시 상상을 펼쳐보는 것도 좋을 것 같습니다. 만약 ES6 와 Babel, Broccoli 를 가지고 더 많은 일을 해보고 싶다면, 반드시 이 레파지토리를 체크해봐야 합니다. broccoli-babel-boilerplate 레파지토리입니다. 이 레파지토리 역시 Broccoli+Babel 셋업입니다. 적어도 2가지 시도가 더해져 있습니다. 이 보일러플레이트는 모듈, import, 그리고 단위 테스트를 다룹니다.

그런 설정이 동작하는 예제를 여기 es6-modules 에서 시도해볼 수 있습니다. 모든 마법은 Brocfile 에 존재합니다. 그리고 그 마법이라는 것이 우리가 이미 해본 것과 유사합니다.


살펴본 것처럼, Babel 과 Broccoli 는 ES6 기능들을 지금 당장 웹사이트에서 정말 쓸 수 있는 실용적인 것으로 만듭니다. 이번 글을 작성하느라 수고한 Gastón I. Silva 에게 감사를 전합니다!

지금까지 ES6 In Depth 시리즈를 통해 기초적인 내용을 다뤘습니다. 하지만 ES6 의 가장 강력한 기능을 아직 다루지 않았습니다. 다음 글을 기다려 주세요.

Jason Orendorff

ES6 In Depth Editor

이 글은 가 쓴 ES6 In Depth: Using ES6 today with Babel and Broccoli의 한국어 번역본입니다.

작성자: ingeeKim

"누구에게나 평등하고 자유로운 웹"에 공감하는 직장인.

ingeeKim가 작성한 문서들…


댓글이 없습니다.

댓글 쓰기