<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" 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">이 사이트는 IT, 웹개발, 자바스크립트, 웹 프로그래밍, 웹2.0, 웹 애플리케이션 등에 대한 내용을 다룹니다.</subtitle><logo>http://firejune.com/attach/image/441618.jpg</logo><updated>1970-01-01T00:00:00+00:00</updated><generator>Tatter Tools Hybrid</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 xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="firejune" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><title type="text">파비콘을 이용하여 새소식을 알리는 - Tinycon</title><link rel="alternate" type="text/html" href="http://firejune.com/1735" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="캔버스" /><category term="Tinycon" /><category term="파비콘" /><category term="급뻘짓" /><author><name>파이어준</name></author><updated>2012-02-07T21:28:44-08:00</updated><id>http://firejune.com/1735</id><content type="html">&lt;a href="http://tommoor.github.com/tinycon/"&gt;Tinycon&lt;/a&gt;은 현재 웹사이트에서 사용 중인 파비콘에 작은 번호를 동적으로 표시되도록 조작하여 사용자에게 새 소식을 알리는 작은 라이브러리입니다. HTML5 캔버스 요소를 지원하는 브라우저에서만 작동합니다.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="image-align center"&gt;&lt;a href="http://tommoor.github.com/tinycon/" title=""&gt;&lt;img src="http://firejune.com/attach/0208/120208133909496977/616071.png" width="653" alt="tinycon.png" class="centered hasborder"/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
구글 지메일, 리더 등과 같은 서비스에서는 새 소식을 알리는 카운트를 동적으로 타이틀 텍스트에 출력하여 사용자에게 확인이 필요함을 시각적으로 알리고 있습니다만 구글의 "탭 고정"이나 파이어폭스에서 "앱 탭" 기능을 사용하면 페이지의 타이틀이 숨고 파비콘만 남게 되어 무용지물입니다. 이러한 경우라면 웹사이트에서 발생한 이벤트를 전달할 때 아주 효과적으로 사용할 수 있는 기막힌 아이디어입니다. &lt;br /&gt;
&lt;br /&gt;
기본적인 사용방법은 다음과 같습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;Tinycon.setBubble(6);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
다음과 같은 옵션을 이용하여 사용자화 할 수 있습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;Tinycon.setOptions({
    width: 7, // the width of the alert bubble
    height: 9, // the height of the alert bubble
    font: '10px arial', // a css string to use for the fontface (recommended to leave this)
    colour: '#ffffff', // the foreground font colour
    background: '#549A2F', // the alert bubble background colour
    fallback: true // should we fallback to a number in brackets for browsers that don't support canvas/dynamic favicons
});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;del&gt;1초마다 1씩 카운트가 증가하도록 이곳에 적용했습니다. 보이나요?&lt;/del&gt;&lt;br /&gt;
이 사이트를 사용 중인 접속자(활성 사용자) 카운트로 변경했습니다.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1735#p1735"&gt;Comments(2)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1735"&gt;Hits(1220)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/wQ-PsGF5vGgMZ0FFWsk26s-KYIE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wQ-PsGF5vGgMZ0FFWsk26s-KYIE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/wQ-PsGF5vGgMZ0FFWsk26s-KYIE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wQ-PsGF5vGgMZ0FFWsk26s-KYIE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">한 문서를 실시간으로 여럿이 동시에 편집하는 - ShareJS</title><link rel="alternate" type="text/html" href="http://firejune.com/1734" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="Node.JS" /><category term="모듈" /><category term="ShareJS" /><category term="Google Wave" /><category term="Socket.IO" /><category term="Redis" /><category term="Operational Transform" /><category term="운영변환" /><author><name>파이어준</name></author><updated>2012-02-03T14:30:23-08:00</updated><id>http://firejune.com/1734</id><content type="html">Joseph Gentle씨는 &lt;a href="http://en.wikipedia.org/wiki/Operational_transformation"&gt;Operational Transform(운영변환) 알고리즘&lt;/a&gt;을 구현한 &lt;a href="http://nodejs.org/"&gt;Node.JS&lt;/a&gt;용 모듈인 &lt;a href="http://sharejs.org/"&gt;ShareJS&lt;/a&gt;를 배포했습니다. 이 모듈은 여러명의 사용자가 웹브라우저에서 하나의 문서를 실시간으로 동시에 편집 할 수 있는 애플리케이션을 쉽게 구축할 수 있도록 돕는 것이 목적입니다. 그는 운영변환 알고리즘을 제대로 구현하는 것은 아주 어렵고 오랜 시간이 소요된다고 토로했습니다. 이 알고리즘의 핵심은 하나의 문서를 여럿이 동시에 편집해도 충돌이 일어나지 않게하는 구조를 가진다는 것입니다. 그리고 자신을 &lt;a href="http://wave.google.com/"&gt;Google Wave&lt;/a&gt; 팀의 엔지니어로 일하면서 2년동안 &lt;a href="https://docs.google.com/present/view?id=dggjrx3s_1573xdhxprd"&gt;연구한 결과&lt;/a&gt;를 ShareJS로 다시 작성한 것이라 밝혔습니다. 이제 여러분도 &lt;a href="http://etherpad.com/"&gt;Etherpad&lt;/a&gt;와 같은 문서 편집 도구를 쉽사리 만들어 낼 수 있게 된 것입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;sharejs.open('blag', 'text', function(error, doc) {
  var elem = document.getElementById('pad');
  doc.attach_textarea(elem);
});&lt;/code&gt;&lt;/pre&gt;이봐봐... 정말이에요! 단 4줄로 Etherpad 같은 앱을 구현해 버리잖아요.&lt;br /&gt;
&lt;br /&gt;
&lt;div id="editor" style="border: 1px solid #aaa; width: 664px; height: 331px; position: relative;"&gt;&lt;/div&gt;








&lt;br /&gt;
위 데모는 ShareJS의 예제 중 하나인 실시간 협업 코드 편집기입니다. 당신이 지금 편집하고 있는 상황은 이 웹페이지에 접속한 모든 사용자에게 실시간으로 방송됩니다. 물론 동시에 편집하는 것도 가능합니다. 이것은 웹상에서 코드를 편집하기 위한 라이브러리인 &lt;a href="http://ace.ajax.org/"&gt;Ace&lt;/a&gt;(&lt;a href="http://www.cloud9ide.com/"&gt;Cloud9 IDE&lt;/a&gt;에서 사용됨)와 실시간 통신을 위한 &lt;a href="http://socket.io/"&gt;Socket.IO&lt;/a&gt;, 그리고 &lt;a href="http://redis.io/"&gt;Redis&lt;/a&gt; 데이터베이스를 이용하여 마지막 편집 데이터를 영구 보존하도록 만들어졌습니다.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1734#p1734"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1734"&gt;Hits(3142)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/46b2qpQdpEjXik7cBYZLh4uSGGo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/46b2qpQdpEjXik7cBYZLh4uSGGo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/46b2qpQdpEjXik7cBYZLh4uSGGo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/46b2qpQdpEjXik7cBYZLh4uSGGo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">D3.js 기반 실시간 시계열 데이터 그래프 - Rickshaw</title><link rel="alternate" type="text/html" href="http://firejune.com/1733" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="대화형" /><category term="그래프" /><category term="상호작용" /><category term="챠트" /><category term="실시간" /><category term="D3.js" /><category term="Rickshaw" /><category term="시계열 데이터" /><category term="Time-Series" /><category term="Data-Driven" /><author><name>파이어준</name></author><updated>2012-02-01T23:05:28-08:00</updated><id>http://firejune.com/1733</id><content type="html">사진 서비스인 &lt;a href="http://www.shutterstock.com/"&gt;Shutterstock&lt;/a&gt;에서 만든 &lt;a href="http://shutterstock.github.com/rickshaw/"&gt;Rickshaw&lt;/a&gt;는 시계열(Time-Series) 데이터를 웹페이지에 그래프로 그려주는 도구입니다. 히스토리컬 한 정적인 데이터 셋을 실시간으로 지속해서 갱신해 줍니다. 커브, 바, 라인, 퍼센트, 스트림 등의 다양한 그래프를 지원하며 그래프의 특정 구간을 확대하거나 곡선을 단순화 하거나 옵셋을 변경하거나 특정 타임셋에 이벤트를 발생시키고 정보를 조회하는 등의 상호작용이 실시간으로 반영되도록 설계되었습니다.&lt;br /&gt;
&lt;br /&gt;
Rickshaw는 Mike Bostock씨의 &lt;a href="http://mbostock.github.com/d3/"&gt; D3.js&lt;/a&gt;를 래핑하는 형식으로 구축되었으며, D3.js는 데이터 기반 문서(Data-Driven Documents)의 작성을 돕기위한 목적으로 만들어진 작은 라이브러리입니다. 상호작용이 용이하고 데이터의 흐름을 부드러운 애니메이션으로 처리할 수 있도록하여 실시간 데이터를 다루는데 제격입니다. 백번 말하는것보다 한번 보는게 낫겠죠. 이 &lt;a href="http://mbostock.github.com/d3/talk/20110921/"&gt;인터랙티브 슬라이드&lt;/a&gt;를 죽 훓터보세요.&lt;br /&gt;
&lt;br /&gt;
아래 데모는 커스터마이즈 된 것으로 기존 데모의 버그를 수정하고 jQuery 의존성을 제거했으며 스택이 150건이 쌓이면 오래된 순서로 타임셋이 드랍되도록 했습니다. 때마침 시계열 데이터 그래프에서 특정 타임셋에 발생하는 이벤트를 효율적으로 표시할 수 있는 수단을 찾고 있었는데 잘 나와주었네요. 개인적으로 운영하는 &lt;a href="http://firejune.io/"&gt;firejune.io의 실시간 서버 상황&lt;/a&gt;에 에 우선적으로 적용해 볼 예정입니다.&lt;br /&gt;
&lt;br /&gt;

&lt;div id="side_panel"&gt;
  &lt;section id="offsets" class="toggler"&gt;
    &lt;input type="radio" name="offset" id="stack" value="stack" checked&gt;
    &lt;label for="stack"&gt;stack&lt;/label&gt;
    &lt;input type="radio" name="offset" id="percent" value="percent"&gt;
    &lt;label for="percent"&gt;percent&lt;/label&gt;
    &lt;input type="radio" name="offset" id="stream" value="stream"&gt;
    &lt;label for="stream"&gt;stream&lt;/label&gt;
    &lt;input type="radio" name="offset" id="lines" value="lines"&gt;
    &lt;label for="lines"&gt;lines&lt;/label&gt;
  &lt;/section&gt;
  &lt;section id="renderers" class="toggler"&gt;
    &lt;input type="radio" name="renderer" id="stack_renderer" value="curves" checked&gt;
    &lt;label for="stack_renderer"&gt;curves&lt;/label&gt;
    &lt;input type="radio" name="renderer" id="bar_renderer" value="bars"&gt;
    &lt;label for="bar_renderer"&gt;bars&lt;/label&gt;
    &lt;input type="radio" name="renderer" id="step_interpolation" value="step"&gt;
    &lt;label for="step_interpolation"&gt;step&lt;/label&gt;
  &lt;/section&gt;
&lt;/div&gt;

&lt;div id="legend"&gt;&lt;/div&gt;
&lt;div id="chart_container"&gt;
  &lt;div id="chart"&gt;&lt;/div&gt;
  &lt;div id="timeline"&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;link type="text/css" rel="stylesheet" href="/stylesheets/rickshaw.min.css"&gt;
&lt;style&gt;
#chart_container { position: relative; display: inline-block; }
#legend {
  background-color: white; display: inline-block; clear: both;
  vertical-align: top; margin-top: 10px; padding: 0; left: 0;
}
#legend .label { color: #404040; }
#legend .action { color: black; opacity: 0.5; }
#legend ul { padding: 0; }
#legend li { padding: 6px 0; }

#side_panel { margin: 10px 0 20px 94px; }
#offsets { float: left; }
#renderers {}
#timeline { line-height: 18px; text-align: left; }

.toggler { display: block; margin: 0; }
.toggler input[type=radio]:checked {
  outline: 2px solid steelblue;
}
.toggler input[type=radio] {
  margin: 0 7px; width: 39px;
  height: 27px; position: absolute;
  -moz-appearance: button;
}
.toggler input[type=radio]:disabled {
  opacity: 0.5;
}
.toggler label {
  display: inline-block; padding: 0;
  width: 39px; height: 0px; padding-top: 27px;
  text-align: center; font-size: 10px; color: #808080;
  background-repeat: no-repeat; position: relative;
  margin: 0 7px; cursor: pointer;
}

label[for=stack] { background: url(/attach/0202/120202001759880996/945529.png); }
label[for=stream] { background: url(/attach/0202/120202001759880996/697864.png); }
label[for=percent] { background: url(/attach/0202/120202001759880996/943980.png); }
label[for=lines] { background: url(/attach/0202/120202001759880996/104042.png); }
label[for=stack_renderer] { background: url(/attach/0202/120202001759880996/677835.png); }
label[for=bar_renderer] { background: url(/attach/0202/120202001759880996/557999.png); }
label[for=step_interpolation] { background: url(/attach/0202/120202001759880996/802217.png); }
&lt;/style&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1733#p1733"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1733"&gt;Hits(10805)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3apQ16FdHu6PudWCzu5N_rJJna4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3apQ16FdHu6PudWCzu5N_rJJna4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/3apQ16FdHu6PudWCzu5N_rJJna4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3apQ16FdHu6PudWCzu5N_rJJna4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">HTML5 제대로 알고 씁시다! - html5please.us</title><link rel="alternate" type="text/html" href="http://firejune.com/1732" /><category term="웹개발" /><category term="자료" /><category term="HTML5" /><category term="CSS3" /><category term="html5please.us" /><category term="polyfill" /><category term="폴리필" /><category term="땜빵" /><category term="크로스-브라우저" /><category term="마음의 벽" /><author><name>파이어준</name></author><updated>2012-01-31T09:28:23-08:00</updated><id>http://firejune.com/1732</id><content type="html">&lt;div class="image-align center"&gt;&lt;a href="http://html5please.us/" title=""&gt;&lt;img src="http://firejune.com/attach/0201/120201011953124840/346064.png" width="650" height="481" alt="html5please.png" class="centered hasborder"/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
HTML5의 현재 상황을 제대로 알고 사용하자는 취지로 만들어진 웹사이트인 &lt;a href="http://html5please.us/"&gt;html5please.us&lt;/a&gt;는 HTML5와 CSS3 등에서 지원하는 기능들을 찾고, 그것이 사용할 준비가 되어 있는지 알고, 대략적인 개념을 설명하고 있으며, 꼭 필요한 것인지를 판별하고, 필요로 하는 작업을 확정/폴리필(polyfill)/예비로 구분하여 정의하고 있습니다. 추천하는 떔빵(?) 라이브러리 정보도 제공하고 있네요. 그리고 이 웹사이트에 개제된 정보가 잘못되었다고 판단되면 직접 수정하거나 &lt;a href="https://github.com/h5bp/html5please"&gt;GitHub&lt;/a&gt;로 Pull 요청을 할 수 있습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="clear:both;" class="textbox1"&gt;여기에는 듣보잡 단어인 polyfill(폴리필)이라는 신조어가 나오는데 뭔 개소리냐며 지나치다가 하도 눈에 자주 밟혀서 이제야 조사해 보았습니다. "polyfill"이란 최신 브라우저에서는 작동하지만 오래된 브라우저에서는 HTML5 API를 모방하는 자바스크립트 라이브러리 따위의 도움을 받아 정상적으로 작동하도록 대안으로 기능을 구현하는 행위를 의미합니다. 폴리필에 대한 더 자세한 내용은 Remy Sharp씨가 작성한 &lt;a href="http://remysharp.com/2010/10/08/what-is-a-polyfill/"&gt;"What is a Polyfill?"&lt;/a&gt; 포스트를 참고 하세요.&lt;br /&gt;
&lt;br /&gt;
그리고 폴리필하기 위한 라이브러리들의 이름에서 자주 등장하는 단어가 shim(쉠?)인데 "틈 메우는 쐐기, 나무, 쇠, 돌"이란 뜻이 있습니다. 직역하자면 구린 브라우저(또는 브라우저 벤더 지들 끼리 쳐 싸우느라 사용방법이 다르거나 없음)에서 지원하지 않는 어떠한 기능을 땜빵하려는 목적 정도로 이해할 수 있겠네요. &lt;a href="http://www.modernizr.com/"&gt;Modernizr&lt;/a&gt;에서 작성한 &lt;a href="https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills"&gt;HTML5 Cross Browser Polyfills&lt;/a&gt; 위키 문서에는 폴리필하기 위한 각종 땜질용 라이브러리들이 일목요연하게 정리되어 있으니 이 또한 참고하세요.&lt;/div&gt;
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1732#p1732"&gt;Comments(2)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1732"&gt;Hits(17872)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/09BDMu1ckVkeE2mNgZ0QVEkDeIw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/09BDMu1ckVkeE2mNgZ0QVEkDeIw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/09BDMu1ckVkeE2mNgZ0QVEkDeIw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/09BDMu1ckVkeE2mNgZ0QVEkDeIw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">자바스크립트가 비활성화 되었다면 리디렉션</title><link rel="alternate" type="text/html" href="http://firejune.com/1731" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="noscript" /><category term="대체 콘텐츠" /><category term="http-equiv" /><category term="트릭" /><category term="enable-javascript.com" /><category term="번역" /><author><name>파이어준</name></author><updated>2012-01-31T01:23:17-08:00</updated><id>http://firejune.com/1731</id><content type="html">이제는 좀처럼 자바스크립트가 없는 웹페이지를 찾아보기 힘들 정도로 자바스크립트는 대중화되었습니다. 그러나 웹브라우저에는 자바스크립트를 비활성화하는 기능이 존재하기 때문에 모든 사용자가 자바스크립트를 사용할 수 있는 환경이라 판단하는 것은 옳지 않습니다.(그딴 사용자는 무시하자고 하는 분이 계시면 마음의 벽을 쌓으시길...) 이런 경우 사용자에게 자바스크립트를 활성화 할 것을 권고하는 등으로 사용하는 요소가 바로 &lt;a href="http://www.w3.org/TR/html5/scripting-1.html#the-noscript-element"&gt;noscript 요소&lt;/a&gt;인데 요녀석을 대체 컨텐츠로 리디렉션하는 트릭이 있어 공유합니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="html"&gt;&lt;code class="html"&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;noscript&amp;gt;
      &amp;lt;meta http-equiv="refresh" content="0; url=noscript.html"&amp;gt;
    &amp;lt;/noscript&amp;gt;
    &amp;lt;title&amp;gt;Redirect to a Page for Print if JavaScript is disabled&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;header&amp;gt;
      &amp;lt;h1&amp;gt;Redirect to a Page for Print if JavaScript is disabled&amp;lt;/h1&amp;gt;
    &amp;lt;/header&amp;gt;
    &amp;lt;section&amp;gt;
      &amp;lt;p&amp;gt;JavaScript is &amp;lt;em&amp;gt;enabled&amp;lt;/em&amp;gt;.&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
