번역 - Modern JavaScript Explained For Dinosaurs

이 post는 아래 article을 번역한 것입니다.

Modern JavaScript Explained For Dinosaurs

  • 영문명사는 가능한 영문 그대로 썼습니다.
  • 비표준어를 다소 사용했습니다.
  • 저는 전문 번역가가 아닙니다. 오역이 있을 수 있으니 의심 스러운 부분은 원문을 참고하세요!
  • 원문에 있는 링크가 빠졌다거나 오탈자가 있으면 제보 부탁드립니다. 정말 감사하겠습니다.

그럼 시작하겠습니다~~!


구닥다리 공룡을 위한 오늘날의 JavaScript

  • 원제: Modern JavaScript Explained For Dinosaurs
  • 저자: Peter Jang
  • 역자: march23hare

1
Images from Dinosaur Comics by Ryan North

초창기부터 JavaScript에 몸담아 오지 않았다면 오늘날 JavaScript를 공부하는 것은 빡세요. 생태계가 너무 빠르게 성장하고 변하는 바람에 다양한 도구로 해결되는 문제 자체가 이해하기 어려워 졌습니다. 저는 1998년 부터 programming을 했지만 JavaScript를 진지하게 배우기 시작한건 2014년 부터 였습니다. 그 당시 들여다 본, 우연히 접하게 된 Browserify의 tagline을 전 아직도 기억합니다.

“Browserify는 browser에서 require(‘modules’)를 사용하여 모든 의존성을 통합할 수 있습니다.”

전 이 문장의 한 단어도 이해할 수 없었고, 이게 개발자로서 나에게 무슨 도움이 될런지 알고자 발버둥 쳤습니다.

이 article의 목표는 JavaScript 도구들이 2017년 현재까지 진화해 온 역사적 문맥을 알려드리는데에 있습니다. 먼저 구닥다리 공룡같은 예제 website-단순한 HTML, JavaScript 외에 어떤 도구도 사용되지 않는-를 만들어 볼겁니다. 그리고나서 차차 구식 frontend 개발 생태계의 문제점들을 해결하는 도구들을 하나씩 소개할 겁니다. 이 역사적 문맥을 통해 여러분들의 학습은 이전보다 더 좋아질 것이고 끊임없이 변화하고 전진하는 JavaScript 지형에 적응 할 수 있을 겁니다.

구식 JavaScript 사용법

자, 수동으로 file들을 down받고 연결하는 구식 website로 시작해 봅시다. 이 website는 HTML과 JavaScript를 사용합니다. 여기 하나의 JavaScript file이 연결된 간단한 index.html이 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="index.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html>

<script src="index.js"></script> 이 행은 index.html과 같은 directory에 있는 별도의 JavaScript file인 index.js를 참조합니다. index.js의 내용은 아래와 같습니다.

1
console.log("Hello from JavaScript!");

이게 여러분이 website를 만드는데 필요한 전부예요! 이제 다른 누군가 작성한 외부 library를 추가한다고 쳐봅시다. moment.js 같은거요. moment.js는 날짜 형식을 인간이 읽기 쉬운 형식으로 바꾸는데에 사용합니다. 예를 들면 아래와 같이 moment 함수를 사용할 수 있습니다.

1
moment().startOf('day').fromNow(); // 20 hours ago

그치만 이건 어디까지나 moment.js를 여러분의 website에 포함시켰단 가정 하입니다. moment.js의 hompage를 방문해 보면 아래와 같은 지시사항을 볼 수 있습니다.

moment instruction

흠, 오른편의 install section에 무언가 많다는걸 볼 수 있네요. 하지만 일단 전부 무시해도 됩니다. index.html이 들어있는 directory에 moment.min.js를 download 하세요. 그럼 아래 code처럼 index.html에 moment.min.js를 추가 시킬 수 있어요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example</title>
<link rel="stylesheet" href="index.css">
<script src="moment.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html>

moment.min.js가 index.js보다 앞서 load 되고 있다는걸 주의하세요. 이 말인 즉, index.js 안에서 moment 함수를 쓸 수 있게 되었다는걸 의미합니다. 아래 code 처럼요:

1
2
3
// index.js
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());

