<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="ko"><title type="text">Firejune</title><link rel="alternate" type="text/html" href="http://firejune.com/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/firejune" /><subtitle type="html">이 사이트는 자바스크립트 프로그래밍을 중심으로 웹 애플리케이션 개발에 대한 내용을 주로 다룹니다.</subtitle><logo>http://m.firejune.com/attach/image/272778.jpeg</logo><updated>1970-01-01T00:00:00+00:00</updated><generator>TT Firejune</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/firejune" /><feedburner:info uri="firejune" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><id>http://firejune.com/</id><entry><title type="text">초보자용 Webpack 튜토리얼 파트1 - Webpack 입문</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/NCV_D1TeU5A/1798" /><category term="웹개발" /><category term="자료" /><category term="Webpack" /><category term="자바스크립트" /><category term="프로젝트" /><category term="HMR" /><category term="LESS" /><category term="SCSS" /><category term="CSS" /><category term="HTML" /><category term="ES6" /><category term="ES7" /><category term="React" /><category term="번역" /><category term="튜토리얼" /><author><name>파이어준</name></author><updated>2016-02-16T00:21:25-08:00</updated><id>http://firejune.com/1798</id><content type="html">&lt;p&gt;&lt;a href="https://github.com/AriaFallah"&gt;Aria Fallah&lt;/a&gt;씨는 &lt;a href="https://webpack.github.io/"&gt;Webpack&lt;/a&gt;을 시작하기가 쉽지만은 않았다고 합니다. 그래서 친절하고 개괄적인 초보자용 &lt;a href="https://github.com/AriaFallah/WebpackTutorial/tree/master/part1"&gt;Webpack 입문서&lt;/a&gt;를 만들었습니다. 그는 이 튜토리얼을 통해 Webpack의 사용법을 쉽게 배울 수 있기를 바란다고 했습니다.&lt;/p&gt;
&lt;h3 id="webpack을-왜-사용하나요"&gt;&lt;a href="#webpack을-왜-사용하나요"&gt;1-1&lt;/a&gt;. Webpack을 왜 사용하나요?&lt;/h3&gt;

&lt;p&gt;여기에 Webpack을 사용해야 할 몇 가지 현실적인 이유가 있습니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;하나의 파일로 js 파일을 번들할 수 있습니다.&lt;/li&gt;
&lt;li&gt;프론트엔드 코드에 npm 패키지를 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;ES6/ES7 자바스크립트 코드를 작성할 수 있습니다. (Babel을 이용하여)&lt;/li&gt;
&lt;li&gt;코드를 압축 또는 최적화할 수 있습니다.&lt;/li&gt;
&lt;li&gt;LESS/SCSS를 CSS로 돌릴 수 있습니다.&lt;/li&gt;
&lt;li&gt;HMR(Hot Module Replacement)을 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;자바스크립트로 모든 유형의 파일을 포함할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이 글에서 다루지 못한 아주 많은 고급기능이 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;왜 이러한 기능이 필요한가요?&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;js 파일 번들 - 자바스크립트를 모듈로 작성할 수 있습니다, 그래서 각각의 파일에 대해서 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;
태그를 별도로 작성할 필요가 없습니다. (상황에 따라서 둘 이상의 js 파일이 필요한 경우 구성 가능함)&lt;/li&gt;
&lt;li&gt;npm 패키지 사용 - npm은 인터넷상에서 오픈소스 코드의 커다란 생태계입니다.
npm 코드를 저장할 기회가 주어지며, 원하는 프론트엔드 패키지를 가져다 쓸 수 있습니다.&lt;/li&gt;
&lt;li&gt;ES6/ES7 - 많은 기능을 추가되어 더 강력하고 더 쉽게 자바스크립트를 작성할 수 있습니다.
&lt;a href="https://github.com/DrkSephy/es6-cheatsheet"&gt;여기에 소개하는 글&lt;/a&gt;이 있습니다.&lt;/li&gt;
&lt;li&gt;코드 압축/최적화 - 배포되는 파일의 크기를 줄입니다. 페이지 로딩이 빨라지는 등의 장점을 포함합니다.&lt;/li&gt;
&lt;li&gt;LESS/SCSS를 CSS로 돌리기 - CSS를 작성하는 더 좋은 방법입니다.
&lt;a href="http://alistapart.com/article/why-sass"&gt;여기에 소개하는 글&lt;/a&gt;이 있습니다.&lt;/li&gt;
&lt;li&gt;HMR 사용 - 생산성이 향상됩니다. 코드를 저장할 때 마다 페이지의 리프레시가 자동으로 이루어집니다.
코드를 작성하는 동안 페이지의 상태를 최신으로 유지해야 하는 경우 정말 편리합니다.&lt;/li&gt;
&lt;li&gt;자바스크립트로 모든 유형의 파일을 포함 - 추가적인 빌드 도구의 수를 줄일 수 있고,
프로그램적으로 파일을 사용 및 수정할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="기본-익히기"&gt;&lt;a href="#기본-익히기"&gt;1-2&lt;/a&gt;. 기본 익히기&lt;/h3&gt;

&lt;h4 id="설치하기"&gt;&lt;a href="#설치하기"&gt;1-2-1&lt;/a&gt;. 설치하기&lt;/h4&gt;

&lt;p&gt;Webpack의 모든 기능을 사용하려면 전역으로 설치해야 합니다:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install -g webpack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그러나 Webpack의 일부 기능이나 최적화 플러그인 정도만 필요한 경우라면 로컬에 설치합니다:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install --save-dev webpack
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;실행 명령&lt;/h5&gt;

&lt;p&gt;Webpack을 실행하려면:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;webpack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Webpack에서 파일의 상태가 변경되면 자동으로 빌드하려는 경우:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;webpack --watch
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;특정한 이름의 사용자가 정의한 Webpack 설정 파일을 사용하려면:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;webpack --config myconfig.js
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="번들하기"&gt;&lt;a href="#번들하기"&gt;1-2-2&lt;/a&gt;. 번들하기&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/example1"&gt;예제 1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://m.firejune.com/attach/0216/160216145047818365/385977.png" width="666" height="333" alt="Official Dependency Tree" style="max-width:100%;"&gt;&lt;/p&gt;

&lt;p&gt;Webpack은 공식적으로 모듈 번들러라고 합니다.
다음의 두 가지 훌륭한 글은 모듈 액세스에 대한 깊이 있는 설명과 명확한 모듈 번들링에 대하여 다루고 있습니다:
&lt;a href="https://medium.freecodecamp.com/javascript-modules-a-beginner-s-guide-783f7d7a5fcc#.jw1txw6uh"&gt;이것&lt;/a&gt;
과 &lt;a href="https://medium.com/@preethikasireddy/javascript-modules-part-2-module-bundling-5020383cf306#.lfnspler2"&gt;이것&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;간단하게 봅시다. 작동시키는 방법은 하나의 파일을 진입점으로 지정하는 것입니다.
이 파일은 트리의 루트가 될 것입니다. 그러면 &lt;code&gt;require&lt;/code&gt;에 의해 다른 파일이 트리에 추가됩니다.
&lt;code&gt;webpack&lt;/code&gt; 명령을 실행하면, 모든 파일과 모듈은 하나의 파일에 번들로 제공됩니다.&lt;/p&gt;

&lt;p&gt;다음은 간단한 예제입니다:&lt;/p&gt;

&lt;p style="background: #fff; border: 1px solid #aaa; padding: 20px; text-align: center"&gt;&lt;img src="http://m.firejune.com/attach/0216/160216145047818365/991919.png" width="603" height="273" alt="Dependency Tree" style="max-width:100%;"&gt;&lt;/p&gt;

&lt;p&gt;이 그림은 다음과 같은 디렉터리 구조를 가진다고 가정합니다:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MyDirectory
|- index.js
|- UIStuff.js
|- APIStuff.js
|- styles.css
|- extraFile.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이것은 파일의 내용입니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// index.js
require('./styles.css')
require('./UIStuff.js')
require('./APIStuff.js')

// UIStuff.js
var React = require('React')
React.createClass({
  // stuff
})

// APIStuff.js
var fetch = require('fetch') // fetch polyfill
fetch('https://google.com')&lt;/code&gt;&lt;/pre&gt;

&lt;pre class="css"&gt;&lt;code&gt;/* styles.css */
body {
  background-color: rgb(200, 56, 97);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;webpack&lt;/code&gt; 명령을 실행하면, 이 트리의 내용을 번들로 얻을 수 있겠지만,
같은 디렉터리에 있는 &lt;code&gt;extraFile.js&lt;/code&gt;는 &lt;code&gt;require&lt;/code&gt;에 참조되지 않았기 때문에
결코 번들의 일부가 되지 않습니다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bundle.js&lt;/code&gt;는 다음과 같이 표시됩니다:&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// contents of styles.css
// contents of UIStuff.js + React
// contents of APIStuff.js + fetch&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;즉, 번들로 제공되는 것들은 파일의 참조를 통한 경우의 것들입니다.&lt;/p&gt;

&lt;h4 id="로더란"&gt;&lt;a href="#로더란"&gt;1-2-3&lt;/a&gt;. 로더란?&lt;/h4&gt;

&lt;p&gt;이미 눈치챘겠지만, 위의 예제에서 이상한 일을 저질렀습니다.
바로 CSS 파일을 자바스크립트 파일에 &lt;code&gt;require&lt;/code&gt;를 사용한 것입니다. 이것은 정말 멋집니다, Webpack의 흥미로운 점은
&lt;code&gt;require&lt;/code&gt;에 자바스크립트 파일 말고도 다른 것을 더 할 수 있다는 것입니다.&lt;/p&gt;

&lt;p&gt;Webpack에는 로더라는 것이 있습니다.
이 로더를 사용하면, &lt;code&gt;require&lt;/code&gt;를 이용하여 &lt;code&gt;.css&lt;/code&gt;와 &lt;code&gt;.html&lt;/code&gt;, &lt;code&gt;.png&lt;/code&gt; 등을 불러올 수 있습니다.&lt;/p&gt;

&lt;p&gt;위 그림의 예를 들어 보겠습니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// index.js
require('./styles.css')&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Webpack의 구성에 &lt;a href="https://github.com/webpack/style-loader"&gt;스타일-로더&lt;/a&gt;와
&lt;a href="https://github.com/webpack/css-loader"&gt;CSS-로더&lt;/a&gt;를 포함하는 경우,
이것은 단지 완전히 유효하지만 않을 뿐, 실제로는 페이지에 CSS를 적용하게 됩니다.&lt;/p&gt;

&lt;p&gt;이것은 Webpack과 함께 사용할 수 있는 수많은 로더들 중 하나의 사용 예제일 뿐입니다.&lt;/p&gt;

&lt;h4 id="플러그인"&gt;&lt;a href="#플러그인"&gt;1-2-4&lt;/a&gt;. 플러그인&lt;/h4&gt;

&lt;p&gt;플러그인은 이름에서 알 수 있듯이, Webpack에 사용할 수 있는 추가 기능입니다.
자주 사용하는 플러그인 중 하나는 &lt;code&gt;UglifyJsPlugin&lt;/code&gt;입니다.
이는 자바스크립트 코드를 압축(minify)해 줍니다. 이 사용법에 대해서는
나중에 다룰 것입니다.&lt;/p&gt;

&lt;h3 id="설정-파일-구성"&gt;&lt;a href="#설정-파일-구성"&gt;1-3&lt;/a&gt;. 설정 파일 구성&lt;/h3&gt;

&lt;p&gt;Webpack은 박스(?) 밖에서 작동하지 않기 때문에 필요에 맞게 작성해야 합니다.
이를 위해 다음과 같은 파일을 생성할 수 있습니다.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;webpack.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이것은 Webpack이 기본적으로 인식하는 파일명입니다.
다른 이름을 사용하려면 해당 파일의 이름을 지정할 수 있는 &lt;code&gt;--config&lt;/code&gt; 플래그를 사용해야 합니다.&lt;/p&gt;

&lt;h4 id="최소한의-예제"&gt;&lt;a href="#최소한의-예제"&gt;1-3-1&lt;/a&gt;. 최소한의 예제&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/example2"&gt;예제 2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;디렉터리 구조는 다음과 같습니다:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MyDirectory
|- dist
|- src
   |- index.js
|- webpack.config.js

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;다음으로 최소한의 Webpack 설정이 있습니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// webpack.config.js
var path = require('path')

module.exports = {
  entry: ['./src/index'], // file extension after index is optional for .js files
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;새롭게 보이는 속성을 각각 살펴봅시다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webpack.github.io/docs/configuration.html#entry"&gt;entry&lt;/a&gt; - 번들의 엔트리 포인트로써 &lt;a href="#%EB%B2%88%EB%93%A4%ED%95%98%EA%B8%B0"&gt;번들하기&lt;/a&gt;
색션에서 이미 논의했습니다. Webpack은 여러 번들을 생성하는 진입점을 허용하기 때문에 배열입니다.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webpack.github.io/docs/configuration.html#output"&gt;output&lt;/a&gt; - Webpack의 최종 결과물이 되는 형태를 명시합니다.

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webpack.github.io/docs/configuration.html#output-path"&gt;path&lt;/a&gt; - 어디에 번들 파일을 위치시킬 것인지를 지정합니다.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webpack.github.io/docs/configuration.html#output-filename"&gt;filename&lt;/a&gt; - 번들 파일의 이름을 지정합니다.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이제 &lt;code&gt;webpack&lt;/code&gt; 명령을 실행하면, dist라는 폴더에 &lt;code&gt;bundle.js&lt;/code&gt; 파일을 생성합니다.&lt;/p&gt;

&lt;h4 id="플러그인-이해"&gt;&lt;a href="#플러그인-이해"&gt;1-3-2&lt;/a&gt;. 플러그인 이해&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/example3"&gt;예제 3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;모든 파일의 번들에 Webpack을 사용했고 모두 합쳐서 900KB 짜리 파일을 얻었다고 가정해 봅시다.
덩치가 큰 문제는 번들 파일의 압축으로 개선될 수 있습니다. 이 작업을 수행하려면 앞서 언급했던
&lt;a href="https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin"&gt;UglifyJsPlugin&lt;/a&gt;라는
플러그인을 사용합니다.&lt;/p&gt;

&lt;p&gt;또한 실제로 플러그인을 사용할 수 있도록 Webpack을 로컬에 설치해야 합니다.&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install --save-dev webpack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 Webpack에서 필요로 하는 코드를 압축할 수 있습니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// webpack.config.js
var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: ['./src/index'],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },

  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        warnings: false,
      },
    })
  ]
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;새롭게 보이는 속성을 각각 살펴봅시다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;plugins - 보유 중인 플러그인의 배열입니다.

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin"&gt;webpack.optimize.UglifyJsPlugin&lt;/a&gt; - 코드를 축소하고 경고 메시지는 표시하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이제, &lt;code&gt;webpack&lt;/code&gt; 명령을 실행하면, &lt;code&gt;UglifyJsPlugin&lt;/code&gt;에 의해 모든 공백을 제거하는 등의 과정을 거쳐
900KB 짜리 파일을 200KB로 줄일 수 있습니다.&lt;/p&gt;

&lt;p&gt;또한 &lt;a href="https://webpack.github.io/docs/list-of-plugins.html#occurrenceorderplugin"&gt;OccurrenceOrderPlugin&lt;/a&gt;을 추가할 수도 있습니다.&lt;/p&gt;

&lt;blockquote&gt;이 플러그인은 발생 횟수에 따라서 모듈 및 청크 id를 할당합니다. 자주 사용되는 id가 낮은(짧은) id를 얻습니다.
이 id는 예측(predictable)이 가능하며, &lt;del&gt;전체 파일 크기를 줄이는데&lt;/del&gt;(역자주: 파일 용량을 줄이는 것과는 무관함) 추천됩니다.&lt;/blockquote&gt;

&lt;p&gt;솔직히 말해서 기반 메커니즘이 어떻게 작동하는지 잘 모르지만,
&lt;a href="https://gist.github.com/sokra/27b24881210b56bbaff7"&gt;Webpack2 베타 버전에는 기본으로 포함&lt;/a&gt; 되어 있다고 합니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// webpack.config.js
var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: ['./src/index'],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        warnings: false,
      },
    }),
    new webpack.optimize.OccurenceOrderPlugin()
  ]
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;여기까지 자바스크립트의 번들을 압축하는 설정을 작성했습니다.
이 번들을 다른 프로젝트의 디렉터리에 붙여넣고 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 태그에 대입할 수 있습니다.
여기에서 &lt;a href="#%EA%B2%B0%EB%A1%A0"&gt;결론&lt;/a&gt;으로 바로 넘어가도 좋습니다. &lt;em&gt;오직 자바스크립트&lt;/em&gt; 에 대한
기본적인 Webpack 사용법만 필요하다면 말이죠.&lt;/p&gt;

&lt;h3 id="조금-더-복잡한-예제"&gt;&lt;a href="#조금-더-복잡한-예제"&gt;1-4&lt;/a&gt;. 조금 더 복잡한 예제&lt;/h3&gt;

&lt;p&gt;추가적으로, Webpack은 자바스크립트에 관련한 단순 작업보다 더 많은 일을 할 수 있으므로, 수동으로 복사-붙여넣기 하는 일을 없애고 Webpack으로 전체 프로젝트를 관리할 수 있습니다.&lt;/p&gt;

&lt;p&gt;다음 섹션에서는, Webpack을 사용하여 아주 간단한 웹사이트를 만들 것입니다.
예제를 수행하고자 하는 경우, 다음과 같은 구조의 디렉터리를 생성하세요.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MyDirectory
|- dist
|- src
   |- index.js
   |- index.html
   |- styles.css
|- package.json
|- webpack.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;학습 내용&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="#%EB%A1%9C%EB%8D%94-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0"&gt;로더 이해하기&lt;/a&gt; - 번들에 CSS를 추가할 수 있도록 로더를 추가해 볼 것입니다.&lt;/li&gt;
&lt;li&gt;&lt;a href="#%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0"&gt;플러그인 추가하기&lt;/a&gt; - HTML 파일을 생성하고 사용할 수 있도록 도와주는 플러그인을 추가해 볼 것입니다.&lt;/li&gt;
&lt;li&gt;&lt;a href="#%EA%B0%9C%EB%B0%9C%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0"&gt;개발서버 구성하기&lt;/a&gt; - &lt;code&gt;development&lt;/code&gt;와 &lt;code&gt;production&lt;/code&gt;을 구분한 Webpack의 구성 파일을 분할하고
&lt;code&gt;webpack-dev-server&lt;/code&gt;를 이용하여 HMR을 활성화해 볼 것입니다.&lt;/li&gt;
&lt;li&gt;&lt;a href="#%EC%BD%94%EB%94%A9-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0"&gt;코딩 시작하기&lt;/a&gt; - 실제로 자바스크립트의 일부를 작성해 볼 것입니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id="로더-이해하기"&gt;&lt;a href="#로더-이해하기"&gt;1-4-1&lt;/a&gt;. 로더 이해하기&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/example4"&gt;예제 4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;이전 튜토리얼에서 &lt;a href="#%EB%A1%9C%EB%8D%94"&gt;로더&lt;/a&gt;에 대해 언급했습니다.
이제 자바스크립트가 아닌 파일을 다루어 보기로 하겠습니다.
스타일 로더와 CSS 로더가 필요하게 되었습니다. 먼저 로더를 설치해 봅시다:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install --save-dev style-loader css-loader
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;설치된 CSS 로더를 포함하도록 설정 파일을 조정해 봅시다:&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// webpack.config.js
var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: ['./src/index'],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        warnings: false,
      },
    }),
    new webpack.optimize.OccurenceOrderPlugin()
  ],
  module: {
    loaders: [{
      test: /\.css$/,
      loaders: ['style', 'css']
    }]
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;새롭게 보이는 속성을 각각 살펴봅시다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://webpack.github.io/docs/configuration.html#module"&gt;module&lt;/a&gt; - 이 옵션은 파일에 영향을 줍니다.

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://webpack.github.io/docs/configuration.html#module-loaders"&gt;loaders&lt;/a&gt; - 애플리케이션에서 사용할 로더를 배열로 지정합니다.

&lt;ul&gt;
&lt;li&gt;test - 정규식을 이용해서 로더에 사용될 파일을 검출합니다.&lt;/li&gt;
&lt;li&gt;loaders - 일치하는 파일에 사용되는 로더를 호출합니다.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;webpack&lt;/code&gt; 명령을 실행하면, &lt;code&gt;.css&lt;/code&gt;로 확장자를 가진 파일을 &lt;code&gt;require&lt;/code&gt;하는 경우,
이 파일은 &lt;code&gt;style&lt;/code&gt;과 &lt;code&gt;css&lt;/code&gt; 로더에 적용되고, 번들에 CSS가 추가됩니다.&lt;/p&gt;

&lt;p&gt;로더를 가지고 있지 않은 경우, 다음과 같은 오류를 보게 될 것입니다:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;ERROR in ./test.css
Module parse failed: /Users/Developer/workspace/tutorials/webpack/part1/example1/test.css
Line 1: Unexpected token {
You may need an appropriate loader to handle this file type.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;선택사항&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;만약 CSS 대신 SCSS를 사용하는 경우 다음과 같이 실행해야 합니다:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install --save-dev sass-loader node-sass webpack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;그리고 로더는 다음과 같이 작성되어야 합니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;{
  test: /\.scss$/,
  loaders: ["style", "css", "sass"]
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 과정은 LESS도 비슷합니다.&lt;/p&gt;

&lt;p&gt;중요한 것은 지정할 &lt;em&gt;순서&lt;/em&gt;가 존재한다는 것입니다. 위의 예제에서 &lt;code&gt;sass&lt;/code&gt; 로더에 가장 먼저 &lt;code&gt;.scss&lt;/code&gt; 파일을 적용하고,
그다음으로 &lt;code&gt;css&lt;/code&gt; 로더, 마지막에 &lt;code&gt;style&lt;/code&gt; 로더에 적용합니다. 즉, 순서 패턴은 오른쪽에서 왼쪽으로 로더에 적용되는 것입니다.&lt;/p&gt;

&lt;h4 id="플러그인-추가하기"&gt;&lt;a href="#플러그인-추가하기"&gt;1-4-2&lt;/a&gt;. 플러그인 추가하기&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/example5"&gt;예제 5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;이제 웹사이트의 스타일링을 위한 인프라를 구축했으니, 스타일을 적용할 실제 페이지가 필요하게 되었습니다.
HTML 페이지를 생성하거나 기존의 것을 그대로 사용할 수 있는 &lt;a href="https://github.com/ampedandwired/html-webpack-plugin"&gt;html-webpack-plugin&lt;/a&gt;을
이용하여 이 작업을 수행할 수 있습니다. 여기서는 기존의 &lt;code&gt;index.html&lt;/code&gt;를 사용할 것입니다.&lt;/p&gt;

&lt;p&gt;먼저 플러그인을 설치합니다:&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install --save-dev html-webpack-plugin@2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;다음으로 설정을 추가 합니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// webpack.config.js
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: ['./src/index'],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        warnings: false,
      },
    }),
    new webpack.optimize.OccurenceOrderPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  module: {
    loaders: [{
      test: /\.css$/,
      loaders: ['style', 'css']
    }]
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이번에 &lt;code&gt;webpack&lt;/code&gt; 명령을 실행하면, &lt;code&gt;HtmlWebpackPlugin&lt;/code&gt;이 &lt;code&gt;./src/index.html&lt;/code&gt; 지정하기 때문에
&lt;code&gt;./src/index.html&lt;/code&gt; 파일의 내용을 &lt;code&gt;dist&lt;/code&gt; 폴더에 &lt;code&gt;index.html&lt;/code&gt;파일로 생성할 것입니다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.html&lt;/code&gt;의 내용이 비어 있다면 기본 템플릿을 사용하기 때문에 아무런 문제가 없습니다. 하지만 그 내용을 채워 보도록 하겠습니다.&lt;/p&gt;

&lt;pre class="html"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Webpack Tutorial&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;Very Website&amp;lt;/h1&amp;gt;
  &amp;lt;section id="color"&amp;gt;&amp;lt;/section&amp;gt;
  &amp;lt;button id="button"&amp;gt;Such Button&amp;lt;/button&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 &lt;code&gt;bundle.js&lt;/code&gt;의 내용을 HTML의 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 태그에 직접 넣지 않아도 됩니다.
이 플러그인이 자동으로 넣어주기 때문입니다. 만약 스크립트 태그에 직접 넣은 경우라면, 같은 코드를 두 번 로드하게 될 것입니다.&lt;/p&gt;

&lt;p&gt;이 시점에서 &lt;code&gt;styles.css&lt;/code&gt;에 몇 가지 기본 스타일을 추가해 봅시다.&lt;/p&gt;

&lt;pre class="css"&gt;&lt;code&gt;h1 {
  color: rgb(114, 191, 190);
  text-align: center;
}

#color {
  width: 300px;
  height: 300px;
  margin: 0 auto;
}

button {
  cursor: pointer;
  display: block;
  width: 100px;
  outline: 0;
  border: 0;
  margin: 20px auto;
}&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="개발서버-구성하기"&gt;&lt;a href="#개발서버-구성하기"&gt;1-4-3&lt;/a&gt;. 개발서버 구성하기&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/example6"&gt;예제 6&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;이제 실제로 브라우저에서 웹사이트를 볼 수 있도록, 지금까지 만들어진 코드를 제공하는 웹서버가 필요하게 되었습니다.
편리하게도, Webpack은 &lt;code&gt;webpack-dev-server&lt;/code&gt;를 제공합니다. 로컬과 글로벌에 모두 설치합니다.&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install -g webpack-dev-server
npm install --save-dev webpack-dev-server
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;개발 서버는 작업 된 웹사이트를 브라우저에서 바로 확인할 수 있어 매우 유용하며, 더 빠른 개발을 할 수 있습니다.
기본적으로 &lt;code&gt;http://localhost:8080&lt;/code&gt;를 방문할 수 있습니다.
아쉽지만, 핫-리로드 기능은 박스(?) 밖에서는 작동하지 않아서 약간의 추가 구성이 필요합니다.&lt;/p&gt;