noscript 요소를 head 요소에서 사용하고 meta 요소에 http-equiv속성을 refresh로 설정했습니다. 지정된 시간이 지나면 특정 주소로 이동하는 meta 요소입니다. 위 코드는 자바스크립트를 사용할 수 없는 환경의 사용자를 구분하여 noscript.html로 리디렉션합니다. 이를 응용하면 link 요소와 style 요소등을 응용하여 스타일의 변화를 꾀할 수도 있을 것입니다.(HTML4에서도 작동하는지는 확인하지 못했어요;)&lt;br /&gt;
&lt;br /&gt;
마땅히 리디렉션 시킬 곳이 없다거나 대체 컨텐츠를 만들기 귀찮다면 &lt;a href="http://www.enable-javascript.com"&gt;enable-javascript.com&lt;/a&gt;으로 연결하는 것을 권장합니다. 크로아티아 친구들이 서비스하는 이 웹사이트는 방문객이 자바스크립트의 필요성과 활성화하는 방법을 상세히 설명하는 내용을 다루고 있습니다.(한국어가 없길래 날로 번역해서 넘겨줬더니 발빠르게 &lt;a href="http://www.enable-javascript.com/ko/"&gt;페이지를 추가&lt;/a&gt;해 주더군요. 리소스 작업을 구글닥에서 실시간으로 협업한 Toni씨 반가웠고, 번역에 도움주신 여친님 고마워요. ㅎㅎ)
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1731#p1731"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1731"&gt;Hits(5958)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/YqaXRV79gCY9WcLqwmPJZKp4vUg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YqaXRV79gCY9WcLqwmPJZKp4vUg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/YqaXRV79gCY9WcLqwmPJZKp4vUg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YqaXRV79gCY9WcLqwmPJZKp4vUg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">인상적인 오디오 신디사이저 - Morning Star Synth</title><link rel="alternate" type="text/html" href="http://firejune.com/1730" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="HTML5" /><category term="Morning Start Synth" /><category term="오디오" /><category term="신디사이저" /><category term="Monophonic Bassline" /><author><name>파이어준</name></author><updated>2012-01-30T12:37:04-08:00</updated><id>http://firejune.com/1730</id><content type="html">&lt;div class="image-align center"&gt;&lt;a href="http://bitterspring.net/ms/morningstar/" title=""&gt;&lt;img src="http://firejune.com/attach/0131/120131044550819878/331001.png" width="650" height="406" alt="morningstar.png" class="centered null"/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Cristiano Belloni씨는 순수 HTML5만을 이용하여 스텝 시퀸서에 의해 제어되는 &lt;a href="http://blog.dubspot.com/moog-minitaur-the-new-tabletop-monophonic-bass-synth-namm-2012/"&gt;Monophonic Bassline&lt;/a&gt; 신디사이저인 &lt;a href="http://bitterspring.net/ms/morningstar/"&gt;Morning Star&lt;/a&gt;를 만들었습니다. 모질라가 밀고있는 &lt;a href="https://wiki.mozilla.org/Audio_Data_API"&gt;Audio Data API&lt;/a&gt;와 구글이 밀고있는 &lt;a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html"&gt;Web Audio API&lt;/a&gt;를 모두 지원하는 &lt;a href="http://kievii.net/"&gt;KievII&lt;/a&gt; 라이브러리를 사용했습니다. 이 라이브러리 역시 Cristiano Belloni씨가 배포한 것으로, 오디오 DSP 뿐만 아니라 다양한 GUI 요소들까지도 포함하고 있습니다. 손잡이(knobs), 슬라이더 등은 캔버스 요소로 만들어 졌으며, 전체 UI가 하나의 캔버스 요소입니다.&lt;br /&gt;
&lt;br /&gt;
신디사이저 자체에는 스탭 편집, 피아노 롤, 페턴 제어, 녹음 및 재생, 다양한 효과들 등, 아주 많은 기능을 포함하고 있습니다. 사용자의 마지막 변동 사항을 자동으로 localStorage에 저장하여 편집 도중 브라우저를 닫았다 열어도 계속해서 작업을 이어나갈 수 있습니다. 또한 자신이 작업한 음악을 URL로 내보낼수 있어 친구와 협업하는 것도 가능하다고 합니다. 보다 자세한 정보는 &lt;a href="http://bitterspring.net/blog/2012/01/25/morning-star-synth-0-1-released/"&gt;Cristiano Belloni씨의 블로그에 작성&lt;/a&gt;되어 있으며, 소스코드는 &lt;a href="https://github.com/janesconference/MorningStar"&gt;Github에 공개&lt;/a&gt;했습니다.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1730#p1730"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1730"&gt;Hits(5016)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/43fof7gqwdOsBvjLuJOk1frJQTc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/43fof7gqwdOsBvjLuJOk1frJQTc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/43fof7gqwdOsBvjLuJOk1frJQTc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/43fof7gqwdOsBvjLuJOk1frJQTc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">WebRTC 데모 - 포토부스 스타일 라이브 비디오 이펙츠</title><link rel="alternate" type="text/html" href="http://firejune.com/1728" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="WebGL" /><category term="포토부스" /><category term="getUserMedia" /><category term="웹캠" /><category term="WebRTC" /><category term="구글은 참 무섭습니다." /><author><name>파이어준</name></author><updated>2012-01-29T12:38:37-08:00</updated><id>http://firejune.com/1728</id><content type="html">애플의 기본 프로그램인 &lt;a href="http://www.apple.com/kr/ipad/built-in-apps/photobooth.html"&gt;포토부스&lt;/a&gt;의 멋진 그래픽 필터 효과들을 사용해 본 경험이 있으세요? Paul Neave씨는 브라우저만으로 이와 유사한 기능을 제공하는 &lt;a href="http://neave.com/webcam/html5/"&gt;HTML5 비디오 이펙츠 데모&lt;/a&gt;를 선보였습니다. 아쉽게도, 지금 출시된 브라우저들은 플래시를 이용하지 않는 한 웹캠을 사용할 수 있는 방법이 없지만, 최근 구글 크롬은 &lt;a href="http://tools.google.com/dlpage/chromesxs"&gt;차기 버전(코드네임 카나리아)&lt;/a&gt;에 들어갈 핵심기능인 &lt;a href="http://www.webrtc.org/"&gt;WebRTC(Web Real-Time Communications)&lt;/a&gt; API의 getUserMedia를 이용하여 이 데모를 정상적으로 수행해 볼 수 있습니다. 카나리아를 구하는 방법과 설정하는 방법은 조금있다가 살펴 보기로 하고요. 이 데모에 포함된 비디오에 실시간으로 반영되는 효과들은 자바스크립트와 WebGL을 이용한 것입니다. 대략 살펴 보면, HTML5의 비디오 요소에서 얻어낸 아웃풋 데이터를 캔버스 요소로 불러와서 WebGL의 쉐이더 효과를 입힌 것입니다. 포토부스와 유사한 카운트가 나타나면서 스냅샷을 찍을 수 있고 찍은 사진을 소셜 네트워크에 공유하거나 다운로드 할 수 있습니다. 정말 멋지지 않아요?&lt;br /&gt;
&lt;br /&gt;
&lt;div class="image-align center"&gt;&lt;a href="http://neave.com/webcam/html5/" title=""&gt;&lt;img src="http://firejune.com/attach/0130/120130024501287808/119058.png" width="664" alt="videoeffects.png" class="centered hasborder"/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
만약 getUserMedia가 활성 되지 않은 경우 별도로 준비된 동영상이 나타나며 여러 그래픽 효과들만 실험해 볼 수 있습니다. 웹캠과 연동하여 태스트하려면 조금 복잡한 과정을 거쳐야 합니다. 앞서 말씀드린 크롬의 카나리아 버전을 설치한 후에 실행 옵션을 주어야 하는데, 윈도 버전은 속성창을 열어 대상란의 쌍따옴표(")뒤에 "--enable-media-stream" 이라고 작성해 주어야 하고 맥용은 다음과 같이 실행합니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="command"&gt;&lt;code class="command"&gt;open -a "/Applications/Google Chrome Canary.app" --args --enable-media-stream&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
그리고 주소창에 "chrome://flags"를 입력하여 나타난 옵션 패널에서 "Media Stream Flag"를 활성시키고 재시작 합니다. 보다 자세한 사용법은 &lt;a href="http://www.webrtc.org/running-the-demos"&gt;이 곳&lt;/a&gt;에 있으니 참고하세요. 최대 100fps까지 확보되며 안정적으로 작동하는 것을 확인하였습니다. &lt;a href="http://www.apple.com/kr/mac/facetime/"&gt;페이스타임&lt;/a&gt;과 같은 웹앱이 나오는 것도 이제 시간문제로군요. 떨거지들이 어영부영 만든 블라블라 API의 웃는 얼굴에 침이라도 뱉듯이 종지부를 찍어버리는 구글은 참 무섭습니다.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1728#p1728"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1728"&gt;Hits(11227)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/2XfXfaGBv6DvAeg_uXGAnXmiVZk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2XfXfaGBv6DvAeg_uXGAnXmiVZk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/2XfXfaGBv6DvAeg_uXGAnXmiVZk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2XfXfaGBv6DvAeg_uXGAnXmiVZk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">Clog 버전 0.1.2 업데이트 릴리즈</title><link rel="alternate" type="text/html" href="http://firejune.com/1727" /><category term="웹개발" /><category term="자료" /><category term="Node.JS" /><category term="모듈" /><category term="배포" /><category term="업데이트" /><category term="컬러풀" /><category term="콘솔" /><category term="디버깅" /><category term="업데이트는 당분간 이걸로 땡" /><category term="GitHub" /><author><name>파이어준</name></author><updated>2012-01-16T00:30:03-08:00</updated><id>http://firejune.com/1727</id><content type="html">머리털나고 처음으로 배포했던 &lt;a href="http://nodejs.org/"&gt;Node.JS&lt;/a&gt;용 모듈인 &lt;a href="http://firejune.com/1701/Node.JS%EC%97%90%EC%84%9C+%EC%BB%AC%EB%9F%AC%ED%92%80%ED%95%9C+%EC%BD%98%EC%86%94+%EB%94%94%EB%B2%84%EA%B9%85%EC%9D%84+-+Clog+%EB%B0%B0%ED%8F%AC"&gt;Clog&lt;/a&gt;를 0.1.2 버전으로 업데이트했습니다. 이 마이너 업데이트는 종전과 로그 출력방법이 동일하며 새롭게 &lt;code&gt;configure&lt;/code&gt; 메서드가 추가되었습니다. 이 메서드를 이용하면 콘솔의 유형 단위로 로그를 필터링 할 수 있습니다. 사용 방법은 다음과 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var clog = require('clog');

// display level configration:
clog.configure({'log level': 2});
//=&amp;gt; {'log': true, 'info': true, 'warn': false, 'error': false, 'debug': false}

// custom display level configration:
clog.configure({
  'log level': {
    'log': true,
    'info': true,
    'warn': false,
    'error': true,
    'debug': false
  }
});
//=&amp;gt; {'log': true, 'info': true, 'warn': false, 'error': true, 'debug': false}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;code&gt;configure&lt;/code&gt; 메서드는 객체를 인자로 받으며 'log level' 속성에 정의된 디스플레이 래밸의 숫자 또는 커스텀 래밸 플래그들로 구성된 객체를 인식하도록 만들었습니다. 래밸의 수는 log, info, warn, error, debug순으로 허용할 단계를 의미합니다. 즉 2를 기입하면 log와 info만 출력하게 되는 것이죠. 만약 당신이 모든 로그를 제외한 경고만 출력하고 싶다면 다음처럼 작성하면 됩니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;clog.configure({
  'log level': {
    'log': false,
    'info': false,
    'warn': true,
    'error': false,
    'debug': false
  }
});

// or