그리고 이게 바로 우리가 website에 JavaScript labrary를 사용하는 (구닥다리)방식입니다. 이해하기 쉽다는 게 이 방식의 장점이죠. 나쁜 점은 library 제작자가 update를 할 때마다 그것의 새 version을 매번 찾아야 하고 download 하기 번거롭다는 겁니다.

JavaScript package manager (npm) 사용하기

2010년 즈음 하여, 중앙 repository로부터 library들을 download 하고 upgrade 해주는 과정을 자동화 해주는, 여러 경쟁적인 JavaScript package manager가 등장했습니다. 2013년 Bower는 거의 이견 없이 최고의 인기 제품 이었습니다만, 2015년 즈음 하여 마침내 npm이 그 인기를 따라잡았습니다. (2016년 말부터 시작 된, yarn이 npm interface 대안으로 나타나긴 했지만 yarn도 여전히 내부적으로는 npm package를 사용하고 있습니다.)

npm은 원래부터 node.js를 위해 특별히 만들어진 package manager라는 점을 염두해 두세요. node.js는 frontend가 아닌 server 위에서 구동되도록 설계된 JavaScript 실행 환경입니다. 이 때문에 npm을, browser에서 실행하려는 JS library들을 위한 frontend JavaScript package manager로 선택했다는 게 꽤 괴팍해 보입니다. server 환경에서 쓰던걸 frontend 환경에서 쓰려는 것이니까요.

Note: package manager를 쓴다는 것은 보통 command line 사용과 연관있습니다. 구닥다리 frontend 개발 방식에는 전혀 필요 없던 부분이었죠. 만약 한번도 command line을 써본적이 없다면 이 tutorial을 훑어 보시길 권합니다. 좋든 나쁘든 command line은 오늘날의 frontend 개발에 있어 중요한 부분이 되었습니다 (그리고 다른 개발 영역으로의 훌륭한 열린 문이 될겁니다).

자 이제 npm을 사용해서 어떻게 moment.js package를 자동으로 설치하는지 살펴 보도록 하죠. 구닥다리 방식의 단점으로 지적되었던 번거롭게 매번 수동으로 다운 받는 대신에 말이죠. 만약 node.js가 설치되어 있다면 npm도 이미 설치 된 것입니다. 그러면 command line으로 index.html이 있는 directory로 이동한 후 다음 명령어를 입력 할수 있습니다.

1
$ npm init

이 명령어를 입력하면 몇가지 질의응답 과정이 이어집니다. 응답은 기본 값으로 충분하니 모든 질문에 enter키를 눌러주세요. 그러면 package.json 이라는 새로운 file 하나가 생성 됩니다. 이 녀석은 configuration(설정, 구성) file로 npm이 이 project에 대한 모든 정보를 저장하는데 사용됩니다. 기본적인 package.json의 내용은 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
{
"name": "your-project-name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

이제 moment.js JavaScript package를 설치하기 위해서 moment.js의 hompage에 안내되어 있던 지시사항을 따라 할 수 있습니다. 아래의 명령어를 command line에 입력하세요:

1
$ npm install moment --save

이 명령은 두가지 일을 수행합니다. 첫째로 moment.js package의 모든 code를 내려받아 node_modules라는 folder에 저장합니다. 둘째로 package.json file을 아래와 같이 고쳐 project 의존성으로써 moment.js를 지속적으로 추적하도록 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
}
}

이는 나중에 다른이와 project를 공유할때 유용합니다. 대개의 경우 용량이 매우 클 node_modules folder를 직접 공유하는 대신 package.js file 하나만 공유하면 다른 개발자들이 필요한 package들을 자동으로 설치 할 수 있습니다. npm install이라는 명령어 하나로 말이죠.

이로써 우리의 project도 더는 수동으로 moment.js를 내려받을 필요가 없어졌습니다. npm을 사용하여 자동으로 download 받고 update 받을 수 있습니다. node_modules folder 안을 살펴보면 moment.min.js file이 node_modules/moment/min directory안에 있습니다. npm으로 download 한 moment.min.js를 index.html에 연결 할 수 있다는 의미지요.

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="node_modules/moment/min/moment.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html>