&lt;p&gt;이 시점에서 개발용(development)과 제품용(production)을 구분해 보겠습니다.
이 튜토리얼은 간단함을 유지하고 있으므로 큰 차이는 없지만, Webpack의 단적인 기능 설정에 관한 예입니다.
&lt;code&gt;webpack.config.dev.js&lt;/code&gt;와 &lt;code&gt;webpack.config.prod.js&lt;/code&gt;를 호출할 수 있도록 합니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// webpack.config.dev.js
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  devtool: 'cheap-eval-source-map',
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/dev-server',
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  module: {
    loaders: [{
      test: /\.css$/,
      loaders: ['style', 'css']
    }]
  },
  devServer: {
    contentBase: './dist',
    hot: true
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;바뀐점&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;개발 설정에서 지속해서 다시 구축하거나 최적화하는 일은 불필요한 오버헤드가 발생하기 때문에 생략합니다.
그래서 &lt;code&gt;webpack.optimize&lt;/code&gt; 플러그인이 없습니다.&lt;/li&gt;
&lt;li&gt;개발 설정은 개발 서버에 필요한 것만 작성합니다. 더 자세한 내용을
&lt;a href="https://webpack.github.io/docs/webpack-dev-server.html"&gt;여기&lt;/a&gt;에서 볼 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;요약:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;entry: 두 개의 새로운 엔트리 포인트는 HMR이 가능하도록 브라우저에 서버를 연결합니다.&lt;/li&gt;
&lt;li&gt;devServer

&lt;ul&gt;
&lt;li&gt;contentBase: 브라우저에서 접근하는 파일의 위치입니다.&lt;/li&gt;
&lt;li&gt;hot: HMR 사용 여부입니다.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;제품용 설정의 구성은 별로 변경되지 않습니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// webpack.config.prod.js
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  devtool: 'source-map',
  entry: ['./src/index'],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        warnings: false,
      },
    }),
    new webpack.optimize.OccurenceOrderPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  module: {
    loaders: [{
      test: /\.css$/,
      loaders: ['style', 'css']
    }]
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;또한, 개발용 구성과 제품용 구성 모두 새로운 속성을 추가했습니다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webpack.github.io/docs/configuration.html#devtool"&gt;devtool&lt;/a&gt; - 디버깅을 지원합니다.
오류가 발생하는 경우, 크롬 개발자 콘솔과 같은 도구를 이용하여 실수한 위치를 확인하는 데 도움됩니다.
&lt;code&gt;source-map&lt;/code&gt;과 &lt;code&gt;cheap-eval-source-map&lt;/code&gt;의 차이에 대해서는 문서를 읽고도 이해하기가 조금 어려웠지만,
확실히 알 수 있었던 것은 &lt;code&gt;source-map&lt;/code&gt;이 제품용 모드에서 오버헤드가 많다는 점과,
&lt;code&gt;cheap-eval-source-map&lt;/code&gt;이 더 작은 오버헤드를 가지며, 이것은 단지 개발을 위한 것이라는 점입니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;개발 서버는 다음과 같이 실행합니다.&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;webpack-dev-server --config webpack.config.dev.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;제품용 코드를 구축하기 위해서는 다음과 같이 실행합니다.&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;webpack --config webpack.config.prod.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이와 같은 명령을 매번 입력하지 않아도 되도록 &lt;code&gt;package.json&lt;/code&gt;에 약간의 기능을 작성하는 것으로
더 간단하게 명령을 수행할 수 있습니다.&lt;/p&gt;

&lt;p&gt;설정에 &lt;code&gt;scripts&lt;/code&gt; 속성을 추가합니다.&lt;/p&gt;

&lt;pre class="json"&gt;&lt;code&gt;// package.json
{
  //...
  "scripts": {
    "build": "webpack --config webpack.config.prod.js",
    "dev"  : "webpack-dev-server --config webpack.config.dev.js"
  }
  //...
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이제 더욱 간단하게 명령을 실행할 수 있습니다.&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm run build
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;npm run dev&lt;/code&gt; 명령을 실행하고 &lt;code&gt;http://localhost:8080&lt;/code&gt;으로 이동하여 작업 된 웹사이트를 볼 수 있습니다.&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;노트:&lt;/strong&gt; 이 부분을 테스트하는 동안 &lt;code&gt;index.html&lt;/code&gt; 파일을 수정할 때 서버가 핫-리로드 되지 않는 것을 깨달았습니다.
이 문제에 대한 해결책은 &lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/html-reload"&gt;html-reload&lt;/a&gt;에
있습니다. 이것은 Webpack 옵션에 대하여 조금 더 유용한 정보를 얻을 수 있어서 읽어보길 추천합니다. 너무 사소한 내용이고
튜토리얼을 너무 길게 쓰는 느낌이 들었기 때문에 별도로 구분했습니다.&lt;/blockquote&gt;

&lt;h4 id="코딩-시작하기"&gt;&lt;a href="#코딩-시작하기"&gt;1-4-4&lt;/a&gt;. 코딩 시작하기&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/example7"&gt;예제 7&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;많은 사람이 Webpack에 당황해할 것 같습니다. 이유는 실제 작업용 자바스크립트 코드를 작성하기까지
여태것 공부했던 여러 과정을 모두 숙지해야 하기 때문입니다; 다행히도 이 튜토리얼의 클라이막스에 도달했습니다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run dev&lt;/code&gt; 명령 실행 및 &lt;code&gt;http://localhost:8080&lt;/code&gt;를 하지 않았으면 수행합니다.
개발 서버에 핫-리로드를 설정하는 것은 단순히 보기만을 위한 것이 아닙니다.
프로젝트 일부를 편집하고 저장하는 매시간 변경사항을 표시하도록 브라우저는 다시 로드합니다.&lt;/p&gt;

&lt;p&gt;이제 이것을 프론트엔드에서 사용할 수 있는 방법을 보여주기 위해 몇몇 npm 패키지가 필요합니다.&lt;/p&gt;

&lt;pre class="command"&gt;&lt;code&gt;npm install --save pleasejs
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;PleaseJS는 임의 색상 발생기입니다. 특정 버튼을 이용해 div의 색상을 변경해 보겠습니다.&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;// index.js

// Accept hot module reloading
if (module.hot) {
  module.hot.accept()
}

require('./styles.css') // The page is now styled
var Please = require('pleasejs')
var div = document.getElementById('color')
var button = document.getElementById('button')

function changeColor() {
  div.style.backgroundColor = Please.make_color()
}

button.addEventListener('click', changeColor)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;흥미롭게도, &lt;a href="https://webpack.github.io/docs/hot-module-replacement-with-webpack.html#what-is-needed-to-use-it"&gt;HMR이 작동하려면&lt;/a&gt;
다음과 같은 코드를 포함해야 합니다:&lt;/p&gt;

&lt;pre class="javascript"&gt;&lt;code&gt;if (module.hot) {
  module.hot.accept()
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;모듈 또는 상위 모듈에서요.&lt;/p&gt;

&lt;p&gt;이제 마지막입니다!&lt;/p&gt;

&lt;blockquote&gt;&lt;strong&gt;노트:&lt;/strong&gt; 예제를 통해 CSS를 적용하면서 꺼림칙한 부분은,
CSS가 자바스크립트 파일에 있다는 사실입니다.
다른 파일에 CSS를 넣는 방법에 대한 자세한 설명을 별도로 작성했습니다.
&lt;a href="https://github.com/firejune/WebpackTutorial/tree/master/css-extract"&gt;css-extract&lt;/a&gt;를 확인하세요.&lt;/blockquote&gt;

&lt;h3 id="결론"&gt;&lt;a href="#결론"&gt;1-5&lt;/a&gt;. 결론&lt;/h3&gt;

&lt;p&gt;축하합니다! div의 색상을 변경하는 버튼을 만들기까지 모두 학습했습니다! Webpack, 참 훌륭하죠?&lt;/p&gt;

&lt;p&gt;Webpack은 처음 접한 모듈 번들러입니다. 그리고 매우 유용한 도구였습니다.
파트 1에서는 가장 일반적인 사용 사례를 다루었지만, 아직 ES6과 React를 연결하여
사용하는 방법에 대해서는 다루지 않았습니다.&lt;/p&gt;

&lt;p&gt;나중에&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;파트 2에서는 Webpack과 Babel을 함께 사용하여 ES6을 ES5로 transpile 하는 방법을 살펴보겠습니다.&lt;/li&gt;
&lt;li&gt;파트 3에서는 Webpack과 React + Babel을 함께 사용하는 방법을 살펴보겠습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;도움이 되었기를 바랍니다.&lt;/p&gt;


 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1798#p1798"&gt;Comments(3)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1798"&gt;Hits(228312)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/NCV_D1TeU5A" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1798</feedburner:origLink></entry><entry><title type="text">실시간 트랜스파일과 모듈화된 프론트앤드 개발환경</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/qJQZiD2VXJg/1797" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="프로젝트" /><category term="Node.js" /><category term="React" /><category term="ES6" /><category term="Webpack" /><category term="Nightwatch.js" /><category term="테스트 자동화" /><category term="Hello  world!" /><category term="Electron" /><category term="프로젝트" /><category term="Jest" /><category term="ESLint" /><category term="쓰다가 급 귀찮아짐" /><author><name>파이어준</name></author><updated>2016-01-28T05:19:44-08:00</updated><id>http://firejune.com/1797</id><content type="html">&lt;p&gt;최근 들어 더욱 가속화되어 발전하는 자바스크립트 트렌드는 이제 지켜보는 것조차 버겁네요. 최근 신규 프로젝트의 개발환경을 구축하면서 최종적으로 반영된 구성에 관해서 얘기해 보겠습니다. 브라우저에서 Node.js의 모듈 인프라를 이용할 수 있게 하고 실시간으로 트랜스파일(Transpile)이 되도록 개발환경을 구성해 보았습니다. 이 개발환경에 적응하면서 느끼는 것은 "이제 브라우저에서도 자바스크립트로 개발하기가 정말 좋은 세상이 되었구나!" 입니다. 그래서 다른 프로젝트에도 이를 적용할 수 있도록 사내에 전파를 시도하면서 정리했던 내용 일부를 공유합니다.&lt;/p&gt;
&lt;p&gt;우리는 Node.js를 기반으로 한 개발환경에 &lt;a href="http://browserify.org/"&gt;Browserify&lt;/a&gt;나 &lt;a href="https://webpack.github.io/"&gt;Webpack&lt;/a&gt;과 같은 모듈 번들러를 이용하여 자바스크립트로 작성된 기능의 모듈화를 효율적이고 안정적으로 구현한 바 있습니다. 그리고 개발환경에서는 와쳐(Watcher)를 이용하여 파일이 변동되면 번들링한 후 브라우저에서 새로 고침까지 자동으로 되도록 했습니다. &lt;/p&gt;
&lt;p&gt;그러나 답답합니다. 코드량이 많아질수록 눈에 띄게 느려지는 번들링 시간과 마이너 픽스에도 꼭 거쳐야만 하는 이 과정은 이제 지긋지긋합니다. 그냥 예전처럼 브라우저에서 새로 고치면 즉시 그 결과를 확인하고 싶습니다. 그래서 이처럼 불편하고 느려터진 번들링 과정을 개발하는 동안에는 하지 않아도 되도록 하는 것이 컨셉이라 하겠습니다. 즉, 실시간 트랜스파일이 되도록 한다는 것은, &lt;code&gt;import&lt;/code&gt; 또는 &lt;code&gt;require&lt;/code&gt; 문을 브라우저에서 직접 사용할 수 있도록 하는 것입니다. &lt;/p&gt;
&lt;h3&gt;Requirements&lt;/h3&gt;
&lt;p&gt;개발환경을 구성하는 주요한 설치 요구사항입니다. 테스트 라이브러리나 태스크 매니저는 취향에 맞게 사용하면 됩니다.&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;&lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; - 실시간으로 트랜스파일이 가능한 개발환경의 기반이 됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://electron.atom.io/"&gt;Electron&lt;/a&gt; - 구글 크롬 브라우저에서 import(require)문을 직접 이용하는 데 필요합니다.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://babeljs.io/"&gt;Babel&lt;/a&gt; - ES6, ES7, JSX등 차세대 자바스크립트 코드를 구사하기 위해 사용합니다.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://eslint.org/"&gt;ESLint&lt;/a&gt; -  코드 스타일을 안내해주고, 빈번히 발생하는 개발 실수를 줄여줍니다.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://webpack.github.io/"&gt;Webpack&lt;/a&gt; - 프로덕션 빌드 과정에서 모듈 패키징에 사용됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://facebook.github.io/jest/"&gt;Jest&lt;/a&gt; - 자바스크립트 유닛 테스트의 고통을 덜 수 있습니다.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://nightwatchjs.org/"&gt;Nightwatch.js&lt;/a&gt; - 구글 크롬 외 다른 브라우저에서의 작동 여부를 테스트하고 자동화를 위해 사용합니다.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://gruntjs.com/"&gt;Grunt&lt;/a&gt; - 이 모든 과정을 수월하게 관리할 수 있도록 도와주는 태스크 매니저입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;노트: Electron을 개발환경에 적합하다고 판단한 이유가 하나 더 있습니다. Node.js에 내장된 V8 버전(가장 최근에 나온 Node.js 5.1.1의 V8 버전은 4.6) 보다 Electron에 내장된 V8 버전이 4.8로 한참 앞서있기 때문입니다. 또한, &lt;a href="http://v8project.blogspot.kr/"&gt;V8의 블로그&lt;/a&gt;를 보면 ECMAScript 2015(ES6) 스펙을 구현하는 작업이 한창인 것을 알 수 있는데, 안정적인 버전의 크롬이 나오면 1, 2주 내로 Electron에 반영되는 장점은 덤입니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;이제 설정 파일을 작성해 봅시다. React와 jQuery를 사용하는 웹앱을 만든다고 가정합니다. 임의의 프로젝트 폴더에 &lt;code&gt;package.json&lt;/code&gt;을 비롯한 프로젝트를 구성하는 파일과 폴더를 생성합니다.&lt;/p&gt;
&lt;pre class="bash"&gt;&lt;code&gt;$ mkdir my-project
$ cd my-project
$ touch .babelrc
$ touch .eslintrc
$ touch .gitignore
$ touch package.json
$ touch main.js
$ touch index.html
$ touch LICENSE.md
$ touch README.md
$ mkdir scripts
$ mkdir src
$ cd src
$ touch index.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.babelrc&lt;/code&gt; Babel 트랜스파일러에서 사용할 플러그인을 지정합니다. 일반적으로 프리셋(&lt;code&gt;babel-preset-*&lt;/code&gt;)을 사용하지만, 프리셋은 아주 많은 Babel 플러그인들을 포함하고 있어서 실시간으로 트랜스파일 하는 데에는 적합하지 않습니다. 오래 걸리기 때문입니다. 아래의 구성은 Electron 현재 버전 0.36.x(Node.js 5.1.1, 구글 크롬 47)에 내장된 V8 버전 4.7에서 ES2015 스펙이 미구현 되거나 일부만 구현되어 필요하게 된 플러그인을 개별적으로 로드하는 내용입니다.  이렇게 했을 때 프리셋을 사용할 때보다 매 새로 고침 마다 6초에서 4초 정도 시간을 절약할 수 있습니다. 보통 2초 정도면 페이지 로드가 완료됩니다.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;노트: Node.js의 &lt;code&gt;require&lt;/code&gt;를 훅(Hook)하는 &lt;code&gt;babel-register&lt;/code&gt;는 캐시(Cache) 옵션이 기본으로 활성화되어 있습니다. 첫 로딩보다 그 다음 로딩이 훨씬 빠릅니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;pre class="json"&gt;&lt;code&gt;{
  "plugins": [
    "transform-es2015-destructuring",
    "transform-es2015-for-of",
    "transform-es2015-modules-commonjs",
    "transform-es2015-object-super",
    "transform-es2015-parameters",
    "transform-es2015-shorthand-properties",
    "transform-object-rest-spread",
    "transform-react-jsx"
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;노트: &lt;code&gt;sticky-regex&lt;/code&gt;와 &lt;code&gt;unicode-regex&lt;/code&gt; 그리고 &lt;code&gt;typeof-symbol&lt;/code&gt; 플러그인은 테스트를 통과하지 못해 로드하지 않았습니다. 그리고 크롬의 최신 버전인 48(아마도 Electron 0.37.x)에서는 &lt;code&gt;object-super&lt;/code&gt;와 &lt;code&gt;typeof-symbol&lt;/code&gt; 플러그인이 필요하지 않게 됩니다. 지난 26일 릴리즈된 &lt;a href="http://v8project.blogspot.kr/2016/01/v8-release-49.html"&gt;V8 버전 4.9&lt;/a&gt;(크롬 49)에서는 &lt;code&gt;destructuring&lt;/code&gt;, &lt;code&gt;parameters&lt;/code&gt;, &lt;code&gt;sticky-regex&lt;/code&gt;등이 구현되었습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Electron 버전 0.35.x(Node 4.1.1, 크롬 45, v8 버전 4.5)에서는 아래의 3개 플러그인을 추가로 로드하여 ES6을 정상적으로 사용할 수 있습니다. &lt;a href="https://github.com/babel/babel/tree/master/packages"&gt;Babel의 Github 리파지토리&lt;/a&gt;에 있는 380여 개 테스트 케이스를 돌려서 확인했으며 속도 역시 그럭저럭 나옵니다.&lt;/p&gt;
&lt;pre class="js"&gt;&lt;code&gt;    ...
    // Require if using electron version 0.35.x
    "transform-es2015-block-scoping",
    "transform-es2015-classes",
    "transform-es2015-spread"
    ...
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;object-rest-spread&lt;/code&gt;는 ES2015의 공식 스펙은 아니지만 ES7의 꽃이라 할만한 &lt;a href="https://github.com/sebmarkbage/ecmascript-rest-spread"&gt;멋진 연산자&lt;/a&gt;입니다. 이미 널리 사용되고 있으며, ESLint의 &lt;a href="http://eslint.org/docs/1.10.3/rules/object-shorthand"&gt;object-shorthand&lt;/a&gt; 룰에 영향을 받습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;.eslintrc&lt;/code&gt; 파일은 ESLint의 설정입니다. ESLint는 코드를 작성하는 과정에서 빈번하게 발생하는 실수를 예방하고, 엘레강스한 코드 스타일을 추천해 주며, 미래에 발생할 수 있는 잠재적 오류를 수정할 수 있도록 도와줍니다. 제가 사용하는 코드 편집기는 &lt;a href="https://panic.com/coda/"&gt;CODA 2&lt;/a&gt;인데, 여기에 &lt;a href="https://panic.com/coda/plugins.php?id=131"&gt;ESLint JS Validator&lt;/a&gt; 플러그인을 추가하면 아래 설정에 기반을 두어 코드 검증기를 통해 꾸역꾸역 잔소리(?)해 대도록 꾸몄습니다. 만약 아톰(Atom) 편집기를 사용한다면, &lt;a href="https://atom.io/packages/linter-eslint"&gt;linter-eslint&lt;/a&gt; 패키지를 설치하여 사용할 수 있고 &lt;a href="http://www.sublimetext.com/"&gt;Sublime Text&lt;/a&gt;에도 비슷한 녀석이 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://m.firejune.com/attach/0119/160119214611791010/419654.gif" width="666" height="269" alt="eslint.gif" /&gt;&lt;/p&gt;
&lt;p&gt;요런 느낌입니다. 가장 우선순위에 있는 룰은 얼마 전에 번역한 바 있는 &lt;a href="//firejune.com/1794/Airbnb%EC%9D%98+ES6+%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8+%EC%8A%A4%ED%83%80%EC%9D%BC+%EA%B0%80%EC%9D%B4%EB%93%9C"&gt;Airbnb 코드 스타일&lt;/a&gt;이고, 그다음으로 &lt;a href="http://standardjs.com/"&gt;JavaScript Standard Style&lt;/a&gt;의 프리셋과 React 플러그인을 적용하고, 개인적으로 탐탁지 못한 몇몇 규칙을 &lt;code&gt;"rules"&lt;/code&gt;에 재정의한 것입니다.&lt;/p&gt;
&lt;pre class="json"&gt;&lt;code&gt;{
  "ecmaFeatures": {
    "jsx": true,
    "modules": true,
    "experimentalObjectRestSpread": true
  },
  "env": { "es6": true, "node": true, "browser": true },
  "extends": ["standard", "airbnb"],
  "globals": { "$": true },
  "parser": "babel-eslint",
  "plugins": ["standard", "react"],
  "rules": {
    "comma-dangle": [2, "never"],
    "default-case": 0,
    "func-names": 0,
    "new-cap": [2, { "newIsCap": true, "capIsNew": false }],
    "no-console": 0,
    "object-curly-spacing": 0,
    "react/prop-types": 0,
    "react/sort-comp": 0,
    "space-before-function-paren": [2, "never"],
    "strict": 0
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;노트: 경우에 따라서는 ESLint관련 패키지를 글로벌에 설치해야 할 수도 있습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;pre class="command"&gt;&lt;code&gt;$ npm install -g eslint
$ npm install -g eslint-config-airbnb eslint-config-standard
$ npm install -g eslint-plugin-react eslint-plugin-standard&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; 파일의 내용은 다음과 같습니다. 프로젝트에서 필요한 모듈들의 정보를 포함한 여러 내용으로 구성됩니다. 더 자세한 내용은 &lt;a href="https://docs.npmjs.com/files/package.json"&gt;이곳&lt;/a&gt;을 참고하세요.&lt;/p&gt;
&lt;pre class="json"&gt;&lt;code&gt;{
  "name": "MyProject",
  "version": "0.0.1",
  "license": "MIT",
  "description": "My Awesome Project",
  "author": "firejune",
  "main": "main.js",
  "dependencies": {
    "jquery": "^2.2.0",
    "jquery-ui": "^1.10.5",
    "react": "^0.14.3",
    "react-dom": "^0.14.3"
  },
  "devDependencies": {
    "babel-core": "^6.4.5",
    "babel-eslint": "^5.0.0-beta6",
    "babel-loader": "^6.2.1",
    "babel-plugin-transform-es2015-destructuring": "^6.4.0",
    "babel-plugin-transform-es2015-for-of": "^6.3.13",
    "babel-plugin-transform-es2015-modules-commonjs": "^6.4.5",
    "babel-plugin-transform-es2015-object-super": "^6.4.0",
    "babel-plugin-transform-es2015-parameters": "^6.4.5",
    "babel-plugin-transform-es2015-shorthand-properties": "^6.3.13",
    "babel-plugin-transform-object-rest-spread": "^6.3.13",
    "babel-plugin-transform-react-jsx": "^6.4.0",
    "electron-prebuilt": "^0.36.5",
    "eslint": "^1.10.3",
    "eslint-config-airbnb": "^3.1.0",
    "eslint-config-standard": "^4.4.0",
    "eslint-plugin-react": "^3.15.0",
    "eslint-plugin-standard": "^1.3.1",
    "grunt": "^0.4.5",
    "jest-cli": "^0.8.2",
    "nightwatch": "^0.8.15",
    "webpack": "^1.12.11"
  },
  "scripts": {
    "start": "electron .",
    "lint": "eslint ./src",
    "test:unit": "npm run lint &amp;amp;&amp;amp; jest -c ./scripts/unit-test.json",
    "test:ui": "npm run test:unit  &amp;amp;&amp;amp; nightwatch --test ./scripts/ui-test.js",
    "build": "npm run test:ui  &amp;amp;&amp;amp; webpack --config ./scripts/package.js --release"
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;굳이 ES6 문법까지는 필요는 없고, JSX 트랜스파일만 필요한 상황이라면 &lt;code&gt;"transform-react-jsx"&lt;/code&gt; 플러그인만 남기거나 Babel이 아닌 &lt;a href="https://github.com/petehunt/node-jsx"&gt;node-jsx&lt;/a&gt; 모듈을 이용하는 방법도 있습니다. node-jsx가 사용법도 간단하고 빠르긴 한데, Babel로 이관되면서 deprecated 되었습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3&gt;Electron Starter&lt;/h3&gt;
&lt;p&gt; 이제 Electron에서의 작업환경을 구성할 차례입니다. &lt;code&gt;main.js&lt;/code&gt; 파일은 &lt;code&gt;package.json&lt;/code&gt;에 명시되어  Electron이 처음으로 접근하는 파일이며, Electron에 의해 브라우저 윈도를 만들어줍니다. 아쉽지만, 이 파일은 ES6으로 작성할 수 없습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;'use strict';

const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;

// 실행 준비를 마치면 브라우저 창 생성
app.on('ready', function () {
  // 브라우저 생성
  const mainWindow = new BrowserWindow({width: 800, height: 600});
  // 브라우저에서 처음으로 그려질 페이지
  mainWindow.loadURL('file://' + __dirname + '/index.html');
  // 브라우저의 개발자 도구 자동으로 열기
  mainWindow.webContents.openDevTools();
  // 창이 닫히면 프로세스 종료
  mainWindow.on('closed', function() {
    app.quit();
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;index.html&lt;/code&gt; 파일은 &lt;code&gt;main.js&lt;/code&gt;에 의해 브라우저(&lt;code&gt;BrowserWindow&lt;/code&gt;)에서 처음으로 그려질 페이지입니다. 이 브라우저가 바로 앞으로 동고동락할 작업용 브라우저입니다. 일단 다음과 같이 내용을 작성합니다.&lt;/p&gt;
&lt;pre class="html"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;title&amp;gt;Hello World!&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id="example"&amp;gt;Hello&amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
      // Install babel hooks to the browser process
      require('babel-core/register')();
      require('./src');
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;노트: Electron은 main과 browser로 구분된 프로세스 두 개를 실행합니다. 따라서 두 프로세스 간에는 IPC 스타일의 통신을 이용해야 하지만, Electron 앱이 아닌, 일반적인 웹앱 개발에 Electron을 이용하는 것이므로 main 프로세스에서 하는 일에 대해서는 크게 걱정하지 않아도 됩니다. &lt;code&gt;main.js&lt;/code&gt;는 main 프로세서에서 작동하고 &lt;code&gt;index.html&lt;/code&gt; 및 하위 참조 스크립트들은 browser 프로세스에서 작동합니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;자, 이제 기본적인 프로젝트 파일의 구성과 작업 실행 환경이 완료되었습니다.  &lt;code&gt;scripts&lt;/code&gt; 폴더는 정적 또는 동적 테스트 코드, 자동화 관련 코드, 작업 태스크 관리, 빌드 스크립트 등을 넣어둘 장소입니다. 이 글은 개발 환경을 구성하는 데 목적을 두기 때문에 이와 관련한 자세한 내용은 다루지 않을 것입니다. (엄청나게 다양하고 복잡하고 일일이 설명하기가 귀찮기도 하고 뭐 그렇습니다) LICENSE.md, README.md 파일에는 프로젝트와 관련된 내용을 작성하면 됩니다. 이제, 커멘드 라인에 개발에  필요한 모듈들을 설치하고 작업 결과를 확인할 수 있는 브라우저(Electron)를 실행해 봅시다. 'Hello'문자가 보이나요?&lt;/p&gt;
&lt;pre class="bash"&gt;&lt;code&gt;$ npm install
$ npm start&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Enjoying Web Development with Electron&lt;/h3&gt;
&lt;p&gt;모든 준비는 끝났습니다. 멋들어지게 최신 자바스크립트 문법을 이용하여 본격적으로 개발을 시작해 봅시다. &lt;code&gt;src/index.js&lt;/code&gt; 파일을 열고 간단한 React 기반의 'Hello,  world!' 애플리케이션을 만들겠습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import 'jquery-ui/draggable';

class Title extends React.Component {
  constructor() {
    super();
    this.state = {
      value: 'Hello, world!'
    };
  }
  componentDidMount() {
    $(this.refs.el).draggable();
  }
  render() {
    return (
      &amp;lt;h1 ref="el"&amp;gt;
        {this.state.value}
      &amp;lt;/h1&amp;gt;
    );
  }
}

ReactDOM.render(
  &amp;lt;Title /&amp;gt;,
  document.getElementById('example')
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;파일 작성 후 브라우저 창에서 새로 고칩니다. 단축키는 맥인 경우 CMD+R(윈도: Ctrl+R)입니다. 브라우저 개발자 도구는 CMD+ALT+I(윈도: Ctrl+Shift+I)로 열 수 있습니다. 'Hello, world!' 문자가 보이나요? 정상적으로 작동하는 것입니다. 놀랍죠? 문자의 드래그 앤 드롭 기능도 확인해 보세요!&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;크로스-브라우저는 어찌할까요? Browserify나 Webpack을 이용해서 번들링 한 후 실제 브라우저에서 동작하는 동적-테스트 도구(Nightwatch 와 같은)로 테스트 수행을 자동화하고 오류가 보고되면 그때 처리합니다.&lt;/p&gt;&lt;/blockquote&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1797#p1797"&gt;Comments(2)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1797"&gt;Hits(278105)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/qJQZiD2VXJg" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1797</feedburner:origLink></entry><entry><title type="text">Airbnb의 React/JSX 스타일 가이드</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/aVf1WmmSE30/1795" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="React" /><category term="JSX" /><category term="스타일 가이드" /><category term="ES6" /><category term="Airbnb" /><category term="Electron" /><category term="CommonJS" /><category term="Node.js" /><author><name>파이어준</name></author><updated>2016-01-11T06:10:07-08:00</updated><id>http://firejune.com/1795</id><content type="html">&lt;p&gt;기왕 &lt;a href="//firejune.com/1794/Airbnb%EC%9D%98+ES6+%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8+%EC%8A%A4%ED%83%80%EC%9D%BC+%EA%B0%80%EC%9D%B4%EB%93%9C"&gt;번역&lt;/a&gt;한 김에 하나 더 했습니다. Airbnb에서 &lt;a href="https://github.com/airbnb/javascript/tree/master/react"&gt;Reac와 JSX 스타일 가이드&lt;/a&gt;도 작성했더군요. ES6과 JSX기반의 React 코드를 작성하는 것을 마치 기본적인 것인 양 소개하고 있습니다. 요즘 &lt;a href="http://electron.atom.io/"&gt;Electron&lt;/a&gt; 기반 하이브리드 데스크탑 애플리케이션을 개발하고 있는데 전반에 걸쳐 &lt;a href="https://facebook.github.io/react/"&gt;React&lt;/a&gt;를 적용하는 중입니다. Electron은 구글 크롬 브라우저와 Node.js를 포함하고 있어서 하나의 브라우저에서 작동하는 것에만 집중할 수 있고 &lt;a href="http://browserify.org/"&gt;browserify&lt;/a&gt;또는 &lt;a href="https://webpack.github.io/"&gt;webpack&lt;/a&gt;와 같은 모듈 번들러(module bundler)의 도움 없이 네이티브 &lt;code&gt;require&lt;/code&gt;를 브라우저에서도 사용할 수 있다 보니, 의존성(dependency)이나 네임스페이스(namespace) 관리는 물론이고, 실시간 트랜스파일(Transpile)이 가능해서 ES6 코드를 마구 내질러도 크게 문제 될 것이 없는 개발환경을 만들 수 있다는 것은... 이건 뭐 그냥 완전히 다른 세상이라고 밖에 표현하지 못하겠군요.&lt;/p&gt;

&lt;h3&gt;&lt;a id="basic-rules" href="#basic-rules"&gt;1&lt;/a&gt;. 기본 규칙(Basic Rules)&lt;/h3&gt;

&lt;ul class="square"&gt;
  &lt;li&gt;
    하나의 파일에는 오직 하나의 React 컴포넌트를 사용합니다.
    그러나, 다중 &lt;a href="https://facebook.github.io/react/docs/reusable-components.html#stateless-functions"&gt;스테이트가 없는(Stateless) 또는 순수한 함수나 컴포넌트&lt;/a&gt;는 허용됩니다.
    eslint rule: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless"&gt;&lt;code&gt;react/no-multi-comp&lt;/code&gt;&lt;/a&gt;.
  &lt;/li&gt;
  &lt;li&gt;항상 JSX 문법을 사용합니다.&lt;/li&gt;
  &lt;li&gt;JSX파일이 아닌 다른 app에서 초기화하는 경우를 제외하고는 &lt;code&gt;React.createElement&lt;/code&gt;를 사용하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;&lt;a id="class-vs-reactcreateclass" href="#class-vs-reactcreateclass"&gt;2&lt;/a&gt;. 클래스(Class) vs React.createClass&lt;/h3&gt;

&lt;p&gt;특별한 이유로 믹스인(mixin)하는 경우를 제외하고는 &lt;code&gt;class extends React.Component&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md"&gt;&lt;code&gt;react/prefer-es6-class&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;pre class="jsx"&gt;&lt;code&gt;// bad
const Listing = React.createClass({
  render() {
    return &amp;lt;div /&amp;gt;;
  }
});

// good
class Listing extends React.Component {
  render() {
    return &amp;lt;div /&amp;gt;;
  }
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="naming" href="#naming"&gt;3&lt;/a&gt;. 명명(Naming)&lt;/h3&gt;

&lt;P&gt;&lt;strong&gt;확장자&lt;/strong&gt;: React 컴포넌트는 &lt;code&gt;.jsx&lt;/code&gt; 확장자를 사용합니다.&lt;/P&gt;
&lt;P&gt;&lt;strong&gt;파일명&lt;/strong&gt;: 파일명에는 PascalCase(대문자로 시작)를 사용합니다. 예), &lt;code&gt;ReservationCard.jsx&lt;/code&gt;.&lt;/P&gt;
&lt;P&gt;&lt;strong&gt;참조명&lt;/strong&gt;: React 컴포넌트의 참조 이름에는 PascalCase를 쓰고 그 인스턴스의 이름에는 camelCase(소문자로 시작)를 사용합니다.&lt;/P&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md"&gt;&lt;code&gt;react/jsx-pascal-case&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
import reservationCard from './ReservationCard';

// good
import ReservationCard from './ReservationCard';

// bad
const ReservationItem = &amp;lt;ReservationCard /&amp;gt;;

// good
const reservationItem = &amp;lt;ReservationCard /&amp;gt;;&lt;/code&gt;&lt;/pre&gt;

&lt;P&gt;&lt;strong&gt;컴포넌트명&lt;/strong&gt;: 컴포넌트명으로 파일명을 씁니다. 예), &lt;code&gt;ReservationCard.jsx&lt;/code&gt; 파일은 &lt;code&gt;ReservationCard&lt;/code&gt;라는 참조명을 가집니다. 그러나, 루트 컴포넌트가 디렉토리에 구성되었다면 파일명을 &lt;code&gt;index.jsx&lt;/code&gt;로 쓰고 디렉토리명을 컴포넌트명으로 사용합니다:&lt;/P&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
import Footer from './Footer/Footer';

// bad
import Footer from './Footer/index';

// good
import Footer from './Footer';&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="declaration" href="#declaration"&gt;4&lt;/a&gt;. 선언(Declaration)&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;displayName&lt;/code&gt;을 이용하여 컴포넌트명을 정하지 않습니다. 그대신, 참조에 의해 이름을 지정합니다.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
export default React.createClass({
  displayName: 'ReservationCard',
  // stuff goes here
});

// good
export default class ReservationCard extends React.Component {
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="alignment" href="#alignment"&gt;5&lt;/a&gt;. 조정(Alignment)&lt;/h3&gt;

&lt;p&gt;JSX 구문에 따른 정렬 스타일을 사용합니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md"&gt;&lt;code&gt;react/jsx-closing-bracket-location&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
&amp;lt;Foo superLongParam="bar"
     anotherSuperLongParam="baz" /&amp;gt;

// good
&amp;lt;Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
/&amp;gt;

// if props fit in one line then keep it on the same line
&amp;lt;Foo bar="bar" /&amp;gt;

// children get indented normally
&amp;lt;Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
&amp;gt;
  &amp;lt;Spazz /&amp;gt;
&amp;lt;/Foo&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="quotes" href="#quotes"&gt;6&lt;/a&gt;. 인용(Quotes)&lt;/h3&gt;

&lt;p&gt;JSX 속성(attributes)에는 항상 큰 따옴표(&lt;code&gt;"&lt;/code&gt;)를 사용합니다. 그러나 다른 모든 자바스크립트에는 작은 따옴표(single quotes)를 사용합니다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;왜죠? JSX 속성(attributes)은 &lt;a href="http://eslint.org/docs/rules/jsx-quotes"&gt;따옴표(quotes)의 탈출(escaped)을 포함할 수 없습니다&lt;/a&gt;. 그래서 큰 따옴표를 이용하여 &lt;code&gt;"don't"&lt;/code&gt;와 같은 접속사를 쉽게 입력할 수 있습니다.
일반적으로 HTML 속성(attributes)에는 작은 따옴표 대신 큰 따옴표를 사용합니다. 그래서 JSX 속성역시 동일한 규칙이 적용됩니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/jsx-quotes"&gt;&lt;code&gt;jsx-quotes&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;pre class="jsx"&gt;&lt;code&gt;// bad
&amp;lt;Foo bar='bar' /&amp;gt;

// good
&amp;lt;Foo bar="bar" /&amp;gt;

// bad
&amp;lt;Foo style={{ left: "20px" }} /&amp;gt;

// good
&amp;lt;Foo style={{ left: '20px' }} /&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="spacing" href="#spacing"&gt;7&lt;/a&gt;. 공백(Spacing)&lt;/h3&gt;

&lt;p&gt;자신을 닫는(self-closing) 태그에는 항상 하나의 공백만을 사용합니다.&lt;/p&gt;

&lt;pre class="jsx"&gt;&lt;code&gt;// bad
&amp;lt;Foo/&amp;gt;

// very bad
&amp;lt;Foo                 /&amp;gt;

// bad
&amp;lt;Foo
 /&amp;gt;

// good
&amp;lt;Foo /&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="props" href="#props"&gt;8&lt;/a&gt;. 속성(Props)&lt;/h3&gt;

&lt;p&gt;prop 이름은 항상 camelCase(소문자로 시작)를 사용합니다.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
&amp;lt;Foo
  UserName="hello"
  phone_number={12345678}
/&amp;gt;

// good
&amp;lt;Foo
  userName="hello"
  phoneNumber={12345678}
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;명시적으로 &lt;code&gt;true&lt;/code&gt; 값을 가지는 prop은 그 값을 생략할 수 있습니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md"&gt;&lt;code&gt;react/jsx-boolean-value&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
&amp;lt;Foo
  hidden={true}
/&amp;gt;

// good
&amp;lt;Foo
  hidden
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="parentheses" href="#parentheses"&gt;9&lt;/a&gt;. 괄호(Parentheses)&lt;/h3&gt;

&lt;p&gt;JSX 태그가 감싸여(Wrap) 있어 한 줄 이상인 경우 괄호(parentheses)를 사용합니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md"&gt;&lt;code&gt;react/wrap-multilines&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt; // bad
  render() {
    return &amp;lt;MyComponent className="long body" foo="bar"&amp;gt;
             &amp;lt;MyChild /&amp;gt;
           &amp;lt;/MyComponent&amp;gt;;
  }
  
  // good
  render() {
    return (
      &amp;lt;MyComponent className="long body" foo="bar"&amp;gt;
        &amp;lt;MyChild /&amp;gt;
      &amp;lt;/MyComponent&amp;gt;
    );
  }
  
  // good, when single line
  render() {
    const body = &amp;lt;div&amp;gt;hello&amp;lt;/div&amp;gt;;
    return &amp;lt;MyComponent&amp;gt;{body}&amp;lt;/MyComponent&amp;gt;;
  }&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="tags" href="#tags"&gt;10&lt;/a&gt;. 태그(Tags)&lt;/h3&gt;

&lt;p&gt;자식(children)을 가지지 않는다면 항상 자신을 닫는(self-close) 태그로 작성합니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md"&gt;&lt;code&gt;react/self-closing-comp&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
&amp;lt;Foo className="stuff"&amp;gt;&amp;lt;/Foo&amp;gt;

// good
&amp;lt;Foo className="stuff" /&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;만약, 컴포넌트의 속성(properties)을 여러 줄에 있는 경우, 닫는 태그는 다음 줄에 작성합니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md"&gt;&lt;code&gt;react/jsx-closing-bracket-location&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
&amp;lt;Foo
  bar="bar"
  baz="baz" /&amp;gt;

// good
&amp;lt;Foo
  bar="bar"
  baz="baz"
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="methods" href="#methods"&gt;11&lt;/a&gt;. 메소드(Methods)&lt;/h3&gt;

&lt;p&gt;렌더(Render) 메소드에서 이벤트 핸들러에 바인드(Bind)가 필요한 경우에는 생성자(constructor)에서 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 렌더러 메소드에서 바인드(bind)를 호출하게 되면 랜더링 할 때 마다 매번 새로운 함수를 생성하게 됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md"&gt;&lt;code&gt;react/jsx-no-bind&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
class extends React.Component {
  onClickDiv() {
    // do stuff
  }

  render() {
    return &amp;lt;div onClick={this.onClickDiv.bind(this)} /&amp;gt;
  }
}

// good
class extends React.Component {
  constructor(props) {
    super(props);

    this.onClickDiv = this.onClickDiv.bind(this);
  }

  onClickDiv() {
    // do stuff
  }

  render() {
    return &amp;lt;div onClick={this.onClickDiv} /&amp;gt;
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;React 컴포넌트의 내부 메소드에 밑줄(underscore)을 접두사로 사용하지 않습니다.&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;// bad
React.createClass({
  _onClickSubmit() {
    // do stuff
  },

  // other stuff
});

// good
class extends React.Component {
  onClickSubmit() {
    // do stuff
  }

  // other stuff
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="ordering" href="#ordering"&gt;12&lt;/a&gt;. 호출순서(Ordering)&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;class extends React.Component&lt;/code&gt;의 호출순서(Ordering):&lt;/p&gt;
&lt;ul class="number"&gt;
&lt;li&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;추가적인(optional) &lt;code&gt;static&lt;/code&gt; 메소드&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getChildContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillMount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentDidMount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillReceiveProps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shouldComponentUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentDidUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillUnmount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onClickSubmit()&lt;/code&gt;와 같은 &lt;em&gt;clickHandlers 또는 eventHandlers&lt;/em&gt; 또는 &lt;code&gt;onChangeDescription()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getSelectReason()&lt;/code&gt;와 같은 &lt;em&gt;&lt;code&gt;render&lt;/code&gt;를 위한 getter methods&lt;/em&gt; 또는 &lt;code&gt;getFooterContent()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;renderNavigation()&lt;/code&gt;와 같은 &lt;em&gt;추가적인 렌더러 메소드&lt;/em&gt; 또는 &lt;code&gt;renderProfilePicture()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;render&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;React.createClass&lt;/code&gt;의 호출순서(Ordering):&lt;/p&gt;
&lt;ul class="number"&gt;
&lt;li&gt;&lt;code&gt;displayName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;propTypes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contextTypes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;childContextTypes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mixins&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;statics&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;defaultProps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getDefaultProps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getInitialState&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getChildContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillMount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentDidMount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillReceiveProps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shouldComponentUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentDidUpdate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentWillUnmount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onClickSubmit()&lt;/code&gt;와 같은 &lt;em&gt;clickHandlers 또는 eventHandlers&lt;/em&gt; 또는 &lt;code&gt;onChangeDescription()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getSelectReason()&lt;/code&gt;와 같은 &lt;em&gt;&lt;code&gt;render&lt;/code&gt;를 위한 getter methods&lt;/em&gt; 또는 &lt;code&gt;getFooterContent()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;renderNavigation()&lt;/code&gt;와 같은 &lt;em&gt;추가적인 렌더러 메소드&lt;/em&gt; 또는 &lt;code&gt;renderProfilePicture()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;render&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md"&gt;&lt;code&gt;react/sort-comp&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;propTypes&lt;/code&gt;, &lt;code&gt;defaultProps&lt;/code&gt;, &lt;code&gt;contextTypes&lt;/code&gt;, 등을 어떻게 정의할까요...&lt;/p&gt;
&lt;pre class="jsx"&gt;&lt;code&gt;import React, { PropTypes } from 'react';

const propTypes = {
  id: PropTypes.number.isRequired,
  url: PropTypes.string.isRequired,
  text: PropTypes.string,
};

const defaultProps = {
  text: 'Hello World',
};

class Link extends React.Component {
  static methodsAreOk() {
    return true;
  }

  render() {
    return &amp;lt;a href={this.props.url} data-id={this.props.id}&amp;gt;{this.props.text}&amp;lt;/a&amp;gt;
  }
}

Link.propTypes = propTypes;
Link.defaultProps = defaultProps;

export default Link;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="ismounted" href="#ismounted"&gt;13&lt;/a&gt;. isMounted&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;isMounted&lt;/code&gt;는 사용하지 않습니다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;왜죠? &lt;code&gt;isMounted&lt;/code&gt;는 &lt;a href="https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html"&gt;안티-패턴(anti-pattern)&lt;/a&gt;입니다. ES6 클래스에서는 사용할수도 없습니다. 그리고 공식적으로 사용되지 않게(deprecated) 될 것입니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;eslint rules: &lt;a href="https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md"&gt;&lt;code&gt;react/no-is-mounted&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;


 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1795#p1795"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1795"&gt;Hits(302917)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/aVf1WmmSE30" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1795</feedburner:origLink></entry><entry><title type="text">Airbnb의 ES6 자바스크립트 스타일 가이드</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/Zj921ANDbvc/1794" /><category term="웹개발" /><category term="자료" /><category term="ES6" /><category term="ES5" /><category term="ECMAScript 2015" /><category term="Airbnb" /><category term="자바스크립트" /><category term="스타일 가이드" /><category term="까막눈" /><category term="Node.js" /><author><name>파이어준</name></author><updated>2016-01-10T16:39:45-08:00</updated><id>http://firejune.com/1794</id><content type="html">&lt;p&gt;&lt;a href="https://people.mozilla.org/~jorendorff/es6-draft.html"&gt;ECMAScript 2015&lt;/a&gt;(ES6)가 우후죽순처럼 활개를 치고 있습니다. &lt;a href="https://en.wikipedia.org/wiki/Source-to-source_compiler"&gt;Transpile&lt;/a&gt;(Source-to-source compiler)이 필요한 소스들은 몇 번이고 무시하려고 했지만 ES6만큼은 그러지 못했습니다. 요즘 왠만한 Node.js 프로젝트나 &lt;a href="https://facebook.github.io/react/"&gt;React&lt;/a&gt;에 기반을 둬서 나오는 결과물들은 이미 장악되었다고 해도 과언이 아닐정도로 많은 사람이 애용하고 있어, 원만히 소스를 읽는 데에 큰 장애가 생겨났습니다. 이런, 까막눈이 되고 말았군요. 그러던 중 때마침 Airbnb에서 ES5이었던 &lt;a href="https://github.com/airbnb/javascript"&gt;자바스크립트 스타일 가이드&lt;/a&gt;를 ES6으로 업데이트했기에 학습차 번역해 보았습니다.&lt;/p&gt;
&lt;h3&gt;&lt;a id="types" href="#types"&gt;1&lt;/a&gt;. 유형(Types)&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Primitives&lt;/strong&gt;: 원시형(Primitive type)은 그 값을 직접 조작합니다.&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;&lt;code&gt;string&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;number&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;null&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // =&gt; 1, 9&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Complex&lt;/strong&gt;: 참조형(Complex type)은 참조를 통해 값을 조작합니다.&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;&lt;code&gt;object&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;array&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;function&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const foo = [1, 2];
const bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // =&gt; 9, 9&lt;/code&gt;&lt;/pre&gt;


&lt;h3&gt;&lt;a id="references" href="#references"&gt;2&lt;/a&gt;. 참조(References)&lt;/h3&gt;

&lt;p&gt;모든 참조에는 &lt;code&gt;const&lt;/code&gt;를 사용하고 &lt;code&gt;var&lt;/code&gt;를 사용하지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 참조를 다시 할당할 수 없어서, 버그로 연결되거나 이해하기 어려운 코드가 되는 것을 예방합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/prefer-const.html"&gt;&lt;code&gt;prefer-const&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://eslint.org/docs/rules/no-const-assign.html"&gt;&lt;code&gt;no-const-assign&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
var a = 1;
var b = 2;

// good
const a = 1;
const b = 2;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;참조를 다시 할당해야 하는 경우 &lt;code&gt;var&lt;/code&gt; 대신에 &lt;code&gt;let&lt;/code&gt;을 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? &lt;code&gt;var&lt;/code&gt;는 함수-범위(function-scoped)이고 &lt;code&gt;let&lt;/code&gt;은 블록-범위(block-scoped)이기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/no-var.html"&gt;&lt;code&gt;no-var&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
var count = 1;
if (true) {
  count += 1;
}

// good, use the let.
let count = 1;
if (true) {
  count += 1;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;let&lt;/code&gt;과 &lt;code&gt;const&lt;/code&gt;는 모두 블록-범위(block-scoped)인 것에 주의해야 합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// const와 let은 선언 된 블록 안에서만 존재함. 
{
  let a = 1;
  const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError&lt;/code&gt;&lt;/pre&gt;


&lt;h3&gt;&lt;a id="objects" href="#objects"&gt;3&lt;/a&gt;. 객체(Objects)&lt;/h3&gt;

&lt;p&gt;객체를 만들 때에는 리터럴 구문을 사용합니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/no-new-object.html"&gt;&lt;code&gt;no-new-object&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const item = new Object();

// good
const item = {};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;코드가 브라우저에서 실행되는 경우 &lt;a href="http://es5.github.io/#x7.6.1"&gt;예약어&lt;/a&gt;를 키로 사용하지 마세요. 이것은 IE8에서 작동하지 않습니다. &lt;a href="https://github.com/airbnb/javascript/issues/61"&gt;더 알아보기&lt;/a&gt;. ES6 모듈과 서버 사이드에서는 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const superman = {
  default: { clark: 'kent' },
  private: true,
};

// good
const superman = {
  defaults: { clark: 'kent' },
  hidden: true,
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;예약어 대신에 알기 쉬운 동의어(Readable Synonyms)를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const superman = {
  class: 'alien',
};

// bad
const superman = {
  klass: 'alien',
};

// good
const superman = {
  type: 'alien',
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a name="es6-computed-properties"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;동적인 속성 이름을 가진 객체를 만들 때에는 계산된 속성 이름(Computed Property Names)을 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이렇게하면 객체 속성을 한 개의 장소에서 정의 할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;function getKey(k) {
  return `a key named ${k}`;
}

// bad
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
  id: 5,
  name: 'San Francisco',
  [getKey('enabled')]: true,
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a name="es6-object-shorthand"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;메소드에 단축 구문(Object Shorthand)을 사용하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/object-shorthand.html"&gt;&lt;code&gt;object-shorthand&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const atom = {
  value: 1,

  addValue: function (value) {
    return atom.value + value;
  },
};

// good
const atom = {
  value: 1,

  addValue(value) {
    return atom.value + value;
  },
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a name="es6-object-concise"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;속성에 단축 구문(Object Concise)을 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 표현이나 설명이 간결해지기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/object-shorthand.html"&gt;&lt;code&gt;object-shorthand&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
};

// good
const obj = {
  lukeSkywalker,
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;속성의 단축 구문(Object Concise)은 객체 선언의 시작 부분에 무리를 지어줍니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 어떤 속성이 단축 구문을 사용하고 있는지를 알기가 쉽기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  lukeSkywalker,
  episodeThree: 3,
  mayTheFourth: 4,
  anakinSkywalker,
};

// good
const obj = {
  lukeSkywalker,
  anakinSkywalker,
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  episodeThree: 3,
  mayTheFourth: 4,
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;속성 이름에 작은 따옴표를 사용하는 경우는 오직 잘못된 식별자(Invalid Identifiers)일 때입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 주관적으로 쉽게 읽을 수 있는 것을 항상 고민해야 합니다. 이 것은 구문이 강조되고, 수많은 JS엔진에 쉽게 최적화되어 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/quote-props.html"&gt;&lt;code&gt;quote-props&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const bad = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5,
};

// good
const good = {
  foo: 3,
  bar: 4,
  'data-blah': 5,
};&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="arrays" href="#arrays"&gt;4&lt;/a&gt;. 배열(Arrays)&lt;/h3&gt;

&lt;p&gt;배열을 만들 때 리터럴 구문을 사용하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/no-array-constructor.html"&gt;&lt;code&gt;no-array-constructor&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const items = new Array();

// good
const items = [];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;배열에 항목을 직접 대체하지 말고 Array#push를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const someStack = [];

// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a name="es6-array-spreads"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;배열을 복사하는 경우, 배열의 확장 연산자인 &lt;code&gt;...&lt;/code&gt;을 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i $lt; len; i++) {
  itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Array-Like 객체를 배열로 변환하려면 Array#from을 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="destructuring" href="#destructuring"&gt;5&lt;/a&gt;. 구조화 대입(Destructuring)&lt;/h3&gt;

&lt;p&gt;여러 속성에서 객체에 접근할 때 객체 구조화 대입(Destructuring)을 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 구조화 대입을 이용하여 그 속성에 대한 중간 참조를 줄일 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
}

// good
function getFullName(user) {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
}

// best
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;배열에 구조화 대입(Destructuring)을 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;여러 값을 반환하는 경우, 배열의 구조화 대입이 아니라 객체의 구조화 대입을 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이렇게하면 나중에 새 속성을 추가하거나 호출에 영향을 주지않고 순서를 변경할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function processInput(input) {
  // 그러면 기적이 일어난다
  return [left, right, top, bottom];
}

// 호출자에 반환되는 데이터의 순서를 고려해야 함
const [left, __, top] = processInput(input);

// good
function processInput(input) {
  // 그러면 기적이 일어난다
  return { left, right, top, bottom };
}

// 호출하면서 필요한 데이터만 선택할 수 있음
const { left, right } = processInput(input);&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="strings" href="#strings"&gt;6&lt;/a&gt;. 문자열(Strings)&lt;/h3&gt;

&lt;p&gt;문자열에는 작은 따옴표&lt;code&gt;''&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/quotes.html"&gt;&lt;code&gt;quotes&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const name = "Capt. Janeway";

// good
const name = 'Capt. Janeway';&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;100자 이상의 문자열은 여러 행을 사용하여 연결할 수 있습니다.&lt;/p&gt;
&lt;p&gt;주의: 문자열 연결이 많으면 성능에 영향을 줄 수 있습니다. &lt;a href="http://jsperf.com/ya-string-concat"&gt;jsPerf&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/airbnb/javascript/issues/40"&gt;Discussion&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';

// bad
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';

// good
const errorMessage = 'This is a super long error that was thrown because ' +
  'of Batman. When you stop to think about how Batman had anything to do ' +
  'with this, you would get nowhere fast.';&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a name="es6-template-literals"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;프로그램에서 문자열을 생성하는 경우, 문자열 연결이 아닌 템플릿 문자열(Template Strings)을 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 템플릿 문자열의 문자열 완성 기능과 다중 문자열 기능을 가진 간결한 구문으로 가독성이 좋아지기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/prefer-template.html"&gt;&lt;code&gt;prefer-template&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function sayHi(name) {
  return 'How are you, ' + name + '?';
}

// bad
function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}&lt;/code&gt;&lt;/pre&gt;

&lt;a href="#6.5" name="6.5"&gt;&lt;/a&gt; 절대로 &lt;code&gt;eval()&lt;/code&gt;을 사용하지 않습니다. 이것은 지금까지 수많은 취약점을 만들어 왔기 때문입니다.



&lt;h3&gt;&lt;a id="functions" href="#functions"&gt;7&lt;/a&gt;. 함수(Functions)&lt;/h3&gt;

&lt;p&gt;함수 선언 대신에 함수 표현식을 사용합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이름이 붙은 함수 선언은 콜스택에서 쉽게 알수 있습니다. 또한 함수 선언의 몸 전체가 Hoist됩니다. 반면 함수는 참조만 Hoist됩니다. 이 규칙은 함수 부분을 항상 &lt;a href="#arrow-functions"&gt;애로우 함수&lt;/a&gt;로 대체 사용할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const foo = function () {
};

// good
function foo() {
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수 표현식(Function expressions):&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// 즉시-호출(Immediately-Invoked) 함수 표현식(IIFE)
(() =&gt; {
  console.log('Welcome to the Internet. Please follow me.');
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수 이외의 블록 (&lt;code&gt;if&lt;/code&gt;나 &lt;code&gt;while&lt;/code&gt; 등)에 함수를 선언하지 않습니다. 브라우저는 변수에 함수를 할당하는 처리를 할 수는 있지만, 모두 다르게 해석됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;주의:&lt;/strong&gt; ECMA-262에서 &lt;code&gt;block&lt;/code&gt;은 statements 목록에 정의되지만, 함수 선언은 statements가 없습니다. &lt;a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97"&gt;이 문제는 ECMA-262의 설명을 참조하세요&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good
let test;
if (currentUser) {
  test = () =&gt; {
    console.log('Yup.');
  };
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;매개변수(parameter)에 &lt;code&gt;arguments&lt;/code&gt;를 절대로 지정하지 않습니다. 이것은 함수 영역으로 전달 될 &lt;code&gt;arguments&lt;/code&gt;객체의 참조를 덮어 써버릴 것입니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function nope(name, options, arguments) {
  // ...stuff...
}

// good
function yup(name, options, args) {
  // ...stuff...
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a name="es6-rest"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;arguments&lt;/code&gt;를 사용하지 않습니다. 대신 레스트(Rest) 문법인 &lt;code&gt;...&lt;/code&gt;을 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? &lt;code&gt;...&lt;/code&gt; 를 이용하여 여러가지 매개변수를 모두 사용할 수 있습니다. 추가로 rest 매개변수인 &lt;code&gt;arguments&lt;/code&gt;는 Array-Like 객체가 아니라 진정한 배열(Array)입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good
function concatenateAll(...args) {
  return args.join('');
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a name="es6-default-parameters"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;함수의 매개변수를 조작하지 말고 기본 매개변수(Default Parameters)를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// really bad
function handleThings(opts) {
  // 안되! 함수의 매개변수를 조작하지 않습니다. 
  // 만약 opts가 falsy 인 경우는 바란대로 객체가 설정됩니다. 
  // 그러나 미묘한 버그를 일으키는 원인이 될수도 있습니다. 
  opts = opts || {};
  // ...
}

// still bad
function handleThings(opts) {
  if (opts === void 0) {
    opts = {};
  }
  // ...
}

// good
function handleThings(opts = {}) {
  // ...
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;부작용이 있는 기본 매개변수를 사용하지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 혼란스럽기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;var b = 1;
// bad
function count(a = b++) {
  console.log(a);
}
count();  // 1
count();  // 2
count(3); // 3
count();  // 3&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;항상 기본 매개변수는 앞쪽에 배치하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function handleThings(opts = {}, name) {
  // ...
}

// good
function handleThings(name, opts = {}) {
  // ...
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;새로운 함수를 만드는 데 Function 생성자를 사용하지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이 방법은 문자열을 구분하는 새로운 함수를 만들 수 있는 eval()과 같은 취약점이 발생할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
var add = new Function('a', 'b', 'return a + b');

// still bad
var subtract = Function('a', 'b', 'return a - b');&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수 시그네이쳐(Signature)에 공백을 사용합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 일관성이 좋고, 함수이름을 추가 하거나 삭제할 때 공백을 제거할 필요가 없습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const f = function(){};
const g = function (){};
const h = function() {};

// good
const x = function () {};
const y = function a() {};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;절대로 매개변수를 조작하지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 매개변수로 전달 된 객체를 조작하는 것은 원래의 호출에 원치 않는 변수 부작용을 일으킬 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/no-param-reassign.html"&gt;&lt;code&gt;no-param-reassign&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function f1(obj) {
  obj.key = 1;
};

// good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;절대로 매개변수를 다시 지정하지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? &lt;code&gt;arguments&lt;/code&gt; 객체에 접근하는 경우 다시 지정된 매개변수는 예기치 않은 동작이 발생할 수 있습니다. 그리고 특히 V8 최적화에 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/no-param-reassign.html"&gt;&lt;code&gt;no-param-reassign&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function f1(a) {
  a = 1;
}

function f2(a) {
  if (!a) { a = 1; }
}

// good
function f3(a) {
  const b = a || 1;
}

function f4(a = 1) {
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="arrow-functions" href="#arrow-functions"&gt;8&lt;/a&gt;. 애로우 함수(Arrow Functions)&lt;/h3&gt;

&lt;p&gt;함수 표현식을 사용해야하는 경우(익명 함수와 같은), 애로우 함수(Arrow Functions)를 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 애로우 함수는 함수가 실행되는 컨텍스트의 &lt;code&gt;this&lt;/code&gt;를 가두어줍니다. 이것은 너무나 원했던 것이며 구문도 더욱 간결해집니다.&lt;/p&gt;
&lt;p&gt;언제 쓰죠? 복잡한 함수 논리를 정의한 함수의 바깥쪽으로 이동하고 싶은 경우입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/prefer-arrow-callback.html"&gt;&lt;code&gt;prefer-arrow-callback&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://eslint.org/docs/rules/arrow-spacing.html"&gt;&lt;code&gt;arrow-spacing&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});

// good
[1, 2, 3].map((x) =&gt; {
  const y = x + 1;
  return x * y;
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수의 본체가 하나의 표현식으로 구성되어있는 경우 중괄호&lt;code&gt;{}&lt;/code&gt;를 생략하고 암묵적 &lt;code&gt;return&lt;/code&gt;을 사용할 수 있습니다. 그렇지 않으면 &lt;code&gt;return&lt;/code&gt; 문을 사용해야 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 가독성이 좋아지기 때문입니다. 여러 함수가 연결되는 경우에 쉽게 읽을 수 있습니다.&lt;/p&gt;
&lt;p&gt;언제 쓰죠? 객체를 반환하는 경우.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/arrow-parens.html"&gt;&lt;code&gt;arrow-parens&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://eslint.org/docs/rules/arrow-body-style.html"&gt;&lt;code&gt;arrow-body-style&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// good
[1, 2, 3].map(number =&gt; `A string containing the ${number}.`);

// bad
[1, 2, 3].map(number =&gt; {
  const nextNumber = number + 1;
  `A string containing the ${nextNumber}.`;
});

// good
[1, 2, 3].map(number =&gt; {
  const nextNumber = number + 1;
  return `A string containing the ${nextNumber}.`;
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;구문의 길이가 여러 행에 걸치는 경우 가독성을 향상시키기 위해 괄호&lt;code&gt;()&lt;/code&gt; 안에 써주세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 함수의 시작과 끝 부분을 알아보기 쉽게 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
[1, 2, 3].map(number =&gt; 'As time went by, the string containing the ' +
  `${number} became much longer. So we needed to break it over multiple ` +
  'lines.'
);

// good
[1, 2, 3].map(number =&gt; (
  `As time went by, the string containing the ${number} became much ` +
  'longer. So we needed to break it over multiple lines.'
));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수의 인수가 한 개인 경우 괄호&lt;code&gt;()&lt;/code&gt;를 생략할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 시각적 혼란이 덜하기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/arrow-parens.html"&gt;&lt;code&gt;arrow-parens&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
[1, 2, 3].map((x) =&gt; x * x);

// good
[1, 2, 3].map(x =&gt; x * x);

// good
[1, 2, 3].map(number =&gt; (
  `A long string with the ${number}. It’s so long that we’ve broken it ` +
  'over multiple lines!'
));

// bad
[1, 2, 3].map(x =&gt; {
  const y = x + 1;
  return x * y;
});

// good
[1, 2, 3].map((x) =&gt; {
  const y = x + 1;
  return x * y;
});&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="constructors" href="#constructors"&gt;9&lt;/a&gt;. 생성자(Constructors)&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;prototype&lt;/code&gt;의 직접 조작을 피하고 항상 &lt;code&gt;class&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? &lt;code&gt;class&lt;/code&gt; 구문은 간결하고 의도를 알아내기가 쉽기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function Queue(contents = []) {
  this._queue = [...contents];
}
Queue.prototype.pop = function () {
  const value = this._queue[0];
  this._queue.splice(0, 1);
  return value;
}


// good
class Queue {
  constructor(contents = []) {
    this._queue = [...contents];
  }
  pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;상속에는 &lt;code&gt;extends&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 프로토타입을 상속하기 위해 내장된 방식으로 &lt;code&gt;instanceof&lt;/code&gt; 를 파괴할 수 없기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
  return this._queue[0];
}

// good
class PeekableQueue extends Queue {
  peek() {
    return this._queue[0];
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;메소드의 반환값에 &lt;code&gt;this&lt;/code&gt;를 돌려주는 것으로, 메소드 체인을 구현할 수 있습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
Jedi.prototype.jump = function () {
  this.jumping = true;
  return true;
};

Jedi.prototype.setHeight = function (height) {
  this.height = height;
};

const luke = new Jedi();
luke.jump(); // =&gt; true
luke.setHeight(20); // =&gt; undefined

// good
class Jedi {
  jump() {
    this.jumping = true;
    return this;
  }

  setHeight(height) {
    this.height = height;
    return this;
  }
}

const luke = new Jedi();

luke.jump()
  .setHeight(20);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;조작된(Custom) &lt;code&gt;toString()&lt;/code&gt; 메소드를 이용할 수도 있지만, 올바르게 작동 하는지, 부작용이 없는지를 꼭 확인하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;class Jedi {
  constructor(options = {}) {
    this.name = options.name || 'no name';
  }

  getName() {
    return this.name;
  }

  toString() {
    return `Jedi - ${this.getName()}`;
  }
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="modules" href="#modules"&gt;10&lt;/a&gt;. 모듈(Modules)&lt;/h3&gt;

&lt;p&gt;비표준 모듈 시스템이 아니라면 항상 (&lt;code&gt;import&lt;/code&gt;/&lt;code&gt;export&lt;/code&gt;) 를 사용하세요. 이렇게 함으로써 원하는 모듈 시스템에 언제든지 트랜스파일(Transpile) 할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 모듈은 곧 미래입니다. 미래를 선점하고 애용합시다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// best
import { es6 } from './AirbnbStyleGuide';
export default es6;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;와일드카드를 이용한 가져오기는 사용하지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? single default export인 것에 주의할 필요가 있기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;import 문에서 직접 추출(Export)하지 않습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 한개의 라인이라 간결하기는 하지만, import와 export하는 방법을 명확하게 구분함으로써 일관성을 유지할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';

// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="iterators-and-generators" href="#iterators-and-generators"&gt;11&lt;/a&gt;. 이터레이터와 제너레이터(Iterators and Generators)&lt;/h3&gt;

&lt;p&gt;이터레이터(Iterators)를 사용하지 않습니다. &lt;code&gt;for-of&lt;/code&gt; 루프 대신  &lt;code&gt;map()&lt;/code&gt;과 &lt;code&gt;reduce()&lt;/code&gt;같은 자바스크립트의 고급함수(higher-order functions)를 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이것은 불변(Immutable)의 규칙을 적용합니다. 값을 반환하는 함수를 처리하는 것이 부작용을 예측하기가 더 쉽습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/no-iterator.html"&gt;&lt;code&gt;no-iterator&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const numbers = [1, 2, 3, 4, 5];

// bad
let sum = 0;
for (let num of numbers) {
  sum += num;
}

sum === 15;

// good
let sum = 0;
numbers.forEach((num) =&gt; sum += num);
sum === 15;

// best (use the functional force)
const sum = numbers.reduce((total, num) =&gt; total + num, 0);
sum === 15;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;현재 제너레이터(Generators)는 사용하지 않는 것이 좋습니다&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? ES5에서 트랜스파일(Transpile)이 올바로 작동하지 않습니다.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;h3&gt;&lt;a id="properties" href="#properties"&gt;12&lt;/a&gt;. 속성(Properties)&lt;/h3&gt;

&lt;p&gt;속성에 접근하려면 점&lt;code&gt;.&lt;/code&gt;을 사용하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/dot-notation.html"&gt;&lt;code&gt;dot-notation&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const luke = {
  jedi: true,
  age: 28,
};

// bad
const isJedi = luke['jedi'];

// good
const isJedi = luke.jedi;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;변수를 사용하여 속성에 접근하려면 대괄호&lt;code&gt;[]&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const luke = {
  jedi: true,
  age: 28,
};

function getProp(prop) {
  return luke[prop];
}

const isJedi = getProp('jedi');&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="variables" href="#variables"&gt;13&lt;/a&gt;. 변수(Variables)&lt;/h3&gt;

&lt;p&gt;변수를 선언할 때는 항상 &lt;code&gt;const&lt;/code&gt;를 사용하세요. 그렇지 않으면 전역 변수로 선언됩니다. 글로벌 네임 스페이스가 오염되지 않도록 캡틴 플래닛(역자주: 환경보호와 생태를 테마로 한 슈퍼히어로 애니메이션)도 경고하고 있습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;하나의 변수 선언에 대해 하나의 &lt;code&gt;const&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이 방법은 새로운 변수를 쉽게 추가할 수 있습니다. 또한 구분 기호의 차이에 의한 &lt;code&gt;;&lt;/code&gt;을 &lt;code&gt;,&lt;/code&gt;로 다시금 대체하는 작업에 대해 신경쓸 필요가 없습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/one-var.html"&gt;&lt;code&gt;one-var&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;먼저 &lt;code&gt;const&lt;/code&gt;를 그룹화하고 그 다음으로 &lt;code&gt;let&lt;/code&gt;을 그룹화 하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이전에 할당 된 변수에 따라 나중에 새로운 변수를 추가하는 경우에 유용하기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;변수를 할당을 필요로 하는 부분에서 적당한 장소에 배치해야 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? &lt;code&gt;let&lt;/code&gt;과 &lt;code&gt;const&lt;/code&gt;는 함수 범위에는 없는 블록 범위이기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// good
function () {
  test();
  console.log('doing stuff..');

  //..other stuff..

  const name = getName();

  if (name === 'test') {
    return false;
  }

  return name;
}

// bad - unnecessary function call
function (hasName) {
  const name = getName();

  if (!hasName) {
    return false;
  }

  this.setFirstName(name);

  return true;
}

// good
function (hasName) {
  if (!hasName) {
    return false;
  }

  const name = getName();
  this.setFirstName(name);

  return true;
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="hoisting" href="#hoisting"&gt;14&lt;/a&gt;. 호이스팅(Hoisting)&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;var&lt;/code&gt; 선언은 할당이 없는 상태로 범위(Scope)의 위로 Hoist될 수 있습니다. 하지만 &lt;code&gt;const&lt;/code&gt;와 &lt;code&gt;let&lt;/code&gt; 선언은 시간적 데드 존(&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let"&gt;Temporal Dead Zones (TDZ)&lt;/a&gt;)이라는 새로운 개념의 혜택을 받고 있습니다. 이것은 왜 typeof가 안전하지 않은가(&lt;a href="http://es-discourse.com/t/why-typeof-is-no-longer-safe/15"&gt;typeof is no longer safe&lt;/a&gt;)를 알고있는 것이 중요합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// (notDefined가 글로벌 변수에 존재하지 않는다고 가정했을 경우)
// 이것은 잘 작동하지 않습니다. 
function example() {
  console.log(notDefined); // =&gt; throws a ReferenceError
}

// 변수를 참조하는 코드 후에 그 변수를 선언한 경우
// 변수가 Hoist되어서 작동합니다.
// 주의: `true` 값 자체는 Hoist할 수 없습니다.
function example() {
  console.log(declaredButNotAssigned); // =&gt; undefined
  var declaredButNotAssigned = true;
}

// 인터프린터는 변수 선언을 범위(Scope)의 시작부분에 Hoist합니다.
// 위의 예는 다음과 같이 다시 작성할 수 있습니다:
function example() {
  let declaredButNotAssigned;
  console.log(declaredButNotAssigned); // =&gt; undefined
  declaredButNotAssigned = true;
}

// const와 let을 사용하는 경우
function example() {
  console.log(declaredButNotAssigned); // =&gt; throws a ReferenceError
  console.log(typeof declaredButNotAssigned); // =&gt; throws a ReferenceError
  const declaredButNotAssigned = true;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;익명 함수 표현식에서는 함수가 할당되기 전에 변수가 Hoist될 수 있습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;function example() {
  console.log(anonymous); // =&gt; undefined

  anonymous(); // =&gt; TypeError anonymous is not a function

  var anonymous = function () {
    console.log('anonymous function expression');
  };
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;명명된 함수의 경우도 마찬가지로 변수가 Hoist될 수 있습니다. 함수이름과 함수본문은 Hoist되지 않습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;function example() {
  console.log(named); // =&gt; undefined

  named(); // =&gt; TypeError named is not a function

  superPower(); // =&gt; ReferenceError superPower is not defined

  var named = function superPower() {
    console.log('Flying');
  };
}

// 함수이름과 변수이름이 같은 경우에도 같은 일이 일어납니다.
function example() {
  console.log(named); // =&gt; undefined

  named(); // =&gt; TypeError named is not a function

  var named = function named() {
    console.log('named');
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;함수 선언은 함수이름과 함수본문이 Hoist됩니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;function example() {
  superPower(); // =&gt; Flying

  function superPower() {
    console.log('Flying');
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;더 자세한 정보는 &lt;a href="http://www.adequatelygood.com/"&gt;Ben Cherry&lt;/a&gt;의 &lt;a href="http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/"&gt;JavaScript Scoping &amp;amp; Hoisting&lt;/a&gt;을 참조하세요.&lt;/p&gt;



&lt;h3&gt;&lt;a id="comparison-operators--equality" href="#comparison-operators--equality"&gt;15&lt;/a&gt;. 조건식과 등가식(Comparison Operators &amp;amp; Equality)&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;==&lt;/code&gt;와 &lt;code&gt;!=&lt;/code&gt; 보다는 &lt;code&gt;===&lt;/code&gt;와 &lt;code&gt;!==&lt;/code&gt;를 사용하세요.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if&lt;/code&gt;와 같은 조건문은 &lt;code&gt;ToBoolean&lt;/code&gt;방법에 의한 강제 형(Type) 변환으로 구분되고 항상 다음과 같은 간단한 규칙을 따릅니다:&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/eqeqeq.html"&gt;&lt;code&gt;eqeqeq&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;&lt;strong&gt;Objects&lt;/strong&gt;는 &lt;strong&gt;true&lt;/strong&gt;로 구분됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Undefined&lt;/strong&gt;는 &lt;strong&gt;false&lt;/strong&gt;로 구분됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Null&lt;/strong&gt;은 &lt;strong&gt;false&lt;/strong&gt;로 구분됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Booleans&lt;/strong&gt;은 &lt;strong&gt;boolean형의 값&lt;/strong&gt;으로 구분됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Numbers&lt;/strong&gt;는 &lt;strong&gt;true&lt;/strong&gt;로 구분됩니다. 그러나, &lt;strong&gt;+0, -0, 또는 NaN&lt;/strong&gt;인 경우 &lt;strong&gt;false&lt;/strong&gt;로 구분됩니다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Strings&lt;/strong&gt;는 &lt;strong&gt;true&lt;/strong&gt;로 구분됩니다. 그러나, 비어있는 &lt;code&gt;''&lt;/code&gt;경우는 &lt;strong&gt;false&lt;/strong&gt;로 구분됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;if ([0]) {
  // true
  // 배열은 객체이므로 true로 구분됩니다.
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;손쉬운 방법(Shortcuts)을 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
if (name !== '') {
  // ...stuff...
}

// good
if (name) {
  // ...stuff...
}

// bad
if (collection.length &gt; 0) {
  // ...stuff...
}

// good
if (collection.length) {
  // ...stuff...
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;더 자세한 내용은 여기를 참조하세요. &lt;a href="http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108"&gt;Truth Equality and JavaScript&lt;/a&gt; by Angus Croll.&lt;/p&gt;


&lt;h3&gt;&lt;a id="blocks" href="#blocks"&gt;16&lt;/a&gt;. 블록(Blocks)&lt;/h3&gt;

&lt;p&gt;여러 줄의 블록은 중괄호&lt;code&gt;{}&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
if (test)
  return false;

// good
if (test) return false;

// good
if (test) {
  return false;
}

// bad
function () { return false; }

// good
function () {
  return false;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;여러 블록에 걸친 &lt;code&gt;if&lt;/code&gt;와 &lt;code&gt;else&lt;/code&gt;를 사용하는 경우, &lt;code&gt;else&lt;/code&gt;는 &lt;code&gt;if&lt;/code&gt;블록의 끝 중괄호&lt;code&gt;{}&lt;/code&gt;와 같은 행에 두세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/brace-style.html"&gt;&lt;code&gt;brace-style&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
if (test) {
  thing1();
  thing2();
}
else {
  thing3();
}

// good
if (test) {
  thing1();
  thing2();
} else {
  thing3();
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="comments" href="#comments"&gt;17&lt;/a&gt;. 주석(Comments)&lt;/h3&gt;

&lt;p&gt;여러 줄의 주석에는 &lt;code&gt;/** ... */&lt;/code&gt;를 사용하세요. 그 안에는 설명과 모든 매개변수와 반환값에 대한 형식과 값을 표기합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {

  // ...stuff...

  return element;
}

// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make(tag) {

  // ...stuff...

  return element;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;한 줄 주석에는 &lt;code&gt;//&lt;/code&gt;를 사용하세요. 주석을 추가하고 싶은 코드의 상단에 배치하세요. 또한 주석 앞에 빈 줄을 넣어주세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const active = true;  // is current tab

// good
// is current tab
const active = true;

// bad
function getType() {
  console.log('fetching type...');
  // set the default type to 'no type'
  const type = this._type || 'no type';

  return type;
}

// good
function getType() {
  console.log('fetching type...');

  // set the default type to 'no type'
  const type = this._type || 'no type';

  return type;
}

// also good
function getType() {
  // set the default type to 'no type'
  const type = this._type || 'no type';

  return type;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;문제를 지적하고 재고를 촉구하거나 문제의 해결책을 제시하는 경우 등, 주석 앞에 &lt;code&gt;FIXME&lt;/code&gt; 또는 &lt;code&gt;TODO&lt;/code&gt; 를 붙이는 것으로 다른 개발자의 빠른 이해를 도울 수 있습니다. 이들은 어떠한 액션을 따른다는 의미에서 일반 댓글과 다를 수 있습니다. 액션은 &lt;code&gt;FIXME -- 해결책 필요&lt;/code&gt; 또는 &lt;code&gt;TODO -- 구현 필요&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;문제에 대한 주석으로 &lt;code&gt;// FIXME:&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;class Calculator extends Abacus {
  constructor() {
    super();

    // FIXME: shouldn't use a global here
    total = 0;
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;해결책에 대한 주석으로 &lt;code&gt;// TODO:&lt;/code&gt;를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;class Calculator extends Abacus {
  constructor() {
    super();

    // TODO: total should be configurable by an options param
    this.total = 0;
  }
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="whitespace" href="#whitespace"&gt;18&lt;/a&gt;. 공백(Whitespace)&lt;/h3&gt;

&lt;p&gt;탭에는 공백 2개를 설정하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/indent.html"&gt;&lt;code&gt;indent&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function () {
∙∙∙∙const name;
}

// bad
function () {
∙const name;
}

// good
function () {
∙∙const name;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;중괄호&lt;code&gt;{}&lt;/code&gt; 앞에 공백을 넣어주세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/space-before-blocks.html"&gt;&lt;code&gt;space-before-blocks&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function test(){
  console.log('test');
}

// good
function test() {
  console.log('test');
}

// bad
dog.set('attr',{
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});

// good
dog.set('attr', {
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;제어 구문(&lt;code&gt;if&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt; 등)의 괄호&lt;code&gt;()&lt;/code&gt; 앞에 공백을 넣어주세요. 함수 선언과 함수 호출시 인수 목록 앞에는 공백을 넣지 않습니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/space-after-keywords.html"&gt;&lt;code&gt;space-after-keywords&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://eslint.org/docs/rules/space-before-keywords.html"&gt;&lt;code&gt;space-before-keywords&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
if(isJedi) {
  fight ();
}

// good
if (isJedi) {
  fight();
}

// bad
function fight () {
  console.log ('Swooosh!');
}

// good
function fight() {
  console.log('Swooosh!');
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;연산자 사이에는 공백이 있습니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/space-infix-ops.html"&gt;&lt;code&gt;space-infix-ops&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const x=y+5;

// good
const x = y + 5;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;파일의 마지막에 빈 줄을 하나 넣어주세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
(function (global) {
  // ...stuff...
})(this);

// bad
(function (global) {
  // ...stuff...
})(this);↵
↵

// good
(function (global) {
  // ...stuff...
})(this);↵
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;메소드 체인이 길어지는 경우 적절히 들여쓰기(indentation) 하세요. 행이 메소드 호출이 아닌 새로운 문장임을 강조하기 위해 선두에 점&lt;code&gt;.&lt;/code&gt;을 배치하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// bad
$('#items').
  find('.selected').
    highlight().
    end().
  find('.open').
    updateCount();

// good
$('#items')
  .find('.selected')
    .highlight()
    .end()
  .find('.open')
    .updateCount();

// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

// good
const leds = stage.selectAll('.led')
    .data(data)
  .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
  .append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;블록과 다음 Statement 사이에 빈 줄을 넣어주세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
if (foo) {
  return bar;
}
return baz;

// good
if (foo) {
  return bar;
}

return baz;

// bad
const obj = {
  foo() {
  },
  bar() {
  },
};
return obj;

// good
const obj = {
  foo() {
  },

  bar() {
  },
};

return obj;

// bad
const arr = [
  function foo() {
  },
  function bar() {
  },
];
return arr;

// good
const arr = [
  function foo() {
  },

  function bar() {
  },
];

return arr;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;블록에 빈 줄을 끼워넣지 않습니다.&lt;/p&gt;

&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/padded-blocks.html"&gt;&lt;code&gt;padded-blocks&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function bar() {

  console.log(foo);

}

// also bad
if (baz) {

  console.log(qux);
} else {
  console.log(foo);

}

// good
function bar() {
  console.log(foo);
}

// good
if (baz) {
  console.log(qux);
} else {
  console.log(foo);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;괄호&lt;code&gt;()&lt;/code&gt; 안에 공백을 추가하지 않습니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/space-in-parens.html"&gt;&lt;code&gt;space-in-parens&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function bar( foo ) {
  return foo;
}

// good
function bar(foo) {
  return foo;
}

// bad
if ( foo ) {
  console.log(foo);
}

// good
if (foo) {
  console.log(foo);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;대괄호&lt;code&gt;[]&lt;/code&gt; 안에 공백을 추가하지 않습니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/array-bracket-spacing.html"&gt;&lt;code&gt;array-bracket-spacing&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);

// good
const foo = [1, 2, 3];
console.log(foo[0]);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;중괄호&lt;code&gt;{}&lt;/code&gt; 안에 공백을 추가합니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/object-curly-spacing.html"&gt;&lt;code&gt;object-curly-spacing&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const foo = {clark: 'kent'};

// good
const foo = { clark: 'kent' };&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;한 줄에 100문자(공백 포함)가 넘는 코드는 피하세요.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 가독성과 유지 보수성을 보장합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/max-len.html"&gt;&lt;code&gt;max-len&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';

// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() =&gt; console.log('Congratulations!')).fail(() =&gt; console.log('You have failed this city.'));

// good
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
  'Whatever wizard constrains a helpful ally. The counterpart ascends!';

// good
$.ajax({
  method: 'POST',
  url: 'https://airbnb.com/',
  data: { name: 'John' },
})
  .done(() =&gt; console.log('Congratulations!'))
  .fail(() =&gt; console.log('You have failed this city.'));&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="commas" href="#commas"&gt;19&lt;/a&gt;. 쉼표(Commas)&lt;/h3&gt;

&lt;p&gt;쉼표로 시작: &lt;strong&gt;제발 그만하세요.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/comma-style.html"&gt;&lt;code&gt;comma-style&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const story = [
    once
  , upon
  , aTime
];

// good
const story = [
  once,
  upon,
  aTime,
];

// bad
const hero = {
    firstName: 'Ada'
  , lastName: 'Lovelace'
  , birthYear: 1815
  , superPower: 'computers'
};

// good
const hero = {
  firstName: 'Ada',
  lastName: 'Lovelace',
  birthYear: 1815,
  superPower: 'computers',
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;마지막에 쉼표: &lt;strong&gt;좋습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/comma-dangle.html"&gt;&lt;code&gt;comma-dangle&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;왜죠? 이것은 git의 diff를 깨끗하게 합니다. 또한 Babel과 같은 트랜스 컴파일러는 끝에 불필요한 쉼표를 알아서 제거합니다. 이것은 기존 브라우저에서 &lt;a href="https://github.com/firejune/javascript/blob/master/es5/README.md#commas"&gt;불필요한 쉼표 문제&lt;/a&gt;를 걱정할 필요가 없다는 것을 의미합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad - git diff without trailing comma
const hero = {
     firstName: 'Florence',
-    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb graph', 'modern nursing']
};

// good - git diff with trailing comma
const hero = {
     firstName: 'Florence',
     lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
};

// bad
const hero = {
  firstName: 'Dana',
  lastName: 'Scully'
};

const heroes = [
  'Batman',
  'Superman'
];

// good
const hero = {
  firstName: 'Dana',
  lastName: 'Scully',
};

const heroes = [
  'Batman',
  'Superman',
];&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="semicolons" href="#semicolons"&gt;20&lt;/a&gt;. 세미콜론(Semicolons)&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;물론 사용합시다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/semi.html"&gt;&lt;code&gt;semi&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
(function () {
  const name = 'Skywalker'
  return name
})()

// good
(() =&gt; {
  const name = 'Skywalker';
  return name;
})();

// good (guards against the function becoming an argument when two files with IIFEs are concatenated)
;(() =&gt; {
  const name = 'Skywalker';
  return name;
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214"&gt;Read more&lt;/a&gt;.&lt;/p&gt;



&lt;h3&gt;&lt;a id="type-casting--coercion" href="#type-casting--coercion"&gt;21&lt;/a&gt;. 형변환과 강제(Type Casting &amp;amp; Coercion)&lt;/h3&gt;

&lt;a href="#21.1" name="21.1"&gt;&lt;/a&gt; 문장의 시작 부분에서 형(Type)을 강제합니다.

&lt;p&gt;String:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;//  =&gt; this.reviewScore = 9;

// bad
const totalScore = this.reviewScore + '';

// good
const totalScore = String(this.reviewScore);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Number: &lt;code&gt;Number&lt;/code&gt;형으로 변환하려면 &lt;code&gt;parseInt&lt;/code&gt;를 사용하세요. 항상 형변환을 위한 기수(radix)를 인수로 전달합니다.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/radix"&gt;&lt;code&gt;radix&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const inputValue = '4';

// bad
const val = new Number(inputValue);

// bad
const val = +inputValue;

// bad
const val = inputValue &gt;&gt; 0;

// bad
const val = parseInt(inputValue);

// good
const val = Number(inputValue);

// good
const val = parseInt(inputValue, 10);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;어떤 이유로 &lt;code&gt;parseInt&lt;/code&gt;가 병목이되고, &lt;a href="http://jsperf.com/coercion-vs-casting/3"&gt;성능적인 이유&lt;/a&gt;에서 Bitshift를 사용해야 하는 경우, 무엇을(what) 왜(why)에 대한 설명을 댓글로 남겨 주세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// good
/**
 * parseInt가 병목이되고 있었기 때문에, 
 * Bitshift 문자열을 수치로 강제로 변환하여 
 * 성능을 향상시킵니다.
 */
const val = inputValue &gt;&gt; 0;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;주의:&lt;/strong&gt; Bitshift를 사용하는 경우 수치는 &lt;a href="http://es5.github.io/#x4.3.19"&gt;64-비트 값들&lt;/a&gt;로 표현되어 있지만, Bitshift를 연산하면 항상 32-비트 단 정밀도로 돌려 주어집니다(&lt;a href="http://es5.github.io/#x11.7"&gt;source&lt;/a&gt;). 32-비트 이상의 값을 비트 이동하면 예상치 못한 행동을 일으킬 가능성이 있습니다. &lt;a href="https://github.com/airbnb/javascript/issues/109"&gt;Discussion&lt;/a&gt;. 부호있는 32-비트 정수의 최대 값은 2,147,483,647입니다:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;2147483647 &gt;&gt; 0 //=&gt; 2147483647
2147483648 &gt;&gt; 0 //=&gt; -2147483648
2147483649 &gt;&gt; 0 //=&gt; -2147483647&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Booleans:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const age = 0;

// bad
const hasAge = new Boolean(age);

// good
const hasAge = Boolean(age);

// good
const hasAge = !!age;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="naming-conventions" href="#naming-conventions"&gt;22&lt;/a&gt;. 명명 규칙(Naming Conventions)&lt;/h3&gt;

&lt;p&gt;하나의 문자로 구성된 이름은 피하세요. 이름에서 의도를 읽을 수 있도록 해야 합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function q() {
  // ...stuff...
}

// good
function query() {
  // ..stuff..
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;객체, 함수 인스턴스에는 camelCase(소문자로 시작)를 사용하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/camelcase.html"&gt;&lt;code&gt;camelcase&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}

// good
const thisIsMyObject = {};
function thisIsMyFunction() {}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;클래스와 생성자는 PascalCase(대문자로 시작)를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function user(options) {
  this.name = options.name;
}

const bad = new user({
  name: 'nope',
});

// good
class User {
  constructor(options) {
    this.name = options.name;
  }
}

const good = new User({
  name: 'yup',
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Private 속성 이름은 앞에 밑줄&lt;code&gt;_&lt;/code&gt;을 사용하세요.&lt;/p&gt;
&lt;p&gt;eslint rules: &lt;a href="http://eslint.org/docs/rules/no-underscore-dangle.html"&gt;&lt;code&gt;no-underscore-dangle&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';

// good
this._firstName = 'Panda';&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;this&lt;/code&gt;에 대한 참조를 저장하지 않습니다. 애로우 함수 또는 Function#bind를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function foo() {
  const self = this;
  return function () {
    console.log(self);
  };
}

// bad
function foo() {
  const that = this;
  return function () {
    console.log(that);
  };
}

// good
function foo() {
  return () =&gt; {
    console.log(this);
  };
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;파일을 하나의 클래스로 추출(Export)할 경우 파일 이름은 클래스 이름과 정확하게 일치해야 합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// file contents
class CheckBox {
  // ...
}
export default CheckBox;

// in some other file
// bad
import CheckBox from './checkBox';

// bad
import CheckBox from './check_box';

// good
import CheckBox from './CheckBox';&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;export-default 함수의 경우, camelCase(소문자로 시작)를 사용하세요. 파일이름은 함수이름과 동일해야 합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;function makeStyleGuide() {
}

export default makeStyleGuide;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;싱글톤(singleton) / 함수 라이브러리(function library) / 단순한 객체(bare object)를 추출하는 경우, PascalCase(대문자로 시작)를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;const AirbnbStyleGuide = {
  es6: {
  }
};

export default AirbnbStyleGuide;&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="accessors" href="#accessors"&gt;23&lt;/a&gt;. 액세서(Accessors)&lt;/h3&gt;

속성에 대한 접근자(Accessor) 함수는 필요하지 않습니다.

&lt;p&gt;접근자 함수가 필요한 경우 &lt;code&gt;getVal()&lt;/code&gt;과 &lt;code&gt;setVal('hello')&lt;/code&gt;로 하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
dragon.age();

// good
dragon.getAge();

// bad
dragon.age(25);

// good
dragon.setAge(25);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;속성이 &lt;code&gt;boolean&lt;/code&gt;의 경우 &lt;code&gt;isVal()&lt;/code&gt; 또는 &lt;code&gt;hasVal()&lt;/code&gt;로 하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
if (!dragon.age()) {
  return false;
}

// good
if (!dragon.hasAge()) {
  return false;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;일관된다면, &lt;code&gt;get()&lt;/code&gt;과 &lt;code&gt;set()&lt;/code&gt; 함수를 작성해도 좋습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;class Jedi {
  constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
  }

  set(key, val) {
    this[key] = val;
  }

  get(key) {
    return this[key];
  }
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="events" href="#events"&gt;24&lt;/a&gt;. 이벤트(Events)&lt;/h3&gt;

&lt;p&gt;(DOM 이벤트, Backbone 이벤트)처럼 자신의 이벤트 페이로드 값을 전달하려면 원시값 대신 해시인수를 전달합니다. 이렇게 하면 나중에 개발자가 이벤트에 관련된 모든 핸들러를 찾아 업데이트하지 않고 이벤트 페이로드에 값을 추가할 수 있습니다. 예를 들면:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
$(this).trigger('listingUpdated', listing.id);

...

$(this).on('listingUpdated', function (e, listingId) {
  // do something with listingId
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;보다 아래쪽이 더 선호됨:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// good
$(this).trigger('listingUpdated', { listingId: listing.id });

...

$(this).on('listingUpdated', function (e, data) {
  // do something with data.listingId
});&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="jquery" href="#jquery"&gt;25&lt;/a&gt;. jQuery&lt;/h3&gt;

&lt;p&gt;jQuery 객체 변수 앞에는 &lt;code&gt;$&lt;/code&gt;로 구분합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
const sidebar = $('.sidebar');

// good
const $sidebar = $('.sidebar');

// good
const $sidebarBtn = $('.sidebar-btn');&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;jQuery의 검색 결과를 캐시합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
function setSidebar() {
  $('.sidebar').hide();

  // ...stuff...

  $('.sidebar').css({
    'background-color': 'pink'
  });
}

// good
function setSidebar() {
  const $sidebar = $('.sidebar');
  $sidebar.hide();

  // ...stuff...

  $sidebar.css({
    'background-color': 'pink'
  });
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;DOM의 검색에는 &lt;code&gt;$('.sidebar ul')&lt;/code&gt; 또는 &lt;code&gt;$('.sidebar &amp;gt; ul')&lt;/code&gt;과 같은 Cascading을 사용하세요. &lt;a href="http://jsperf.com/jquery-find-vs-context-sel/16"&gt;jsPerf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;jQuery 객체의 검색에는 범위가있는 &lt;code&gt;find&lt;/code&gt; 를 사용하세요.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// bad
$('ul', '.sidebar').hide();

// bad
$('.sidebar').find('ul').hide();

// good
$('.sidebar ul').hide();

// good
$('.sidebar &gt; ul').hide();

// good
$sidebar.find('ul').hide();&lt;/code&gt;&lt;/pre&gt;



&lt;h3&gt;&lt;a id="ecmascript-5-compatibility" href="#ecmascript-5-compatibility"&gt;26&lt;/a&gt;. ECMAScript 5 호환성(ECMAScript 5 Compatibility)&lt;/h3&gt;

&lt;a href="https://twitter.com/kangax/"&gt;Kangax&lt;/a&gt;의 ES5 &lt;a href="http://kangax.github.io/es5-compat-table/"&gt;호환성 표&lt;/a&gt;를 참조하세요.



&lt;h3&gt;&lt;a id="ecmascript-6-styles" href="#ecmascript-6-styles"&gt;27&lt;/a&gt;. ECMAScript 6 스타일(ECMAScript 6 Styles)&lt;/h3&gt;

&lt;a href="#27.1" name="27.1"&gt;&lt;/a&gt; 이것은 ES6 명세 링크를 모아 놓은 것입니다.
&lt;ul class="square"&gt;
  &lt;li&gt;&lt;a href="#arrow-functions"&gt;애로우 함수(Arrow Functions)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#constructors"&gt;클래스(Classes)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#es6-object-shorthand"&gt;객체 단축 구문(Object Shorthand)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#es6-object-concise"&gt;속성 단축 구문(Object Concise)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#es6-computed-properties"&gt;계산된 속성 이름(Object Computed Properties)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#es6-template-literals"&gt;템플릿 문자열(Template Strings)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#destructuring"&gt;구조화 대입(Destructuring)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#es6-default-parameters"&gt;기본 매개변수(Default Parameters)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#es6-rest"&gt;레스트(Rest)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#es6-array-spreads"&gt;배열 스프레드(Array Spreads)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#references"&gt;Let과 Const(Let and Const)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#iterators-and-generators"&gt;이터레이터와 제너레이터(Iterators and Generators)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#modules"&gt;모듈(Modules)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;&lt;a id="testing" href="#testing"&gt;28&lt;/a&gt;. 테스팅(Testing)&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;물론 해야 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;function () {
  return true;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;물론 심각하게&lt;/strong&gt;:&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;대부분 테스트 프레임워크를 이용하여 테스트를 작성합니다.&lt;/li&gt;
  &lt;li&gt;작은 기능의 함수를 자주 쓰고 이변이 발생할 수 있는 부분을 최소화하기 위해 노력합니다.&lt;/li&gt;
  &lt;li&gt;stubs와 mocks에 주의하세요. 이 것들로 인해 테스트가 깨지기 쉽습니다.&lt;/li&gt;
  &lt;li&gt;Airbnb는 &lt;a href="https://www.npmjs.com/package/mocha"&gt;&lt;code&gt;mocha&lt;/code&gt;&lt;/a&gt;를 이용하고 있습니다. 작게 분할된 개별 모듈은 &lt;a href="https://www.npmjs.com/package/tape"&gt;&lt;code&gt;tape&lt;/code&gt;&lt;/a&gt;을 사용합니다.&lt;/li&gt;
  &lt;li&gt;지금은 달성할 필요가 없어도 100%의 테스트 커버리지를 목표로하는 것이 좋습니다.&lt;/li&gt;
  &lt;li&gt;버그를 수정할 때 마다 &lt;em&gt;회귀 테스트를 씁니다&lt;/em&gt;. 회귀 테스트 없는 버그 수정은 나중에 반드시 다시 출현할 것입니다.&lt;/li&gt;
&lt;/ul&gt;


 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1794#p1794"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1794"&gt;Hits(114283)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/Zj921ANDbvc" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1794</feedburner:origLink></entry><entry><title type="text">2015년 회고</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/Pca6RWES8ZM/1796" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="JSDoc" /><category term="Nightwatch.js" /><category term="UI Test Automation" /><category term="Electron" /><category term="React" /><category term="Cordova" /><category term="하이브리드 애플리케이션" /><category term="Node.js" /><author><name>파이어준</name></author><updated>2015-12-31T00:16:40-08:00</updated><id>http://firejune.com/1796</id><content type="html">&lt;p&gt;아! 2015년에는 하나도 포스팅한게 없군요. 그래서 땜빵용으로 회고록을 몰래 끼워넣기로 했습니다. 2015년은 변화가 많았던 해였습니다. &lt;a href="https://www.mobizen.com/"&gt;모비즌2 프로젝트&lt;/a&gt;에서 나와 새로운 프로젝트를 제안하고 비전을 현실로 만드는 작업을 수행하고 있습니다. 알서포트에 재입사 한지도 벌써 3년째군요. 새로운 프로젝트에 걸맞은 새로운 기술을들 공부하다 보니 자바스크립트도 아주 많이 변하고 발전했습니다. 특히 문법적으로 많은 변화를 가져온 ES6을 접하면서 문맹이 된듯한 기분이 들었을 정도입니다. &lt;/p&gt;
&lt;h3&gt;API 문서 작성 - JSDoc3&lt;/h3&gt;
&lt;p&gt; 모비즌2 프로젝트에서 발을 빼기 위해(?) &lt;a href="https://github.com/jsdoc3/jsdoc"&gt;JSDoc3&lt;/a&gt;를 이용해서 &lt;a href="http://mobizen.firejune.io/doc/"&gt;API 문서화 프로젝트&lt;/a&gt;를 별도로 진행했습니다. JSDoc은 JavaDoc과 비슷한 것으로 코드에 표현한 주석을 이용하여 API 문서를 자동으로 생성해 주는 도구입니다. 함수의 파라미터 설명은 기본이고 클래스 간 관계 표현이나 이벤트 리스너와 트리거의 링크를 제공하는 등 작성한 코드를 협업자가 별 어려움 없이 이해하고 사용할 수 있도록 할 수 있습니다. 느낀 점들:&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;총 6만 라인이었던 소스코드가 8만 라인 정도로 늘어났습니다. 약 20%~30% 정도 늘어납니다.&lt;/li&gt;
  &lt;li&gt;문서를 작성하면서 구조적으로 관계가 더욱 명확해지는 것을 발견했습니다.&lt;/li&gt;
  &lt;li&gt;LINT에서는 발견하기 어려운 구조적 문제점 발견하고 개선할 기회가 됩니다.&lt;/li&gt;
  &lt;li&gt;템플릿을 이용하여 문서를 뽑아내는 데까지 3개월 정도 걸렸습니다.&lt;/li&gt;
  &lt;li&gt;함수에 매개변수를 설명하는 주석 정도만 충실히 달았어도 이렇게 오래걸리지는 않았을 것이라는 반성을 했습니다.&lt;/li&gt;
  &lt;li&gt;템플릿은 davidshimjs님의 &lt;a href="https://github.com/davidshimjs/jaguarjs-jsdoc"&gt;jaguarjs-jsdoc&lt;/a&gt;이 예쁩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;웹 UI 테스트 자동화 - Nightwatch.js&lt;/h3&gt;
&lt;p&gt; 웹 프로젝트의 UI 테스트 자동화를 도입하기 위해 &lt;a href="https://en.wikipedia.org/wiki/Headless_browser"&gt;Headless browser&lt;/a&gt; 방식의 여러 가지 방법을 검토해 보았는데, 가장 와 닫는 것이 &lt;a href="http://nightwatchjs.org/"&gt;Nightwatch.js&lt;/a&gt;이었습니다. 이 녀석은 End-to-End 테스트가 가능한 녀석으로,  Node.js로 작성되었으며 &lt;a href="https://code.google.com/p/selenium/wiki/JsonWireProtocol"&gt;Selenium WebDriver API&lt;/a&gt;를 이용하여 DOM 요소에 직접 접근하는 방식으로 테스트 코드를 작성할 수 있습니다. 느낀 점:&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;테스트는 필수입니다. 총 작업 시간의 최소 3할 이상을 테스트하는 것에 소비해야 합니다.&lt;/li&gt;
  &lt;li&gt;테스트를 자동화해 놓으면 테스트에 들어가는 시간과 노력의 비용을 낮출 수 있습니다.&lt;/li&gt;
  &lt;li&gt;유닛 테스트는 프로젝트 빌드 직전인 시점에, UI 테스트는 빌드 이후에 자동으로 수행하도록 grunt나 gulp을 이용해서 구성하는 게 좋습니다.&lt;/li&gt;
  &lt;li&gt;모든 기능을 사람이 직접 쓰는 것처럼 테스트 해 주니, 주위 사람들이 신기해합니다.&lt;/li&gt;
  &lt;li&gt;동적 테스트입니다. 정적 테스트보다 변수가 많고 기능이 추가되거나 변경되었을 때 테스트 코드 유지/보수에 추가적인 리소스를 투자해야 합니다.&lt;/li&gt;
  &lt;li&gt;브라우저별로 동일한 테스트를 반복하게 할 경우, 상당히 오래 걸립니다. 별도의 테스트 머신에서 돌리거나 퇴근 직전에 돌리고 가는 것이 좋겠다는 생각이 들었습니다.&lt;/li&gt;
  &lt;li&gt;UI에 웬 스트레스 테스트냐 하는 분도 있겠지만, 가능하긴 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;하이브리드 데스크탑 애플리케이션 개발 - Electron&lt;/h3&gt;
&lt;p&gt; &lt;a href="http://nwjs.io/"&gt;NW.js&lt;/a&gt;와 &lt;a href="http://electron.atom.io/"&gt;Electron&lt;/a&gt; 둘 중에 Electron이 뭔가 더 깔끔한 것 같다는 생각이 들어서 선택했을 뿐입니다. &lt;a href="https://github.com"&gt;GitHub&lt;/a&gt;에서 만들었고, &lt;a href="https://atom.io/"&gt;Atom editor&lt;/a&gt;가 이 녀석을 이용해서 만들어졌고요. 새롭게 시작하는 프로젝트의 데스크탑 애플리케이션이 필요한 부분에 쓰이고 있습니다. 구글 크롬 브라우저와 Node.js를 포함하고 있어서 하나의 브라우저에서 작동하는 것에만 집중할 수 있습니다. 이 밖에도:&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;하이브리드하게 개발하는 모든 장점을 안고 있습니다. 특히, 크로스-플랫폼&lt;/li&gt;
  &lt;li&gt;프로세스를 관리해야 하고 IPC 스타일 통신에 익숙해져야 합니다.&lt;/li&gt;
  &lt;li&gt;크로스-브라우징을 하지 않는 대신에 OS별 특징들을 관리해야 합니다.&lt;/li&gt;
  &lt;li&gt;React와 환상의 궁합입니다. &lt;a href="http://gabrielbull.github.io/react-desktop/demo/"&gt;요런 컴포넌트&lt;/a&gt;가 마구 쏟아진다고 생각해 보세요.&lt;/li&gt;
  &lt;li&gt;네이티브 &lt;code&gt;require&lt;/code&gt;를 WebView에서 사용할 수 있습니다.&lt;/li&gt;
  &lt;li&gt;ES6과 같은 Transpile이 필요한 코드를 더욱 마음껏 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;UI 라이브러리 - React&lt;/h3&gt;
&lt;p&gt; &lt;a href="https://facebook.github.io/react/"&gt;React&lt;/a&gt;는 Facebook 개발자들이 만든 UI 라이브러리입니다. 복잡하고 귀찮은 MVC 개념 따위 과감하게 던져버리고 로직에만 집중할 수 있게 한 새로운 개념 덩어리라고 볼 수 있습니다. 지금까지 OO스럽게 프로그램하려고 상당히 노력해왔는데 그 목마름을  React에서 찾은듯한 느낌입니다. React를 학습하면서 메모했던 내용을 공유합니다.&lt;/p&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;React의 컴포넌트는 소유관계이다. 부모는 자식 컴포넌트의 값이나 상태 등을 변경할 수 있지만, 자식은 부모가 가진 모든 것에 접근할 수 없다.&lt;/li&gt;
  &lt;li&gt;심지어 자식은 자신이 가진 값(props)도 변경할 수 없으며, 오직 부모에 의해 변경되거나 할당 받을 수 있다.&lt;/li&gt;
  &lt;li&gt;HTML 코드를 자바스크립트로 직접 만들어 낼 필요가 없다. state가 변하면(setState) 항상 render가 호출되기 때문에 그때 처리하면 된다.&lt;/li&gt;
  &lt;li&gt;DOM event를 직접 바인딩 할 필요 없다. render 메소드 안에서 컴포넌트 메소드에 직접 매핑할 수 있기 때문이다.&lt;/li&gt;
  &lt;li&gt;render는 무조건 하나의 객체(요소?)만을 리턴해야 한다.&lt;/li&gt;
  &lt;li&gt;show/hide, active/disable 등과 같은 상태성 코드를 작성할 필요가 없다. 만약 이와 같은 코드가 존재한다면 state와 render의 상호작용으로  작동되도록 다시 작성하는 것을 고민해야 한다.&lt;/li&gt;
  &lt;li&gt;마찬가지로 dom-storage에 의존할 필요도 없다. 만약 dom-storage를 이용하는 코드가 존재한다면 props와 render의 상호작용으로 작동되도록 다시 작성하는 것을 고민해야 한다.&lt;/li&gt;
  &lt;li&gt;같은 레벨의 자식들끼리 소통할 수 없으며 오직 부모에 의해 자식들의 행동이 결정되어야 한다.&lt;/li&gt;
  &lt;li&gt;state는 자신이 스스로 값을 변경할 수 있지만, 부모에서 refs 속성으로 접근하여 변경할 수도 있다.&lt;/li&gt;
  &lt;li&gt;Virtual DOM을 이용하기 때문에 DOM 선택자를 이용하는 비용을 줄일 수 있다.&lt;/li&gt;
  &lt;li&gt;JSX와 궁합이 아주 잘 맞지만, 별도의 Transpiler를 필요로 한다.&lt;/li&gt;
  &lt;li&gt;ES6과의 호환성이 좋다. 특히 ES7의 Decorator와 잘 어울린다.&lt;/li&gt;
  &lt;li&gt;state를 너무 남용하면 불필요한 렌더링이 자주 발생하기 때문에 컴포넌트의 값을 props와 state중 어디에 담을 것인지 신중히 정하자.&lt;/li&gt;
  &lt;li&gt;7가지 생명주기를 가진 컴포넌트 메소드들의 명세를 항상 숙지하고 있어야 한다.&lt;/li&gt;
  &lt;li&gt;React 컴포넌트 클래스는 우리가 일반적으로 생각하는 자바스크립 클래스와는 크게 다르며, 모든 메소드는 private으로 작동한다. statics 속성을 이용하여 static 메소드를 작성할 수 있지만, 인스턴스의 this에는 접근할 수 없다.&lt;/li&gt;
  &lt;li&gt;그래서 React 컴포넌트의 메소드를 밑줄(&lt;code&gt;_&lt;/code&gt;)을 이용하여 private으로 구분해야 할 필요가 없다.&lt;/li&gt;
  &lt;li&gt;클래스 상속과 유사한 개념으로 mixins 기능을 제공한다. 이것은 단순히 공통 메소드를 공유하는 목적일 뿐이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;변화&lt;/h3&gt;
&lt;p&gt;개인적인 변화로는 가을에 결혼한 것인데, 덕이 부족하여 생각보다 쉽지 않은 변화된 환경에 적응을 잘하지 못하고 있습니다. 이런저런 변화에 대처하느라 자원를 효율적이지 못하게 소모해 버린 정신없는 한 해였던 것 같습니다.&lt;/p&gt;
&lt;p&gt;2016년에는 일단 정신줄부터 다잡고 진행 중인 프로젝트 성공적으로 마무리 지을 수 있도록 노력하고, 개인적으로는 &lt;a href="https://cordova.apache.org/"&gt;Cordova&lt;/a&gt;를 이용한 하이브리드 모바일 앱 개발에 도전해 보고 싶습니다.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1796#p1796"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1796"&gt;Hits(39296)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/Pca6RWES8ZM" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1796</feedburner:origLink></entry><entry><title type="text">웹에서 사무용 문서 다루기 - WebODF</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/VypOAYWFQdk/1793" /><category term="웹개발" /><category term="자료" /><category term="WebODF" /><category term="ViewerJS" /><category term="ODF" /><category term="KO GmbH" /><category term="그래도 일년에 한번은 써야지..." /><category term="자바스크립트" /><author><name>파이어준</name></author><updated>2014-12-21T18:12:11-08:00</updated><id>http://firejune.com/1793</id><content type="html">&lt;p&gt;&lt;a href="http://kogmbh.com/"&gt;KO GmbH&lt;/a&gt;에 의해서 시작된 &lt;a href="http://www.webodf.org/"&gt;WebODF&lt;/a&gt;는 &lt;a href="http://www.opendocumentformat.org/"&gt;OpenDocument Format&lt;/a&gt;(ODF)를 웹사이트에서 사용할 수 있도록 CSS와 HTML을 이용하여 문서를 출력하거나 생성할 수 있게 하는 자바스크립트 라이브러리입니다. 실시간 협업 편집 서비스인 &lt;a href="http://owncloud.org/"&gt;ownCloud&lt;/a&gt;와 문서를 손쉽게 웹페이지에 추가할 수 있게 하는 &lt;a href="http://viewerjs.org/"&gt;ViewerJS&lt;/a&gt; 등에서 WebODF를 사용하고 있습니다.&lt;/p&gt;
&lt;p&gt;ODF는 여러 업무용 애플리케이션에서 사용할 수 있도록 고안되었습니다. XML기반의 파일 포맷이고 스프레드시트, 차트, 프리젠테이션 그리고 워드 프로세싱 등의 사무용 문서를 저장할 수 있습니다. 이 파일 포맷은 Microsoft의 오피스에서 지원되며, &lt;a href="http://en.wikipedia.org/wiki/Sun_Microsystems"&gt;선 마이크로스시템즈&lt;/a&gt;의 &lt;a href="http://blogs.office.com/"&gt;OpenOffice&lt;/a&gt;에서 사용었고 지금은 순수 웹 브라우저만으로도 다룰 수 있게 된 것입니다.&lt;/p&gt;
&lt;p&gt;이 라이브러리는 뷰어와 에디터로 구분하여 사용할 수 있는데, 단순히 ODF 파일을 보여주기만 할 것이라면  &lt;a href="http://webodf.org/download/webodf.js-0.5.4.zip"&gt;webodf.js&lt;/a&gt;를 로드하고 다음과 같이 코드를 작성하면 됩니다.&lt;/p&gt;
&lt;pre class="html"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
 &amp;lt;head&amp;gt;
  &amp;lt;script src="webodf.js" type="text/javascript" charset="utf-8"&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script type="text/javascript" charset="utf-8"&amp;gt;
function init() {
  var odfelement = document.getElementById("odf"),
      odfcanvas = new odf.OdfCanvas(odfelement);
  odfcanvas.load("myfile.odt");
}
window.setTimeout(init, 0);
  &amp;lt;/script&amp;gt;
 &amp;lt;/head&amp;gt;
 &amp;lt;body&amp;gt;
  &amp;lt;div id="odf"&amp;gt;&amp;lt;/div&amp;gt;
 &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;webodf.js는 여러 개의 자바스크립트가 하나로 합쳐져 만들어진 것입니다. 소스는 &lt;a href="https://github.com/kogmbh/WebODF/"&gt;GitHub 리파지토리&lt;/a&gt;에서 확인하거나 &lt;a href="https://github.com/kogmbh/WebODF/archive/master.zip"&gt;다운로드&lt;/a&gt; 할 수 있으며, &lt;a href="http://webodf.org/tools/index.html#cmake"&gt;CMake&lt;/a&gt;를 이용하여 빌드하는 방법은 다음과 같습니다.&lt;/p&gt;
&lt;pre class="command"&gt;&lt;code&gt;git clone https://github.com/kogmbh/WebODF.git webodf
mkdir build
cd build
cmake ../webodf
make webodf.js-target&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;에디터는 일반 편집과 협업 편집모드를 제공하며, &lt;a href="http://webodf.org/download/wodotexteditor-0.5.4.zip"&gt;Wodo.TextEditor&lt;/a&gt;라는 컴포넌트를 필요로 합니다. Wodo.TextEditor는 &lt;a href="http://dojotoolkit.org/"&gt;Dojo&lt;/a&gt;를 기반으로 작성되었으며, webodf.js를 포함하고 있습니다. 다음과 같은 코드로 페이지에 추가할 수 있습니다.&lt;/p&gt;
&lt;pre class="html"&gt;&lt;code&gt;&amp;lt;head&amp;gt;
&amp;lt;!-- ... --&amp;gt;

&amp;lt;script src="wodotexteditor/wodotexteditor.js" type="text/javascript" charset="utf-8"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;!-- ... --&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;!-- ... --&amp;gt;

&amp;lt;div id="editorContainer" style="width: 600px;height: 600px;"&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;!-- ... --&amp;gt;

&amp;lt;script type="text/javascript"&amp;gt;
// ...

Wodo.createTextEditor('editorContainer', {
    allFeaturesEnabled: true,
    userData: {
        fullName: "Tim Lee",
        color:    "blue"
    }
}, function (err, editor) {
    if (err) {
        // something failed unexpectedly, deal with it (here just a simple alert)
        alert(err);
        return;
    }
    editor.openDocumentFromUrl("document.odt", function(err) {
        if (err) {
            // something failed unexpectedly, deal with it (here just a simple alert)
            alert("There was an error on opening the document: " + err);
        }
    });
});

// ...
&amp;lt;/script&amp;gt;

&amp;lt;!-- ... --&amp;gt;

&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://www.nodejs.org/"&gt;Node.js&lt;/a&gt;용 프로젝트인 &lt;a href="http://etherpad.org/"&gt;Etherpad&lt;/a&gt;에서 제공하는 것과 같은 협업모드 편집은 웹소켓을 이용한 것으로 서버 컴포넌트가 필요하지만 서버-사이드의 소스코드는 공개되어 있지 않습니다. 단, "programs/editor/backend/pullbox"에 클라이언트-사이드에서 데이터 송/수신을 위한 구현한 코드가 포함했으니 참고하라고 하네요. 이 과정은100% 클라이언트(브라우저)에서 발생하며, 데이터를 서버로 전송하지는 않는다고 합니다.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1793#p1793"&gt;Comments(1)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1793"&gt;Hits(643068)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/VypOAYWFQdk" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1793</feedburner:origLink></entry><entry><title type="text">자바스크립트 바이너리 데이터 스트럭쳐 - struct.js</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/6hwt6rHEeaM/1792" /><category term="웹개발" /><category term="자료" /><category term="Struct" /><category term="자바스크립트" /><category term="C/C++" /><category term="GitHub" /><category term="ArrayBuffer" /><category term="DataView" /><category term="HTML5" /><category term="프로젝트" /><category term="struct.js" /><category term="배포" /><author><name>파이어준</name></author><updated>2013-08-01T09:24:06-07:00</updated><id>http://firejune.com/1792</id><content type="html">&lt;p&gt;일반적으로 C언어에서 struct에 의해 구조화된 바이너리 데이터를 자바스크립트에서 사용할 수 있게 변환하고 이를 다시 조작하여 바이너리로 생성하는 과정이 엘레강스(?)하지가 않아서 C의 struct와 유사하게 자바스크립트에서도 구조체를 사용할 수 있도록 작은 유틸리티를 만들었습니다. 만들고 보니, 다른 곳에서도 유용하게 사용될 수 있을 것 같아 공개합니다.&lt;/p&gt;
&lt;p&gt;우선, 자바스크립트만으로 ArrayBuffer를 다루어 보겠습니다. 예제에 사용되는 바이너리 데이터는 0번째 번지에 Uint8 유형의 정수,  1번째 번지에 Int8 유형의 정수, 3번째 번지에는 Uint16 유형의 2바이트짜리 정수이며, 이를 쓰고 다시 읽어내는 것입니다:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// define struct
var struct = {
  foo: 255,
  bar: 127,
  baz: {
    qux: 65535
  }
};

// write arraybuffer from javascript object
var ab = new ArrayBuffer(4);
var dv = new DataView(ab);
dv.setUint8(0, struct.foo);
dv.setInt8(1, struct.bar);
dv.setUint16(2, struct.baz.qux, true);

console.log(dv.buffer);
// =&amp;gt; ArrayBuffer {byteLength: 4, slice: function}

// read data from arraybuffer
var dv2 = new DataView(dv.buffer);
var data = {
  foo: dv2.getUint8(0),
  bar: dv2.getInt8(1),
  baz: {
    qux: dv2.getUint16(2, true)
  }
};

console.log(data);
// =&amp;gt; Obejct {foo: 255, bar: 127, baz: {qux: 65535}}}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;옵셋을 손으로 패딩해야하며 형식이 동일한 구조의 데이터를 읽고 생성하려 했지만 도저히 같다고는 느껴지지 않습니다. 그리고 버퍼의 크기가 크면 클수록 사용성이 떨어지는 문제도 있습니다. struct.js를 사용하면 다음과 같이 코드를 작성할 수 있습니다:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// define struct
var struct = new Struct({
  foo: ['uint8', 255],
  bar: ['int8', 127], 
  baz: {
    qux: ['uint16', 65535]
  }
}, 0, true);

// write arraybuffer from javascript object
var ab = struct.write();
console.log(ab);
// =&amp;gt; ArrayBuffer {byteLength: 4, slice: function}

// read data from arraybuffer
var data = struct.read(ab);
console.log(data);
// =&amp;gt; Obejct {foo: 255, bar: 127, baz: {qux: 65535}}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;옵셋을 자동으로 카운트하고, 자바스크립트 형식으로 작성한  데이터 구조체를 재활용하여 새로운 arraybuffer를 생성하거나 반대로 자바스립트에서 읽을 수 있는 데이터로 만들어 사용하기가 수월합니다. 이는 마치 C에서 생성하는 구조체를 사용하는 느낌입니다.&lt;/p&gt;
&lt;p&gt;속성(키)/[유형(타입), 값(밸류)]로 구조를 작성해야 하며 '속성/유형'만 지정하면 버퍼를 작성하는 경우 기본값이 할당됩니다. 즉, '밸류'는 &lt;code&gt;write&lt;/code&gt; 메서드를 이용하여 ArrayBuffer를 생성하는 곳에만 사용되며, 단순히 &lt;code&gt;read&lt;/code&gt; 메서드로 데이터를 읽기만 한다거나, 속성마다 특정한 값을 설정할 필요가 없는 경우라면 타입만 지정해도 된다는 의미입니다. 다음은 &lt;code&gt;read&lt;/code&gt; 메서드의 두 번째 인자에 사용자 지정 옵셋을 입력하여 동일한 형식의 데이터가 복수로 담긴 청크를 처리하는 모습입니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;/**
 * read multiple data with custom offset
 */

var struct = new Struct({
  sig: 'uint8',
  mimeType: 'uint8',
  id: 'uint16',
  byteLength: 'uint32'
});

...

function parseBinary(chunk, count, callback) {
  var offset = 0;
  for (var index = 0; index &amp;lt; count; index++) {
    var meta = struct.read(chunk, offset)
      , buffer = chunk.slice(
        offset += struct.byteLength,
        offset += meta.byteLength
      );

    callback(meta, new Uint8Array(buffer));
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;write&lt;/code&gt; 메서드에 변경할 내용이 담긴 객체를 인자로 전달하여 복수의 값을 갱신할 수 있도 있습니다. 입력 객체는 하위 구조의 값까지 모두 비교하여 값을 할당하기 때문에 다음과 같이 작성해도 무방합니다.(jquery의 $.extend와는 개념이 다름)&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// define struct
var struct = new Struct({
  foo: ['uint8', 255],
  bar: 'int8', 
  baz: {
    qux: ['uint16', 65535],
    quux: ['uint32', 0]
  }
}, 1, true);

// update values and write arraybuffer
var ab = struct.write({
  foo: 0,
  baz: {
    quux: 4294967295
  }
});
// write =&amp;gt; ArrayBuffer {byteLength: 8, slice: function}
// read =&amp;gt; Obejct {foo: 0, bar: 1, baz: {qux: 65535, quux: 4294967295}}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;끝으로, 하나의 속성에 멀티-바이트 타입 배열을 지정할 수 있도록 했습니다. 각각의 번지마다 연속된 값(문자열 또는 배열)을 지정할 수 있으며, 속성에 지정되는 배열의 마지막에 버퍼의 크기를 지정하거나 생략한 경우 그 크기를 자동으로 계산하합니다. 그리고 문자열 형식으로 선언된 속성은 버퍼에서 값을 읽어 올 때 정수들을 모두 문자열로 자동 변환하여 반환합니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;/**
 * create struct with multi-byte value
 */

var struct = new Struct({
  foo: ['uint16', [0xffff, 4095]],
  bar: ['uint8', 'firejune', 8]
});

var ab = struct.write();
// =&amp;gt; ArrayBuffer {byteLength: 12, slice: function}

var obj = struct.read(ab);
// =&amp;gt; Object {bar: [65535, 4095], qux: "firejune"}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;처음 구성한 구조의 크기(유형)는 변경될 수 없으며, 값만 갱신할 수 있는 규칙을 가집니다. 이 작은 유틸리티의 이름은 거창하게도 &lt;a href="https://github.com/firejune/struct.js/blob/master/struct.js"&gt;struct.js&lt;/a&gt;이며, MIT 라이센스를 따릅니다. 대략적인 사용법과 소스코드를 &lt;a href="https://github.com/firejune/struct.js"&gt;GitHub에 올려&lt;/a&gt; 두었으니 필요하신 분은 맘껏 사용하세요.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1792#p1792"&gt;Comments(2)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1792"&gt;Hits(1605360)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/6hwt6rHEeaM" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1792</feedburner:origLink></entry><entry><title type="text">Signed와 Unsigned 차이</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/ZrGoG_0zIrM/1791" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="Unsigned" /><category term="Signed" /><category term="차이점" /><category term="C" /><category term="Uint8" /><category term="Int8" /><category term="ArrayBuffer" /><category term="HTML5" /><author><name>파이어준</name></author><updated>2013-07-31T09:21:02-07:00</updated><id>http://firejune.com/1791</id><content type="html">&lt;p&gt;자바스크립트에서 버퍼를 읽거나 쓰는 예문들을 볼때 C에서 사용되는 'signed'와 'unsigned'라는 키워드에 비유하는 내용을 자주 접하게 됩니다. 이게 무엇이고 왜 구분을 해야 하는 것인지를 몰라서 우리 팀장님께 커피 한 잔 사드리고 특강을 받았습니다. 제가 이해하기 쉽게 '음수를 표현하느냐 안 하느냐의 차이'라고 알려 주셨고, 메모리에 비트를 기록하는 방식이 다르다고 했습니다. "더 자세히 설명해 주세요~"했더니 CPU가 어쩌니 어셈블리가 저쩌니 한 귀로 듣고 흘려 버릴수 밖에 없는 내용이어서... 나중을 대비해 나름 이해한 내용을 정리합니다.&lt;/p&gt;
&lt;p&gt;예를 들어 C언어에서는 다음과 같이 8비트 정수 타입을 선언할 수 있습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;signed char
unsigned char&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;unsigned char는 비트를 투명하게 볼 수 있는 특성이 있으며, 임의의 메모리에 바이트 단위로 접근해서 값을 다룰 수 있습디다. 이 경우에는 unsigned char를 사용하는 것이 강제됩니다. 그리고  signed char는 unsigned char와 값이 같아도 같지 않은 경우가 발생할 수도 있습니다. 왜냐하면 signed는 음수 표현을 위해 2의 보수 체계를 사용하고 부호 비트(MSB)가 필요하기 때문입니다.(0 이면 양수 1 이면 음수) 그래서 부호 비트가 없는 unsigned는 양수 범위를 두 배로 늘리는 역활을 한답디다. 즉, char 형식은 8비트이므로 signed char은 -128~127의 범위를 표현할 수 있고 unsigned chare은 0~255의 표현범위를 가지는 것입니다.&lt;/p&gt;
&lt;p&gt;조금더 이해하기 쉽게 그림으로 예를 들어봅시다. 정수 3인 1바이트(8비트)를 2진수로 기록하면 &lt;span style="color:#d10"&gt;0&lt;/span&gt;&lt;span style="color:#d10"&gt;0&lt;/span&gt;&lt;span style="color:#f00"&gt;0&lt;/span&gt;&lt;span style="color:#f00"&gt;0&lt;/span&gt;&lt;span style="color:#f73"&gt;0&lt;/span&gt;&lt;span style="color:#f73"&gt;0&lt;/span&gt;&lt;span style="color:#f90"&gt;1&lt;/span&gt;&lt;span style="color:#f90"&gt;1&lt;/span&gt;이 됩니다. Unsigned에서는 다음과 같이 부호가 할당되겠죠.&lt;/p&gt;
&lt;table class="grid border" style="font-size: 13px;"&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;span style="color:#d10"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#d10"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f00"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f00"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f73"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f73"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f90"&gt;1&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f90"&gt;1&lt;/span&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Signed는 음수를 표현하기 위해 제일 앞 하나의 비트를 소비한다고 했습니다. 그럼 정수 3은 이렇게 되겠군요.&lt;/p&gt;
&lt;table class="grid border" style="font-size: 13px;"&gt;
  &lt;tr&gt;
    &lt;td style="background: #aaa"&gt;0&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#d10"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f00"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f00"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f73"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f73"&gt;0&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f90"&gt;1&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f90"&gt;1&lt;/span&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;a href="http://www.binaryconvert.com/index.html"&gt;온라인 이진수-정수 변환기&lt;/a&gt;를 사용해 보면 금방 이해할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;(나도 언젠가는 CPU의 마음과 메모리의 정신을 이해할 수 있겠...)&lt;/del&gt;&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1791#p1791"&gt;Comments(3)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1791"&gt;Hits(1605226)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/ZrGoG_0zIrM" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1791</feedburner:origLink></entry><entry><title type="text">빅엔디안과 리틀엔디안 개념</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/w7tqNFzWme0/1790" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="빅엔디안" /><category term="리틀엔디안" /><category term="바이너리" /><category term="DataView" /><category term="HTML5" /><category term="...시발...뭐래니..." /><author><name>파이어준</name></author><updated>2013-07-23T09:09:38-07:00</updated><id>http://firejune.com/1790</id><content type="html">&lt;p&gt;자바스크립트의 DataView를 이용하여 바이너리를 메모리에 쓰거나(Write) 전송(Send)하면서 삽질하는 과정들이 저에게는 마냥 신세경입니다. &lt;a href="//firejune.com/1787/HTML5+ArrayBuffer+API+%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0"&gt;ArrayBuffer API&lt;/a&gt;를 소개하면서 간소하게 언급했던 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/DataView"&gt;DataView 인터페이스&lt;/a&gt;에서 등장하는 리틀엔디안과 빅엔디안에 대한 개념을 탑재해 보겠습니다.&lt;/p&gt;
&lt;p&gt;컴퓨터에서 어떤 크기의 데이터를 메모리에 저장할 때 바이트 단위로 나누어 저장합니다. CPU 아키텍처에 따라 바이트 저장순서가 달라질 수 있기 때문에 두 가지로 나뉘는 데 그것이 바로 '리틀-엔디안'과 '빅-엔디안' 방식입니다. 어떤 CPU에서는 이 두 가지 방식을 모두 지원하도록 구성할 수도 있답디다.&lt;/p&gt;
&lt;h3&gt;리틀-엔디안 (Little-Endian)&lt;/h3&gt;
&lt;p&gt;낮은(시작) 주소에 하위 바이트부터 기록,  Intel CPU 계열&lt;br&gt;
예) 32비트형 (4바이트) 값: 0x&lt;span style="color:#d10"&gt;01&lt;/span&gt;&lt;span style="color:#f00"&gt;02&lt;/span&gt;&lt;span style="color:#f73"&gt;03&lt;/span&gt;&lt;span style="color:#f90"&gt;04&lt;/span&gt;&lt;/p&gt;
&lt;table class="grid border" style="font-size: 13px;"&gt;
  &lt;tr&gt;
    &lt;td style="background: #eee"&gt;하위 주소&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f90"&gt;0x04&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f73"&gt;0x03&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f00"&gt;0x02&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#d10"&gt;0x01&lt;/span&gt;&lt;/td&gt;
    &lt;td style="background: #eee"&gt;상위 주소&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;h3&gt;빅-엔디안 (Big-Endian)&lt;/h3&gt;
&lt;p&gt;낮은(시작) 주소에 상위 바이트부터 기록, Sparc / RISC CPU 계열&lt;br&gt;
예) 32비트형 (4바이트) 값: 0x&lt;span style="color:#d10"&gt;01&lt;/span&gt;&lt;span style="color:#f00"&gt;02&lt;/span&gt;&lt;span style="color:#f73"&gt;03&lt;/span&gt;&lt;span style="color:#f90"&gt;04&lt;/span&gt;&lt;/p&gt;
&lt;table class="grid border" style="font-size: 13px;"&gt;
  &lt;tr&gt;
    &lt;td style="background: #eee"&gt;하위 주소&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#d10"&gt;0x01&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f00"&gt;0x02&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f73"&gt;0x03&lt;/span&gt;&lt;/td&gt;
    &lt;td&gt;&lt;span style="color:#f90"&gt;0x04&lt;/span&gt;&lt;/td&gt;
    &lt;td style="background: #eee"&gt;상위 주소&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;빅엔디안은 우리가 평소에 보던 방식으로 메모리에 쓴다고 생각하면 되고 리틀엔디안은 뒤집혀서 쓴다고 이해하면 되겠죠? 그럼 왜 빅엔디안으로 안 쓰는 걸까요? 그 이유는 산술연산유닛(ALU)에서 메모리를 읽는 방식이 메모리 주소가 낮은 쪽에서부터 높은 쪽으로 읽기 때문에 산술 연산의 수행이 더 쉽습니다. 또한, 데이터를 다른 시스템으로 전송할 때 서로 다른 데이터 저장 방식의 시스템끼리 통신하게 되면 전혀 엉뚱한 값을 주고받기 때문이랍니다.&lt;/p&gt;
&lt;p&gt;자바스크립트의 DataView 인터페이스는 특정 파일 또는 수신된 바이너리 데이터를 읽고 쓸 수 있도록 설계되었습니다. 브라우저가 작동 중인 CPU에 상관 없이 일관되고 올바른 결과를 얻을 수 있도록 작동하기 위해 모든 값의 모든 접근에 엔디안(Endianness)을 지정해야 합니다.
&lt;pre class="javascript"&gt;&lt;code&gt;var buffer = new ArrayBuffer(12);
var dv = new DataView(buffer);
dv.setInt32(0, 25, false); // set big-endian int32 at byte offset 0 to 25
dv.setInt32(4, 25); // set big-endian int32 at byte offset 4 to 25
dv.setFloat32(8, 2.5, true); // set little-endian float32 at byte offset 8 &lt;/code&gt;&lt;/pre&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1790#p1790"&gt;Comments(3)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1790"&gt;Hits(894085)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/w7tqNFzWme0" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1790</feedburner:origLink></entry><entry><title type="text">자바스크립트에서의 BLOB 객체</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/NdkCXNs3wYc/1788" /><category term="웹개발" /><category term="자료" /><category term="HTML5" /><category term="Blob" /><category term="File API" /><category term="FileReader API" /><category term="MediaSource API" /><category term="URL.createObjectURL" /><category term="자바스크립트" /><category term="Base64 꺼져" /><author><name>파이어준</name></author><updated>2013-07-17T07:18:33-07:00</updated><id>http://firejune.com/1788</id><content type="html">&lt;p&gt;&lt;a href="//firejune.com/1787/HTML5+ArrayBuffer+API+%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0"&gt;이전 포스트&lt;/a&gt;에서 ArrayBuffer에 대해 알아보았습니다. 일반적으로 컴퓨팅에서 말하는 블랍(BLOB)은 대체로 커다란 파일을 일컫는 말이며, 사운드, 비디오와 같은 멀티미디어 데이터를 객체로 다루기 위해 주로 사용되는 것으로 알려졌습니다. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File"&gt;File API&lt;/a&gt;가 생겨나면서 동시에 등장한 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob"&gt;BLOB&lt;/a&gt;은 자바스크립트에서 조금 다른 의미로 해석되는데 지금은 파일을 다루기 위한 메모리 참조 수단 정도입니다.&lt;/p&gt;
&lt;p&gt;원래 BLOB 객체는 스트림을 이용해서 읽기/쓰기를 하는 것이 정설이지만 자바스크립트에서는 바이너리로 수신한 청크(Chunk)들을 한방에 생성하여 File로 인식하는 용도로 자주 사용되며, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/FileReader"&gt;FileReader API&lt;/a&gt;와 함께 사용하여 자바스크립트로 버퍼링을 제어하는 수준의 미디어 스트리밍을 구현할 수 있게 합니다. 또한, 이를 DOM으로 연결하기 위한 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/window.URL.createObjectURL"&gt;URL.createObjectURL&lt;/a&gt;이 존재합니다. 다음 예제는 AJAX에서 BLOB 데이터 형식으로 수신한 이미지를 DOM에 참조시키고 이미지가 출력된 후 &lt;code&gt;URL.revokeObjectURL&lt;/code&gt;을 이용하여 BLOB을 해제하는 것입니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;var req = new XMLHttpReqest();
xhr.open("GET", "download?name=" + name, true);
xhr.responseType = "blob";
xhr.onreadystatechange = function () {
  if (xhr.readyState == xhr.DONE) {
    var blob = xhr.reponse;
    var image = document.getElementById("my-image");
    image.addEventListener("load", function (evt) {
      URL.revokeObjectURL(evt.target.src);
    }
    image.src = URL.createObjectURL(blob);
  }
}
xhr.send();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node.js의 &lt;a href="http://binaryjs.com/"&gt;BinaryJS&lt;/a&gt;모듈을 이용하여 스트림으로 수신한 Chunk를 배열로 취합하고 스트림이 끝나는 시점에 &lt;code&gt;Blob&lt;/code&gt;으로 변환하여 DOM으로 참조시키는 예제:&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// Connect to Binary.js server
var client = new BinaryClient('ws://localhost:9000');
// Received new stream from server!
client.on('stream', function(stream, meta){    
  // Buffer for parts
  var parts = [];
  // Got new data
  stream.on('data', function(arrayBuffer){
    parts.push(arrayBuffer);
  });
  stream.on('end', function(){
    // Display new data in browser!
    var img = document.createElement("img");
    img.src = URL.createObjectURL(new Blob(parts));
    document.body.appendChild(img);
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다음 예제는 &lt;a href="http://html5-mediasource-api.googlecode.com/svn/tags/0.1/draft-spec/mediasource-draft-spec.html"&gt;MediaSource API&lt;/a&gt;와 연계하여 작동하는 것을 가정한 코드로서 현재 구글 크롬 브라우저에서 구현 중인 명세이고 &lt;a href="http://webm.html5.org/"&gt;WebM&lt;/a&gt; 포맷(vorbis, vp8 인코딩)으로 만들어진 미디어만 &lt;a href="//firejune.io:8081"&gt;정상적으로 작동(웹 소켓 바이너리 통신)&lt;/a&gt;하는 것을 직접 확인한 것입니다. 처음으로 수신한 Chunk의 Blob 객체를 생성하고 FileReader API에서 뒤이어 수신하는 Chunk를 추가(Append)하여 미디어 스트리밍을 구현한 예제입니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;var xhr = new XMLHttpRequest();
xhr.open('GET', 'test.webm', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
  if (xhr.status != 200) {
    alert("Unexpected status code " + xhr.status + " for " + url);
    return false;
  }
  callback(new Uint8Array(xhr.response));
};
xhr.send();

function callback(uInt8Array) {
  var file = new Blob([uInt8Array], {type: 'video/webm'});
  var chunkSize = Math.ceil(file.size / NUM_CHUNKS);
  console.log('num chunks:' + NUM_CHUNKS);
  console.log('chunkSize:' + chunkSize + ', totalSize:' + file.size);
  // Slice the video into NUM_CHUNKS and append each to the media element.
  var i = 0;
  (function readChunk_(i) {
    var reader = new FileReader();
    // Reads aren't guaranteed to finish in the same order they're started in,
    // so we need to read + append the next chunk after the previous reader
    // is done (onload is fired).
    reader.onload = function(e) {
      sourceBuffer.append(new Uint8Array(e.target.result));
      console.log('appending chunk:' + i);
      if (i == NUM_CHUNKS - 1) {
        mediaSource.endOfStream();
      } else {
        if (video.paused) {
          video.play(); // Start playing after 1st chunk is appended.
        }
        readChunk_(++i);
      }
    };
    var startByte = chunkSize * i;
    var chunk = file.slice(startByte, startByte + chunkSize);
    reader.readAsArrayBuffer(chunk);
  })(i); // Start the recursive call by self calling.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기에 서버에서 재생시간 대비 클라이언트의 수신 속도를 측정하여 패킷을 절약하는 작업만 추가로 해주면 레알 미디어 스트리밍이 구현되는 것입니다.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1788#p1788"&gt;Comments(1)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1788"&gt;Hits(117528)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/NdkCXNs3wYc" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1788</feedburner:origLink></entry><entry><title type="text">HTML5 ArrayBuffer API 이해하기</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/_UsWA4mj6bs/1787" /><category term="웹개발" /><category term="자료" /><category term="HTML5" /><category term="ArrayBuffer" /><category term="DataView" /><category term="StringView" /><category term="API" /><category term="바이너리" /><category term="WHAT THE HELL" /><category term="자바스크립트" /><author><name>파이어준</name></author><updated>2013-07-15T08:17:49-07:00</updated><id>http://firejune.com/1787</id><content type="html">&lt;p&gt;최근 자바스크립트로 바이너리를 다룰 일이 생겨서 이런저런 삽질 중에 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/ArrayBuffer"&gt;ArrayBuffer API&lt;/a&gt;는 짚고 넘어가야겠다는 생각이 들어 학습차 정리합니다. HTML5에서 지원하는 ArrayBuffer를 이용하여 Ajax 또는 WebSocket을 통해 바이너리 데이터를 서버와 브라우저 간에 송/수신할 수 있습니다. 이를테면, 서버와 클라이언트에서 &lt;a href="https://en.wikipedia.org/wiki/Base64"&gt;Base64&lt;/a&gt;로 인코딩/디코딩하는 비용을 줄이거나 사진에서 &lt;a href="http://code.flickr.net/2012/06/01/parsing-exif-client-side-using-javascript-2/"&gt;EXIF 정보를 뽑아&lt;/a&gt;내거나, MediaSource API를 함께 사용하여 &lt;a href="http://html5-demos.appspot.com/static/media-source.html"&gt;동영상 스트리밍&lt;/a&gt;을 구현할 수도 있으며, WebSocket으로 파일을 업로드하는 등 다양하게 활용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;일반적인 스트림(Stream) 객체에 의해 순차적으로 발생하는 이벤트로 전달되는 ArrayBuffer는 우리가 소위 말하는 Chunk 데이터이며 메모리에 위치하게 됩니다. 이것을 자바스크립트에서 다룰 수 있도록 하기 위해서 타입 배열 뷰(Typed array views)로 만들 수 있는데, 이는 ArrayBufferView 클래스로 생성할 수 있으며 그 종류는 다음과 같습니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// Signed integer arrays.
var i8 = new Int8Array(64)             // 1 byte,  8-bit twos complement signed integer
var i16 = new Int16Array(32)           // 2 bytes, 16-bit twos complement signed integer
var i32 = new Int32Array(16)           // 4 bytes, 32-bit twos complement signed integer

// Unsigned integer arrays.
var u8 = new Uint8Array(64)            // 1 byte,  8-bit unsigned integer
var u16 = new Uint16Array(32)          // 2 bytes, 16-bit unsigned integer
var u32 = new Uint32Array(16)          // 4 bytes, 32-bit twos complement signed integer
var pixels = new Uint8ClampedArray(64) // 1 byte,  8-bit unsigned integer

// Floating point arrays.
var f32 = new Float32Array(16)         // 4 bytes, 32-bit IEEE floating point number
var f64 = new Float64Array(8)          // 8 bytes, 64-bit IEEE floating point number&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;멋모르고 프로그래밍 세계에 발을 들여 놓은 저로서는(보는 것 만으로도 화가 남) 이렇게 많은 형식이 어디에 어떻게 쓰이는지 막연하기만 했는데, &lt;a href="http://www.insightbook.co.kr/books/programming-insight/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C"&gt;O'Reilly에서 출판한 Javascript: The Definitive Guide(자바스크립트 완벽 가이드) 6판 챕터 22&lt;/a&gt;에 뭔가 감을 잡을 수 있는 예문(문제 되면 삭제하겠습니다)이 있더군요.
&lt;pre class="javascript"&gt;&lt;code&gt;var matrix = new Float64Array(9);   // A 3x3 matrix
var 3dPoint = new Int16Array(3);    // A point in 3D space
var rgba = new Uint8Array(4);       // A 4-byte RGBA pixel value
var sudoku = new Uint8Array(81);    // A 9x9 sudoku board&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이것은 그냥 처리할 바이너리가 메모리에서 비트 연산에 효율적으로 사용될 유형을 선택해 주면 되는 정도로 어렴풋이 이해했습니다. 더 많은 내공이 쌓여야 이 타입들에 대한 개념을 탑재할 수 있을 듯합니다. ArrayBuffer를 다루는 더 쉬운 방법은 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/DataView"&gt;DataView&lt;/a&gt;와  &lt;a href="https://developer.mozilla.org/en-US/docs/Code_snippets/StringView"&gt;StringView&lt;/a&gt;를 이용하는 것입니다. DataView는 ArrayBuffer로부터 값을 읽거나 쓸 수 있도록 로우 레벨의 인터페이스를 제공하며, StringView는 문자열에 대한 C 스타일의 인터페이스를 제공합니다. 끝으로 준비한 예제는 비동기로 수신한 바이너리를 자바스크립트에서 사용할 수 있는 배열로 변환하여 어딘가에 써먹는 예제입니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;var req = new XMLHttpRequest();
req.open('GET', "/your/binary/data");
req.responseType = "arraybuffer";
req.onload = function () {
    if (req.status != 200) {
        alert("Unexpected status code " + req.status);
        return false;
    }
    var buffer = req.response;
    var dataView = new DataView(buffer);
    var vectorLength = dataView.getUint8(0);
    var width = dataView.getUint16(1); // 0 + uint8 = 1 bytes offset
    var height = dataView.getUint16(3); // 0 + uint8 + uint16 = 3 bytes offset
    var vectors = new Float32Array(width * height * vectorLength);
    for (var i = 0, off = 5; i &amp;lt; vectors.length; i++, off += 4) {
        vectors[i] = dataView.getFloat32(off);
    }
    ...
}
req.send();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;참고로 C언어에서는 다음과 같이 타입(Typedefs)을 선언합니다.&lt;/p&gt;
&lt;pre class="c"&gt;&lt;code&gt;typedef unsigned char       uint8 // 8 bit unsigned integer
typedef          char        int8 // 8 bit signed integer 
typedef unsigned short     uint16 // 16 bit unsigned integer 
typedef          short      int16 // 16 bit signed integer 
typedef unsigned int  	   uint32 // 32 bit unsigned integer 
typedef          int        int32 // 32 bit signed integer 
typedef unsigned long 	   uint32 // 32 bit unsigned integer 
typedef          long       int32 // 32 bit signed integer&lt;/code&gt;&lt;/pre&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1787#p1787"&gt;Comments(4)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1787"&gt;Hits(179709)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/_UsWA4mj6bs" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1787</feedburner:origLink></entry><entry><title type="text">3D 드로잉 펜 - 3Doodler</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/aLZiiMj_LAM/1786" /><category term="정보" /><category term="3Doodler" /><category term="3D 드로잉 펜" /><author><name>파이어준</name></author><updated>2013-02-21T03:09:57-08:00</updated><id>http://firejune.com/1786</id><content type="html">&lt;p&gt;&lt;iframe width="664" height="374" style="border:1px solid #aaa" src="//www.youtube.com/embed/DQWyhezIze4?rel=0"  allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.the3doodler.com/"&gt;3Doodler&lt;/a&gt;는 세계 최초이자 유일한 공기중에 입체로 그림을 그릴 수 있는 펜입니다. 전원에 연결하고 몇 분 정도 기다리면 바로 사용할 수 있으며, 소프트웨어나 컴퓨터를 필요로 하지 않습니다. 심은  ABS 플라스틱(3D 프린터에서 흔히 사용되는 재료)를 이용한다고 합니다.&lt;/p&gt;
&lt;p&gt;3Doodler는 어린이용 장난감으로는 부적합하며, 가격은 50달러군요.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1786#p1786"&gt;Comments(3)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1786"&gt;Hits(203230)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/aLZiiMj_LAM" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1786</feedburner:origLink></entry><entry><title type="text">스타크래프트2 - 군단의 심장 시네마틱</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/j5lnFzJSwis/1784" /><category term="게임/오락" /><category term="문화" /><category term="스타크래프트2" /><category term="시네마틱" /><category term="동영상" /><category term="퀄리티 쩔어" /><author><name>파이어준</name></author><updated>2013-01-22T06:50:46-08:00</updated><id>http://firejune.com/1784</id><content type="html">&lt;p&gt;&lt;iframe id="sc2" width="666" height="375" src="//www.youtube.com/embed/MVbeoSPqRs4" allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;퀄리티 쩌네요. &lt;a href="//www.youtube.com/embed/TroQ7fKj7hE" onclick="Event.stop(event); $('sc2').src = this.href"&gt;여기를 클릭&lt;/a&gt;하면 한글 더빙판으로 토글합니다.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1784#p1784"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1784"&gt;Hits(182047)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/j5lnFzJSwis" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1784</feedburner:origLink></entry><entry><title type="text">웹 분석도구 Piwik 한국어 현지화 완료!</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/weLNbgcc_IM/1783" /><category term="뻘짓" /><category term="일상" /><category term="Piwik" /><category term="웹 분석도구" /><category term="오픈 소스" /><category term="Analytics" /><category term="번역" /><category term="PHP" /><category term="리소스" /><category term="한국어" /><category term="현지화" /><category term="프로젝트" /><author><name>파이어준</name></author><updated>2013-01-21T08:39:27-08:00</updated><id>http://firejune.com/1783</id><content type="html">&lt;figure class="image-align"&gt;&lt;a href="http://piwik.org/"&gt;&lt;img src="/attach/0122/130122004029865641/220348.png" width="666" height="355"  alt="Piwik.org.png" class="hasborder"&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href="http://piwik.org/"&gt;Piwik&lt;/a&gt;은 오픈 소스 기반의 웹사이트 분석도구입니다. 구글 Analytics의 대안으로 손꼽히고 있으며, 비교적 트래픽이 많은 사이트도 소화할 수 있다고 합니다. 최신 버전인 1.10.1의 한국어 현지화를 방금 마무리했어요. 작업을 시작할 무렵 김종인님이 36% 진행해 놓은 것에 바톤을 이어받아 100% 까지 총 2,153개의 리소스 번역을 한 주에 걸쳐 완료했습니다. 다음 버전 릴리즈 때 반영되겠지만 먼저 적용해 보실 분들은 &lt;a href="//firejune.com/download/457004.phps"&gt;여기에서 다운로드&lt;/a&gt; 할 수 있습니다.(확장자를 php로 바꾸고 piwik/lang/ 디렉토리에 덮어쓰세요)&lt;/p&gt;
&lt;figure class="image-align"&gt;&lt;img src="/attach/0122/130122004029865641/779823.png" width="666" height="382"  alt="piwik translations.png" class="hasborder"&gt;&lt;br&gt;
&lt;figcaption class="cap1" style="margin:4px 0 3px 0;"&gt;뿌..., 뿌듯함!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;직접 사용해 보면서 문맥이 이상한 부분들은 최대한 수정하고 있지만 미처 발견하지 못한 오역이나 이상한 부분이 있다면 알려주세요. 즉시 수정해서 반영하도록 하겠습니다. 그런데 이걸 왜했냐고요? 가끔은 자기 자신을 무작정 괴롭혀 보는 것도 기분전환에 큰 도움이 되더군요….&lt;/p&gt;
&lt;p&gt;덧. Piwik을 설치하고 알아낸 유용한 것들을 정리해 보았습니다. &lt;/p&gt;
&lt;ul class="square"&gt;
&lt;li&gt;&lt;a href="http://piwik.org/mobile/"&gt;Piwik 모바일&lt;/a&gt;을 사용한다면, 보고서의 그래프에 문자가 깨집니다. &lt;a href="http://piwik.org/wp-content/uploads/unifont.ttf.zip"&gt;unifont.ttf&lt;/a&gt;를 다운로드하고, 압축을 풉니다. piwik/plugins/ImageGraph/fonts 디렉토리로 업로하면 문자가 깨지는 것을 바로잡을 수 있습니다.&lt;/li&gt;
&lt;li&gt;Maxmind에서 유료로 제공하는 데이터베이스를 misc 디렉토리에 업로드하면, ISP 공급자와 업체를 추적할 수 있습니다.&lt;/li&gt;
&lt;li&gt;20개 이상의 다양한 &lt;a href="http://dev.piwik.org/trac/query?status=new&amp;amp;status=assigned&amp;status=reopened&amp;milestone=Third+Party+Piwik+Plugins&amp;order=priority"&gt;플러그인&lt;/a&gt;을 사용할 수 있습니다. 압축을 푼 후 plugins 디렉토리에 업로드하기만 하면 플러그인 목록에서 활성/비활성 할 수있습니다.&lt;/li&gt;
&lt;li&gt;검색어 보고서에서 네이버의 한글 검색어가 깨져 보이는 문제는 core/DataFiles/SearchEngines.php 파일에서 &lt;code&gt;'search.naver.com'&lt;/code&gt; 문자열을 찾고 정의되는 배열 마지막에 &lt;code&gt;, 'EUC-KR'&lt;/code&gt;을 제거하여 해결할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;덧. 2013-01-25 업데이트: 추가적인 문맥 수정&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1783#p1783"&gt;Comments(2)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1783"&gt;Hits(202629)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/weLNbgcc_IM" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1783</feedburner:origLink></entry><entry><title type="text">실시간 모바일 앱 분석 도구 - Countly</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/vrGKaRFTpq4/1782" /><category term="웹개발" /><category term="자료" /><category term="Countly" /><category term="앱" /><category term="Node.js" /><category term="자바스크립트" /><category term="Express.js" /><category term="오픈 소스" /><category term="MongoDB" /><category term="NginX" /><category term="Python" /><category term="Supervisor" /><category term="Cafe24" /><category term="Analytics" /><category term="프로젝트" /><category term="모바일" /><author><name>파이어준</name></author><updated>2013-01-16T09:01:32-08:00</updated><id>http://firejune.com/1782</id><content type="html">&lt;p&gt;&lt;a href="http://count.ly/"&gt;Countly&lt;/a&gt;는 4인으로 구성된 터키출신의 착한 젊은 친구들에 의해 만들어진 실시간 모바일 앱 분석 도구입니다. 모바일 앱 엔드-유저의 행동 자료를 수집하고 분석하여 시각화해 줍니다. 구글 Analytics와 같은 웹 분석 도구를 모바일 앱에 최적화한 것으로 이해할 수 있겠네요. &lt;a href="https://github.com/Countly"&gt;오픈 소스&lt;/a&gt;이며 데이터 수집 및  관리할 수 있는 서버와 안드로이드, 윈도폰, iOS, 블랙베리(WebWorks)용 SDK를 동시에 제공합니다. (서드-파티에서 만든 유니티, 앱셀러레이터 타이타니움, 맥OSX용 SDK도 있습니다.) 재미있는 것은 SDK에서 제공하는 사용자 이벤트 API를 이용하면 앱 안에서의 사용자 패턴도 분석할 수 있습니다. (예를 들면, 게임 내 어떤 무기가 빈번하게 사용되는지 라던가...)&lt;/p&gt;

  &lt;div id="gallery3186" class="g_container" style="width:px;height:px;"&gt;
    &lt;img src="/attach/0117/130117000119993498/070369.png" width="" height="" id="g_img3186_0" alt="attachment"&gt;
  &lt;/div&gt;
  &lt;div class="cap1" id="scap3186"&gt;&lt;/div&gt;
  

&lt;p&gt;그들이 제공하는 &lt;a href="http://count.ly/login"&gt;데모&lt;/a&gt;를 실행해 보고 가지고 싶다는 생각이 들더군요. 나중에 서비스할지는 모르겠습니다만, 앱을 유료로 등록하고 사용할 수 있게 하는 기능은 없습니다. 그래서 Countly를 지금 사용해 보기 위해서는 운영 가능한 웹서버에 설치를 해야 하는 상황인 거죠. 서버는 Node.js와 MongoDB로 구축되었고 NginX와 Supervisor, Python 등의 패키지 설치를 요구하며, 리눅스 머신(우분투 권장)에 설치할 수 있습니다. 자체적으로 제공하는 설치 스크립트를 이용하면 Node.js를 비롯한 서버 실행 환경을 자동으로 구성해 줍니다. (비추천) 서버는 API와 Frontend로 양분되었으며, API 서버는 데이터의 입/출력을 담당하고, Express.js기반의 Frontend 서버는 데이터의 섹시한 비주얼라이제이션과 사용자 관리 기능을 포함합니다. 이 두 서버는 supervisord으로 관리되며, NginX에서 라우팅 룰에의해 서로 다른 포트를 가진 서버 프로세스로 프락시 패스하도록 구성되어 있었습니다. Cafe24에 놀리고 있는 &lt;a href="http://firejune.io"&gt;가상서버&lt;/a&gt;에 설치하기 위해, 반나잘 삽질하고 &lt;a href="http://kerberosj.tistory.com/"&gt;윤진군&lt;/a&gt;의 도움으로 &lt;a href="http://countly.firejune.io"&gt;성공적으로 실행&lt;/a&gt;했습니다. 그런데 아쉽게도 등록할 만한 앱이 없네요. 혹시 관심 있는 분은 무료로 앱을 등록해 드릴께요. 요청에 한해서 등록한 앱 단위로 데이터베이스까지 덤프해서 드릴 생각입니다.&lt;/p&gt;
&lt;p&gt;아쉽게도 한글 지원이 없어서 직접 &lt;a href="https://www.transifex.com/projects/p/countly/"&gt;한글화 작업을 완료&lt;/a&gt;하고 적용해 줄 것을 요청한 상태입니다. 그리고 각종 데이터를 PDF 문서로 생성해 주는 기능과 모바일에서 액세스할 수 있는 네이티브 앱을 개발 중이며 곧 선보일 예정이라고 하네요. 아주 멋집니다!&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1782#p1782"&gt;Comments(1)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1782"&gt;Hits(77322)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/vrGKaRFTpq4" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1782</feedburner:origLink></entry><entry><title type="text">요센 게임도 참 쉽게 만드네 - 유니티</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/URISlsU-xcQ/1781" /><category term="게임/오락" /><category term="문화" /><category term="유니티" /><category term="자바스크립트" /><category term="게임" /><category term="유니티 테크놀로지스 코리아" /><category term="메카님" /><category term="다이렉트X 11" /><category term="게임 엔진" /><category term="크로스 플랫폼" /><category term="모바일" /><category term="인디 게임" /><category term="프로젝트" /><category term="쩌... 쩐다." /><author><name>파이어준</name></author><updated>2013-01-03T04:24:24-08:00</updated><id>http://firejune.com/1781</id><content type="html">&lt;p&gt;회사 분위기가 싱숭생숭하여 생존 본능이 발동했습니다. 웹은 좀 지겨우니까 이제 다른 쪽 기술들도 살펴볼겸 해서 어깨너어로 슬쩍슬쩍 넘겨보던 와중에 게임회사 다니는 쫑맹형이 "너 자바스크립트 하지?"라고는 질문을 던졌고, "유니티라는거 알아? 그거 죽이니까 살펴봐"라는 정보를 줬습니다. &lt;a href="http://unity3d.com/"&gt;유니티 홈페이지&lt;/a&gt;에 들러 대략 훓어보니 이거 완전 물건이더군요. 게임제작에 필요한 시작부터 끝을 보장하며, &lt;a href="http://korea.unity3d.com/content/content.php?cont=multiplatform"&gt;멀티 플랫폼&lt;/a&gt;이고, 2011년도에 &lt;a href="http://korea.unity3d.com/"&gt;한국 지사&lt;/a&gt;가 설립되어 개발 지원이 풍부하며, 언리얼이나 기타 게임엔진에 비해 저렴한 등 장점을 모두 나열하자면 입이 아플 지경입니다.&lt;/p&gt;
&lt;figure class="image-align left"&gt;&lt;img src="/attach/0103/130103195117322335/470164.jpg" width="320" height="235"  alt="unity_editor.jpg" class="hasborder"&gt;&lt;/figure&gt;
&lt;p&gt;최근 런칭한 &lt;a href="http://korea.unity3d.com/content/content.php?cont=download_unity4_0"&gt;유니티 4.0&lt;/a&gt;은 다이렉트X 11을 공식적으로 지원하고, GUI를 완전히 재작성하여 낮은 성능의 개발환경에서의 효율성을 높였으며, 강력한 애니메이션 시스템(&lt;a href="http://korea.unity3d.com/content/content.php?cont=mecanim"&gt;메카님&lt;/a&gt;)을 제공하여 모션 캡처한 애니메이션을 마치 동영상 편집하듯이 손쉽게 다룰 수 있고, 리눅스 플랫폼을 추가로 지원하며, 그래픽 기능에 엄청난 향상이 있고, 곡선과 기울기 중심의 모듈 파티클 시스템 도구를 강화해 폭발, 충돌, 태풍 등을 보다 적은 예산으로 구현하는 등 많은 발전이 있었다고 합니다. 30일간 사용해 볼 수 있는 트라이얼 버전을 설치하고 이런저런 스크린캐스트를 보면서 따라 하는 중인데, 감탄을 금치 못하고 있습니다. 개발과정에 필요한 모든 라이브러리가 모듈화 되었고 개발도구와 잘 결합하여 조금만 집중하면 간단한 게임 정도는 뚝딱 만들겠다는 생각이 들더군요. 유니티로 만들어진 게임들을 살펴보면 iOS 2012 최고의 게임으로 선정된 &lt;a href="http://unity3d.com/gallery/made-with-unity/profiles/profile#fireproof-theroom"&gt;Room&lt;/a&gt;, 앵그리 버드로 유명한 Rovio사의 &lt;a href="http://unity3d.com/gallery/made-with-unity/profiles/profile#rovio-badpiggies"&gt;Bad Piggies&lt;/a&gt;, &lt;a href="http://unity3d.com/gallery/made-with-unity/profiles/profile#crescent-ravensword"&gt;Ravensword&lt;/a&gt; 이름만 들어도 알 수 있을만한 히트작들이 꽤 있었습니다.&lt;/p&gt;
&lt;p&gt;그리고 홈페이지는 물론 모든 &lt;a href="http://korea.unity3d.com/content/content.php?cont=document_list"&gt;메뉴얼과 레퍼런스 문서&lt;/a&gt;를 완벽한 한글로 제공해 주어 힘들게 구한 외국 문서를 번역해가면서 삽질할 필요가 없다는 것이죠. 그리고 한국만을 위한 &lt;a href="http://korea.unity3d.com/content/content.php?cont=community_main"&gt;개발자 커뮤니티&lt;/a&gt;도 자체적으로 활성화를 도모하는 아름다운 자세를 엿볼 수 있었습니다. 너무 칭찬 일색이네요. 마지막으로 가장 마음에 드는 부분은 자바스크립트를 이용한 스크립트 작성을 지원하는 것입니다. C#(Mono 기반) 또는 Boo로도 스크립트를 작성할 수 있습니다. "아~ 이걸 왜 지금에 와서야 안 거지?"라는 아쉬움이 들 정도입니다. 취미로 삼아 가지고 놀다가 뭔가 재미있는 아이디어가 떠오르면 본격적으로 시작해 봐야겠습니다. 2013년은 왠지 뭔가 터질 것만 같은 이래저래 흥분되는 해가 될 것 같습니다.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1781#p1781"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1781"&gt;Hits(64730)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/URISlsU-xcQ" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1781</feedburner:origLink></entry><entry><title type="text">다트와 타입스크립트 뭘 해야하나?</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/d8Q43Baq-1I/1779" /><category term="웹개발" /><category term="자료" /><category term="Dart" /><category term="TypeScript" /><category term="자바스크립트" /><category term="ECMA" /><category term="VM" /><category term="프로그래밍" /><category term="전 다트 할겁니다." /><author><name>파이어준</name></author><updated>2013-01-02T02:06:51-08:00</updated><id>http://firejune.com/1779</id><content type="html">&lt;p&gt;1년 전 구글은 자바스크립트를 대체할만한 새로운 언어인 &lt;a href="http://www.dartlang.org/"&gt;다트(Dart)&lt;/a&gt; 만들겠다고 발표했고 1년이 지난 지금 &lt;a href="http://news.dartlang.org/2012/12/dart-milestone-2-has-been-announced.html"&gt;M2 버전&lt;/a&gt;을 발표하면서 쓸만한 수준으로까지 만들어 놓은 것 같습니다. 이에 질세라 MS도 비슷한 시기에 &lt;a href="http://www.typescriptlang.org/"&gt;타입스크립트(TypeScript)&lt;/a&gt;라는 새로운 자바스크립트 프로그래밍 대안을 제시했습니다. 이 둘의 공통적인 지향점은 자바스크립트를 대체하기 보다는 부족한 점을 보완하고 편리성, 확장성, 생산성 향상을 도모해 애플리케이션 규모의 개발을 이롭게 하자는 것입니다. 이러한 측면에서 개발자에게 애플리케이션을 개발하는 새로운 방법을 제시하는 것 그 이상도 그 이하도 아니라는 점은 분명합니다. 일부 시각에서 대체라는 둥 한계라는 둥, 신경 거슬리는 말을 자주 사용하는데 전혀 동요할 필요가 없겠습니다. 그래도 이 새로운 언어들이 계속해서 눈에 밟힐 것 같아 차이점 정도는 집고 넘어야겠다 싶어 알아보았습니다.&lt;/p&gt;
&lt;p&gt;타입스크립트는 ECMA 표준에 근거한 다음 버전의 자바스크립트 규격에 기반을 뒸습니다. 이것은 앞으로 표준이 될 가능성을 염두에 둔 자바스크립트 슈퍼셋입니다.(예: C++은 C의 슈퍼셋) 타입을 검사할 수 있고, 기존 라이브러리를 그대로 사용하며 크로스 플랫폼으로 작성할 수 있는 장점이 있다고 합니다. 기존 자바스크립트와의 호환성을 위해 타입스크립트로 작성된 코드를 자바스크립트로 컴파일 할 수 있으며, 그 완성도가 매우 뛰어납니다. 타입스크립트는 &lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt;에서도 사용할 수 있도록 모듈을 배포하고 있네요. 타입스크립트의 개발은 비주얼 스튜디오 2012에서 이루어지며 플러그인 형태로 설치할 수 있습니다. 혹자는 액션스크립트 3으로 자바스크립트를 짜는 거네... 차라리 &lt;a href="http://coffeescript.org/"&gt;CoffeeScript&lt;/a&gt;를 쓰겠어, 성능은?, 네이티브로 작동하기 위한 계획이나 확산 계획이 불분명하다는 부정적인 반응도 보입니다. 아래는 타입스크립트가 자바스크립트로 컴파일된 예입니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;// typescript
class Greeter {
    greeting: string;
    constructor (message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

// javascript
var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
})();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다트는 전통적인 클래스기반 객제지향언어입니다. C#, Java를 다뤄본 경험이 있다면 다트의 문법이 친숙하게 느껴질 것입니다. 자바스크립트와 마찬가지로 싱글 스레드로 작동하지만 자체적으로 제공하는 다트 VM(가상 머신)에 탑재된 Isolate를 이용하여 멀티 스레드를 구현할 수 있습니다. VM이 있다는 것은 곧 Node.js와 같은 서버-사이드 프로그래밍도 가능하다는 말입니다. 서버와 클라이언트간 코드가 동일하다는 점에서 비롯되는 이점은 의외로 많습니다. 예를 들면, 시스템 사양이 좋은 테스크탑에서는 클라이언트의 리소스를 활용하고 그렇지 않은 모바일에서는 서버의 리소스를 활용하는 로직을 하나로 코드로 관리할 수 있게 되는 것이죠. 그러나 이것은 모든 환경에 VM이 탑재되었을때 비로소 가능한 것입니다. VM이 보편화하기 전까지는 다트로 작성된 코드가 자바스크립트로 컴파일되도록 하자는 전략입니다. 이클립스기반의 &lt;a href="http://www.dartlang.org/docs/editor/"&gt;다트 에디터&lt;/a&gt;에서 통합 개발환경을 제공하고 다트 VM이 탑재된 크롬 브라우저인 &lt;a href="http://www.dartlang.org/dartium/"&gt;Dartium&lt;/a&gt;에서 다트 코드를 테스트할 수 있습니다. 그리고 자바스크립트 컴파일러, VM, 패키지 매니저로 구성된 &lt;a href="http://www.dartlang.org/docs/sdk/"&gt;SDK&lt;/a&gt;를 제공합니다. 다음은 다트의 예문입니다.&lt;/p&gt;
&lt;pre class="javascript"&gt;&lt;code&gt;import 'dart:html';

main() {
  var msg = query('#msg');
  var btn = new ButtonElement();
  btn.text = 'Click me!';
  btn.on.click.add((e) =&amp;gt; msg.text = 'Dart!');
  document.body.nodes.add(btn);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;덧. 실제로 다트나 타입스크립트를 직접 경험하지 못한 상황에서 바라본 시각으로 작성했다는 점 참고해주세요. 두 언어 모두 근사한 기능을 말하고 있지만 현시점에서 실무에 적용하려면 컴파일러를 통해 자바스크립트로 변환하여 적용하는 일이 고작인듯합니다. 그렇다고 아직 개발 중인 녀석들에게 왈가왈부할 수도 없는 상황이네요. 저는 올해부터 다트를 공부하기로 마음먹었습니다. 다트가 자체적인 런타임을 가지고 있다는 부분이 정말 매력적으로 느껴지더군요. 웹의 범주를 넘어서는 가능성을 열어두었다는 생각이 듭니다. VM위에서는 어느 정도의 성능이 나올지 벌써부터 기대되는군요.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1779#p1779"&gt;Comments(1)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1779"&gt;Hits(47049)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/d8Q43Baq-1I" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1779</feedburner:origLink></entry><entry><title type="text">Schema.org 이용하여 HTML 데이터 구조화하기</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/5scidnL4kn4/1778" /><category term="웹개발" /><category term="자료" /><category term="HTML5" /><category term="데이터 구조화" /><category term="마이크로포맷" /><category term="RDFa" /><category term="마이크로데이터" /><category term="data-vocabulary.org" /><category term="Schema.org" /><category term="SEO" /><category term="밀린 포스트 갑자기 마무리하기" /><category term="리치 스니펫" /><author><name>파이어준</name></author><updated>2012-12-31T05:18:41-08:00</updated><id>http://firejune.com/1778</id><content type="html">&lt;p&gt;개발자들이 정의한 각종 데이터를 최종 결과물인 HTML 문서를 통해서 출력하지만 이를 다시 데이터형으로 돌리기에는 많은 어려움이 있기 때문에 HTML 문서에서 데이터를 구분해 낼 수 있는 방법으로 &lt;a href="http://en.wikipedia.org/wiki/Microdata_(HTML)"&gt;마이크로데이터&lt;/a&gt;이나 &lt;a href="http://microformats.org/wiki/html5"&gt;마이크로포맷&lt;/a&gt; 또는 &lt;a href="http://en.wikipedia.org/wiki/RDFa"&gt;RDFa&lt;/a&gt;를 이용할 수 있습니다. RDFa는 XHTML에나 잘 어울릴 법한 모양새이고 사용법이 다소 까다롭다는 점에서 개인적으로는 마이크로포맷이나 마이크로데이터를 선호합니다. 마이크로포맷은 별도의 네임스페이스 선언을 필요로하지 않고 class와 rel 속성만을 이용하여 구조화된 데이터를 표현할 수 있지만, 단계가 있거나 관계가 있는 데이터를 구조화하기에는 부족한 면이 있습니다. 마이크로데이터는 RDFa와 마이크로포맷의 중간쯤으로 볼 수 있습니다. 자세한 사용 방법은 구글이 문서(&lt;a href="https://support.google.com/webmasters/bin/answer.py?answer=176035"&gt;microdata&lt;/a&gt;, &lt;a href="https://support.google.com/webmasters/bin/answer.py?answer=146897"&gt;microformats&lt;/a&gt;, &lt;a href="https://support.google.com/webmasters/bin/answer.py?answer=146898"&gt;RDFa&lt;/a&gt;)를 잘 만들어 뒀습니다.&lt;/p&gt;
&lt;p&gt;이 얘기는 수년 전부터 거론되었지만 웹사이트 소유주에게 돌아가는 이렇다 할 가치가 없었기 때문에 지들끼리 말만 많았던 HTML5 스팩이죠. 최근 구글은 이 구조화된 데이터를 이용하여 저자나 인물을 표시하거나 제품이나 장소 등의 평점 및 리뷰, 앨범의 트랙 정보 이벤트 등을 검색결과에 노출하고 있으며, 이것을 &lt;a href="https://support.google.com/webmasters/bin/answer.py?hl=ko&amp;amp;answer=99170"&gt;리치 스니펫&lt;/a&gt;이라 부르고 있습니다. 구글은 리치 스니펫을 제공하는 방법으로는 마이크로데이터를 권장하고 있으며 이는 데이터가 어떤 종류인지를 정의하는 스키마를 필요로합니다. 대표적으로 &lt;a href="http://data-vocabulary.org"&gt;data-vocabulary.org&lt;/a&gt;또는 &lt;a href="http://schema.org"&gt;schema.org&lt;/a&gt;에서 제공하는 데이터 스키마를 선언하는데 이를 잘 이용하면 관계형 데이터 구조화가 가능해 집니다.&lt;/p&gt;
&lt;p&gt;구글은 웹마스터 도구를 통해 &lt;a href="http://www.google.com/webmasters/tools/richsnippets"&gt;구조화된 데이터 테스팅 도구&lt;/a&gt;를 제공합니다. 다음은 schema.org의 데이터 스키마를 기반으로 이 블로그의 데이터를 구조화하고 테스팅 도구를 통해 얻은 &lt;a href="http://www.google.com/webmasters/tools/richsnippets?url=http%3A%2F%2Ffirejune.com%2F1776%2FNode.JS%25EC%259A%25A9%2BScribd%2B%25EB%25AA%25A8%25EB%2593%2588%2B%25EB%25B0%25B0%25ED%258F%25AC&amp;amp;html="&gt;결과&lt;/a&gt;입니다. 제법 쓸만한 데이터가 만들어 지더라고요. 조금 더 발전하면 ATOM이나 RSS를 별도로 제공하는게 무의미해질지도 모르겠네요.&lt;/p&gt;

&lt;h4&gt;Item&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; http://schema.org/blog&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;property:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;url: &lt;a href="//firejune.com/"&gt;Firejune&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;name: &lt;a href="//firejune.com/"&gt;Firejune&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;description: The Web is still changing.&lt;/li&gt;
      &lt;li&gt;version: 2.19&lt;/li&gt;
      &lt;li&gt;blogpost: &lt;strong&gt;Item 1&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;author: &lt;strong&gt;Item 2&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Item 1&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; http://schema.org/article&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;property:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;datepublished: 2012-12-19&lt;/li&gt;
      &lt;li&gt;name: &lt;a href="//firejune.com/1776/Node.JS%EC%9A%A9+Scribd+%EB%AA%A8%EB%93%88+%EB%B0%B0%ED%8F%AC"&gt;Node.JS용 Scribd 모듈 배포&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="//firejune.com/1776/Node.JS%EC%9A%A9+Scribd+%EB%AA%A8%EB%93%88+%EB%B0%B0%ED%8F%AC"&gt;Node.JS용 Scribd 모듈 배포&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;author: &lt;a href="//firejune.com/1776/Node.JS%EC%9A%A9+Scribd+%EB%AA%A8%EB%93%88+%EB%B0%B0%ED%8F%AC#author"&gt;파이어준&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;interactioncount: &lt;a href="//firejune.com/1776/Node.JS%EC%9A%A9+Scribd+%EB%AA%A8%EB%93%88+%EB%B0%B0%ED%8F%AC#comments-1776"&gt;Reactions4&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;articlebody: 역시나, 기업 요구사항은 파일 교환보다는 문서 관리 쪽으로 많이 치우쳐져 있더군요. 그래서 문서관련 기능을 강화하기 위해 매시업할 수 있는...&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/entries/8-42"&gt;자료 - 웹개발&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/Node.JS"&gt;Node.JS&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/Scribd"&gt;Scribd&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/HTML5"&gt;HTML5&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8"&gt;자바스크립트&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8"&gt;프로젝트&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/GitHub"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/NPM"&gt;NPM&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;keywords: &lt;a href="//firejune.com/tag/%EB%A7%A4%EC%8B%9C%EC%97%85"&gt;매시업&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;comment: &lt;strong&gt;Item 3&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;comment: &lt;strong&gt;Item 4&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;comment: &lt;strong&gt;Item 5&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;comment: &lt;strong&gt;Item 6&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Item 3&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; http://schema.org/comment&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;property:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;datecreated: 2012-12-20T08:55:50+09:00&lt;/li&gt;
      &lt;li&gt;image: http://a0.twimg.com/profile_images/2399594250/8dp14wildkk8xbt8ownl_normal.png&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="http://twitter.com/beyonditblog"&gt;beyonditblog&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;name: &lt;a href="http://twitter.com/beyonditblog"&gt;beyonditblog&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;text: Node.JS용 Scribd 모듈 배포: 역시나, 기업 요구사항은 파일 교환 보다는 문서 관리쪽으로 많이 치우쳐져 있더군요. 그래서 문서관련 기능을 강화하기...&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Item 4&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; http://schema.org/comment&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;property:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;datecreated: 2012-12-20T09:24:06+09:00&lt;/li&gt;
      &lt;li&gt;image: http://a0.twimg.com/profile_images/1123405595/IT_NEWS_normal.jpg&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="http://twitter.com/All_IT_News"&gt;All_IT_News&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;name: &lt;a href="http://twitter.com/All_IT_News"&gt;All_IT_News&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;text: 
text:	[경준호] Node.JS용 Scribd 모듈 배포: 역시나, 기업 요구사항은 파일 교환 보다는 문서 관리쪽으로 많이 치우쳐져 있더군요. 그래서 문서관련 기능을...&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Item 5&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; http://schema.org/comment&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;property:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;datecreated: 2012-12-22T00:50:28+09:00&lt;/li&gt;
      &lt;li&gt;image: http://a0.twimg.com/profile_images/2514809179/stcr8k585g4tkcch9pgw_normal.jpeg&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="http://twitter.com/nanhapark"&gt;nanhapark&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;name: &lt;a href="http://twitter.com/nanhapark"&gt;nanhapark&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;text: Node.JS용 Scribd 모듈 배포 http://t.co/qpzDlro0 #nodeqa&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Item 6&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; http://schema.org/comment&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;property:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;datecreated: 2012-12-22T01:20:16+09:00&lt;/li&gt;
      &lt;li&gt;image: http://a0.twimg.com/profile_images/1032276925/imazine_normal.jpg&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="http://twitter.com/iMaZiNe80"&gt;iMaZiNe80&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;name: &lt;a href="http://twitter.com/iMaZiNe80"&gt;iMaZiNe80&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;text: RT @nanhapark: Node.JS용 Scribd 모듈 배포 http://t.co/qpzDlro0 #nodeqa&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Item 2&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;type:&lt;/strong&gt; http://schema.org/person&lt;/li&gt;
  &lt;li&gt;
    &lt;strong&gt;property:&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;image: //firejune.com/attach/image/272778.jpeg&lt;/li&gt;
      &lt;li&gt;name: Joon Kyoung&lt;/li&gt;
      &lt;li&gt;jobtitle: web front-end development&lt;/li&gt;
      &lt;li&gt;worksfor: Spark &amp; Associates Corp&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="//twitter.com/firejune"&gt;@firejune&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="//firejune.com/"&gt;Blog&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;url: &lt;a href="//firejune.com/author"&gt;More information&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;


 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1778#p1778"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1778"&gt;Hits(73214)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/5scidnL4kn4" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1778</feedburner:origLink></entry><entry><title type="text">구글봇이 깨진 링크를 한 달 만에 60만 건 가져가</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/pBjBCQyrU_o/1777" /><category term="블로그" /><category term="자료" /><category term="깨진링크" /><category term="Integrity" /><category term="SEO" /><category term="구글 웹마스터 도구" /><category term="구글봇" /><category term="301 리디렉션" /><category term="새해 복 많이 받으세요." /><author><name>파이어준</name></author><updated>2012-12-31T02:18:46-08:00</updated><id>http://firejune.com/1777</id><content type="html">&lt;p&gt;구글봇의 크롤 정보가 갱신되는 시간인 오후 한 시가 되면 어김없이 &lt;a href="https://www.google.com/webmasters/tools/home?hl=ko"&gt;구글 웹마스터 도구&lt;/a&gt;에 들어갑니다. 최근 블로그를 리뉴얼 하면서 주소형식이 변경되는 일이 있었습니다. 그래서 이전의 주소로 접근했을 때 올바른 주소로 301 리디렉션하도록 했어요. 그런데 만드는 과정에서의 실수로 잘못된 주소가 대량 방출되었다는 사실을 뒤늦게 알았습니다. 한 달 만에, 무려 60만 건에 달하는 크롤 오류가 감지되었더군요. &lt;/p&gt;
&lt;figure class="image-align"&gt;&lt;img src="/attach/1231/121231175010769023/231468.png" width="662" height="402"  alt="404.png" class="hasborder"&gt;&lt;/figure&gt;
&lt;p&gt;웹마스터 도구에서 보고한 오류 정보를 토대로 수정 작업을 완료하고 Googlebot에게 이제 다시 크롤해도 좋다고 알려줄 수 있습니다. 그런데 이는 오류 종류(500, 404, 401, 410 등)별로 하루 최대 1,000개이므로 60만 건을 모두 처리하려면 600일이 소요됩니다.(물론, 놔두면 구글봇이 알아서 처리한다고 합니다.) 그런데, 이게 은근 중독성인 게, 마치 롤플레잉 게임에서 몹을 때려잡는 듯한 감칠맛까지 나더라고요.&lt;/p&gt;
&lt;p&gt;구글이 개밥 주듯이 조금씩 내주는 정보에 만족할 수 없어서 사이트 전체의 깨진 링크를 검사해 주는 도구인 &lt;a href="http://peacockmedia.co.uk/integrity/"&gt;Integrity&lt;/a&gt;(맥용)를 이용해서 내부적으로 잘못된 주소가 생성되는지 확인했습니다. 공짜치고는 꽤 쓸만한 도구였어요. 전체 5만 여건의 내부링크가 검색되었는데 그중에서 잘못된 링크를 모두 찾아 고치고 수개월째 경과를 지켜보고 있습니다.&lt;/p&gt;
&lt;p&gt;구글봇이 가져간 크롤오류가 바로잡히는 데에는 생각보다 오랜 시간이 걸렸습니다. 약 3개월에 걸쳐 조금씩 줄어들어 지금은 22만여 건으로 많이 줄어든 상태에요. 기존에 서비스되고 있는 라우트를 건드릴 때에는 정말 조심스럽게 작업에 임해야겠습니다.&lt;/p&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1777#p1777"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1777"&gt;Hits(67111)&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/pBjBCQyrU_o" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1777</feedburner:origLink></entry><entry><title type="text">Node.JS용 Scribd 모듈 배포</title><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firejune/~3/DSeIStfW50Q/1776" /><category term="웹개발" /><category term="자료" /><category term="Node.JS" /><category term="Scribd" /><category term="HTML5" /><category term="자바스크립트" /><category term="프로젝트" /><category term="GitHub" /><category term="NPM" /><category term="플래시" /><category term="매시업" /><category term="모듈" /><category term="내가 돈있으면 투자하고 싶은 회사" /><author><name>파이어준</name></author><updated>2012-12-19T06:54:49-08:00</updated><id>http://firejune.com/1776</id><content type="html">&lt;p&gt;역시나, 기업 요구사항은 파일 교환보다는 문서 관리 쪽으로 많이 치우쳐져 있더군요. 그래서 문서관련 기능을 강화하기 위해 매시업할 수 있는 서비스들을 찾아보던 중 &lt;a href="http://www.scribd.com/"&gt;Scribd&lt;/a&gt;라는 서비스를 알게 되었습니다. 이 서비스는 업로드한 문서를 HTML5(또는 플래시)에서 출력할 수 있도록 컨버전해주는 서비스였습니다. 변환된 문서는 원본 문서와 다른 점을 찾아보기 어려울 정도로 품질이 좋더군요. 그리고 자신들이 가진 기술을 공유에 목적을 두고 마음껏 사용해 주기를 바라는 착한 회사였습니다. &lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.scribd.com/developers"&gt;Scribd의 API&lt;/a&gt;는 문서를 웹페이지에 임베드하기 위한 &lt;a href="http://www.scribd.com/developers/javascript_api"&gt;자바스크립트 API&lt;/a&gt;와 문서를 등록하거나 메타데이터를 조회하거나 검색 등을 할 수 있는 &lt;a href="http://www.scribd.com/developers/platform"&gt;플랫폼 API&lt;/a&gt;로 나뉩니다. 플랫폼 API에는 Ruby, Java, PHP, Python 등의 라이브러리가 있었지만 아쉽게도 &lt;a href="http://nodejs.org/"&gt;Node.JS&lt;/a&gt;용 라이브러리만 쏙 빠져있어서 래핑 모듈을 만들고 배포합니다. 아래는 &lt;a href="https://github.com/firejune/scribd"&gt;GitHub에 공개&lt;/a&gt;한 scribd 모듈의 API 문서입니다.&lt;/p&gt;
&lt;h3&gt;Scribd Platform API binding for Node.JS&lt;/h3&gt;
&lt;p&gt;Installing:
&lt;pre class="command"&gt;&lt;code&gt;$ npm install scribd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usage:
&lt;pre class="javascript"&gt;&lt;code&gt;var Scribd = require('scribd');

var key = "ENTER-YOUR-API-KEY-HERE"
  , secret = "ENTER-YOUR-API-SECRET-HERE"; 

var scribd = new Scribd(key, secret);

// or (&amp;gt;= 0.1.5)

var scribd = new Scribd({
    apikey: key
  , secret: secret
});


/**
 * docs.upload
 */

scribd.upload(function(err, res) {
  console.log("\n scribd.upload", err, res);
}, "./my.docx", "doc", "private");

// or (&amp;gt;= 0.1.5)

scribd.upload({
    file: "./my.docx"
  , docType: "doc"
  , access: "private"
}, function(err, res) {
  console.log("\n scribd.upload", err, res);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Methods:
&lt;pre class="javascript"&gt;&lt;code&gt;/**
 * Docs Method
 */

// docs.upload (callback, file, [docType], [access], [revId])
scribd.upload(function(err, res) {
  console.log('\n scribd.upload', err, res);
}, './document.path');

// docs.uploadFromUrl (callback, url, [docType], [access], [revId])
scribd.uploadFromUrl(function(err, res) {
  console.log('\n scribd.uploadFromUrl', err, res);
}, 'url');

// docs.getList (callback)
scribd.getList(function(err, res) {
  console.log('\n scribd.getList', err, res);
});

// docs.getConversionStatus (callback, docId)
scribd.getConversionStatus(function(err, res) {
  console.log('\n scribd.getConversionStatus', err, res);
}, 'docId');

// docs.getSettings (callback, docId)
scribd.getSettings(function(err, res) {
  console.log('\n scribd.getSettings', err, res);
}, 'docId');

// docs.changeSettings (callback, docId, [title], [description], [access], [license], [showAds], [tags])
scribd.changeSettings(function(err, res) {
  console.log('\n scribd.changeSettings', err, res);
}, 'docId', 'title');

// docs.getDownloadUrl (callback, docId)
scribd.getDownloadUrl(function(err, res) {
  console.log('\n scribd.getDownloadUrl', err, res);
}, 'docId');

// docs.getStats (callback, docId)
scribd.getStats(function(err, res) {
  console.log('\n scribd.getStats', err, res);
}, 'docId');

// docs.delete (callback, docId)
scribd.delete(function(err, res) {
  console.log('\n scribd.remove', err, res);
}, 'docId');

// docs.search (callback, query, [numResults], [numStart], [scope])
scribd.search(function(err, res) {
  console.log('\n scribd.search', err, res);
}, 'Node.JS', 1);

// docs.getCategories (callback, docId)
scribd.getCategories(function(err, res) {
  console.log('\n scribd.getCategories', err, res);
}, 'docId');

// docs.featured (callback, [limit], [offset], [scope])
scribd.featured(function(err, res) {
  console.log('\n scribd.featured', err, res);
});

// docs.browse (callback, [limit], [offset], [categoryId], [sort])
scribd.browse(function(err, res) {
  console.log('\n scribd.browse', err, res);
});

// docs.uploadThumb (callback, file, docId)
scribd.uploadThumb(function(err, res) {
  console.log('\n scribd.uploadThumb', err, res);
}, 'thumbnail.path', 'docId');


/**
 * Thumbnail Method
 */

// thumbnail.get (callback, docId, [width], [height])
scribd.getThumbnail(function(err, res) {
  console.log('\n scribd.getThumbnail', err, res);
}, 'docId', 256);


/**
 * User Method
 */

// user.login (callback, username, password)
scribd.login(function(err, res) {
  console.log('\n scribd.login', err, res);
}, 'username', 'password');

// user.signup (callback, username, password, email, [name])
scribd.signup(function(err, res) {
  console.log('\n scribd.signup', err, res);
}, 'username', 'password', 'email', 'name');

// user.getAutoSigninUrl (callback, [nextUrl])
scribd.getAutoSigninUrl(function(err, res) {
  console.log('\n scribd.getAutoSigninUrl', err, res);
}, '/');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have fun!&lt;/p&gt;
&lt;h3&gt;License&lt;/h3&gt;
&lt;p&gt;MIT &amp;lt;3&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firejune/~4/DSeIStfW50Q" height="1" width="1" alt=""/&gt;</content><feedburner:origLink>http://firejune.com/1776</feedburner:origLink></entry></feed>