clog.configure({'log level': 0});
clog.configure({'log level': {'warn': true}});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
또한 &lt;code&gt;configure&lt;/code&gt; 메서드는 나중에 다시 정의해도 문제되지 않으며, 커스텀 레이블도 필터링 할 수도 있도록 처리되었습니다. 커스텀 레이블을 사용중이라면 다음과 같이 필터링 할 수 있습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;clog('server', 'start listening on port 3000');  // custom head
clog.configure({'log level': {'server': false}});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
이 옵션들을 잘 이용하면 운영과 개발 모드를 구분하여 자동으로 필터링하거나 어떤 상황에서 특정 메시지만 출력하도록 하는 동적인 구현도 가능할 거예요. 다음 업데이트에는 사용자 지정 색상을 지원하도록 할 예정입니다만, 언제 실행에 옮길지는 저도 잘 모르겠습니다; 끝으로 &lt;a href="https://github.com/firejune/clog"&gt;GitHub&lt;/a&gt;를 통해 이 아이디어를 주신 &lt;a href="https://github.com/hashar"&gt;Antoine Musso&lt;/a&gt;씨와 여로모로 전파에 힘써주신 &lt;a href="http://ajaxian.kr/"&gt;A.J&lt;/a&gt;님, &lt;a href="http://blog.outsider.ne.kr/"&gt;Outsider&lt;/a&gt;님 그리고 Clog를 애용해 주시는 모든 여러분들께 감사드립니다.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1727#p1727"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1727"&gt;Hits(135069)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/IYhHt41RubZByUvuBU0mI-3FgV8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IYhHt41RubZByUvuBU0mI-3FgV8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/IYhHt41RubZByUvuBU0mI-3FgV8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IYhHt41RubZByUvuBU0mI-3FgV8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">CSS3와 자바스크립트를 이용한 프리젠테이션 impress.js</title><link rel="alternate" type="text/html" href="http://firejune.com/1725" /><category term="웹개발" /><category term="자료" /><category term="impress.js" /><category term="자바스크립트" /><category term="CSS3" /><category term="프리젠테이션" /><category term="HTML5" /><category term="10만원 상품권 득" /><author><name>파이어준</name></author><updated>2012-01-13T00:39:56-08:00</updated><id>http://firejune.com/1725</id><content type="html">사내 개발자가 아닌 분들을 대상으로 하는 HTML5 발표가 있어서 자료를 조사하던 중 Bartek Szopka씨가 만든 &lt;a href="http://bartaz.github.com/impress.js/#/bored"&gt;impress.js&lt;/a&gt;를 발견하고 소개했습니다. 보시던 분들의 반응이 아주 좋더군요. impress.js는 CSS3와 자바스크립트를 이용하여 인상깊은 프리젠테이션을 웹기반으로 만들어 줍니다. 화면 전환에는 다양한 CSS transition과 3D transform이 사용되었습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe src="http://jsbin.firejune.com/impress/2#/bored" width="666" height="444" style="border: 1px solid #aaa;"&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
HTML5의 스펙들 중 &lt;code&gt;classList&lt;/code&gt;와 &lt;code&gt;dataset&lt;/code&gt; API를 사용하기 때문에 이를 지원하지 않는 브라우저에서는 정상적으로 작동하지 않는다고 합니다. impress.js가 제대로 작동하는 브라우저는 현재 크롬과 사파리밖에 없습니다. 위 데모를 작동시키려면 프레임 영역을 클릭하고 방향키를 눌러보세요.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1725#p1725"&gt;Comments(6)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1725"&gt;Hits(46126)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/NbnbVtK3lFYsjws3OZT8eWrfjf0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NbnbVtK3lFYsjws3OZT8eWrfjf0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/NbnbVtK3lFYsjws3OZT8eWrfjf0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NbnbVtK3lFYsjws3OZT8eWrfjf0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">iOS 디바이스별 화면 구성요소 치수들</title><link rel="alternate" type="text/html" href="http://firejune.com/1723" /><category term="웹개발" /><category term="자료" /><category term="iOS" /><category term="디바이스" /><category term="화면" /><category term="치수" /><category term="아이폰" /><category term="아이패드" /><category term="프로젝트" /><category term="레티나 디스플레이" /><category term="아이팟" /><category term="모바일" /><author><name>파이어준</name></author><updated>2012-01-12T22:37:53-08:00</updated><id>http://firejune.com/1723</id><content type="html">iOS 기기용 웹앱이나 네이티브앱 개발시 유용하게 사용될 수 있는 치수들입니다. 특히, 모든 iOS 기기를 지원하는(유니버셜) 앱을 개발할 때 자주 찾게 되는 자료중 하나입니다. 스테이터스 바, 네비게이션 바, 탭 바, 키보드 등의 사이즈를 세웠을 때와 눕혔을 때로 구분하여 측정했습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;아이폰의 화면 치수&lt;/h3&gt; 아이폰에서 해상도를 계산할 때 굳이 레티나 디스플레이의 해상도(640 x 960)를 의식하여 계산할 필요는 없습니다. 단순히 이미지가 사용되는 곳에 더블픽셀(@x2)의 고해상도 이미지를 한 셋트 더 준비하면 된다는 사실만 기억하세요.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Portrait - 세웠을 때&lt;/h4&gt;앱 시작시 나타나는 오프닝 이미지는 기본으로 320 x 367 픽셀의 "filename.png"로 설정하고 레티나용으로 표시될 이미지는 640 x 734 픽셀의 "filename@2x.png"로 설정하면 됩니다.&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/0113/120113112137615483/643901.jpg" width="540" height="744" alt="iphone_portrait_dimensions.jpg" /&gt;&lt;br /&gt;
iPhone Portrait Width: &lt;strong&gt;320px&lt;/strong&gt;&lt;br /&gt;
iPhone Portrait Height: &lt;strong&gt;480px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Status Bar Height: &lt;strong&gt;20px&lt;/strong&gt;&lt;br /&gt;
Nav Bar Height: &lt;strong&gt;44px&lt;/strong&gt;&lt;br /&gt;
Main Content Area Height: &lt;strong&gt;367px&lt;/strong&gt;&lt;br /&gt;
Tab Bar Height: &lt;strong&gt;49px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Keyboard Height: &lt;strong&gt;216px&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/0113/120113112137615483/698460.jpg" width="440" height="216" alt="iphone_portrait_keyboard_sizes.jpg" /&gt;&lt;br /&gt;
키보드가 나타난 상황에서는 메인 스크린의 일부와 탭바를 가립니다. 키보드 크기를 제외한 나머지 영역의 크기를 계산할 때 참고하세요.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Landscape - 눕혔을 때&lt;/h4&gt;아이폰을 눕혔을 때 나타나는 변화는 메인 컨텐츠 영역의 높이가 480에서 219픽셀로 줄어드는 것이고 네비게이션 바의 높이가 44에서 32픽셀로 줄어든다는 것입니다. 오프닝 이미지는 기본으로 480 x 219 픽셀 그리고 레티나용(@2x)은 960 x 438 픽셀입니다. 참고로 아이폰의 앱 시작 이미지는 세로와 가로 모두 준비할 필요가 없습니다. 자신의 앱이 가로와 세로 중 어디에 최적화 되어있는지를 판단하여 한가지만 준비하면 됩니다.&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/0113/120113112137615483/856283.jpg" width="580" height="309" alt="iphone_landscape_dimensions.jpg" /&gt;&lt;br /&gt;
iPhone Landscape Width: &lt;strong&gt;480px&lt;/strong&gt;&lt;br /&gt;
iPhone Landscape Height: &lt;strong&gt;320px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Status Bar Height: &lt;strong&gt;20px&lt;/strong&gt;&lt;br /&gt;
Nav Bar Height: &lt;strong&gt;32px&lt;/strong&gt;&lt;br /&gt;
Main Content Area Height: &lt;strong&gt;219px&lt;/strong&gt;&lt;br /&gt;
Tab Bar Height: &lt;strong&gt;49px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Keyboard Height: &lt;strong&gt;162px&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/0113/120113112137615483/452171.jpg" width="534" height="150" alt="iphone_landscape_keyboard_sizes.jpg" /&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;아이패드의 화면 치수&lt;/h3&gt;아이패드는 아직 레티나 디스플레이를 지원하지 않기 때문에 더블픽셀(@2x) 이미지를 준비할 필요가 없습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Portrait - 세웠을 때&lt;/h4&gt;&lt;img src="http://firejune.com/attach/0113/120113112137615483/365119.jpg" width="580" height="599" alt="ipad_portrait_dimensions.jpg" /&gt;&lt;br /&gt;
iPad Portrait Width: &lt;strong&gt;768px&lt;/strong&gt;&lt;br /&gt;
iPad Portrait Height: &lt;strong&gt;1024px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Status Bar Height: &lt;strong&gt;20px&lt;/strong&gt;&lt;br /&gt;
Nav Bar Height: &lt;strong&gt;44px&lt;/strong&gt;&lt;br /&gt;
Main Content Area Height: &lt;strong&gt;911px&lt;/strong&gt;&lt;br /&gt;
Tab Bar Height: &lt;strong&gt;49px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Keyboard Height: &lt;strong&gt;264px&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/0113/120113112137615483/436220.jpg" width="580" height="172" alt="ipad_portrait_keyboard_sizes.jpg" /&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Landscape - 눕혔을 때&lt;/h4&gt; 아이폰과 달리 아이패드는 화면을 눕혔을 때 네비게이션 바의 높이에는 변화가 없습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/0113/120113112137615483/294447.jpg" width="580" height="449" alt="ipad_landscape_dimensions.jpg" /&gt;&lt;br /&gt;
iPad Landscape Width: &lt;strong&gt;1024px&lt;/strong&gt;&lt;br /&gt;
iPad Landscape Height: &lt;strong&gt;768px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Status Bar Height: &lt;strong&gt;20px&lt;/strong&gt;&lt;br /&gt;
Nav Bar Height: &lt;strong&gt;44px&lt;/strong&gt;&lt;br /&gt;
Main Content Area Height: &lt;strong&gt;655px&lt;/strong&gt;&lt;br /&gt;
Tab Bar Height: &lt;strong&gt;49px&lt;/strong&gt;&lt;br /&gt;
 	 &lt;br /&gt;
Keyboard Height: &lt;strong&gt;352px&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/0113/120113112137615483/441908.jpg" width="580" height="172" alt="ipad_landscape_keyboard_sizes.jpg" /&gt;
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1723#p1723"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1723"&gt;Hits(5780)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/wFkNnxneJXW4SKL3nuL6k0z_l7M/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wFkNnxneJXW4SKL3nuL6k0z_l7M/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/wFkNnxneJXW4SKL3nuL6k0z_l7M/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wFkNnxneJXW4SKL3nuL6k0z_l7M/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">앱셀러레이터 타이타니움을 이용한 앱개발 - 아이콘</title><link rel="alternate" type="text/html" href="http://firejune.com/1722" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="앱셀러레이터" /><category term="타이타니움" /><category term="웹앱" /><category term="프로젝트" /><category term="아이콘" /><category term="아이폰" /><category term="아이패드" /><author><name>파이어준</name></author><updated>2012-01-12T20:53:19-08:00</updated><id>http://firejune.com/1722</id><content type="html">&lt;div class="image-align left"&gt;&lt;img src="http://firejune.com/attach/0111/120111170339996345/587291.png" width="396" height="217" alt="tab_bar_icons_closeup.png" class=" null"/&gt;&lt;/div&gt; 타이타니움을 이용하여 아이폰 또는 아이패드 애플리케이션을 구축하다 보면 탭바(탭 표시줄)에 표시될 아이콘 사용법에 대하여 의문이 생길 것입니다. 애플은 이러한 목적으로 사용할 수 있는 내장 시스템 아이콘 12개를 제공합니다. 이 시간에는 기본으로 제공되는 내장 시스템 아이콘의 사용법과 사용자가 지정한 이미지 아이콘을 탭바에 적용하는 방법에 대하여 알아봅니다.&lt;br /&gt;
&lt;br /&gt;
아래 코드는 두개의 윈도를 탭으로 전환하는 유저 인터페이스를 간단하게 구성한 것입니다. 이 코드를 기반으로 탭바 아이콘 지정방법에 대하여 설명하도록 하겠습다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var tabGroup = Ti.UI.createTabGroup();

var win1 = Ti.UI.createWindow({
  title: "Home Page"
});

var tab1 = Ti.UI.createTab({
  icon: "tabbaricons/light_home.png",
  title: "Home",
  window: win1
});

tabGroup.addTab(tab1);

var win2 = Ti.UI.createWindow({
  title: "Bookmarks Page"
});

var tab2 = Ti.UI.createTab({
  icon: Titanium.UI.iPhone.SystemIcon.BOOKMARKS,
  window: win2
});

tabGroup.addTab(tab2);