그래서 정리하자면, 좋은점은 command line으로 npm을 사용해 우리의 package들을 download하고 update 할 수 있다는 겁니다. 이 상황에서 나쁜 점은 package가 실제로 담겨 있는 node_modules folder의 깊은 곳 까지 파고들어야지만 수동으로 HTML에 불러올 수 있다는 것이죠. 이건 꽤 불편합니다. 그래서 다음으로 이 과정을 자동으로 처리해 주는 방법을 살펴볼 겁니다.

2

JavaScript module bundler (webpack) 사용하기

대부분의 programming 언어들이 하나의 code에서 다른 code를 불러들이는 방법을 제공합니다. 그러나 JavaScript는 이런 기능이 없습니다. 왜냐하면 JavaScript는 오직 browser 환경에서만 구동되도록 만들어졌고 보안상의 이유로 computer의 file system에는 접근할 수 없기 때문입니다. 그래서 오랜 시간동안 다수의 file로 JavaScript code를 구성하는 일은 각 file을 전역 변수로 할당하여 공유해야만 했습니다.

우리가 앞서 한 행위도 이겁니다. 예를들어 전체 moment.min.js file은 HTML로 load 됩니다. 그 안에 정의 되어 있는 변수 moment는 전역 변수가 됩니다. 이 후에 load 되는 모든 file에서 이 전역 변수를 사용 할 수 있습니다. 접속 권한이 있는지 없는지는 상관 없이요.

2009년, CommonJS라는 이름의 project가 시작되었습니다. 이것의 목표는 browser 외의 JavaScript 생태계를 만드는 것이었죠. CommonJS의 대부분은 module에 대한 명세였습니다. 이 명세로 마침내 JavaScript가 다른 대부분의 programming 언어들 처럼 file을 통해 code를 들여오고(import) 내보낼(export) 수 있게 되었죠. 전역 변수에 할당하는 방법 대신 말입니다. 이 CommonJS module의 구현체중 가장 유명한 것이 바로 node.js 입니다.

logo node.js

앞서 이야기 했던 것 처럼 node.js는 server에서 구동되도록 설계된 JavaScript 실행 환경 입니다. 아래에 초기의 node.js module 사용 예시가 있습니다. moment.min.js를 HTML script tag로 불러오는 것 대신 JavaScript file을 직접 JavaScript 안에 불러올 수 있습니다:

1
2
3
4
5
// index.js
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());

다시 말씀드리지만 이건 node.js에서 작동하는 module loading 방식입니다. node.js가 computer의 file system에 접근 가능한 server side 언어이기에 가능한 것이죠. node.js는 또 각 npm module이 어느 경로에 위치하는지를 알고 있습니다. 이 덕분에 require('./node_modules/moment/min/moment.min.js)라고 쓰는 대신 require('moment')라고 쓸 수 있죠. 달달하쥬?

node.js가 이렇게 좋습니다만, 위 code를 browser 상에서 실행 하려고 하면 require가 정의되지 않았다는 error가 뜹니다. browser는 file system에 접근할 권한이 없습니다. 따라서 이 방식으로 module을 불러 오고자 한다면 아주 까다롭습니다. file들을 불러오는 것이 (실행속도를 느리게 하는)동기적인 것이든 (timing 문제가 있는)비동기적인 것이든 동적으로 수행되어야만 합니다. (역주: require와 같은 module loading 구문은 실행시에 그 구문이 load한 다른 file의 code로 대치된다-이렇게 이해해도 큰 무리는 없다. 만약, JavaScript file이 browser에서 실행되기 이전에 require구문의 대치가 동적으로 완료된다면 module loading으로써도 유의미한 것이고 browser 오류도 없지 않겠느냐는 말이다.)

이 부분이 바로 module bundler가 필요해 지는 지점입니다. JavaScript module bundler는 file system에 접근 할 수있는 build 단계에서 위와 같은 문제를 해결하여 브라우저와 호환되는 최종 결과물(파일 시스템에 액세스 할 필요가 없음)을 만들어내는 도구입니다. 우리의 경우에는 모든 require 구문(browser JavaScript 문법에는 맞지 않음)을 찾아내어 이를 요구된(required) file들의 실제 내용으로 대치시켜주는 module bundler가 필요합니다. 이때 최종 결과물은 하나로 묶인(bundled) 단일 JavaScript file(require 구문이 없음)인 것이죠.

가장 인기 있었던 module bundler는 Browserify로, 2011년에 출시 되었으며 node.js 방식의 require 구문을 frontend에 사용하는데 있어 선구자적 역할을 했습니다. 근본적으로 npm이 frontend package manager가 될 수 있게 해주었죠. 2015년 즘엔 webpack이 등장하여 더 보편적으로 사용되는 module bundler가 됩니다(webpack의 이점을 몽땅 활용 가능한 frontend framework인 React의 인기가 치솟게 됩니다). (역주: 엄밀히 말하면 React는 framework가 아니라 library 입니다.)

이제 어떻게 webpack을 써야 위의 require('moment')예제를 browser에서 돌릴 수 있을지 살펴봅시다. 먼저 webpack을 project에 추가해야 겠지요. webpack 자체는 그저 npm package 이고, 따라서 아래와 같은 command line으로 설치 할 수 있습니다:

1
$ npm install webpack --save-dev

--save-dev 인수로 설치하면 package가 개발용 의존으로 저장 된다는 점을 알아두세요. 이 말은 이 package가 개발 환경에서만 쓰이고 server에 올라간 제품 상태에선 사용하지 않을 것이란 의미입니다. 이 점은 package.json에도 자동으로 반영됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"webpack": "^3.7.1"
}
}