tabGroup.open();&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;사용자 지정 탭바 아이콘&lt;/h3&gt; 위 코드에서 tab1에 정의된 탭은 사용자 지정 아이콘으로 만들어지는 탭바 버튼입니다. “Resources” 폴더에 "tabbaricons"라는 폴더를 만들고 "light_home.png"라는 이름의 이미지 경로를 지정하는 것으로 사용자 지정 아이콘을 사용할 수 있습니다. 여기에 사용할 수 있는 양질의 탭바 아이콘을 무료로 제공하는 사이트들을 몇몇 소개합니다.&lt;br /&gt;
&lt;br /&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;&lt;a href="http://www.glyphish.com/"&gt;glyphish.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://app-bits.com/"&gt;app-bits.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.iconsweets.com/"&gt;iconsweets.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.iconsweets2.com/"&gt;iconsweets2.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
아이폰 4이상에서 지원하는 레티나 디스플레이(Retina Display)를 위해서는 약간의 달러를 지불하고 @2x 버전을 구입할 수도 있습니다. 이 경우 동일한 위치에 "light_home@2x.png"파일이 위치하는 것 만으로 별도의 추가 작업 없이 서로다른 해상도에 대응할 수 있습니다. 멋지죠?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;내장 시스템 탭바 아이콘&lt;/h3&gt;그리고 tab2에 정의된 탭은 애플에서 기본으로 제공하는 내장 시스템 아이콘을 이용하여 만들어진 탭바 버튼입니다. 모든 해상도에 대응하는 아이콘을 무료로 사용할 수 있지만 애플이 의도하는 목적으로만 사용할 수 있습니다. 예를 들면 '즐겨찾기' 아이콘을 사용하는 경우 레이블이 시스템에 설정된 언어에 따라 자동으로 기입되기 때문에 개발자기 이를 다른 것으로 대치할 수 없습니다. 때문에 이 경우라면 title속성을 작성하는 것은 무의미 합니다.&lt;br /&gt;
&lt;br /&gt;
애플에서 기본으로 제공하는 12가지 내장 시스템 아이콘은 다음과 같습니다.&lt;br /&gt;
&lt;table class="grid" summary="시스템 아이콘"&gt;
&lt;tr&gt;
&lt;th width="100px" align="left"&gt;Icon&lt;/th&gt;
&lt;th width="200px" align="left"&gt;Name&lt;/th&gt;
&lt;th align="left"&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="bookmarks tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/686063.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Bookmarks&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.BOOKMARKS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="contacts tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/117666.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Contacts&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.CONTACTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="downloads tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/070808.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Downloads&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.DOWNLOADS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="favorites tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/915407.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Favorites&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.FAVORITES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="featured tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/683420.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Featured&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.FEATURED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="history tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/364141.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;History&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.HISTORY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="more tab bar icon" src="http://firejune.com/attach/0111/120111170339996345/372399.png"
width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;More&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.MORE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="most recent tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/267522.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Most Recent&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.MOST_RECENT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="most viewed tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/237848.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Most Viewed&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.MOST_VIEWED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="recents tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/869551.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Recents&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.RECENTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="search tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/318754.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.SEARCH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img class="imageStyle" alt="tor rated tab bar icon" src=
"http://firejune.com/attach/0111/120111170339996345/842806.png" width="65" height="50" /&gt;&lt;/td&gt;
&lt;td&gt;Top Rated&lt;/td&gt;
&lt;td&gt;Ti.UI.iPhone.SystemIcon.TOP_RATED&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;style&gt;
  .grid {
    width: 100%; border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; background: #fff;
  }
  .grid td, .grid th { text-align: left; vertical-align: middle; }
  .grid th { background: #FAFAFA; padding: 3px 5px; }
  .grid th small { font-weight: normal; }
  .grid.border td,  .grid.border th { border-left: 1px solid #ccc; border-right: 1px solid #ccc; }
  .grid .tags { background: #FAFAFA; }
&lt;/style&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1722#p1722"&gt;Comments(2)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1722"&gt;Hits(4093)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sPjg4W-bh1mUncJpnYOxQScK3yA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sPjg4W-bh1mUncJpnYOxQScK3yA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sPjg4W-bh1mUncJpnYOxQScK3yA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sPjg4W-bh1mUncJpnYOxQScK3yA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">앱셀러레이터 타이타니움을 이용한 앱개발 - 변수 전달</title><link rel="alternate" type="text/html" href="http://firejune.com/1721" /><category term="웹개발" /><category term="자료" /><category term="앱셀러레이터" /><category term="타이타니움" /><category term="모바일" /><category term="웹앱" /><category term="자바스크립트" /><category term="프로젝트" /><author><name>파이어준</name></author><updated>2012-01-11T00:07:05-08:00</updated><id>http://firejune.com/1721</id><content type="html">&lt;a href="http://www.appcelerator.com/"&gt;앱셀러레이터 타이타니움&lt;/a&gt;에서 여러 화면으로 구성된 앱을 구축할 때 화면전환에 사용되는 코드를 크게 두 가지로 방법으로 작성할 수 있습니다. 하나의 큰 덩어리로 작성하거나, 전환될 화면 단위로 기능들을 모듈화하는 방법입니다. 큰 덩어리로 작성하면 시스템 메모리를 조금더 효율적으로 관리하도록 신경써야하고 경우에 따라서는 앱의 로딩시간이 길어질 수도 있습니다. 반면 공통으로 사용되는 함수나 변수를 다루기가 수월합니다. 모듈화하는 방법은 모듈간 변수를 공유하는 것이 다소 까다롭고, 사용자가 기능을 실행할 때에 해당 모듈이 발동되는 구조이기 때문에 다소 버벅이는 느낌을 줄 수 있지만 앱의 기본 로딩시간이 단축됩니다. 이들간의 속도차이는 중/소규모 앱에서 아주 근소한 차이이기 때문에 일반 사용자가 알아채기 힘든 수준입니다. 자신이 작성할 앱의 규모에 따라서 두가지 방법중 하나를 선택하면 되겠습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="image-align center"&gt;&lt;a href="http://firejune.com/attach/0111/120111164134693977/988048.png" onclick="Zooming(this, 1920, 960); return false;" title=""&gt;&lt;img src="http://firejune.com/attach/0111/120111164134693977/988048.png" width="666" height="333" alt="IMG_0570 사본.png" class="centered null"/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
아래 코드는 하나의 파일에 무뚱그려 작성한 예입니다. 윈도에 edit 버튼이 위치하고 탭하면 cancel 버튼이 토글되도록 하는 기능을 수행합니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var window = Ti.UI.createWindow({title: userName});

//  create edit/cancel buttons for nav bar
var cancel = Ti.UI.createButton({title: 'Cancel'});
cancel.addEventListener('click', function() {
  window.setRightNavButton(edit);
  tableView.editing = false;
});

var edit = Ti.UI.createButton({title: 'Edit'});
edit.addEventListener('click', function() {
  window.setRightNavButton(cancel);
  tableView.editing = true;
});

window.setRightNavButton(edit);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
아래는 윈도에서 작동할 기능이 들어있는 해당 파일을 윈도우 속성에 지정하여 구분하는 예입니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var window = Ti.UI.createWindow({url: '/modules/ui.js', title: userName})&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
ui.js에 edit와 cancel 버튼이 토글되는 기능이 담긴 코드입니다. Ti.UI.currentWindow를 통하여 상위 윈도 객체로 접근할 수 있습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var currentWindow = Ti.UI.currentWindow;

//  create edit/cancel buttons for nav bar
var edit = Ti.UI.createButton({title: 'Edit'});
edit.addEventListener('click', function() {
  currentWindow.setRightNavButton(cancel);
  tableView.editing = true;
});

var cancel = Ti.UI.createButton({title: 'Cancel'});
cancel.addEventListener('click', function() {
  currentWindow.setRightNavButton(edit);
  tableView.editing = false;
});

currentWindow.setRightNavButton(edit);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
저야 당연히 후자쪽이 쉽게 확장할 수 있고 효율적일 것이라 판단하고 여러 단위의 모듈들을 구분했습니다. 역시나 여기에서 첫 번째 난관에 봉착하게 되는데 그것은 서로 다른 모듈간의 변수를 전달하는 것이었습니다. 예를 들면 부모화면에서 만들어진 데이터를 자식화면으로의 전달하는 것입니다. 이 경우 변수를 전달 할 수 있는 방법은 크게 세 가지가 있는데 첫 번째는 Custom Event를 이용한 변수 전달 방법이고 두 번째는 Current Window 속성을 이용하는 방법 그리고 세 번째는 Include를 이용하여 변수 또는 함수를 공유하는 방법입니다. 첫 번째와 두 번째는 구현 용도에 따라 구분할 수 있는데, 이벤트를 이용하는 것은 사용자가 어떠한 행동이나 데이터 푸시 등 비동기식으로 발생하는 이벤트 헨들러에 의해 데이터가 전달되는 경우 또는 자식이 부모에게 데이터를 전달하는 용도로 사용됩니다.&lt;br /&gt;
&lt;br /&gt;
다음은 부모에 커스텀 이벤트를 리스닝 하도록 하는 예입니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;// Custom Events

Titanium.App.addEventListener('event_one', function(e) {
  alert('app.js: event one, array length = ' + e.data.length);
});

Titanium.App.addEventListener('event_two', function(e) {
  alert('app.js: event two, name = ' + e.name);
});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
다음은 자식이 커스텀 이벤트를 호출하면서 변수를 전달하는 예입니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;// Fire Custom Events

Titanium.App.fireEvent('event_one', {data: ['1','2','3']});
Titanium.App.fireEvent('event_two', {name: 'Foo', city: 'Palo Alto'});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
다음으로 Current Window 속성을 이용하는 방법은 단순히 부모가 가진 데이터를 자식에게 전달할 때 사용합니다. 아래 코드는 상위에서 변수를 윈도의 속성으로 정의하는 예입니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var userName = "Firejune";
var window = Ti.UI.createWindow({url: '/modules/ui.js', title: userName})&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
자식에 해당하는 ui.js에서는 다음과 같은 방법으로 윈도의 속성으로 지정된 변수에 접근할 수 있습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var currentWindow = Ti.UI.currentWindow;
var title = currentWindow.title; // =&amp;gt; Firejune&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
끝으로 세 번째 include는 여느 프로그래밍에서 사용하는 것과 같은 일반적인 공통 함수나 변수를 끌어다 쓰는 것이기 때문에 엄밀히 말해 변수를 전달 한다기 보다는 공유하는 것입니다.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1721#p1721"&gt;Comments(1)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1721"&gt;Hits(23466)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/yBVklMAuy9R3yGBQLVltI4kJtLM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/yBVklMAuy9R3yGBQLVltI4kJtLM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/yBVklMAuy9R3yGBQLVltI4kJtLM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/yBVklMAuy9R3yGBQLVltI4kJtLM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">앱셀러레이터 타이타니움을 이용한 앱개발 그 첫 경험</title><link rel="alternate" type="text/html" href="http://firejune.com/1720" /><category term="웹개발" /><category term="자료" /><category term="앱셀러레이터" /><category term="타이타니움" /><category term="모바일" /><category term="웹앱" /><category term="자바스크립트" /><category term="사랑" /><category term="프로젝트" /><author><name>파이어준</name></author><updated>2012-01-10T06:43:28-08:00</updated><id>http://firejune.com/1720</id><content type="html">&lt;div class="image-align center"&gt;&lt;a href="http://firejune.com/attach/1215/111215150617176470/440342.png" onclick="Zooming(this, 590, 405); return false;" title=""&gt;&lt;img src="http://firejune.com/attach/1215/111215150617176470/440342.png" width="590" alt="Screen Shot 2012-01-11 at 오전 11.08.34.png" class="centered null"/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
이 포스팅을 막 하려던 찰나, 떄늦은 사랑에 중독되어 만사를 제쳐두고 햄볶는 시간들을 흘려 보낸지도 벌써 한 달째 접어들었군요. 이제서야 이리저리 흩뿌려 놓았던 정신줄들을 긁어 모아야겠다는 생각이 들었습니다. 자, 본론으로 들어가서; 앱셀러레이터 타이타니움(Appcelerator Titanium, 이하 타이타니움)을 이용하여 모바일 애플리케이션을 개발한 첫 경험을 이곳에 정리합니다. 타이타니움의 특징은 &lt;a href="http://firejune.com/1712/HTML5%EB%A5%BC+%EC%9D%B4%EC%9A%A9%ED%95%9C+%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C+%EC%95%B1+%EA%B0%9C%EB%B0%9C+%EB%8F%84%EA%B5%AC%EB%93%A4"&gt;이전 포스트&lt;/a&gt;를 참고하세요. 개발도구의 설치방법이나 사용방법 그리고 그 작동원리에 대해서는 구차하게 거론하지 않겠습니다. &lt;div class="image-align right"&gt;&lt;a href="http://firejune.com/attach/1215/111215150617176470/954297.PNG" onclick="Zooming(this, 640, 960); return false;" title=""&gt;&lt;img src="http://firejune.com/attach/1215/111215150617176470/954297.PNG" height="360" alt="IMG_0754.PNG" class="centered hasborder"/&gt;&lt;/a&gt;&lt;/div&gt;개발하는 것 자체를 도모하기 위한 글이니까요. &lt;br /&gt;
&lt;br /&gt;
초심자에게는 &lt;a href="https://github.com/appcelerator/KitchenSink"&gt;키친싱크(KitchenSink)&lt;/a&gt; 데모를 프로젝트에 등록하여 빌드하고 실행되는 모습을 유심히 관찰할 것을 권장합니다. 타이타니움이 지원하는 거의 모든 기능이 이곳에 담겨 있다해도 과언이 아닌 데모로서 분명히 좋은 길잡이가 되어줄 것입니다.&lt;br /&gt;
&lt;br /&gt;
사실, &lt;a href="http://www.appcelerator.com/"&gt;앱셀러레이터 홈페이지&lt;/a&gt;를 둘러보면서 다소 신빙성 없이 과장해 놓았다는 느낌을 받아 부정적인 시각으로 지켜봐 왔었드랬습니다만, 막상 사용해 보고 사이트를 훓어보면서 &lt;a href="http://www.aptana.com/"&gt;앱테나(Aptana)&lt;/a&gt;를 인수하기 전의 컨텐츠와 후의 컨텐츠가 혼제되어 있고 아직도 비지니스 모델을 찾고 있다는 생각이 들었기에 이들은 아직도 진화중인 듯하여 지금은 그러려니 하고 있습니다.&lt;br /&gt;
&lt;br /&gt;
타이타니움으로 모바일 앱을 개발하면서 든 느낌을 한 마디로 요약하자면, 커다란 자바스크립트 프레임웤을 사용하는 것과도 비슷합니다. DOM 스크립팅를 이해하는 대신에 랩핑 API를 습득하여 기존 웹 애플리케이션 개발자들이 쉽게 접근할 수 있으며 또한 크로스-브라우저 이슈를 자바스크립트로 해결하는 것과 같이 벤더가 서로 다른 디바이스에 대응할 수 있습니다. 기초적인 내용은 &lt;a href="http://developer.appcelerator.com/apidoc/mobile/latest"&gt;개발문서를 참조&lt;/a&gt;하기 바라며, 직접 개발하는 과정에서 발생했던 문제들과 해결하는 과정에 대하여 연재로 포스팅 할 예정입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;관련글:&lt;/h4&gt;
&lt;ul class="square"&gt;
&lt;li&gt;&lt;a href="http://firejune.com/1721/%EC%95%B1%EC%85%80%EB%9F%AC%EB%A0%88%EC%9D%B4%ED%84%B0+%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%88%EC%9B%80%EC%9D%84+%EC%9D%B4%EC%9A%A9%ED%95%9C+%EC%95%B1%EA%B0%9C%EB%B0%9C+-+%EB%B3%80%EC%88%98+%EC%A0%84%EB%8B%AC"&gt;앱셀러레이터 타이타니움을 이용한 앱개발 - 변수 전달&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://firejune.com/1722/%EC%95%B1%EC%85%80%EB%9F%AC%EB%A0%88%EC%9D%B4%ED%84%B0+%ED%83%80%EC%9D%B4%ED%83%80%EB%8B%88%EC%9B%80%EC%9D%84+%EC%9D%B4%EC%9A%A9%ED%95%9C+%EC%95%B1%EA%B0%9C%EB%B0%9C+-+%EC%95%84%EC%9D%B4%EC%BD%98"&gt;앱셀러레이터 타이타니움을 이용한 앱개발 - 아이콘&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://firejune.com/1723/iOS+%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4%EB%B3%84+%ED%99%94%EB%A9%B4+%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C+%EC%B9%98%EC%88%98%EB%93%A4"&gt;iOS 디바이스별 화면 구성요소 치수들&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1720#p1720"&gt;Comments(1)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1720"&gt;Hits(2372)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/327T2YrA2w8t8FsNEJ1kNwBXkk4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/327T2YrA2w8t8FsNEJ1kNwBXkk4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/327T2YrA2w8t8FsNEJ1kNwBXkk4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/327T2YrA2w8t8FsNEJ1kNwBXkk4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">MIT 라이센스 링크는 이제 mit-license.org로</title><link rel="alternate" type="text/html" href="http://firejune.com/1719" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="라이센스" /><category term="MIT" /><category term="mit-license.org" /><category term="curl" /><category term="GitHub" /><category term="JSON" /><category term="copyright" /><category term="프로젝트" /><category term="야리꾸리" /><author><name>파이어준</name></author><updated>2011-12-13T17:01:31-08:00</updated><id>http://firejune.com/1719</id><content type="html">지금까지 많은 사람들이 &lt;a href="http://opensource.org/licenses/mit-license.php"&gt;Open Source Initiative의 MIT 라이센스 페이지&lt;/a&gt;를 이용하여 MIT 라이센스를 명시해 왔습니다. 왜냐하면 라이센스 전문을 복사해서 쓰는 번거로움을 피할 수 있었기 때문이죠. 보통 권리자의 이름 등을 기입하고 라이센스 조항은 URL을 참조하는 형식으로 작성했습니다. 그런데, 이 라이센스 페이지가 좀 야리꾸리(?)하다고 판단한 &lt;a href="http://remysharp.com/"&gt;Remy Sharp씨&lt;/a&gt;는 간단 명료하게 MIT 라이센스만 명시된 웹사이트를 &lt;a href="http://mit-license.org/"&gt;mit-license.org&lt;/a&gt; 도메인에 물려 서비스하기 시작했습니다.&lt;br /&gt;
&lt;br /&gt;
재미있는 것은 사용자 등록을 한 경우 권리자의 이름이 페이지에 표시되며 &lt;a href="http://firejune.mit-license.org/"&gt;firejune.mit-license.org&lt;/a&gt;과 같은 URL로 접근할 수 있는 개인화 된 페이지를 제공합니다. 사용자 등록방법은 두 가지 입니다. 하나는 curl을 이용하는 방법이고 다른 하나는 &lt;a href="https://github.com/remy/mit-license"&gt;GitHub의 mit-license 프로젝트&lt;/a&gt;를 포크하여 'users' 디렉토리에 'username.json'파일을 작성하고 Remy Sharp씨에게 pull 요청을 하는 것입니다. 두 가지 방법 중 curl을 이용하는 방법을 추천합니다. Remy Sharp씨가 손수 등록해 줄 때 까지 기다릴 필요가 없으며, 실행 즉시 자동으로 추가 및 생성되기 때문이죠. curl 실행 방법은 다음과 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="command"&gt;&lt;code class="command"&gt;curl -d'{ "copyright": "Joon Kyung, http://firejune.com/" }' http://firejune.mit-license.org&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
curl에서 JSON을 직접 보내는 방식으로, 사용할 주소 정보를 서브-도메인에 작성해야 한다는 사실을 꼭 기억하세요. JSON에는 copyright(필수), url, email, format, version, theme 항목을 작성할 수 있으며 예를 들면 다음과 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;{
  "copyright": "Joon Kyung, http://firejune.com",
  "url": "http://firejune.com",
  "theme": "flesch"
}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
여기서 format 항목은 지정하지 않은 경우 기본으로 HTML을 생성하지만 "txt" 값을 입력하면 plain text로 표시됩니다. 보다 자세한 사용방법은 &lt;a href="https://github.com/remy/mit-license/blob/master/README.md"&gt;README.md 문서&lt;/a&gt;를 참고 하세요.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1719#p1719"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1719"&gt;Hits(371566)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/-0QLOe-ES8fHbmAMz4g3gKTDhwA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-0QLOe-ES8fHbmAMz4g3gKTDhwA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/-0QLOe-ES8fHbmAMz4g3gKTDhwA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-0QLOe-ES8fHbmAMz4g3gKTDhwA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">Node.JS와 Express를 이용한 디렉터리 파싱</title><link rel="alternate" type="text/html" href="http://firejune.com/1718" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="Node.JS" /><category term="Express" /><category term="디렉터리" /><category term="파싱" /><author><name>파이어준</name></author><updated>2011-11-14T21:49:29-08:00</updated><id>http://firejune.com/1718</id><content type="html">어떠한 데이터라도 받기만 하면 비주얼라이제이션하는 데에는 자신있다고 자부해 왔습니다. 그러나 며칠간 고민에 빠져들게 하는 과제가 하나 생겼습니다. 그것은 바로 '/dir1/dir2/file.name'과 같은 형식으로 반환되는 데이터들에 대한 디렉터리 구조를 표현하는 것이었습니다. 언뜻 보기에는 굉장히 쉬워보였습니다만, 막상 작업에 돌입해 보니 그리 만만한 프로그래밍이 아니구나라는 생각이 들어 그 해결과정을 이곳에 기록합니다. 사실, 예전에도 이와 유사한 고민을 앓았던 적이 있었는데 서버-사이드에서 처리해야 할 문제였으므로 다른 분에게 부탁하고 기억에서 지웠던 일이 고스라니 돌아온 상황이네요.(임대리 난 이렇게 해결했어!)&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;app.get('/files/:container?', function(req, res) {
  var swift = users[req.session.name]
    , container = req.params.container;

  if (!swift)
    return res.redirect('/login');

  swift.listObjects(container, function(err, result) {
    var objects = serialize(JSON.parse(result.body));
    if (req.xhr)
      res.partial('partial/objects', {
          container: container
        , objects: objects
      });
    else
      res.render('files', {
          layout: 'layout/main'
        , view: 'files'
        , container: container
        , objects:objects
      });
  });
});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
위 코드는 &lt;a href="http://nodejs.org/"&gt;Node.JS&lt;/a&gt;와 &lt;a href="http://expressjs.com/"&gt;Express 프레임웤&lt;/a&gt;을 이용하여 진행중인 &lt;a href="http://www.openstack.org/projects/storage/"&gt;OpenStack Object Storage(이하 Swift)&lt;/a&gt;의 웹 클라이언트 프로젝트에서 라우팅 룰의 일부분입니다. 컨테이너 이름을 URL로 받아 Swift에 질의하고 그 결과를 HTML로 응답하는 일을 합니다. 그리고 xhr요청을 구분하여 문서의 일부만 그릴 것인지 문서 전체를 그릴 것인지를 구분하는 조건이 있습니다. 이 때 Swift는 다음과 같은 JSON형식의 데이터를 돌려줍니다.(지면 관계상 이 처리과정과 무관한 hash, bytes, last_modified 속성들은 생략했습니다.)&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;[
  {"name": "README.chromium", "content_type": "application/octet-stream"},
  {"name": "buildinf.h", "content_type": "text/x-chdr"},
  {"name": "config", "content_type": "application/directory"},
  {"name": "config/k8", "content_type": "application/directory"},
  {"name": "config/k8/openssl", "content_type": "application/directory"},
  {"name": "config/k8/openssl/opensslconf.h", "content_type": "text/x-chdr"},
  {"name": "config/piii", "content_type": "application/directory"},
  {"name": "config/piii/openssl", "content_type": "application/directory"},
  {"name": "config/piii/openssl/opensslconf-posix.h", "content_type": "text/x-chdr"},
  {"name": "config/piii/openssl/opensslconf-win32.h", "content_type": "text/x-chdr"},
  {"name": "config/piii/openssl/opensslconf.h", "content_type": "text/x-chdr"},
  {"name": "openssl.gyp", "content_type": "application/octet-stream"},
  {"name": "patches", "content_type": "application/directory"},
  {"name": "patches/handshake_cutthrough.patch", "content_type": "text/x-diff"},
  {"name": "patches/missing_stddef.patch", "content_type": "text/x-diff"},
  {"name": "patches/next_proto_neg.patch", "content_type": "text/x-diff"},
  {"name": "patches/posix_c_source.patch", "content_type": "text/x-diff"},
  {"name": "patches/snap_start.patch", "content_type": "text/x-diff"}
]
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
데이터(오브젝트)들을 잘 살펴보면 "name"속성에는 일반적으로 파일명이 있지만 경로만 있거나 경로명을 포함한 파일명도 있습니다. 그리고 컨텐츠-타입으로 구분할 수 있도록 되어있습니다. 이것은 오브젝트들이 계층구조를 가질수 있게하는 Swift의 &lt;a href="http://docs.openstack.org/api/openstack-object-storage/1.0/content/pseudo-hierarchical-folders-directories.html"&gt;Pseudo-Hierarchical Folders/Directories API&lt;/a&gt;입니다. 이렇게 수신된 데이터들은 serialize 함수에 의해 최초로 가공됩니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;function serialize(objects) {
  var folders = []
    , files = [];

  for (var i = 0; i &amp;lt; objects.length; i++) {
    var object = objects[i]
      , depth = object.name.split('/');

    if (object.content_type == 'application/directory' || depth.length &amp;gt; 1) {
      object.pseudo_path = object.name;
      object.name = depth[depth.length - 1];

      for (var j = 0; j &amp;lt; depth.length; j++) {
        object.parent = depth[j - 1] || null;
        if (!folders[j]) folders[j] = {};
        if (!folders[j][depth[j]]) folders[j][depth[j]] = object;
      }
    } else
      files.push(object);
  }

  folders = hierarchy(folders);
  if (!folders.length) folders = [];

  return folders.concat(files);
}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
오브젝트들의 반복문에서는 크게 파일과 폴더를 구분합니다. 폴더는 슬래시('/')를 구분자로 나누고 폴더의 깊이를 알아내어 배열을 만듭니다. 이 배열의 값으로는 key-value 형식으로 오브젝트를 담아둡니다. 이 때 오브젝트에는 약간의 변화가 발생하는데 부모(상위 디렉터리)는 누구인지, 그리고 name속성은 디스플레이에서 사용될 값으로 교체하고 새로운 pseudo_path 속성에 실제 name값을 백업합니다. 이것으로 1차 가공을 완료했습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;[
  // depth 1
  {
    "config": {name: "config", content_type: "application/directory", pseudo_path: "config", parent: null},
    "patches": {name: "patches", content_type: "application/directory", pseudo_path: "patches", parent: null}
  },
  
  // depth 2
  {
    "k8": {name: "k8", content_type: "application/directory", pseudo_path: "config/k8", parent: "config"},
    "piii": {name: "piii", content_type: "application/directory", pseudo_path: "config/piii", parent: "config"},
    "handshake_cutthrough.patch": {name: "handshake_cutthrough.patch", content_type: "text/x-diff", pseudo_path: "patches/handshake_cutthrough.patch", parent: "patches"},
    "missing_stddef.patch": {name: "missing_stddef.patch", content_type: "text/x-diff", pseudo_path: "patches/missing_stddef.patch", parent: "patches"},
    "next_proto_neg.patch": {name: "next_proto_neg.patch", content_type: "text/x-diff", pseudo_path: "patches/next_proto_neg.patch", parent: "patches"},
    "posix_c_source.patch": {name: "posix_c_source.patch", content_type: "text/x-diff", pseudo_path: "patches/posix_c_source.patch", parent: "patches"},
    "snap_start.patch": {name: "snap_start.patch", content_type: "text/x-diff", pseudo_path: "patches/snap_start.patch", parent: "patches"}
  },
  
  // depth 3
  {
    "openssl": {name: "openssl", content_type: "application/directory", pseudo_path: "config/k8/openssl", parent: "k8"}
  },
  
  // depth 4
  {
    "opensslconf.h": {name: "opensslconf.h", content_type: "text/x-chdr", pseudo_path: "config/k8/openssl/opensslconf.h", parent: "openssl"},
    "opensslconf-posix.h": {name: "opensslconf-posix.h", content_type: "text/x-chdr", pseudo_path: "config/piii/openssl/opensslconf-posix.h", parent: "openssl"},
    "opensslconf-win32.h": {name: "opensslconf-win32.h", content_type: "text/x-chdr", pseudo_path: "config/piii/openssl/opensslconf-win32.h", parent: "openssl"}
  }
]&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
위와 같이 1차적으로 가공을 마친 폴더 데이터를 hierarchy함수에서 2차 가공을 진행합니다. 2차 가공에서는 배열에 속해있는 오브젝트들을 계층구조로 만드는 일을 수행합니다. 배열의 뒤쪽에서부터 순차적으로 처리하며, 부모(parent)가 누구냐라는 근거로 자식들(childs) 배열을 만들어 냅니다. hierarchy함수는 디렉터리의 깊이 단위로 반복해서 자신을 호출하는 재귀함수이며 마지막 처리시점(부모가 더이상 존재하지 않는 시점)에서 완전이 가공된 데이터를 돌려줍니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;function hierarchy(arr, childs) {
  var item = arr.pop()
    , parents = {};

  for (var name in item) {
    var parent = item[name].parent || '_root_';
    if (!parents[parent]) parents[parent] = [];
    if (!item[name].childs &amp;amp;&amp;amp; childs)
      for (var key in childs)
        if (key == name)
          item[name].childs = childs[key];

    parents[parent].push(item[name]);
    delete item[name].parent;
  }

  if (arr.length) parents = hierarchy(arr, parents);
  return parents['_root_'] || parents;
}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
hierarchy함수에 의해 2차적으로 가공된 데이터는 다음과 같습니다. 드디어 궁극적으로 원했던 계층형 데이터 컬렉션이 완성되었습니다. serialize함수는 최종 폴더 데이터와 파일 데이터와 병합하여 돌려줍니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;[{
  name: "config",
  content_type: "application/directory",
  pseudo_path: "config"
  childs: [{
    name: "k8",
    content_type: "application/directory",
    pseudo_path: "config/k8"
    childs: [{
      name: "openssl",
      content_type: "application/directory",
      pseudo_path: "config/k8/openssl"
      childs: [{
        name: "opensslconf-win32.h",
        content_type: "text/x-chdr",
        pseudo_path: "config/piii/openssl/opensslconf-win32.h"
      }, {
        name: "opensslconf-posix.h",
        content_type: "text/x-chdr",
        pseudo_path: "config/piii/openssl/opensslconf-posix.h"
      }, {
        name: "opensslconf.h",
        content_type: "text/x-chdr",
        pseudo_path: "config/k8/openssl/opensslconf.h"
      }]
    }]
  }, {
    name: "piii",
    content_type: "application/directory",
    pseudo_path: "config/piii"
  }]
}, {
  name: "patches",
  content_type: "application/directory",
  pseudo_path: "patches",
  childs: [{
    name: "snap_start.patch",
    content_type: "text/x-diff",
    pseudo_path: "patches/snap_start.patch"
  }, {
    name: "posix_c_source.patch",
    content_type: "text/x-diff",
    pseudo_path: "patches/posix_c_source.patch"
  }, {
    name: "next_proto_neg.patch",
    content_type: "text/x-diff",
    pseudo_path: "patches/next_proto_neg.patch"
  }, {
    name: "missing_stddef.patch",
    content_type: "text/x-diff",
    pseudo_path: "patches/missing_stddef.patch"
  }, {
    name: "handshake_cutthrough.patch",
    content_type: "text/x-diff",
    pseudo_path: "patches/handshake_cutthrough.patch"
  }]
}]&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
이제 이 데이터를 뷰-파셜에 그냥 컬렉션으로 전달해 주기만 하면 됩니다. 다음은 컨텐츠 영역에 해당하는 내용을 담은 뷰 파일입니다. 참고로 템프릿 엔진으로는 &lt;a href="http://github.com/visionmedia/ejs"&gt;EJS&lt;/a&gt;가 사용되고 있습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="html"&gt;&lt;code class="html"&gt;&amp;lt;ul class="section objects" id="objects"&amp;gt;
  &amp;lt;li class="header"&amp;gt;
    &amp;lt;span class="name active"&amp;gt;&amp;lt;a href="#refresh"&amp;gt;File Name &amp;lt;em&amp;gt;asc&amp;lt;/em&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="when"&amp;gt;&amp;lt;a href="#refresh"&amp;gt;Modified &amp;lt;em&amp;gt;asc&amp;lt;/em&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="size"&amp;gt;&amp;lt;a href="#refresh"&amp;gt;Size &amp;lt;em&amp;gt;asc&amp;lt;/em&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;/li&amp;gt;

  &amp;lt;%- partial('partial/object', objects) %&amp;gt;

  &amp;lt;li class="actions"&amp;gt;
    &amp;lt;a class="button upload"&amp;gt;Upload&amp;lt;/a&amp;gt;
    &amp;lt;em class="message"&amp;gt;Drop files here to upload&amp;lt;/em&amp;gt;
    &amp;lt;a class="button action" href="#refresh"&amp;gt;Refresh&amp;lt;/a&amp;gt;
    &amp;lt;div class="progress" style="display: none;"&amp;gt;
      &amp;lt;span id="bar" class="bar" style="width: 0%;"&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;span id="state" class="state"&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
그리고 컬렉션 단위로 그려질 뷰 파셜입니다. 여기에서 하나의 조문이 있는데 만약에 자식이 있다면 뷰 자신에게 자신을 다시 파셜하라는 명령을 수행합니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="html"&gt;&lt;code class="html"&gt;&amp;lt;li class="object"&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;span class="name"&amp;gt;
      &amp;lt;a href="&amp;lt;%= object.content_type == 'application/directory' ? '#open' : '/store/' + container + '/' + (object.pseudo_path || object.name) %&amp;gt;" target="_blank"&amp;gt;&amp;lt;%- object.name %&amp;gt;&amp;lt;/a&amp;gt;
    &amp;lt;/span&amp;gt;
    &amp;lt;span class="when"&amp;gt;&amp;lt;%- object.when %&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;span class="size"&amp;gt;&amp;lt;%- object.size %&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;% if (object.childs) { %&amp;gt;
  &amp;lt;ul class="tree"&amp;gt;
    &amp;lt;%- partial('partial/object', object.childs) %&amp;gt;
  &amp;lt;/ul&amp;gt;
  &amp;lt;% } %&amp;gt;
&amp;lt;/li&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
이리하여 얻어진 결과물은 다음과 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;img src="http://firejune.com/attach/1114/111114163853182856/121140.png" width="666" height="456" alt="tree.png" /&gt;
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1718#p1718"&gt;Comments(5)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1718"&gt;Hits(203979)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/9NdXsCIks7EzNbbNuhYbSu7oaGk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/9NdXsCIks7EzNbbNuhYbSu7oaGk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/9NdXsCIks7EzNbbNuhYbSu7oaGk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/9NdXsCIks7EzNbbNuhYbSu7oaGk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">아이폰 4S의 제품 슬라이드로 배우는 애니메이션 기법</title><link rel="alternate" type="text/html" href="http://firejune.com/1717" /><category term="웹개발" /><category term="자료" /><category term="CSS" /><category term="애플" /><category term="아이폰 4S" /><category term="애니메이션" /><category term="HTML5" /><category term="시퀀스" /><category term="자바스크립트" /><category term="transform" /><category term="transition" /><author><name>파이어준</name></author><updated>2011-10-30T09:26:57-07:00</updated><id>http://firejune.com/1717</id><content type="html">애플 홈의 &lt;a href="http://www.apple.com/iphone/"&gt;아이폰 4S 제폼 웹페이지&lt;/a&gt;에서 메인 비주얼를 장식하는 슬라이드는 총 6개의 애니메이션 시퀀스로 구분된 제품 특징들로 구성되어 있습니다. 이 제품 슬라이드를 구현하는 CSS 애니메이션과 자바스크립트 코드를 사례로 효과적인 애니메이션을 구현하는 방법에 대하여 공부해 봅시다. &lt;br /&gt;
&lt;br /&gt;
아래의 데모는 작동원리를 이해하기 위해 약 20% 수준으로 축소한 것이며, 브라우저의 뷰포트를 확인하기 위한 가상 요소(browser)가 포함되어 있습니다. 슬라이드 요소들이 커다란 하나의 맵(phone-stage)으로 구성되어 있고 아이폰 검정색(bphone)만 별도의 레이어로 취급되고 있습니다. 그리고 슬라이드 6번은 1번 슬라이드와 절묘하게 교차하면서 반복된다는 사실을 알 수 있습니다. 그러나 실제 버전에는 각 슬라이드에 사용된 이미지와 텍스트가 개별 요소들로 구분되어 보다 많은 구성 요소들로 이루어져 있으며, 링크와 텍스트 애니메이션 정보 등의 처리까지 포함하고 있지만 이 데모에서는 그 것을 통합하거나 생략한 것입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="position: relative; height: 555px; border: 1px solid #aaa; overflow: hidden; background: #fafafa"&gt;
  &lt;div class="cutout"&gt;
    &lt;img src="/attach/1030/111030230609003946/751648.jpg" id="phone-stage"&gt;
    &lt;img src="/attach/1030/111030230609003946/249589.jpg" id="bphone"&gt;
  &lt;/div&gt;
  &lt;img src="/attach/1030/111030230609003946/280350.gif" class="browser"&gt;
&lt;/div&gt;
&lt;style&gt;
.browser {
  position: absolute;
  top: 350px;
  left: 300px;
  border: 1px solid #777;
}
.cutout {
  position: absolute;
  height: 90px;
  top: 380px;
  left: 300px;
  width: 189px;
}
#phone-stage {
  border: 1px solid #eee;
  position: absolute;
  top: 0;
  left: 0;
  margin-left: 10px;
}
#bphone {
  position: absolute;
  margin-left: 10px;
}
&lt;/style&gt;데모 출처: &lt;a href="http://johnbhall.com/iphone-4s/"&gt;http://johnbhall.com/iphone-4s/&lt;/a&gt; via John B. Hall&lt;br /&gt;
&lt;br /&gt;
슬라이드 기능을 만드는 자바스크립트 핵심 함수는 &lt;code&gt;Slide&lt;/code&gt;와 &lt;code&gt;Sequencer&lt;/code&gt; 클래스입니다. &lt;code&gt;Sequencer&lt;/code&gt;는 재생/정지 상태를 관장하고 정해진 시간에 다음 시퀀스를 재생합니다. &lt;code&gt;Slide&lt;/code&gt;는 애니메이션 대상 요소와 이벤트 핸들러, CSS &lt;code&gt;transform&lt;/code&gt;과 &lt;code&gt;transition&lt;/code&gt; 속성에 사용될 &lt;code&gt;translate&lt;/code&gt;, &lt;code&gt;rotate&lt;/code&gt; 등의 값들로 구성된 인스턴스를 생산합니다. 대상이 된 HTML 요소에 스타일(style) 속성이 인라인으로 갱신되면서 애니메이션이 발생하는 것입니다. 파이어버그 또는 돔인스펙터(개발자 도구)를 열고 "phone-stage"의 스타일 속성을 감시해 보세요. 위 대모에 사용된 코드는 다음과 같습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  var phoneStage = $("phone-stage"),
      bphone = $("bphone");
  
  var q = new Slide([{
      el: phoneStage,
      x: -325,
      y: -234,
      r: -30
    }, {
      el: bphone,
      x: 0,
      y: 0,
      r: -30
    }]),
  
  t = new Slide([{
      el: phoneStage,
      x: -264,
      y: -316,
      r: -0
    }, {
      el: bphone,
      x: -300,
      y: -300,
      r: -30
    }]),
  
  ...
  
  b = new Sequencer([q, t, z, u, j, f, v]);
  b.play();&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