webpack이 package 중 하나로써 node_modules folder에 설치가 되었습니다. command line에 아래처럼 입력함으로써 webpack을 사용할 수 있습니다.

1
$ ./node_modules/.bin/webpack index.js bundle.js

이 명령은 node_modules에 설치되어 있는 webpack 도구를 실행킵니다. 실행된 도구는 index.js file을 읽기 시작하여 require구문을 찾고 이를 다른 file의 code로 대치시킨 최종 결과물로써 bundle.js를 출력합니다. 이 말은 우리의 예제 HTML에서 더 이상 (require 구문이 있는)index.js를 쓸 수 없게 된다는 말입니다. 대신 bundle.js를 써야합니다.

1
2
3
4
5
6
7
8
9
10
11
12
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript Example</title>
<script src="bundle.js"></script>
</head>
<body>
<h1>Hello from HTML!</h1>
</body>
</html>

browser를 새로고침 해보면 이전과 마찬가지로 모든 것이 정상 작동함을 볼 수 있습니다.

index.js이 수정 될 때마다 webpack 명령을 실행해야 함을 상기하세요. 이 점은 귀찮은 데다가 webpack의 고급 기능을 사용하게 되면 더 귀찮아 집니다. 예를 들어, transpiled code의 오류를 original code에서 고칠 때 도움을 주는 source map을 생성하는 고급 기능 말이지요. 이 귀찮은 점을 해결하기 위해 webpack은 config(설정) file로부터 옵션들을 읽을 수 있습니다. 이 config file은 project의 root folder에 위치하는 webpack.config.js입니다. 우리 예제의 경우에는 아래와 같은 내용이면 됩니다.

1
2
3
4
5
6
7
// webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
}
};

이제 매 번 index.js가 바뀔 때 마다 아래와 같은 명령어를 사용할 수 있습니다.

1
$ ./node_modules/.bin/webpack

index.jsbundle.js를 지정해 주지 않는 것이 보이죠? webpack.config.js에 설정한 option들을 webpack이 알아서 읽기 때문입니다. 이게 더 좋은 방법이긴 합니다. 그러나 명령어를 매 번 입력해야 한다는 건 여전히 귀찮습니다. 이걸 좀 더 편리하게 만들어 보도록 하죠.

전반적으로 시원찮아 보일 수도 있습니다만, 이러한 work flow엔 상당한 이점이 있습니다. 더 이상 전역 변수로 외부 script를 불러들일 필요가 없지요. 어느 JavaScript library도 HTML tag 대신 JavaScript의 require 구문을 통해 추가될 것입니다. 단일 JavaScript 묶음 file을 갖는 것은 성능면에서도 더 좋습니다. 그리고 이제 또 다른 강력한 기능들을 개발 workflow에 추가 할 수 있는 build 단계가 추가 되었습니다.