즉, 이 슬라이드에서 발생하는 애니메이션은 전부 CSS로 처리하지만 이것을 제어하는 것은 자바스크립트입니다. 자바스크립트는 시퀀스를 구성하는 transition과 transform 속성을 만들어 해당 요소의 인라인 스타일 속성에 대입해 주는 거죠. 이 간단한 원리로 얻어지는 가치는 무엇일까요? 자, 우리는 일반적으로 애니메이션을 처리할 때 jQuery에서 제공하는 &lt;code&gt;.animate&lt;/code&gt; 또는 Prototype &amp; Scriptaculous의 &lt;code&gt;.morph&lt;/code&gt;와 같은 자바스크립트 기반 애니메이션 함수를 사용해 왔습니다. 왜냐하면 사용자의 미세한 행동들 까지도 감지하여 화면에 응답하는 것을 만들어야 했기 때문입니다. 그리고 이 애니메이션 함수들은 Frame-by-Frame으로 연산하기 때문에 생각보다 많은 처리 비용이 든다는 사실을 여러분은 잘 알고 있을 것입니다.&lt;br /&gt;
&lt;br /&gt;
CSS로 처리되는 애니메이션을 자바스크립트로 제어한다는 것은 곧 화면상의 상호작용을 직접 제어할 수 있게 되는 것을 의미합니다. 이것은 매우 흥미롭습니다. &lt;code&gt;.animate&lt;/code&gt;나 &lt;code&gt;.morph&lt;/code&gt;를 사용하는 대신에 CSS 속성을 활용하여 브라우저의 자원을 효율적으로 분산 처리하는 효과적인 애니메이션 구현 기법입니다. 이렇게 확보된 자원으로 더욱 복잡한 애플리케이션을 구상할 수 있고, 아름다운 그래픽 애니메이션도 지원 받을 수 있습니다. 특히 CSS transitions는 하드웨어 가속을 지원하기 때문에 자바스크립트 연산으로는 도저히 답이 안나오는 여러가지 사치성(호화로운) 애니메이션 효과들을 제품에 적용여 네이티브 애플리케이션에 하이킥을 날리는 &lt;a href="http://ko.wikipedia.org/wiki/%EB%A0%88%EC%95%8C_(%EC%8B%A0%EC%A1%B0%EC%96%B4)"&gt;레알&lt;/a&gt; 웹애플리케이션을 만들어 낼 수 있습니다.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1717#p1717"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1717"&gt;Hits(97184)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/KxkanBOf6ASHXn1yBmjLPHcjWBg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KxkanBOf6ASHXn1yBmjLPHcjWBg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/KxkanBOf6ASHXn1yBmjLPHcjWBg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KxkanBOf6ASHXn1yBmjLPHcjWBg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">Node.JS에서 대용량 파일 전송을 미친듯 빠르게</title><link rel="alternate" type="text/html" href="http://firejune.com/1716" /><category term="웹개발" /><category term="자료" /><category term="Node.JS" /><category term="자바스크립트" /><category term="Express" /><category term="Swift" /><category term="OpenStack" /><category term="Object Storage" /><category term="클라우드" /><category term="짬뽕" /><category term="미친 Node" /><category term="파일 전송" /><author><name>파이어준</name></author><updated>2011-10-24T06:47:11-07:00</updated><id>http://firejune.com/1716</id><content type="html">요즘 &lt;a href="http://swift.openstack.org/"&gt;OpenStack Object Storage(Swift)&lt;/a&gt;를 가지고 놀고있습니다. Swift는 &lt;a href="http://aws.amazon.com/s3/"&gt;아마존(Amazon)의 S3&lt;/a&gt; 서비스와 유사한 오픈소스화 된 오브젝트 스토리지 서비스이며 최근 주목받는 클라우드 솔루션입니다. 두드러진 특징은 &lt;a href="http://docs.openstack.org/api/openstack-object-storage/1.0/content/"&gt; ReSTful API&lt;/a&gt;를 제공한다는 것입니다. 그래서 웹서버를 통하지 않고 클라이언트(예를 들면 웹브라우저 또는 모바일 애플리케이션)에서 직접 접근하여 관리할 수 있는 인터페이스를 제공합니다. Authentication(인증)과 Container/Object로 이루어진 간단한 계층 구조를 가지며 동일한 URI에 GET, POST, PUT, DELETE 등의 HTTP 메서드를 이용하여 행동을 구분하고, 요청 헤더(Request Header)를 조작하여 인증을 처리합니다. Swift에 대한 소개는 이즘에서 마치고 본론으로 들어가겠습니다.&lt;br /&gt;
&lt;br /&gt;
일단 &lt;a href="http://nodejs.org/"&gt;Node.JS&lt;/a&gt;를 이용하여 실험용으로 Swift기반 클라우드 데이터 스토어 클라이언트를 개발하고 있습니다. 클라이언트에서 직접 접근하는 방법과 웹서버를 통하는 두가지 방법 중 후자를 선행하기로 한 것입니다. 전자의 경우 인증 계정과 키를 클라이언트가 관리하게 되는데 웹이라는 특성상 보안 취약점이 발생할 수 있다는 위험성이 있으며, 만약, 클라이언트가 웹브라우저라면 헤더를 조작하는 수단이 천상 Ajax로 날리는 수 밖에 없는데 응답이 바이너리여서 뭔가 엄청난 시련이 다가올 것 같은 기운이 엄습했기 때문이었죠. 기가-바이트급 Base64 스트링을 자바스크립트로 처리한다고 생각해 보세요.(헐~) 후자는 파일을 송/수신하는 과정에 웹서버를 거치기 때문에 불필요한 I/O가 발생하여 성능이 떨어질 것을 감안하고 작업에 착수했습니만, 그 예상은 하루만에 뒤집혔습니다. Node.JS에서 파일 전송을 매우 효율적으로 처리할 수 있는 방법을 발견하여 이곳에 공유합니다.&lt;br /&gt;
&lt;br /&gt;
다음 자바스크립트 코드는 서버-사이드에서 작동하는 코드로 HTTPS 프로토콜을 사용하는 Swift 서비스의 REST에 접근하도록 만들어진 공통 요청 함수입니다. 이 함수는 swift라 명명한 모듈에 포함되어 있습니다. 첫 번째 인자에서 HTTPS 요청에 필요한 옵션들을, 두 번째 인자는 HTTPS 요청에 대한 결과를 돌려주는 콜백을, 세 번째 인자는 생략 가능한 응답 객체입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;function request(options, callback, pipe) {
  options = extend({
      host: this.options.host
    , port: this.options.port
    , path: '/auth/v1.0'
    , method: 'GET'
    , headers: {
        'X-Auth-Token': this.token
      , 'X-Storage-Token': this.token
    }
  }, options);

  options.headers['User-Agent'] = 'Node.JS Swift API Client';
  options.path = encodeURI(options.path);

  var client = https.request(options, function(res) {
    var buffers = [];
    if (pipe) {
      pipe.header('Content-Length', res.headers['content-length']);
      pipe.header('Content-Type', res.headers['content-type']);
    }

    res.on('data', function(buffer) {
      if (pipe) pipe.write(buffer);
      else buffers.push(buffer);
    });

    res.on('end', function(err){
      buffers.length &amp;amp;&amp;amp; clog.info(res.statusCode, res.headers);
      res.body = buffers.join('');
      callback &amp;amp;&amp;amp; callback(null, res);
    });
  });

  client.on('error', function(err) {
    callback &amp;amp;&amp;amp; callback(err);
    client.end(err);
  });

  client.end();
}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
소스가 전문이 아니기 때문에 세 번째 인자로 받은 응답 객체에만 집중하겠습니다. 이 객체는 Node.JS에서 구축한 HTTP 웹서버의 응답 객체를 그대로 전달한 것입니다. 아래의 코드는 &lt;a href="http://expressjs.com/"&gt;Express&lt;/a&gt;를 이용하여 구성한 라우팅 룰들 중에서 클라이언트의 파일 다운로드 요청을 처리하는 부분입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var swift = new Swift({
    user: 'auth-user'
  , pass: 'auth-key'
  , host: 'auth.api.yourcloud.com'
  , port: 443
});

...

app.get('/store/:container/:object', function(req, res){
  var container = req.params.container
    , object = req.params.object;

  request.call(swift, {
    path: '/v1.0/' + swift.account + '/' + container + '/' + object
  }, function(result, headers) {

    ...

    res.end()
  }, res);
});&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Express로 구성된 웹서버(80 포트)는 클라이언트가 요청한 정보와 인스턴스로 생성된 인증정보를 조합하여 Swift 서버(443 포트)로 다시 요청합니다. 이 처리 과정은 마치 두 부분으로 나뉜듯 보이지만 비동기로 짬뽕이 된 단 하나의 사이클이라는 사실을 기억하세요. 자, 이제 request 함수에서 받는 세 번째 인자의 흐름을 유심히 살펴 봅시다. 세 번째 인자가 있는 경우 Swift 서버로 요청한 바이너리 데이터를 수신한 만큼의 버퍼가 실시간으로 app.get의 응답객체(res)에 전달됩니다. 풀어서 말하면, 스토리지 서버에서 지금 막 내려온 청크(Chunk) 데이터를 받는 즉시 그냥 클라이언트 응답에 막 싸질러 버리는 거에요. 이 방법으로 바이너리 데이터를 가장 효율적이면서도 신속하게 클라이언트에 전달할 수 있었습니다. 기가-바이트급 데이터를 다운로드했는데 클릭과 동시에 속 시원하게 다운로드 게이지가 차더군요.&lt;br /&gt;
&lt;br /&gt;
Node.JS에 또 한번 감동먹는 순간이었습니다. 졸라 간단하지 않나요? 파일 서버에서 받은 바이너리를 메모리에 캐시하는 동안 버퍼가 쌓이면 다시 스트림으로 얻어낸 청크 데이터를 클라이언트로 내려주는 파이프라인 로직을 어거지로 구현한 하루 분량의 소스 코드를 스스로 삭제하면서 세상 참 허무하다는 생각을 했습니다. 여기에는 Swift를 예로 들었지만 웹서버에서 다른 스토리지 서버에 위치한 바이너리 데이터를 클라이언트에 전달할 때에도 유용하게 사용될 수 있을 것입니다. 그리고 업로드 또한 이와 유사한 로직으로 구현할 계획인데 잘 될지 모르겠네요.
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1716#p1716"&gt;Comments(1)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1716"&gt;Hits(35812)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/V8zHQewwX8Rt2Hu56y2ZGy7qYyk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/V8zHQewwX8Rt2Hu56y2ZGy7qYyk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/V8zHQewwX8Rt2Hu56y2ZGy7qYyk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/V8zHQewwX8Rt2Hu56y2ZGy7qYyk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">더 나은 HTML과 CSS 작성하기 - Chris Coyier</title><link rel="alternate" type="text/html" href="http://firejune.com/1714" /><category term="웹개발" /><category term="자료" /><category term="HTML" /><category term="CSS" /><category term="슬라이드" /><category term="작성" /><category term="컨퍼런스" /><category term="런던" /><category term="가짜 요소" /><category term="Speaker Deck" /><author><name>파이어준</name></author><updated>2011-10-11T02:43:40-07:00</updated><id>http://firejune.com/1714</id><content type="html">런던에서 열린 &lt;a href="http://futureofwebapps.com/london-2011/"&gt;Future of Web Apps 컨퍼런스&lt;/a&gt;에서 &lt;a href="http://css-tricks.com/14492-writing-better-html-css-slides-from-fowa-london/"&gt;Chris Coyier씨가 발표한 슬라이드&lt;/a&gt;입니다. &lt;a href="http://www.wufoo.com/"&gt;Wufoo&lt;/a&gt;에서 CSS를 다루는 과정과 자주 범하는 실수들 그리고 가짜 요소(pseudo elements)들의 활용법에 대한 내용을 담고 있습니다. 아래 슬라이드는 &lt;a href="http://speakerdeck.com/"&gt;Speaker Deck&lt;/a&gt;에 등록된 것입니다.&lt;br /&gt;
&lt;br /&gt;

 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1714#p1714"&gt;Comments&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1714"&gt;Hits(22869)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3TvKIR4NG_Q5qVnRZ3BHhSyLiz0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3TvKIR4NG_Q5qVnRZ3BHhSyLiz0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/3TvKIR4NG_Q5qVnRZ3BHhSyLiz0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3TvKIR4NG_Q5qVnRZ3BHhSyLiz0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">섹시한 자바스크립트 코딩 컨벤션</title><link rel="alternate" type="text/html" href="http://firejune.com/1713" /><category term="웹개발" /><category term="자료" /><category term="자바스크립트" /><category term="코딩" /><category term="컨벤션" /><category term="트랜드" /><category term="나이가 들어서 그런지 자주 까먹는다" /><author><name>파이어준</name></author><updated>2011-10-10T01:37:00-07:00</updated><id>http://firejune.com/1713</id><content type="html">자바스크립트를 코딩함에 있어서 그동안 습득했던 문법 규칙, 팁, 노하우, 등을 이곳에 정리합니다. 여기에서 다루는 내용은 읽는 사람에 따라서는  저수준 리팩토링일 수 있고, 성능 최적화일 수도 있고, 가독성을 높이는 동시에 청결함을 유지하는 것일 수도, 그리고 무슨 소리를 하는건지 모를수도 있습니다. 지금부터 최근 인기몰이 중인 몇몇 오픈소스 프로젝트의 소스코드들을 예로 들어 가면서 코딩 트랜드를 파악하고 학습하는 시간을 가져봅시다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;알아볼 수 있게&lt;/h3&gt;이것은 개발자로써 근본이 되어야 하는 관습입니다. 가독성이 높으면 협업에 있어서 야기되는 혼란을 미연에 방지하는 효과가 있기 때문에 일부 회사나 단체에서는 개발자들이 지켜야 할 가이드에 포함하기도 합니다. 참고로 &lt;a href="http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml"&gt;구글의 자바스크립트 스타일 가이드&lt;/a&gt;에서는 세미콜론을 찍는 것 조차도 왈가왈부하고 있습니다. 하지만 우리 실정은 그렇지 않죠? 이러한 규칙이 있을리 만무합니다. 그래서 여러 경험을 통해 얻어낸 자신만의 규칙을 따르거나 개발도구 또는 어떠한 소스로부터 얻어진 규칙을 따라야 합니다. 암묵적으로 지켜지는 규칙들을 대략 읇어보면, 클래스의 첫 문자는 대문자로 구분하고, 전역에서 참조되는 변수는 전부 대문자로 작성하거나 다른 문자를 사용하여 구분하고, 내부에서만 사용되는 메서드나 변수는 언더스코어(_)를 첫 문자로, 괄호와 브레이스 사이는 띄어쓰거나 줄을 변경하거나, 80 또는 160 캐릭터, 2 또는 4 또는 8 탭사이즈 등 수없이 많습니다. 이것들에 대한 자세한 내용을 여기에서 다루지는 않겠습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;의미있는 정의&lt;/h4&gt;많은 사람들이 자바스크립트(정확게는 DOM스크립트)를 좋아하는 이유 중 하나는 짜놓은 코드를 그냥 읽는 것만으로도 쉽사리 이해할 수 있다는 것입니다. 의미있는 이름을 사용하는 것으로 설명될 수 있는 것은 의외로 많거든요. 해당 함수가 어떠한 일을 처리하는지, 해당 변수가 단수인지 복수인지 등을 주석문 없이도 표현할 수 있습니다. 여기에 바보같은 예를 하나 들어보겠습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var foo = document.getElementById('foo'); // get element by id form DOM