3

언어의 새로운 기능을 사용하기 위한 code transpiling (babel)

code를 transpile 한다는 것은 어떤 한 언어로 작성 된 code를 비슷하지만 다른 언어로 변환 시켜 준다는 것입니다. 이는 frontend 개발에 있어 중요한 부분입니다. browser는 언어의 새로운 기능을 추가 지원 해주는 데에 늘 늦장을 부리기 때문에 새 언어의 실험적인 기능은 browser에 호환 되는 언어로 transpile 해야 합니다.

CSS로 말하자면 (Sass)[http://sass-lang.com/], (Less)[http://lesscss.org/], 그리고 (Styleus)[http://stylus-lang.com/]등을 말할 수 있습니다. JavaScript로 치자면 한 때 가장 인기있었던 transpiler는 (CoffeeScript)[http://coffeescript.org/] (2010년즘 출시됨) 이었고, 현재는 대부분의 사람들이 babel 또는 (TypeScript)[http://www.typescriptlang.org/] 를 사용합니다. CoffeeScript는 임의의 괄호, 의미있는 공백등을 적용함으로써 JavaScript를 발전시는데 초점을 맞추고 있습니다. Babel은 새로운 언어는 아닙니다만 아직 모든 browser에서 사용할 수는 없는 다음 세대 JavaScript (ES2015 이후)의 기능을 오래되고 호환 가능한 JavaScript (ES5)로 transpile 해주는 transpiler입니다. TypeScript는 기본적으로 다음 세대 JavaScript와 같지만 임의의 정적 형지정(typing)을 추가하는 언어입니다. 많은 사람들이 babel을 선택했습니다. vanilla JavaScript에 가장 가깝기 때문입니다.

babel을 어떻게 사용하는지 이미 만들어 둔 webpack build 과정과 함께 그 예시를 보도록 하죠. 먼저 babel을 우리의 project에 command line으로 설치할 겁니다. babel은 npm pacakge 입니다:

1
$ npm install babel-core babel-preset-env babel-loader --save-dev

개발용 의존성으로 3가지 별도의 package를 설치하고 있는 것에 주목하세요. babe-core는 babel의 핵심부 입니다. babel-preset-env는 어떠한 JavaScript 새 기능을 transpile할지에 대한 사전정의(preset)입니다. 그리고 babel-loader는 babel이 webpack과 함께 일 할수 있게 해주는 package입니다. 이제 webpack.config.js를 아래와 같이 수정하여 babel-loader를 사용할 수 있습니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
}
};

문법이 번잡스러울수도 있습니다 (다행이도 그렇게 자주 수정할것은 아닙니다). 기본적으로 이 설정을 통해 webpack에게 모든 .js file을 지켜보도록 시키고 있습니다 (node_modules의 항목은 제외합니다). 그리고 babel-preset-env로 사전정의 된 babel-loader를 사용하여 babel의 transpile을 적용합니다. webpack 구성법에 대해선 여기에서 더 자세히 알아볼 수 있습니다.

이제 모든게 갖춰졌습니다. ES2015의 기능을 사용할 수 있다는 말입니다! 아래에 ES2015 template 문자열의 예시를 index.js에 보이고 있습니다:

1
2
3
4
5
6
7
8
// index.js
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);

require 대신 ES2015 import 구문을 module loading에 사용할 수 있습니다. 오늘날 많은 codebase에서 볼 수 있는 것이죠.

1
2
3
4
5
6
7
8
// index.js
import moment from 'moment';
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);

이 예에서 import 문법은 require 문법과 많이 다르지 않습니다. 그러나 더욱 진보한 상황에서 전자가 추가적인 유연성을 갖고 있습니다. 아무튼 index.js가 수정되었기 때문에 command line으로 webpack을 다시 구동해줘야 합니다:

1
$ ./node_modules/.bin/webpack

이제 browser에서 index.html을 새로고침 할 수 있습니다. 이 글이 작성되고 있는 시점에 대부분의 modern browser들이 모든 ES2015 기능을 지원하고 있습니다. 따라서 엄밀히 말하면 babel이 이 작업을 해줬다곤 말하기 힘듭니다. IE9같은 구식 browser에서 시험해 보세요. 아니면 bundle.js를 열어서 transpile된 code를 살펴보세요.