var bars = document.getElementsByClassName('bar'); // get elements by class name form DOM&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
누군가가 작성한 코드에서 위와 같은 주석을 발견했다면 분노의 삭제질을 작렬하면서 마음속으로는 죽여버리고 싶다는 생각이 들지도 모릅니다. 이제 모범 사례들을 차근차근 살펴보면서 안구를 정화해 봅시다. 다음은 &lt;a href="http://nodejs.org/"&gt;Node.JS&lt;/a&gt; 모듈인 &lt;a href="http://socket.io/"&gt;Socket.IO&lt;/a&gt;의 클라이언트-사이드에 사용된 스크립트의 일부분입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  /**
   * Create a new `Socket.IO client` which can establish a persistent
   * connection with a Socket.IO enabled server.
   *
   * @api public
   */

  function Socket (options) {
    this.options = {
        port: 80
      , secure: false
      , document: 'document' in global ? document : false
      , resource: 'socket.io'
      , transports: io.transports
      , 'connect timeout': 10000
      , 'try multiple transports': true
      , 'reconnect': true
      , 'reconnection delay': 500
      , 'reconnection limit': Infinity
      , 'reopen delay': 3000
      , 'max reconnection attempts': 10
      , 'sync disconnect on unload': true
      , 'auto connect': true
      , 'flash policy port': 10843
    };

    io.util.merge(this.options, options);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;code&gt;Socket&lt;/code&gt;이라는 클래스를 정의하는 부분에서 초기화 옵션을 받는 부분으로 &lt;code&gt;this.options&lt;/code&gt;에 정의된 코드들을 살펴보면 주석으로나 사용될 문장들이 식별자(키)로 작성된 것을 확인할 수 있습니다. 이것은 잘 만들어진 래시피와도 같습니다. 다음 예제는 Socket.IO의 서버-사이드 테스트 소스의 일부분입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;module.exports = {

  'test that protocol version is present': function (done) {
    sio.protocol.should.be.a('number');
    done();
  },

  'test that default transports are present': function (done) {
    sio.Manager.defaultTransports.should.be.an.instanceof(Array);
    done();
  },

  'test that version is present': function (done) {
    sio.version.should.match(/([0-9]+)\.([0-9]+)\.([0-9]+)/);
    done();
  },

  'test listening with a port': function (done) {&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
헐~ 테스트 항목에 대한 설명이 그냥 메서드 이름으로 작성되었고 테스트를 수행하는 코드는 하나의 온전한 문장이 체인으로 연결된 정적인 네임스페이스들입니다. 멋지지 않나요?&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;단수/복수 과거/현재&lt;/h4&gt;변수를 정의할 때 이름을 단수와 복수 그리고 과거와 현재를 구분하여 정의하면 가독성도 좋아지고 여러모로 편리합니다. 배열과 같은 하나 이상의 아이템을 가진 데이터 형식을 정의할 때에는 복수로 명명합니다. 다음 코드는 &lt;a href="http://cloud9ide.com/"&gt;Cloud9&lt;/a&gt;의 클라이언트-사이드 코드 중 컨텐츠 마임타입(MIME type)들을 정의한 일부분과 한 아이템을 동적으로 픽업하는 예제입니다.('...'은 중략을 의미 함)&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  var contentTypes = {
    "js": "application/javascript",
    "json": "application/json",
    "css": "text/css",
    "less": "text/css",
    "scss": "text/x-scss",
    "sass": "text/x-sass",

    ...

    "md": "text/x-markdown",
    "markdown": "text/x-markdown"
  };

  ...

  var fileExt = file.getAttribute("name").split(".").pop();
  var customType = contentTypes[fileExt];&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
그리고 과거와 현재의 뜻을 가진 변수는 어떠한 지표(플래그)를 의미하는 용도로 사용되는 경우가 많습니다. 다음 코드는 &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; 내부에서 사용되는 함수인 &lt;code&gt;_Deferred&lt;/code&gt;에서 과거와 현재를 구분한 변수명을 사용하는 예제입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;jQuery.extend({
  // Create a simple deferred (one callbacks list)
  _Deferred: function() {
    var // callbacks list
      callbacks = [],
      // stored [ context , args ]
      fired,
      // to avoid firing when already doing so
      firing,
      // flag to know if the deferred has been cancelled
      cancelled,
      // the deferred itself
      deferred  = {

        // done( f1, f2, ...)
        done: function() {
          if ( !cancelled ) {
            var args = arguments,
              i,
              length,
              elem,
              type,
              _fired;
            if ( fired ) {
              _fired = fired;
              fired = 0;
            }&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;작은 따옴표가 기본&lt;/h4&gt;자바스크립트에서 문자열(String)을 처리할 때 큰 따옴표(")와 작은 따옴표(') 중 어느것을 사용할지 고민되는 경우가 있습니다. 작은 따옴표(Single quotes)로 작성하는 것을 기본으로 하세요. HTML을 작성할 때에는 큰 따옴표가 기본으로 사용되기 때문에 백슬래시(\)를 이용하여 일일이 이스케이프(Escape)하는 상황이 발생하면 가독성을 크게 떨어트립니다. 다음은 HTML에 사용할 앵커를 자바스크립트로 작성하는 예입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var anchor = '&amp;lt;a href="/' + foo + '.html"&amp;gt;' + foo + '&amp;lt;/a&amp;gt;'; // correct

var anchor = "&amp;lt;a href=\"/" + foo + ".html\"&amp;gt;" + foo + "&amp;lt;/a&amp;gt;"; // incorrect

var anchor = "&amp;lt;a href='/" + foo + ".html'&amp;gt;" + foo + "&amp;lt;/a&amp;gt;"; // nonsense&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;띄어쓰기&lt;/h4&gt;일반적으로 다음과 같은 띄어쓰기 규칙을 따르면 가독성이 높아집니다.&lt;br /&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;쉼표(,): 전: 0, 후: 1&lt;/li&gt;
  &lt;li&gt;괄호('(', ')'): 전: 0, 후: 0&lt;/li&gt;
  &lt;li&gt;세미콜론(;): 전: 0, 후: 1&lt;/li&gt;
  &lt;li&gt;산술 연산자(+, -, *, /, % ...): 전: 1, 후: 1&lt;/li&gt;
  &lt;li&gt;관계 연산자(==, ===, !=, !==, &lt;, &gt;, &lt;=, &gt;= ...): 전: 1, 후: 1&lt;/li&gt;
  &lt;li&gt;1진 연산자(++, --): 전: 0, 후: 0&lt;/li&gt;
  &lt;li&gt;할당(a=b): 전: 1, 후: 1&lt;/li&gt;
  &lt;li&gt;논리 연산자(&amp;&amp;, ||): 전: 1, 후: 1&lt;/li&gt;
  &lt;li&gt;키-밸류 연산자({'a':'b'}): 전: 1, 후: 1&lt;/li&gt;
  &lt;li&gt;인라인 주석(//): 후: 1&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h4&gt;파일 해더 작성하기&lt;/h4&gt;자바스크립트 파일의 최상단에 해더를 작성하여 대략적인 정보를 제공해야합니다. 뭐하는 녀석인지, 어떤 종속성(Dependency)이 있는지, 어떻게 사용하는지, 버전, 라이센스, 작성자, 사이트 링크, 등을 기입합니다. 최근에는 &lt;a href="http://code.google.com/p/jsdoc-toolkit/w/list"&gt;JSDoc 포맷&lt;/a&gt;으로 작성하는 경향이 있습니다. 다음은 Cloud9의 플랫폼 코어(apf)에 해당하는 class.js 파일의 해더입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;/**
 * All elements that implemented this {@link term.baseclass baseclass} have
 * {@link term.propertybinding property binding},
 * event handling and constructor &amp;amp; destructor hooks. The event system is 
 * implemented following the W3C specification, similar to the 
 * {@link http://en.wikipedia.org/wiki/DOM_Events event system of the HTML DOM}.
 *
 * @constructor
 * @baseclass
 *
 * @author      Ruben Daniels (ruben AT ajax DOT org)
 * @version     3.0, beta
 * @since       0.8
 *
 * @event propertychange Fires when a property changes.
 *   object:
 *     {String} name          the name of the changed property
 *     {Mixed}  originalvalue the value it had before the change
 *     {Mixed}  value         the value it has after the change
 *
 */&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;들여쓰기와 줄 변경하기&lt;/h4&gt;사실 자바스크립트는 들여쓰기와 줄변경을 무시해도 작동하는 데에는 별 문제가 없습니다. 이것들은 단순히 코드의 가독성을 위한 행위들로 여겨도 무방합니다. 일반적으로 블럭 안쪽에 위치한 문장에 들여쓰기를 하지만 jQuery와 같은 라이브러리의 도움을 받는 경우 체이닝(Chaining) 스타일 코딩에서는 체인 단위로 줄을 변경하고 셀렉션 포인트가 변경되는 시점에 들여씀으로서 훨씬 수월하게 코드를 읽을 수 있습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  $(form)
    .prevAll('div.black:first')
    .html(json.timeLabel)
      .prevAll('div.timeline:first')
        .children('div')
        .css('width', json.gauge + '%')
        .end()
      .end()
    .end()
    .replaceWith('&amp;lt;div class="extend-time"&amp;gt;&amp;lt;p&amp;gt;' + json.optionLabel + '&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;');&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
자바스크립트에서 여러 요소로 구성된 HTML을 파셜(Partial)하는 경우 다음과 같이 배열을 이용하여 들여쓰기하면 가독성이 높아집니다. 물론, 꼭 배열을 사용해야 하는 것은 아니지만 산술 연산자를 이용하는 것 보다는 성능이 좋기 때문입니다. 아래는 jQuery를 이용하여 인플레이스 에디터(in-place editor)를 구현하는 코드의 일부분입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  $(el)
    .parent()
    .before([
      '&amp;lt;form method="post" action="' + el.attr('href') + '"&amp;gt;',
        '&amp;lt;input type="hidden" value="' + el.attr('id') + '" name="id"&amp;gt;',
        '&amp;lt;input type="text" value="" name="key"&amp;gt;',
        '&amp;lt;textarea name="value"&amp;gt;&amp;lt;/textarea&amp;gt;',
        '&amp;lt;p&amp;gt;' + beautify(new Date) + '&amp;lt;/p&amp;gt;',
      '&amp;lt;/form&amp;gt;'
    ].join('\n'))
      .prev()
        .find('input[type=text]')
        .focus();&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
변수를 연속적으로 선언할 때 &lt;code&gt;var&lt;/code&gt;를 생략하고 쉼표(,)로 구분하여 선언하는 것이 효율적입니다.(이것에 대해서는 잠시 후에 다루도록 하겠습니다.) 이 때 보통 줄바꿈을 하는데, 요즘은 쉼표를 앞쪽에 작성하는 것이 유행하고 있습니다. 다음은 Node.JS용 MVC 프레임웍인 &lt;a href="http://expressjs.com/"&gt;Express&lt;/a&gt;의 서버-사이드 소스에서 모듈을 가져오는 부분입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;/**
 * Module dependencies.
 */

var connect = require('connect')
  , HTTPSServer = require('./https')
  , HTTPServer = require('./http')
  , Route = require('./router/route')&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
위 처럼 쉼표를 앞에 작성하면, 아이템을 추가하거나 주석처리하거나 삭제할 때 용이합니다. 단적으로 앞서 소개한 예제 코드들을 비교해 보면 '의미있는 정의'의 Socket 클래스 소스에 사용된 &lt;code&gt;this.options&lt;/code&gt;와 '단수/복수 과거/현재'의 &lt;code&gt;contentTypes&lt;/code&gt; 소스에서 맨 마지막에 위치한 아이템을 삭제하거나 주석 처리한다고 가정할 때 문법 오류가 발생하지 않는 코드는 쉼표가 전방에 배치된 쪽입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;코드를 줄이자&lt;/h3&gt;대부분의 개발자들은 적은 량의 코드로 많은 일을 시키려는 경향이 있습니다. 이것은 과하면 독이되고 안하면 병신소리 듣습니다. 그래서 적절하게 코드를 경량화하는 것이 좋습니다. 적절하다는 의미는 가독성을 해치지 않는 범위 정도로 해석하면 되겠네요. 제 경우 다른 곳에서 가져온 프로그램을 자신의 것으로 소화할 필요가 있을 때 가장 처음으로 수행하는 작업이 바로 경량화입니다. 중복성이거나 불필요한 부분을 제거 및 교정하고 동시에 가능한 한 최대로 경량화하면서 소스의 전반을 이해합니다. 이 작업을 거치고 나면 원본 소스가 가진 라인 수 보다 절반 이하로 줄어드는 경우가 다반사입니다.(그렇다고 서드파티 라이브러리를 함부로 뜯어 고치자는 의미는 아닙니다.)&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;변수 할당&lt;/h4&gt;앞서 잠시 언급한 바와 같이 변수를 연속해서 선언해야하는 경우 중복으로 작성되는 &lt;code&gt;var&lt;/code&gt;를 생략할 수 있습니다. 다음은 &lt;a href="http://firejune.com/1684/%EB%A7%88%EC%9A%B0%EC%8A%A4+%ED%8F%AC%EC%9D%B8%ED%8A%B8+%EC%BB%A4%EB%AE%A4%EB%8B%88%EC%BC%80%EC%9D%B4%ED%84%B0+-+Smails"&gt;Smails&lt;/a&gt;의 서버-사이드 소스 중 Socket.IO를 초기화하는 부분입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;/**
 * Socket server.
 */
var io = require('socket.io').listen(smails.port)
  , sockets = io.sockets.sockets
  , clog = require('clog')
  , dgram = require('dgram');

io.configure(function () {
  io.enable('browser client minification');
  io.enable('browser client etag');
  io.set('log level', 2);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
순차적이고 동기식으로 발동하기 때문에 첫 라인에서 선언한 &lt;code&gt;io&lt;/code&gt;를 두번째 라인에서 바로 접근해도 문제되지 않습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;스위치가 싫어요&lt;/h4&gt;여러 자바스크립트 문법들 중에서도 가장 가독성을 해치는 것이 바로 스위치 문입니다. 한 문장임에도 두 단계의 들여쓰기 깊이를 가지기에 꼭 필요한 상황에서만 사용하는 것이 좋습니다. 보통 if ... else ... 문이 여러차례 반복되는 문장을 스위치로 리팩토링하는 경우가 많은데, 아래와 같은 단순히 변수를 할당하려는 목적이라면 스위치 보다는 객체 사전을 이용하는 방법이 더 효율적이고 가독성도 높일 수 있습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;function getLabel(module) {
  var label;
  witch(module) {
    case 'todo' :
      label = '할일';
      break;
    case 'calendar' :
      label = '달력';
      break;
    case 'note' :
      label = '공책';
      break;
  }
  return label;
}

getLabel('todo'); //=&amp;gt; '할일'

function getLabel(module) {
  return {
      todo: '할일'
    , calendar: '달력'
    , note: '공책'
  }[module];
}

getLabel('todo'); //=&amp;gt; '할일'
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;3항 연산과 조건부 할당&lt;/h4&gt;다음은 3가지 코드에 작성된 &lt;code&gt;value&lt;/code&gt;는 같은 결과값를 가집니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  var value;
  if (n) {
    value = n;
  } else {
    value = 1;
  }

  // equal next

  var value = n ? n : 1;

  // equal next

  var value = n || 1;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;조건부 함수 호출&lt;/h4&gt; &lt;code&gt;callback&lt;/code&gt;이 있는 경우 실행하라는 조건부 호출은 다음과 같이 사용될 수 있습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  if (callback) {
    callback();
  }

  // equal either

  callback &amp;amp;&amp;amp; callback();&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;정의와 동시에 사용&lt;/h4&gt;아래 코드는 정의와 동시에 &lt;code&gt;push&lt;/code&gt;메서드를 호출하고 있습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  (dep.using || (dep.using = [])).push(extension);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;무의미한 변수 선언&lt;/h4&gt;단순 참조용으로 선언된 변수는 생략할 수 있습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  var self = this;
  $.ajax({
    type: self.method,
    url: self.action,
    success: function(text){
      $('#article').html(text)
    }
  });

  // to be

  $.ajax({
    type: this.method,
    url: this.action,
    success: function(text)
      $('#article').html(text);
    }
  });&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;더 빠르게&lt;/h3&gt;보다 더 빠르게 작동하는 코드를 원하시죠? 지금부터 소개할 내용은 기존의 코드를 재작성하여 속도를 향상시키는 방법들입니다. 성능 최적화에서 가장 중요하게 여겨야 하는 것은 '측정'입니다. 측정없는 최적화는 단지 시간 낭비일 뿐이라는 사실을 기억하세요. 속도 향상을 위한 몇가지 기본적인 팁은 가급적 네이티브 코드를 사용할 것(특히 반복문), try/catch는 속도 저하 요인이 될 수 있으니 지양할 것, 캐시를 잘 활용할 것 등이 있습니다.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;네이티브 코드 사용&lt;/h4&gt;코드의 사용성을 크게 향상시키는 자바스크립트 라이브러리들이 성행하면서 개발 생산성이 좋아졌고 초심자들의 진입장벽도 많이 허물어졌습니다. 분명히 좋은 현상이긴 합니다만, 네이티브 코드를 이해하지 못하면 결코 최적화된 애플리케이션을 만들어낼 수 없습니다.(참고로 구글은 서드파티 라이브러리에 의존하여 개발하는 것을 그리 달갑게 여기지 않습니다.)&lt;br /&gt;
&lt;br /&gt;
간략한 예를 들어 보겠습니다. jQuery에서 $.each를 이용한 반복문 처리 보다는 for(…) 문을 사용하는 것이 빠릅니다. Prototype.js의 bind 메서드를 사용하는 것 보다. Function.call 또는 Function.apply를 사용하는 것이 빠릅니다.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;문자열 결합&lt;/h4&gt;문자열을 결합할 때 산술연산을 이용하는 것 보다는 배열로 작성하고 join으로 결합하는 것이 더 빠릅니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;// Instead of this...
var result = 'a' + 'b' + 'c' + 'd';

// This is much faster:
var result = ['a', 'b', 'c', 'd'].join('');&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;캐시 효과&lt;/h4&gt;반복문 안에서 배열 길이를 추가 정의하는 것만으로 더 빠르게 작동합니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;// Instead of this...
function nodeJam(){
  nodes = document.getElementsByTagName('P');

  for (var i = 0; i &amp;lt; nodes.length; i++) {
    nodes[i].innerHTML += 'test';
  }
}

// This is much faster
function nodeJam(){
  nodes = document.getElementsByTagName('P');

  for (var i = 0, len = nodes.length; i &amp;lt; len; i++) {
    nodes[i].innerHTML += 'test';
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
계층 구조를 가진 함수를 반복해서 호출할 때 변수로 지정하면 더 빠르게 작동합니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;// Instead of this...
function iterateOverMe(){
  for (var i = 0; i &amp;lt; 1000; i++){
    lorem.ipsum.dolor.sit(i);
  }
}

// This is much faster
function iterateOverMe(){
  var sit = lorem.ipsum.dolor.sit;

  for (var i = 0; i &amp;lt; 1000; i++){
    sit(i);
  }
}

&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
DOM에 삽입하기 전에 먼저 계산하는 것으로 훨씬 더 빠르게 작동합니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;// Instead of this...
function subTrees(){
  var ul = document.getElementById("myUL");

  for (var i = 0; i &amp;lt; 200; i++) {
    ul.appendChild(document.createElement("LI"));
  }
}

// This is much faster
function subTrees(){
  var ul = document.getElementById("myUL");
  var li = document.createElement("LI");
  var parentNode = ul.parentNode;

  parentNode.removeChild(ul);

  for (var i = 0; i &amp;lt; 200; i++) {
    ul.appendChild(li.cloneNode(true));
  }

  parentNode.appendChild(ul);
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
함수 속에서 또 다른 함수를 중첩(Nesting Functions)해서 사용하지 마세요. 함수안에 정의된 함수는 상위 함수가 호출될 때마다 내부 함수를 새롭게 생성하는 처리과정이 포함되기 때문에 성능이 떨어집니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;// Instead of this...
function foo(a, b){
  function bar() {
    return a + b;
  }

  return bar();
}

var baz = foo(1, 2) // 3

// This is much faster
function foo(a, b){
  return bar (a, b);
}

function bar(a, b) {
  return a + b;
}

var baz = foo(1, 2) // 3&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;코딩 패턴&lt;/h4&gt;jQuery를 통해 자바스크립트에서 효율을 높이기 위한 코딩 패러다임을 제시한 &lt;a href="http://ejohn.org/"&gt;존 레식&lt;/a&gt;씨가 선호하는 코딩 관습를 살펴보면 &lt;a href="http://www.klauskomenda.com/code/javascript-programming-patterns/#module"&gt;모듈 패턴(Module Pattern)&lt;/a&gt;이라는 것이 있습니다. 익명함수를 작성하고 즉시 실행하는 것으로 클로저(Closure)에 의해 "private static scope"가 제공되어 정적인 컨텍스트(Context)를 가질수 있게하는 검증된 자바스크립트 코딩 패턴입니다.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  (function($) {

    // do something...

  })(jQuery);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
이것은 메모리에 등록된 식별자를 탐색하는 시간을 줄이고 전역 네임스페이스를 오염시키지 않으면서 서로다른 프로그램간의 간섭을 없앨수 있는 매우 좋은 방법입니다. 식별자를 검색하는데 비용이 줄어든다는 것은 곧 성능이 향상되는 것을 의미하며 마치 &lt;a href="http://firejune.com/1372/%26%2339%3Bwith%26%2339%3B%EC%9D%98+%EC%82%AC%EC%9A%A9%EC%9C%BC%EB%A1%9C+%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8+%EC%84%B1%EB%8A%A5%EA%B0%9C%EC%84%A0"&gt;'with'를 사용하는 것과 같은&lt;/a&gt; 효과가 발생합니다. 다음 예제는 얼마전 작성한 &lt;a href="http://firejune.com/1704/%EC%82%AC%EC%9D%B4%ED%8A%B8+%EB%AF%B8%EB%8B%88%EB%A7%B5%28%EC%95%84%EC%9B%83%EB%9D%BC%EC%9D%B8%29+%EC%9C%84%EC%A0%AF+%EC%B6%94%EA%B0%80"&gt;Minimap&lt;/a&gt; 소스코드를 지면 관계상 재구성한 것입니다.(&lt;a href="http://www.prototypejs.org/"&gt;prototype.js&lt;/a&gt;에 기반하며 작동하는 코드 아님)&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var Minimaps = (function(win, doc, undefined) {

  /*
   * Options.
   */
  var $options = {
        width: 100
      , height: 400
      , duration: 0.2
      , focusWidth: 0.5
      , focusHeight: 0.5
    }
    , $canvas
    , $context;

  /*
   * Initialize.
   */
  return function(options) {
    $options = $.extend($options, options);
    $canvas = new Element('canvas', {
        id: 'outline'
      , width: $options.width
      , height: $options.height
    });

    if (!($canvas instanceof HTMLElement)
       || $canvas.nodeName.toLowerCase() !== 'canvas')
      return console.error('Your browser does not support Canvas');

    doc.body.insert($canvas);

    this.version = '0.1.1';
    this.draw = draw;
    this.scroll = scroll;

    $context = $canvas.getContext('2d');

    Event.observe(win, 'load', draw);
    Event.observe(win, 'resize', draw);
    Event.observe(win, 'scroll', draw);

    draw();
  };

  /*
   * Core.
   */
  function scroll(event) {
    var x = event.clientX - $canvas.left
      , y = event.clientY - $canvas.top;

    scroll.ani &amp;amp;&amp;amp; scroll.ani.state == 'running' &amp;amp;&amp;amp; scroll.ani.cancel();
    scroll.ani = new Effect.Scroll(
      y / draw.scale - size.win.height * $options.focusHeight
    , x / draw.scale - size.win.width * $options.focusWidth
    , {
        fps: 100
      , duration: event.type == 'mousemove' ? 0 : $options.duration
    });
  }

  function draw() {
    var scaleX = $options.width / size.doc.width
      , scaleY = $options.height / size.doc.height;

    draw.scale = scaleX &amp;lt; scaleY ? scaleX : scaleY;

    $context.clearRect(0, 0, $canvas.width, $canvas.height);
    $context.scale(1 / draw.scale, 1 / draw.scale);
  }

  ...

})(window, document);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
위 코드를 &lt;a href="http://code.google.com/intl/ko-KR/closure/compiler/"&gt;구글 Closure 컴파일러&lt;/a&gt;로 압축하면 다음과 같습니다.&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;var Minimaps=function(h,d){function g(e){var f=e.clientX-b.left,d=e.clientY-b.top;g.ani&amp;amp;&amp;amp;g.ani.state=="running"&amp;amp;&amp;amp;g.ani.cancel();g.ani=new Effect.Scroll(d/a.scale-size.win.height*c.focusHeight,f/a.scale-size.win.width*c.focusWidth,{fps:100,duration:e.type=="mousemove"?0:c.duration})}function a(){var e=c.width/size.doc.width,d=c.height/size.doc.height;a.scale=e&amp;lt;d?e:d;f.clearRect(0,0,b.width,b.height);f.scale(1/a.scale,1/a.scale)}var c={width:100,height:400,duration:0.2,focusWidth:0.5,
focusHeight:0.5},b,f;return function(e){c=$.extend(c,e);b=new Element("canvas",{id:"outline",width:c.width,height:c.height});if(!(b instanceof HTMLElement)||b.nodeName.toLowerCase()!=="canvas")return console.error("Your browser does not support Canvas");h.body.insert(b);this.version="0.1.1";this.draw=a;this.scroll=g;f=b.getContext("2d");Event.observe(d,"load",a);Event.observe(d,"resize",a);Event.observe(d,"scroll",a);a()}}(window,document);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
제가 무엇을 말하고 싶은지 눈치 까셨나요? 네 그렇습니다. 저는 제가 만든 프로그램이 OO(object-oriented) 스럽게 작성된 클래스이면서 한 인스턴스로 작동되길 바랍니다. 그러나 코딩 스타일은 단순 행동 함수들로 구성된 전형적인 모듈 패턴이지만 초기화 함수를 반환하는 것으로 &lt;a href="http://peter.michaux.ca/articles/lazy-function-definition-pattern"&gt;게으른 함수 정의 패턴(Lazy Function Definition Pattern)&lt;/a&gt;을 혼용하는 복합적인 모습입니다. 이러한 코딩은 여러가지 이점이 있습니다. 원하던 대로 인스턴스를 만들어 사용하는 동시에, 컴파일(압축)하기 좋은 구조를 가지며, 변수들을 자유롭게 포메이션할 수 있고, 행동에 따른 디자인 패턴으로 작성되기 때문에 이해하기 쉽습니다. 그리고 무엇보다도 졸라 빠릅니다.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;다음 편에 계속...&lt;/h3&gt;&lt;br /&gt;
&lt;!--- TEMPLATE&lt;br /&gt;
&lt;h3&gt;이벤트&lt;/h3&gt;&lt;br /&gt;
- 인라인 자바스크립트 사용 금지&lt;br /&gt;
- 이벤트 딜리게이션 &lt;a href="http://icant.co.uk/sandbox/eventdelegation/"&gt;http://icant.co.uk/sandbox/eventdelegation/&lt;/a&gt;&lt;br /&gt;
- 이벤트 드리븐 프로그래밍 &lt;a href="http://en.wikipedia.org/wiki/Event-driven_programming"&gt;http://en.wikipedia.org/wiki/Event-driven_programming&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;배열 처리&lt;/h4&gt;&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;/* 배열에서 특정 값 제외하기 */
var arr = [1, 2, 3];
arr = arr.reject(function(w) { return w == 2});
//-&amp;gt; arr = [1, 3];&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
- 앞에것 부터 잘라내기&lt;br /&gt;
- Queue&lt;br /&gt;
- 객체 사전&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;디버깅&lt;/h4&gt;&lt;br /&gt;
&lt;pre class="javascript"&gt;&lt;code class="javascript"&gt;  // for console debugging
  if (window['console'] === undefined || console.log === undefined) console = {
    log: function() {}, info: function() {}, warn: function() {}, error: function() {}
  };&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;굳이 자바스크립트로 작성할 필요가 없는 것도 있다.&lt;/li&gt;
  &lt;li&gt;방어적 프로그래밍&lt;/li&gt;
  &lt;li&gt;변수 이름에 다른 변수이름을 포함하지 마세요.&lt;/li&gt;
  &lt;li&gt;되풀이 되는 코드를 줄이세요.(복사/붙여넣기 코드)&lt;/li&gt;
  &lt;li&gt;매개 변수가 커지면 커질수록 복잡도는 증가합니다.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;br /&gt;
- eval is Evil : The eval function is the most misused feature of JavaScript. Avoid it.&lt;br /&gt;
eval has aliases. Do not use the Functionconstructor. Do not pass strings to setTimeout or setInterval.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://o2js.com/2011/10/04/o2-js-javascript-conventions/"&gt;http://o2js.com/2011/10/04/o2-js-javascript-conventions/&lt;/a&gt; --&gt;
 &lt;br /&gt;&lt;br /&gt;
 &lt;a href="http://firejune.com/1713#p1713"&gt;Comments(2)&lt;/a&gt; | 
 &lt;a href="http://firejune.com/1713"&gt;Hits(11105)&lt;/a&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3xSdpUWDM4qvEulHp9k_48rfKXE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3xSdpUWDM4qvEulHp9k_48rfKXE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/3xSdpUWDM4qvEulHp9k_48rfKXE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3xSdpUWDM4qvEulHp9k_48rfKXE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title type="text">Socket.IO 0.8.5 업데이트</title><link rel="alternate" type="text/html" href="http://firejune.com/1715" /><category term="웹개발" /><category term="자료" /><author><name>파이어준</name></author><updated>2011-10-09T08:24:21-07:00</updated><id>http://firejune.com/1715</id><content type="html">&lt;a href="http://socket.io/"&gt;Socket.IO&lt;/a&gt;가 0.8.5로 업데이트되었습니다. 버그 수정을 포함하며, 웹소켓 프로토콜 &lt;a href="http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-16"&gt;HyBi-16&lt;/a&gt;(Draft)을 지원합니다. 지난 달 6일 이후 한동안 무소식하여 불안불안했는데, 반가운 소식이네요. 자세한 내용은 다음과 같으며 항목별로 커밋 링크를 찾아 걸었습니다. 분석하시는 분들은 참고하세요.&lt;br /&gt;
&lt;br /&gt;
&lt;ul class="square"&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/52f6a5b124af71ad24a99824f54f4e2c4a4875fc"&gt;Added websocket draft HyBi-16 support.&lt;/a&gt; [einaros]&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/f1cea7e788fc04fc8d126d215395e6ae328b6826"&gt;Fixed websocket continuation bugs.&lt;/a&gt; [einaros]&lt;/li&gt;
  &lt;li&gt;Fixed flashsocket transport name.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/51782fc5d7dcec6347735a0f70a4abb33b1bd9ff"&gt;Fixed websocket tests.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/cb7304837c19a8f255f385ad20ccee0461738b1f"&gt;Ensured 'parser#decodePayload' doesn't choke.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Added http referrer verification to manager verifyOrigin.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/7db146df47e22c8c0da95d5083cd7360a0483e0d"&gt;Added access control for cross domain xhr handshakes&lt;/a&gt; [3rd-Eden]&lt;/li&gt;
  &lt;li&gt;Added support for automatic generation of socket.io files [3rd-Eden]&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/52f6a5b124af71ad24a99824f54f4e2c4a4875fc"&gt;Added websocket binary support&lt;/a&gt; [einaros]&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/b2f9f19d99cd18d78b3455c19b4a3cc344c15a65"&gt;Added gzip support for socket.io.js&lt;/a&gt; [3rd-Eden]&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/LearnBoost/socket.io/commit/11f1a7c491d06166874243f801a46c7e364d4db6"&gt;Expose socket.transport&lt;/a&gt; [3rd-Eden] &lt;/li&gt;
  &lt;li&gt;Updated client.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/01FGOOUeRdEMcE733549M25LOT4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/01FGOOUeRdEMcE733549M25LOT4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/01FGOOUeRdEMcE733549M25LOT4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/01FGOOUeRdEMcE733549M25LOT4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;</content></entry></feed>