1
2
3
4
// bundle.js
// ...
console.log('Hello ' + name + ', how are you ' + time + '?');
// ...

browser 호환성을 맞춰주기 위해 ES2015 template 문자열이 일반적인 JavaScript 문자열 연쇄로 transplile 된 모습을 볼 수 있습니다. 이런 예시가 그닥 흥미롭지 않을 수도 있지만, transpile code의 능력은 아주 강력한 것입니다. 오늘 당장 사용하여 더 좋은 code를 작성할 수 있게 해줄 async/await같은 언어의 재미난 기능이 곧 다가올 것입니다. 그리고 transpile이 지겹고 고통스러워 보일지도 모르지만, 사람들이 내일의 기능을 오늘 시험해 봄으로써 지난 몇년간 언어의 극적인 발전을 이끌어 왔습니다.

이제 거의 다 됐습니다. 그러나 여전히 말끔하지 못한 모서리가 우리의 workflow 안에 남아있습니다. 만약 성능을 고려한다면, bundle file을 축소(minifying) 시킬 필요가 있습니다. 이건 쉬울겁니다. 우리가 이미 build 과정을 결합시켜 놨으니까요. 또 지금은 JavaScript가 변경될 때마다 webpack command를 재실행 시켜줘야 합니다. 그래서 다음 단계에서는 이를 해결하기 위한 편리한 도구들을 살펴 볼 겁니다.

task runner 사용하기 (npm scripts)

이제 build 단계를 사용하여 JavaScript module로 일하고 있으므로 task runner를 사용 해야 할 것 같습니다. 각기 다른 부분의 build process를 자동화 해주는 도구입니다. frontend 개발에 있어 task란 code 압축(minify), image 최적화, test 실행 등을 말합니다.

2013년, Grunt가 가장 인기 있는 frontend task runner였고, Gulp가 뒤를 이었습니다. 둘 모두 다른 command line 도구를 두르고 있는 pulgin에 기대고 있습니다. 요즘에 가장 인기 있는 선택으로 보이는 것은 npm package manager 자체의 scripting 가용성을 이용하는 것으로 plugin을 사용하지 않고 다른 command line 도구와 직접 일합니다.

자 npm script를 작성해서 webpack 사용을 좀 더 쉽게 만들어 봅시다. 이 작업은 `package.json을 조금 바꾸게 됩니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --progress -p",
"watch": "webpack --progress --watch"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"webpack": "^3.7.1"
}
}

buildwatch라는 두 새로운 script가 추가되었습니다. build script를 구동하려면 command line을 아래와 같이 입력하면 됩니다:

1
$ npm run build

이를 통해 webpack(이전에 만들어 둔 webpack.config.js를 설정으로 사용함)이 구동되는데 --progress option을 주면 진행 정도를 백분율로 보여주며 -p option을 주면 제품용 code를 압축하게 됩니다. watch script를 구동하려면 아래와 같습니다:

1
$ npm run watch

이것은 --watch option을 사용하고 있기 때문에 JavaScript가 수정될 때마다 자동으로 webpack을 재실행 해줍니다. 개발에 있어선 최고죠.

package.json의 script에 webpack의 완전한 전체 경로 ./node_modules/.bin/webpack가 없어도 구동할 수 있다는 점을 주목하세요. node.js가 각각의 npm module 경로를 알고 있기 때문입니다. 이건 정말 좋습니다! 심지어 webpack-dev-server를 설치하면 더 좋아집니다. 이 녀석은 실시간 다시 불러오기(live reloading) 기능과 함께 단순한 web server를 제공하는 별도의 도구입니다. 개발 의존성으로 설치하려면 다음과 같습니다:

1
$ npm install webpack-dev-server --save-dev

그러면 package.json에 다음과 같이 npm script가 추가됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"name": "modern-javascript-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --progress -p",
"watch": "webpack --progress --watch",
"server": "webpack-dev-server --open"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"webpack": "^3.7.1"
}
}

이제 개발용 server를 아래의 command로 실행 시킬 수 있습니다:

1
$ npm run server

이걸로 index.html website가 browser에 localhost:8080(기본값)으로써 자동으로 열립니다. 언제든 index.js의 JavaScript를 수정하면, webpack-dev-server는 bundled된 JavaScript를 다시 build 하고 browser를 자동으로 새로고침 할 것입니다. 놀라울 정도로 시간을 절약 시켜 주죠. 새로 바뀐 것을 눈으로 확인하려고 지리하게 code와 browser를 왔다 갔다 하며 맥이 끊기는 대신 계속해서 code에만 집중 할 수 있게 해줍니다.

이건 단지 겉 핥기 수준입니다. 훨씬 많은 option들이 webpack과 webpack-dev-server(이 녀석에 대해 더 자세히 알고 싶다면 이곳으로…)에 존재합니다. 당연히 다른 task를 위해 npm script를 작성할 수 있습니다. Sass를 CSS로 변환하는 일, image를 압축 하는 일, test를 실행하는 일-어떤 일이든 command line tool만 있으면 됩니다. 또한 엄청난 고급 option과 trick들이 npm script 자체에 있습니다-Kate Hudson의 이 담화가 좋은 출발점이 될 겁니다:

결론

네 이게 바로 modern JavaScript 입니다. 단순한 HTML과 JS로부터 시작해 package manager를 써서 3rd party package들을 자동으로 download하고, module bundler를 사용해서 단일 script file을 만들어내고, transplier를 이용해 미래의 JavaScript 기능들을 사용했으며, 그리고 task runner로 다른 부분의 build 과정을 자동화 했습니다. 초보자에겐 버거울 정도로 많은 것들을 다루긴 했습니다. 확실히 web 개발은 programing에 막 들어온 사람들에게 훌륭한 시작점이었습니다. 쉽게 배워서 돌려볼 수 있었으니까요; 요즈음은 이게 꽤 위축 될 수도 있겠네요. 왜냐하면, 특히 그 다양한 도구와 급변하는 성질 때문에 말입니다.

허나 보기만큼 나쁘진 않습니다. 특히 frontend와 함께 작업할 실행가능한 방법으로써 node 생태계를 적용하면 상황은 꽤 안정화 됩니다. 이건 멋지고 일관된 방법입니다. npm을 package manager로 사용하는 것, node requireimport 구문을 module을 위해 사용하는 것, 그리고 npm script로 task를 수행하는 것들 말입니다. 1, 2년 전에 비하면 정말이지 엄청나게 편해진 workflow 입니다.

심지어 초심자들에게나 숙련자들에게나 마찬가지로 더욱 좋은 점은 요즘엔 framework가 과정을 더 쉽게 시작할 수 있도록 해주는 도구를 제공한다는 것입니다. Ember는 Angular의 angular-cli로부터 영향을 받은 ember-cli를 갖고 있고, React는 create-react-app, Vue는 vue-cli등을 갖고 있습니다. 이 모든 도구들은 project를 구성해줍니다. project에 필요한 모든것과 함께 말이죠. 우리가 해야할 일은 그저 code를 작성하기 시작하는 것 밖에 없어요. 그러나 이런 도구들은 마법이 아닙니다. 이 도구들은 일관되고 잘 동작하는 방식으로 모든 것을 구성할 뿐입니다. 종종 별도의 webpack, babel, 기타 등등의 설정이 필요한 부분을 마주치게 될지도 모릅니다. 그래서 이 article에서 다룬 각 조각들이 뭘 하는지 이해하는 일은 여전이 아주 중요합니다.

modern JavaScript는 분명 계속되는 변화와 급격한 진화 속도로 좌절감을 줄 수도 있습니다. 그러나 지금 이 순간이 바퀴를 다시 발명하는 것 같아 보이더라도 JavaScript의 급격한 진화는 hot-reloading, 실시간 문법 검사, time-travel debugging과 같은 혁신에 도움을 주고 있습니다. 개발자에겐 흥미로운 시간입니다. 저는 이 정보가 당신의 여행에 있어 도움이 되기를 바랍니다!

4

@ryanqnorthDinosaur Comics에 특별한 감사를 전합니다. 2003년(공룡들이 web을 지배하던 그때)부터 양질의 부조리 풍자를 제공해 줬습니다.