<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Oddpoet's βetalog]]></title>
  <link href="http://oddpoet.net/atom.xml" rel="self"/>
  <link href="http://oddpoet.net/"/>
  <updated>2013-06-19T15:49:09+09:00</updated>
  <id>http://oddpoet.net/</id>
  <author>
    <name><![CDATA[oddpoet]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[brew로 설치한 Jenkins의 인코딩]]></title>
    <link href="http://oddpoet.net/blog/2013/06/19/encoding-of-jenkins-by-brew/"/>
    <updated>2013-06-19T15:00:00+09:00</updated>
    <id>http://oddpoet.net/blog/2013/06/19/encoding-of-jenkins-by-brew</id>
    <content type="html"><![CDATA[<p>OSX에서 <a href="http://mxcl.github.io/homebrew/">brew</a>를 이용해서 jenkins를 설치하면 JVM의 기본 인코딩(<code>file.encoding</code>) 때문에 한글 처리 등에 문제가 생기는 경우가 있다. </p>

<p>JVM 옵션으로 <code>-Dfile.encoding=UTF-8</code>을 주면 될 것 같지만, <code>~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist</code> 에 JVM옵션을 추가해도 jenkins의 <code>file.encoding</code>은 여전히 US-ASCII 다. </p>

<!--more -->

<p>jenkins가 업데이트 될 때마다 고쳐하는 게 문제이지만, <code>~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist</code>파일에 아래처럼 환경변수를 추가해주면 인코딩을 UTF-8로 jenkins를 띄울 수 있다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>homebrew.mxcl.jenkins.plist</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
</pre></td><td class="code"><pre><code class="xml"><span class="line"><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
</span><span class="line"><span class="cp">&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dt&gt;</span>
</span><span class="line"><span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">&quot;1.0&quot;</span><span class="nt">&gt;</span>
</span><span class="line">  <span class="nt">&lt;dict&gt;</span>
</span><span class="line">    <span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span>
</span><span class="line">    <span class="nt">&lt;string&gt;</span>homebrew.mxcl.jenkins<span class="nt">&lt;/string&gt;</span>
</span><span class="line">    <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
</span><span class="line">    <span class="nt">&lt;array&gt;</span>
</span><span class="line">      <span class="nt">&lt;string&gt;</span>/usr/bin/java<span class="nt">&lt;/string&gt;</span>
</span><span class="line">      <span class="nt">&lt;string&gt;</span>-jar<span class="nt">&lt;/string&gt;</span>
</span><span class="line">      <span class="nt">&lt;string&gt;</span>/usr/local/opt/jenkins/libexec/jenkins.war<span class="nt">&lt;/string&gt;</span>
</span><span class="line">      <span class="nt">&lt;string&gt;</span>--httpListenAddress=127.0.0.1<span class="nt">&lt;/string&gt;</span>
</span><span class="line">    <span class="nt">&lt;/array&gt;</span>
</span><span class="line">    <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
</span><span class="line">    <span class="nt">&lt;true/&gt;</span>
</span><span class="line">    <span class="nt">&lt;key&gt;</span>EnvironmentVariables<span class="nt">&lt;/key&gt;</span>
</span><span class="line">    <span class="nt">&lt;dict&gt;</span>
</span><span class="line">      <span class="nt">&lt;key&gt;</span>LANG<span class="nt">&lt;/key&gt;</span>
</span><span class="line">      <span class="nt">&lt;string&gt;</span>ko_KR.UTF-8<span class="nt">&lt;/string&gt;</span>
</span><span class="line">    <span class="nt">&lt;/dict&gt;</span>
</span><span class="line">  <span class="nt">&lt;/dict&gt;</span>
</span><span class="line"><span class="nt">&lt;/plist&gt;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>위에서 <code>EnvironmentVariables</code> 이하를 추가한다. 다시 말하지만 위쪽의 <code>ProgramArguments</code> 부분에 <code>-Dfile.encoding=UTF-8</code>을 넣는 방식은 환경변수 때문에 정상적으로 작동하지 않는다. </p>

<p>jenkins를 아래와 같이 다시 시작한다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nv">$ </span>launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
</span><span class="line"><span class="nv">$ </span>launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
</span></code></pre></td></tr></table></div></figure></notextile></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Mac OSX에서 JAVA_HOME]]></title>
    <link href="http://oddpoet.net/blog/2013/05/29/java-home-on-osx/"/>
    <updated>2013-05-29T23:30:00+09:00</updated>
    <id>http://oddpoet.net/blog/2013/05/29/java-home-on-osx</id>
    <content type="html"><![CDATA[<p>OSX에서 JAVA_HOME 경로는 아래와 같은 커맨드로 얻을 수 있다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line">&gt; /usr/libexec/java_home
</span><span class="line">/Library/Java/JavaVirtualMachines/jdk1.7.0_21.jdk/Contents/Home
</span><span class="line">&gt; /usr/libexec/java_home -v1.6
</span><span class="line">/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
</span><span class="line">&gt; /usr/libexec/java_home -v1.7
</span><span class="line">/Library/Java/JavaVirtualMachines/jdk1.7.0_21.jdk/Contents/Home
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p><code>.bashrc</code> 파일에 아래처럼 세팅하면 되겠다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nb">export </span><span class="nv">JAVA_HOME</span><span class="o">=</span><span class="sb">`</span>/usr/libexec/java_home<span class="sb">`</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Cucumber-JVM]]></title>
    <link href="http://oddpoet.net/blog/2013/05/22/cucumber-jvm/"/>
    <updated>2013-05-22T20:27:00+09:00</updated>
    <id>http://oddpoet.net/blog/2013/05/22/cucumber-jvm</id>
    <content type="html"><![CDATA[<p>ruby에서 <code>rspec</code>과 함께 대표적인 <strong>BDD(Behavior Driven Development)</strong> 툴로 사용되던, 
<strong><a href="http://cukes.info/">cucumber</a></strong>가 jvm으로 포팅되어 <strong><a href="https://github.com/cucumber/cucumber-jvm">cucumber-jvm</a></strong>으로 돌아왔다. <strong>Cucumber</strong>를 만든 Aslak Hellesøy가 
예전에 jruby로 java와 연동해서 cucumber를 사용할 수 있게 해주던 <a href="https://github.com/cucumber/cuke4duke">cuke4duke</a>라는 걸 만들었는데,
실사용에 문제가 많더니만 결국 java로 cucumber를 재개발했나보다. (<code>cuke4duke</code>는 현재 더이상 개발되지 않음)</p>

<!-- more -->

<p><code>cucumber-jvm</code>은 <code>java</code>외에도 <code>groovy</code>, <code>scala</code>, <code>jython</code> 등 jvm 기반의 
언어 다수를 지원한다. 그래서 <code>cucumber-java</code>가 아니라 <code>cucumber-jvm</code>이라 이름 붙인듯 하다. 
이 글에서 <code>maven</code>, <code>junit</code>, <code>spring framework</code>과 같은 대표적인 java 개발환경에서 <code>cucumber</code>를 
사용하기 위한 방법을 소개한다. </p>

<h2 id="maven-dependency">maven dependency</h2>

<p><code>pom.xml</code> 파일에 아래와 같이 dependency를 추가한다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>pom.xml</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
</pre></td><td class="code"><pre><code class="xml"><span class="line">...
</span><span class="line"><span class="nt">&lt;properties&gt;</span>
</span><span class="line">  <span class="nt">&lt;cucumber.version&gt;</span>1.1.2<span class="nt">&lt;/cucumber.version&gt;</span>
</span><span class="line">  <span class="nt">&lt;junit.version&gt;</span>4.11<span class="nt">&lt;/junit.version&gt;</span>
</span><span class="line"><span class="nt">&lt;/properties&gt;</span>
</span><span class="line"><span class="nt">&lt;dependencies&gt;</span>
</span><span class="line">  ...
</span><span class="line">  <span class="nt">&lt;dependency&gt;</span>
</span><span class="line">    <span class="nt">&lt;groupId&gt;</span>junit<span class="nt">&lt;/groupId&gt;</span>
</span><span class="line">    <span class="nt">&lt;artifactId&gt;</span>junit<span class="nt">&lt;/artifactId&gt;</span>
</span><span class="line">    <span class="nt">&lt;version&gt;</span>${junit.version}<span class="nt">&lt;/version&gt;</span>
</span><span class="line">    <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span>
</span><span class="line">  <span class="nt">&lt;/dependency&gt;</span>
</span><span class="line">  <span class="nt">&lt;dependency&gt;</span>
</span><span class="line">    <span class="nt">&lt;groupId&gt;</span>info.cukes<span class="nt">&lt;/groupId&gt;</span>
</span><span class="line">    <span class="nt">&lt;artifactId&gt;</span>cucumber-junit<span class="nt">&lt;/artifactId&gt;</span>
</span><span class="line">    <span class="nt">&lt;version&gt;</span>${cucumber.version}<span class="nt">&lt;/version&gt;</span>
</span><span class="line">  <span class="nt">&lt;/dependency&gt;</span>
</span><span class="line">  <span class="nt">&lt;dependency&gt;</span>
</span><span class="line">    <span class="nt">&lt;groupId&gt;</span>info.cukes<span class="nt">&lt;/groupId&gt;</span>
</span><span class="line">    <span class="nt">&lt;artifactId&gt;</span>cucumber-java<span class="nt">&lt;/artifactId&gt;</span>
</span><span class="line">    <span class="nt">&lt;version&gt;</span>${cucumber.version}<span class="nt">&lt;/version&gt;</span>
</span><span class="line">  <span class="nt">&lt;/dependency&gt;</span>
</span><span class="line">  <span class="nt">&lt;dependency&gt;</span>
</span><span class="line">    <span class="nt">&lt;groupId&gt;</span>info.cukes<span class="nt">&lt;/groupId&gt;</span>
</span><span class="line">    <span class="nt">&lt;artifactId&gt;</span>cucumber-spring<span class="nt">&lt;/artifactId&gt;</span>
</span><span class="line">    <span class="nt">&lt;version&gt;</span>${cucumber.version}<span class="nt">&lt;/version&gt;</span>
</span><span class="line">  <span class="nt">&lt;/dependency&gt;</span>
</span><span class="line"><span class="nt">&lt;dependencies&gt;</span>
</span><span class="line">...
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>주의할 점은 <code>junit</code>은 최신버전(현재 4.11)을 사용해야 한다. <code>4.8</code>버전에서 문제가 있다. 
또, 현재 최신인 <code>1.1.3</code>버전의 <code>cucumber</code>는 <em>pretty formatter</em>에 오류가 있으니, <code>1.1.2</code>버전을 사용한다. </p>

<ul>
  <li><code>cucumber-junit</code>은 cucumber를 JUnitRunner을 이용하여 실행하기 위해 필요하다. 대부분의
   IDE가 junit을 지원하므로 <code>cucumber-junit</code>을 사용하는 것이 편하다. </li>
  <li><code>cucumber-java</code>는 java를 이용하여 <em>step definition</em>을 작성하기 위해 필요하다. </li>
  <li><code>cucumber-spring</code>은 <em>Spring Framework</em>과 연동하여 사용할 때 테스트를 위한 <code>Configuration Context</code>를 
   로딩하기 위해서 필요하다. (Spring을 사용하지 않는다면 뺀다.)</li>
</ul>

<h2 id="junit-">JUnit 연동</h2>

<p>JUnit을 통해서 Cucumber 테스트를 실행하기 위해서 아래와 같은 파일을 <code>src/test/java/features</code> 디렉토리 밑에 추가한다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>CucumberTests.java</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kn">package</span> <span class="n">features</span><span class="o">;</span>
</span><span class="line">
</span><span class="line"><span class="kn">import</span> <span class="nn">cucumber.api.junit.Cucumber</span><span class="o">;</span>
</span><span class="line"><span class="kn">import</span> <span class="nn">org.junit.runner.RunWith</span><span class="o">;</span>
</span><span class="line">
</span><span class="line"><span class="nd">@RunWith</span><span class="o">(</span><span class="n">Cucumber</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
</span><span class="line"><span class="nd">@Cucumber.Options</span><span class="o">(</span>
</span><span class="line">        <span class="n">features</span> <span class="o">=</span> <span class="s">&quot;classpath:features&quot;</span><span class="o">,</span>
</span><span class="line">        <span class="n">format</span> <span class="o">=</span> <span class="o">{</span><span class="s">&quot;pretty&quot;</span><span class="o">,</span> <span class="s">&quot;json:target/cucumber.json&quot;</span><span class="o">,</span> <span class="s">&quot;html:target/cucumber&quot;</span><span class="o">}</span>
</span><span class="line"><span class="o">)</span>
</span><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CucumberTests</span> <span class="o">{</span>
</span><span class="line"><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>일반적으로 cucumber 관련 파일들은 <code>features</code> 패키지 밑에 둔다. </p>

<ul>
  <li><strong><code>@RunWith</code></strong> : JUnitRunner를 통해서 Cucumber가 실행되도록 한다. <code>Cucumber.class</code>가 
    실질적으로 feature에 대한 테스트를 수행하게 된다. </li>
  <li><strong><code>@Cucumber.Options</code></strong> : Cucumber실행에 대한 옵션. 
    <ul>
      <li><code>features</code> : <code>gherkin</code> DSL로 작성된 feature 파일들의 경로를 지정한다. 
  보통 <code>src/test/resources/features/</code> 밑에 위치하므로 <code>classpath:features</code>라고 지정한다. </li>
      <li><code>format</code> : 출력 포맷을 지정한다. <code>pretty</code>는 텍스트 형태의 console 출력이고, json/html 등은 파일명을 지정하게 된다. </li>
    </ul>
  </li>
  <li><code>CucumberTests</code> 클래스는 JUnit으로 테스트를 실행하기 위한 용도로만 사용하므로 클래스 내부는 비워둔다. </li>
</ul>

<h2 id="spring-">Spring 연동</h2>

<p>Spring Framework 환경에서 JUnit test를 쓸 때 <code>@org.springframework.test.context.ContextConfiguration</code> 어노테이션을 
사용하여 로딩할 context configuration을 지정하게 된다. Cucumber에서는 기본적으로 <code>classpath:cucumber.xml</code> 파일을 
Spring context로 로딩한다. 따라서 <code>cucumber.xml</code>파일을 <code>src/test/resources/</code>에 만든다. </p>

<p>아래는 예시일 뿐이고, 테스트에 필요한 Spring 설정을 추가하거나, 별도로 테스팅용 context configuration 파일이 있다면 import하도록 한다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>cucumber.xml</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="xml"><span class="line"><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
</span><span class="line"><span class="nt">&lt;beans</span> <span class="na">xmlns=</span><span class="s">&quot;http://www.springframework.org/schema/beans&quot;</span>
</span><span class="line">       <span class="na">xmlns:xsi=</span><span class="s">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span>
</span><span class="line">       <span class="na">xmlns:context=</span><span class="s">&quot;http://www.springframework.org/schema/context&quot;</span>
</span><span class="line">       <span class="na">xsi:schemaLocation=</span><span class="s">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span><span class="nt">&gt;</span>
</span><span class="line">    <span class="nt">&lt;context:annotation-config/&gt;</span>
</span><span class="line">    <span class="nt">&lt;context:component-scan</span> <span class="na">base-package=</span><span class="s">&quot;net.oddpoet.cucumber.sample&quot;</span><span class="nt">/&gt;</span>
</span><span class="line"><span class="nt">&lt;/beans&gt;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="feature-">feature 작성</h2>

<p>feature 파일은 위의 <code>CucumberTests.java</code> 에서 <code>features</code> 옵션으로 설정한대로
<code>src/test/resources/features/</code> 밑에 만든다. feature가 많아지면 sub-directory를 만들어 구성할 수도 있다. 
아래는 feature 파일의 예이다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>main.feature</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
<span class="line-number">35</span>
<span class="line-number">36</span>
<span class="line-number">37</span>
<span class="line-number">38</span>
<span class="line-number">39</span>
<span class="line-number">40</span>
<span class="line-number">41</span>
<span class="line-number">42</span>
<span class="line-number">43</span>
<span class="line-number">44</span>
<span class="line-number">45</span>
</pre></td><td class="code"><pre><code class="gherkin"><span class="line"><span class="nt">@interface</span><span class="nf"></span>
</span><span class="line"><span class="k">Feature:</span><span class="nf"> 대화형 팀매칭 시스템</span>
</span><span class="line">
</span><span class="line"><span class="nf">  시스템과 대화형으로 작업하는 사용자는</span>
</span><span class="line"><span class="nf">  입력값을 입력하면 그에 대한 즉각적인 피드백을 받는다.</span>
</span><span class="line"><span class="nf">  모든 입력이 완료되면 시스템은 입력된 선수 정보를 기반으로 2팀을 구성하여 출력한다.</span>
</span><span class="line">
</span><span class="line"><span class="nf">  - 각 선수 정보 포맷 : &lt;선수번호&gt; &lt;싫어하는 선수 수&gt; &lt;싫어하는 선수번호&gt; &lt;싫어하는 선수번호&gt; ...</span>
</span><span class="line">
</span><span class="line"><span class="nf">  </span><span class="k">Scenario:</span><span class="nf"> 기본 예제</span>
</span><span class="line"><span class="k">    Given </span><span class="nf">시스템 출력: &#39;총 선수 수를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">5</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">1</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">1</span><span class="nf"> </span><span class="s">1</span><span class="nf"> </span><span class="s">2</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">  </span><span class="c"># 1번 선수는 1명을 싫어하고 싫어하는 선수는 2번 선수이다</span><span class="nf"></span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">2</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">2</span><span class="nf"> </span><span class="s">1</span><span class="nf"> </span><span class="s">3</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">  </span><span class="c"># 2번 선수는 1명을 싫어하고 싫어하는 선수는 3번 선수이다</span><span class="nf"></span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">3</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">3</span><span class="nf"> </span><span class="s">0</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">  </span><span class="c"># 3번 선수는 싫어하는 선수가 없다</span><span class="nf"></span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">4</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">4</span><span class="nf"> </span><span class="s">2</span><span class="nf"> </span><span class="s">2</span><span class="nf"> </span><span class="s">3</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">  </span><span class="c"># 4번 선수는 2명을 싫어하고 싫어하는 선수는 2, 3번 선수이다</span><span class="nf"></span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">5</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">5</span><span class="nf"> </span><span class="s">1</span><span class="nf"> </span><span class="s">4</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">  </span><span class="c"># 5번 선수는 1명을 싫어하고 싫어하는 선수는 4번 선수이다</span><span class="nf"></span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">팀 구성 결과를 출력한다</span>
</span><span class="line">
</span><span class="line">
</span><span class="line"><span class="nf">  </span><span class="k">Scenario:</span><span class="nf"> 총 선수 수에 오류가 있을 경우</span>
</span><span class="line"><span class="k">    Given </span><span class="nf">시스템 출력: &#39;총 선수 수를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;-</span><span class="s">10</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;총 선수 수를 다시 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">3</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">1</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span><span class="line">
</span><span class="line"><span class="nf">  </span><span class="k">Scenario:</span><span class="nf"> 선수정보에 오류가 있을 경우</span>
</span><span class="line"><span class="k">    Given </span><span class="nf">시스템 출력: &#39;총 선수 수를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">5</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">1</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;A </span><span class="s">1</span><span class="nf"> B&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">1</span><span class="nf">번선수 정보를 다시 입력하시오&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">When </span><span class="nf">사용자 입력: &#39;</span><span class="s">1</span><span class="nf"> </span><span class="s">1</span><span class="nf"> </span><span class="s">2</span><span class="nf">&#39;</span>
</span><span class="line"><span class="nf">    </span><span class="k">Then </span><span class="nf">시스템 출력: &#39;</span><span class="s">2</span><span class="nf">번선수 정보를 입력하시오&#39;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>feature 파일을 만들고 JUnit으로 테스트를 수행하면, Step Definition이 없다는 메시지와 함께 
step definition에 대한 code snippet을 console에 출력해준다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>main.feature</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
</pre></td><td class="code"><pre><code class="gherkin"><span class="line"><span class="nf">You can implement missing steps with the snippets below:</span>
</span><span class="line">
</span><span class="line"><span class="nt">@Given(&quot;^시스템</span><span class="nf"> 출력: &#39;총 선수 수를 입력하시오&#39;$&quot;</span><span class="s">)</span>
</span><span class="line"><span class="s">public void 시스템_출력_총_선수_수를_입력하시오() throws Throwable {</span>
</span><span class="line"><span class="s">    // Express the Regexp above with the code you wish you had</span>
</span><span class="line"><span class="s">    throw new PendingException();</span>
</span><span class="line"><span class="s">}</span>
</span><span class="line">
</span><span class="line"><span class="s">@When(</span><span class="nf">&quot;^사용자 입력: &#39;(\\d+)&#39;$&quot;</span><span class="s">)</span>
</span><span class="line"><span class="s">public void 사용자_입력_(int arg1) throws Throwable {</span>
</span><span class="line"><span class="s">    // Express the Regexp above with the code you wish you had</span>
</span><span class="line"><span class="s">    throw new PendingException();</span>
</span><span class="line"><span class="s">}</span>
</span><span class="line">
</span><span class="line"><span class="s">@Then(</span><span class="nf">&quot;^시스템 출력: &#39;(\\d+)번선수 정보를 입력하시오&#39;$&quot;</span><span class="s">)</span>
</span><span class="line"><span class="s">public void 시스템_출력_번선수_정보를_입력하시오(int arg1) throws Throwable {</span>
</span><span class="line"><span class="s">    // Express the Regexp above with the code you wish you had</span>
</span><span class="line"><span class="s">    throw new PendingException();</span>
</span><span class="line"><span class="s">}</span>
</span><span class="line">
</span><span class="line"><span class="s">@When(</span><span class="nf">&quot;^사용자 입력: &#39;(\\d+) (\\d+) (\\d+)&#39;$&quot;</span><span class="s">)</span>
</span><span class="line"><span class="s">public void 사용자_입력_(int arg1, int arg2, int arg3) throws Throwable {</span>
</span><span class="line"><span class="s">    // Express the Regexp above with the code you wish you had</span>
</span><span class="line"><span class="s">    throw new PendingException();</span>
</span><span class="line"><span class="s">}</span>
</span><span class="line">
</span><span class="line"><span class="s">...(이하 생략)...</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>아직 각 <code>step</code>(<code>Given</code>, <code>When</code>, <code>Then</code>)에 대한  <code>step definition</code>이 정의되지 않아서 
발생하는 경고이다. Cucumber는 이것을 테스트의 <strong>Fail</strong>로 처리하지 않고, <strong>Pending</strong> 처리한다. </p>

<h2 id="step-definition-">Step Definition 작성</h2>

<p>Step definition은 feature에 기술된 각 단계(step)를 어떻게 수행할 것인지를 코딩하는 하는 단계다. 
보통 실제 코드가 만들어진 후 작성하여 연결하게 된다. </p>

<p>Step definition을 작성할 때는 cucumber 실행시 출력되는 code snippet을 복사해서 붙일 수도 있지만, 
Intellij같은 IDE를 사용한다면 Step Definition을 생성해주는 IDE의 기능을 사용한다. </p>

<p>일반적으로 <code>src/test/java/features/step_definitions</code> 밑에 Step Definition 클래스를 정의한다. 
Cucumber는 <em>step</em>과 <em>step definition</em>을 정규식 매칭으로 연결하므로 의미적으로 동일한 step들이 
동일한 step definition으로 연결되도록 정규식을 수정한다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>InteractiveStepDef.java</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kn">package</span> <span class="n">features</span><span class="o">.</span><span class="na">step_definitions</span><span class="o">;</span>
</span><span class="line">
</span><span class="line"><span class="kn">import</span> <span class="nn">java.io.*</span><span class="o">;</span>
</span><span class="line">
</span><span class="line"><span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">hamcrest</span><span class="o">.</span><span class="na">CoreMatchers</span><span class="o">.</span><span class="na">is</span><span class="o">;</span>
</span><span class="line"><span class="kn">import</span> <span class="nn">static</span> <span class="n">org</span><span class="o">.</span><span class="na">junit</span><span class="o">.</span><span class="na">Assert</span><span class="o">.*;</span>
</span><span class="line">
</span><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">InteractiveStepDef</span> <span class="o">{</span>
</span><span class="line">    <span class="nd">@Autowired</span>
</span><span class="line">    <span class="n">TeamCreateService</span> <span class="n">teamCreateService</span><span class="o">;</span>
</span><span class="line">
</span><span class="line">    <span class="nd">@Autowired</span>
</span><span class="line">    <span class="nd">@Qualifier</span><span class="o">(</span><span class="s">&quot;inputWriter&quot;</span><span class="o">)</span>
</span><span class="line">    <span class="n">PrintWriter</span> <span class="n">inputWriter</span><span class="o">;</span>
</span><span class="line">
</span><span class="line">    <span class="nd">@Autowired</span>
</span><span class="line">    <span class="nd">@Qualifier</span><span class="o">(</span><span class="s">&quot;outputWriter&quot;</span><span class="o">)</span>
</span><span class="line">    <span class="n">LoggedWriter</span> <span class="n">outputLog</span><span class="o">;</span>
</span><span class="line">
</span><span class="line">    <span class="nd">@Given</span><span class="o">(</span><span class="s">&quot;^시스템 출력: &#39;(.+)&#39;$&quot;</span><span class="o">)</span>
</span><span class="line">    <span class="kd">public</span> <span class="kt">void</span> <span class="err">시스템</span><span class="n">_</span><span class="err">출력</span><span class="n">_</span><span class="o">(</span><span class="n">String</span> <span class="n">output</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
</span><span class="line">        <span class="n">String</span> <span class="n">systemOut</span> <span class="o">=</span> <span class="n">outputLog</span><span class="o">.</span><span class="na">getLoggedLine</span><span class="o">();</span>
</span><span class="line">        <span class="n">assertThat</span><span class="o">(</span><span class="n">systemOut</span><span class="o">,</span> <span class="n">is</span><span class="o">(</span><span class="n">output</span><span class="o">));</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">
</span><span class="line">    <span class="nd">@When</span><span class="o">(</span><span class="s">&quot;^사용자 입력: &#39;(.+)&#39;$&quot;</span><span class="o">)</span>
</span><span class="line">    <span class="kd">public</span> <span class="kt">void</span> <span class="err">사용자</span><span class="n">_</span><span class="err">입력</span><span class="n">_</span><span class="o">(</span><span class="n">String</span> <span class="n">input</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
</span><span class="line">        <span class="n">inputWriter</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">input</span><span class="o">);</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">    <span class="o">..</span> <span class="err">후략</span><span class="o">...</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li><em>JUnit</em>과 유사하게 <code>@cucumber.api.java.Before</code>, <code>@cucumber.api.java.After</code> 어노테이션으로 
  Step Definition 클래스에 대한 <code>setUp</code>, <code>tearDown</code> 코드를 쓸 수 있다. </li>
  <li>Step Defnition 클래스는 일반적인 Java 구현이므로, Spring DI를 사용하거나, <code>mockito</code> 역시 사용할 수 있다. </li>
</ul>

<h2 id="section">문서로써의 테스트 결과</h2>

<p>Cucumber의 feature는 <strong>test automation</strong>으로써의 기능 외에도 <strong>문서</strong>의 기능도 가진다. 
따라서 프로젝트 관계자들이 모두 그 결과를 보고 검토할 수 있도록 CI 툴과 연계하는 것이 좋다. </p>

<p>Jenkins를 사용한다면 <a href="https://github.com/masterthought/jenkins-cucumber-jvm-reports-plugin-java">cucumber-report</a> 플러그인을 사용하여 보다 fancy한 결과를 얻을 수 있다. </p>

<h2 id="section-1">참고할만한 것들</h2>
<ul>
  <li><a href="http://cukes.info/step-definitions.html">Step Definitions 용례</a></li>
  <li><a href="https://github.com/cucumber/cucumber/tree/master/examples/i18n">Cucumber 다국어 예제</a></li>
  <li><a href="http://blog.codeship.io/2013/05/21/Testing-Tuesday-6-Top-5-Cucumber-best-practices.html">Top5 Cucumber Best Practices</a></li>
  <li><a href="https://github.com/odd-poet/cucumber-sample">자작 Cucumber+Spring Sample</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[ruby 2.0.0-p0 릴리즈]]></title>
    <link href="http://oddpoet.net/blog/2013/02/25/ruby-2-dot-0-0-is-released/"/>
    <updated>2013-02-25T12:19:00+09:00</updated>
    <id>http://oddpoet.net/blog/2013/02/25/ruby-2-dot-0-0-is-released</id>
    <content type="html"><![CDATA[<p>오늘 ruby 2.0의 첫 안정화 버전인 <code>ruby 2.0.0-p0</code>이 릴리즈 되었다: <a href="http://www.ruby-lang.org/en/news/2013/02/24/ruby-2-0-0-p0-is-released/">공식홈페이지</a></p>

<p>그와 발맞춰 일명 ‘곡괭이책’-<a href="http://pragprog.com/book/ruby4/programming-ruby-1-9-2-0">Programming ruby 1.9 &amp; 2.0 (4th ed)</a> 역시 업데이트되었다. </p>

<!-- more -->

<h3 id="ruby-20--"><code>ruby 2.0</code>의 새로운 기능</h3>

<dl>
  <dt>keyword arguement</dt>
  <dd>
    <p>기존에 hash로 named argument처럼 쓰는 것과 비슷해보이지만, 정의한 keyword arguement가 넘겨지지 않으면 에러가 발생한다. </p>
  </dd>
  <dt>Module#prepend</dt>
  <dd>
    <p><code>Module#include</code>는 include되는 모듈의 메소드를 클래스의 메소드가 
override 하지만, <code>Module#prepend</code>는 반대로 클래스의 메소드를 
모듈의 메소드가 override하게 된다. 즉, 모듈 메소드를 <code>call chain</code> 앞에 
추가하는 형태가 된다. </p>
  </dd>
  <dd>
    <p>아래 예를 보면 <code>Module#include</code>와 <code>Module#prepend</code>의 차이를 확인할 수 있다. </p>
  </dd>
</dl>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="ruby"><span class="line"><span class="k">module</span> <span class="nn">A</span><span class="p">;</span> <span class="k">end</span>
</span><span class="line"><span class="k">module</span> <span class="nn">B</span><span class="p">;</span> <span class="k">end</span>
</span><span class="line"><span class="k">class</span> <span class="nc">C</span>
</span><span class="line">    <span class="kp">include</span> <span class="n">A</span>
</span><span class="line">    <span class="n">prepend</span> <span class="n">B</span>
</span><span class="line"><span class="k">end</span>
</span><span class="line">
</span><span class="line"><span class="n">C</span><span class="o">.</span><span class="n">ancestors</span> <span class="c1">#=&gt; [B, C, A, Object, Kernel, BasicObject] </span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<dl>
  <dt>Refinements</dt>
  <dd>
    <p>Rails도 그렇고 ruby 커뮤니티는 기본 core lib나 stdlib의 클래스를 열어서 기존 메소드를 변경하거나 확장하는 경우가 종종 있는데(e.g. <code>3.days.ago</code>), 이런 류의 변경을 Module scope 내에서만 적용할 수 있도록 <code>Module#refine</code> 메소드가 추가됨</p>
  </dd>
</dl>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
</pre></td><td class="code"><pre><code class="ruby"><span class="line"><span class="k">module</span> <span class="nn">NumberQuery</span>
</span><span class="line">    <span class="n">refine</span> <span class="nb">String</span> <span class="k">do</span>
</span><span class="line">        <span class="k">def</span> <span class="nf">number?</span>
</span><span class="line">            <span class="n">match</span><span class="p">(</span><span class="sr">/^[1-9][0-9]+$/</span><span class="p">)</span> <span class="p">?</span> <span class="kp">true</span> <span class="p">:</span> <span class="kp">false</span>
</span><span class="line">        <span class="k">end</span>
</span><span class="line">    <span class="k">end</span>
</span><span class="line"><span class="k">end</span>
</span><span class="line">
</span><span class="line"><span class="s2">&quot;123&quot;</span><span class="o">.</span><span class="n">number?</span>  <span class="c1">#=&gt; NoMethodError</span>
</span><span class="line">
</span><span class="line"><span class="k">module</span> <span class="nn">NumberQuery</span>
</span><span class="line">    <span class="nb">p</span> <span class="s2">&quot;123&quot;</span><span class="o">.</span><span class="n">number?</span>  <span class="c1">#=&gt; true</span>
</span><span class="line"><span class="k">end</span>
</span><span class="line">
</span><span class="line"><span class="k">module</span> <span class="nn">MyApp</span>
</span><span class="line">    <span class="n">using</span> <span class="no">NumberQuery</span>
</span><span class="line">
</span><span class="line">    <span class="nb">p</span> <span class="s2">&quot;123&quot;</span><span class="o">.</span><span class="n">number?</span>  <span class="c1">#=&gt; true</span>
</span><span class="line">    <span class="nb">p</span> <span class="s2">&quot;foo&quot;</span><span class="o">.</span><span class="n">number?</span>  <span class="c1">#=&gt; false</span>
</span><span class="line"><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<dl>
  <dt>symbol list creation</dt>
  <dd>
    <p>심볼 배열을 생성할 수 있는 새로운 literal 표현(<code>%i</code>)이 추가됨 </p>
  </dd>
</dl>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="ruby"><span class="line"><span class="c1"># Ruby 1.9:</span>
</span><span class="line"><span class="no">KEYS</span> <span class="o">=</span> <span class="o">[</span><span class="ss">:foo</span><span class="p">,</span> <span class="ss">:bar</span><span class="p">,</span> <span class="ss">:baz</span><span class="o">]</span>
</span><span class="line">
</span><span class="line"><span class="c1"># Ruby 2.0:</span>
</span><span class="line"><span class="no">KEYS</span> <span class="o">=</span> <span class="o">%</span><span class="n">i</span><span class="p">{</span><span class="n">foo</span> <span class="n">bar</span> <span class="n">baz</span><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<dl>
  <dt>__dir__</dt>
  <dd>
    <p>현재 실행 중인 파일의 경로를 <code>File.dirname(__FILE__)</code>로 쓸 필요가 없어짐. 이제부터는 <code>__dir__</code>을 사용하면 됨. (지금까지 왜 없었는지 의문임)</p>
  </dd>
  <dt>UTF-8 default encoding</dt>
  <dd>
    <p>이제 파일의 default encoding을 UTF-8로 처리함. 파일 첫라인에 매번 <code>#encoding: UTF-8</code>을 써줄 필요가 없음. </p>
  </dd>
</dl>

<h3 id="section">나머지…</h3>

<p>그외의 자세한 변경사항은 <a href="http://www.ruby-lang.org/en/news/2013/02/24/ruby-2-0-0-p0-is-released/">릴리즈 노트</a>를 볼 것. </p>

<p>아래 아티클에서는 Ruby 2.0의 변경점을 예제 코드로 설명하고 있으니 참고.</p>

<ul>
  <li><a href="http://globaldev.co.uk/2012/11/ruby-2-0-0-preview-features/">Preview of the new features in Ruby 2.0.0</a></li>
  <li><a href="http://blog.marc-andre.ca/2013/02/23/ruby-2-by-example/">Ruby 2.0.0 by Example</a></li>
</ul>

<h3 id="section-1">1.9와의 호환성</h3>

<p><code>ruby 1.9</code>가 <code>ruby 1.8</code>과 <code>ruby 2.0</code> 사이의 과도기적 버전이다 보니, 
1.8에서 1.9로 올라올 때만큼의 호환성 문제를 만들것 같지는 않음. </p>

<p><code>ruby 1.9</code>와의 하위 호환에는 큰문제는 없어보이지만 tricky한 코드를 많이 썼다면 관련문서를 확인해볼 필요가 있음. </p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[ssh ProxyCommand]]></title>
    <link href="http://oddpoet.net/blog/2013/01/29/ssh-proxycommand/"/>
    <updated>2013-01-29T11:09:00+09:00</updated>
    <id>http://oddpoet.net/blog/2013/01/29/ssh-proxycommand</id>
    <content type="html"><![CDATA[<p>ssh로 서버에 접근할 때 많은 회사들이 Gateway를 통해서만 접속이 가능하도록 제한한다. 
ssh의 ProxyCommand 설정을 이용하여 Gateway에 접속하는 과정을 줄여보자. </p>

<!-- more -->

<h2 id="ssh-key-">ssh key 추가</h2>

<p>로컬 머신의 공개키(e.g. <code>~/.ssh/id_rsa.pub</code>)를 Gateway서버와 접근하려는 서버의 
<code>~/.ssh/authorized_keys</code> 파일에 추가한다. <code>~/.ssh/authorized_keys</code> 파일이 없으면 
생성해서 추가한다. 단, 파일 퍼미션은 600으로 준다. </p>

<h2 id="ssh-config">ssh config</h2>

<p>로컬 머신에 ~/.ssh/config 파일에 아래와 같은 내용을 넣는다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class=""><span class="line">Host target.server
</span><span class="line">   User irteam
</span><span class="line">   ProxyCommand ssh oddpoet@gateway.server nc %h %p
</span><span class="line">
</span><span class="line">Host *.dev.server
</span><span class="line">   User irteam
</span><span class="line">   ProxyCommand ssh oddpoet@gateway.server nc %h %p
</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li><code>Host</code> : 특정 서버를 지정한다. ‘*‘로 이름패턴을 사용할 수도 있다. </li>
  <li><code>User</code> : 타겟서버에 로그인할 계정이름이다. </li>
  <li><code>ProxyCommand</code> : Gateway 서버 호스트명과 계정명을 사용한다. </li>
</ul>

<p>다음과 같이 직접 접근하는 것처럼 사용할 수 있다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nv">$ </span>ssh target.server <span class="c"># 타겟서버에 접속 (irteam 계정)</span>
</span><span class="line"><span class="nv">$ </span>ssh irteamsu@target.server <span class="c"># 계정을 별도 지정 (irteamsu 계정)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="netcat---">netcat을 쓰지 않는 방법들</h2>

<h3 id="openssh--54">OpenSSH &gt;= 5.4</h3>

<p>OpenSSH 5.4 이상을 쓰는 경우 <code>-W</code> 옵션을 이용해서 SSH의 netcat mode를 쓸 수 있다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line">Host target.server
</span><span class="line">   User irteam
</span><span class="line">   ProxyCommand ssh oddpoet@gateway.server -W <span class="s2">&quot;%h:%p&quot;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="netcat--">netcat이 없을 경우</h3>

<p>Gateway 서버에 <code>netcat</code>이 설치되어 있지 않은 경우에는 아래처럼 설정할 수 있다.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line">Host target.server
</span><span class="line">   User irteam
</span><span class="line">   ProxyCommand ssh oddpoet@gateway.server <span class="s2">&quot;/bin/bash -c &#39;exec 3&lt;&gt;/dev/tcp/%h/%p; cat &lt;&amp;3 &amp; cat &gt;&amp;3;kill $!&#39;&quot;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[토너먼트 시스템]]></title>
    <link href="http://oddpoet.net/blog/2012/10/29/tournament-systems/"/>
    <updated>2012-10-29T14:25:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/10/29/tournament-systems</id>
    <content type="html"><![CDATA[<p>다수가 승부를 겨루는 토너먼트 방식들 </p>

<!-- more -->

<h2 id="single-elimination-tournament">Single elimination tournament</h2>
<p>가장 일반적인 토너먼트 방식. 서든 데스(sudden death)라고도 한다. 
1위를 결정하는데에는 효과적이지만 단1패만 해도 탈락하게 되므로 나머지 순위에 대해서는 
불명확한 방식이다. </p>

<p>참고 : <a href="http://ko.wikipedia.org/wiki/%EC%8B%B1%EA%B8%80_%EC%97%98%EB%A6%AC%EB%AF%B8%EB%84%A4%EC%9D%B4%EC%85%98_%ED%86%A0%EB%84%88%EB%A8%BC%ED%8A%B8">싱글 엘리미네이션</a>, <a href="http://en.wikipedia.org/wiki/Single-elimination_tournament">Single elimination</a></p>

<h2 id="double-elimination-tournament">Double elimination tournament</h2>
<p>싱글 엘리미네이션과 유사하지만 1패 시에는 패자조로 옮겨서 다른 경기의 패자와 싱글 엘리미네이션 방식의 토너먼트를 치룬다. 1패를 하더라도 나머지 경기를 승리하면 최종 우승이 가능하다. </p>

<p>참고 : <a href="http://ko.wikipedia.org/wiki/%EB%8D%94%EB%B8%94_%EC%97%98%EB%A6%AC%EB%AF%B8%EB%84%A4%EC%9D%B4%EC%85%98_%ED%86%A0%EB%84%88%EB%A8%BC%ED%8A%B8">더블 엘리미네이션 토너먼트</a></p>

<h2 id="swiss-system-tournament">Swiss system tournament</h2>
<p>첫 1 라운드에는 무작위로 경기를 치루게 되고 2라운드에서는 1-2등, 3-4등 끼리 대전하게 된다. 그 이후에는 성적이 비슷한 사람끼리 경기를 치루게 되고 모든 경기에서 승리한 사람이 1명 나올 때까지 라운드를 진행한다.</p>

<p>참고 : <a href="http://en.wikipedia.org/wiki/Swiss_tournament">Swiss-system tournament</a>, <a href="http://jeminency.tistory.com/159">스위스시스템 토너먼트</a></p>

<h2 id="round-robin-tournament">Round-Robin tournament</h2>
<p>모든 팀과 한번씩은 승부를 하게 하는 방식이다. N개의 팀이 있으면 N-1 번의 라운드가 진행된다. 보통 리그전이라고 부르는 방식이다. </p>

<p>참고 : <a href="http://en.wikipedia.org/wiki/Round-robin_tournament">Round-robin tournament</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[git 명령어 요약]]></title>
    <link href="http://oddpoet.net/blog/2012/10/26/git-command-summary/"/>
    <updated>2012-10-26T18:31:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/10/26/git-command-summary</id>
    <content type="html"><![CDATA[<p><a href="http://dogfeet.github.com/articles/2012/progit.html">Pro Git 번역판</a>을 보다가 자주 안써서 맨날 까먹는 명령들만 정리한다. </p>

<!-- more -->

<h2 id="diff">diff</h2>

<ul>
  <li>
    <dl>
      <dt><code>git diff</code></dt>
      <dd>staged가 아닌 파일들에 대한 diff 결과 출력</dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git diff --cached</code></dt>
      <dd>staged 파일들에 대한 diff 결과 출력</dd>
    </dl>
  </li>
</ul>

<h2 id="log">log</h2>

<ul>
  <li>
    <dl>
      <dt><code>git log --pretty=format:"%h %s" --graph</code></dt>
      <dd>아스키 그래프와 함께 히스토리 출력 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git log --since=2.weeks</code></dt>
      <dd>2주 내의 커밋만 조회 </dd>
    </dl>
  </li>
</ul>

<h2 id="undo">undo</h2>

<ul>
  <li>
    <dl>
      <dt><code>git commit --amend</code></dt>
      <dd>직전 커밋을 수정하는 경우</dd>
      <dd>변경된 파일이 없을 경우 직전 커밋의 메시지만 수정 </dd>
      <dd>변경된 파일이 있을 경우 직전 커밋에 변경 내역 추가 (하나의 커밋으로 처리됨)</dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git reset HEAD &lt;file&gt;</code></dt>
      <dd>staging area의 파일을 unstange. </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git checkout -- &lt;file&gt;</code></dt>
      <dd>수정된 파일을 원복</dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git clean -f</code></dt>
      <dd>untracked file 삭제</dd>
    </dl>
  </li>
</ul>

<h2 id="remote">remote</h2>

<ul>
  <li>
    <dl>
      <dt><code>git remote -v</code></dt>
      <dd>리모트 저장소들을 URL과 함께 출력 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git remote show origin</code></dt>
      <dd>특정 원격 저장소의 정보를 출력 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git remote add &lt;name&gt; &lt;url&gt;</code></dt>
      <dd>주어진 URL의 리모트 저장소를 추가 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git remote rename &lt;old_name&gt; &lt;new_name&gt;</code></dt>
      <dd>원격 저장소 이름 변경 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git remote rm &lt;name&gt;</code></dt>
      <dd>원격 저장소를 제거 </dd>
    </dl>
  </li>
</ul>

<h2 id="tag">tag</h2>

<ul>
  <li>
    <dl>
      <dt><code>git tag -a v1.4 -m 'my version 1.4'</code></dt>
      <dd>annotated tag 생성 (-a 옵션)</dd>
      <dd>일반 태그와는 달리 메시지와 tag 생성자의 정보도 저장된다. </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git show &lt;tag_name&gt;</code></dt>
      <dd>tag의 정보 및 커밋 정보 확인 </dd>
    </dl>
  </li>
</ul>

<h2 id="branch">branch</h2>

<ul>
  <li>
    <dl>
      <dt><code>git checkout -b iss53</code></dt>
      <dd><code>iss533</code>라는 이름의 브랜치를 생성하고 checkout. </dd>
      <dd>아래 명령과 동일 </dd>
    </dl>
  </li>
</ul>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class=""><span class="line">git branch iss53
</span><span class="line">git checkout iss53</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li>
    <dl>
      <dt><code>git branch -d &lt;branch_name&gt;</code></dt>
      <dd>브랜치 삭제 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git branch --merged | --no-merged</code></dt>
      <dd>현재 브랜치를 기준으로 merge된(혹은 merge되지 않은) 브랜치를 확인. </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git push &lt;remote&gt; &lt;branch&gt;</code></dt>
      <dd>리모트 저장소에 특정 브랜치를 push(처음인 경우 원격저장소에 브랜치 생성됨)</dd>
      <dd>로컬 저장소의 브랜치와 다른 이름으로 push하려면 
<code>git push &lt;remote&gt; &lt;local_branch&gt;:&lt;remote_branch&gt;</code>를 사용. </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git push &lt;remote&gt; :&lt;remote_branch&gt;</code></dt>
      <dd>원격 브랜치를 삭제. </dd>
    </dl>
  </li>
</ul>

<h2 id="rebase">rebase</h2>

<p>rebase는 merge와 달리 두 branch를 선형으로 합친다. </p>

<p><strong>주의</strong> : 공개 저장소에 push 커밋을 rebase하지 말 것.</p>

<ul>
  <li>
    <dl>
      <dt><code>git rebase &lt;master_branch&gt; &lt;topic_branch&gt;</code></dt>
      <dd><code>&lt;topic_branch&gt;</code>를 <code>&lt;master_branch&gt;</code>로 rebase한다. </dd>
    </dl>
  </li>
</ul>

<h2 id="section">배포</h2>

<ul>
  <li>
    <dl>
      <dt><code>git describe master</code></dt>
      <dd>가장 가까운 annotated tag로 부터 빌드 넘버를 생성한다. </dd>
      <dd>e.g. <code>v1.6.2-rc1-20-g8c5b85c</code></dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git archive master --prefix='project/' | greph &gt; `git describe master`.tar.gz</code></dt>
      <dd>소스코드의 snapshot 압축 파일 생성 (이메일 혹은 웹사이트 배포용)</dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git shortlog --no-merges master --not v1.0.1</code></dt>
      <dd>가장 최근 릴리즈(v1.0.1) 이후의 커밋 요약 정보 출력 </dd>
    </dl>
  </li>
</ul>

<h2 id="stashing">stashing</h2>

<p>작업 도중 commit 하지 않고, 브랜치 변경해야 할 때 작업내역 임시 저장. 
(modified, staged 모두 보관 )</p>

<ul>
  <li>
    <dl>
      <dt><code>git stash</code></dt>
      <dd>작업 중인 파일 보관 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git stash list</code></dt>
      <dd>저장된 stash 목록 확인 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git stash apply &lt;stash_name&gt;</code></dt>
      <dd>stash 적용. <code>stash_name</code>이 없으면 가장 최근 stash 사용. </dd>
      <dd><code>--index</code> 옵션을 사용하면 staged 파일들을 staged 상태로 만들어 줌. </dd>
    </dl>
  </li>
</ul>

<h2 id="blame">blame</h2>

<ul>
  <li>
    <dl>
      <dt><code>git blame -L &lt;begin_line&gt;,&lt;end_line&gt; &lt;file&gt;</code></dt>
      <dd>각 라인을 누가 수정했는지 확인. </dd>
      <dd><code>-C</code> 옵션을 주면 파일 이름 출력. (파일 이름이 변경된 경우)</dd>
    </dl>
  </li>
</ul>

<h2 id="config">config</h2>

<ul>
  <li>
    <dl>
      <dt><code>git config --global user.name "John Doe"</code></dt>
      <dd>사용자 이름 설정 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git config --global user.email johndoe@example.com</code></dt>
      <dd>이메일 설정 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git config --global core.editor vim</code></dt>
      <dd>기본 편집기 설정 </dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git config --global color.ui true</code></dt>
      <dd>콘솔 출력에서 컬러 사용</dd>
    </dl>
  </li>
  <li>
    <dl>
      <dt><code>git config --global core.autocrlf input</code></dt>
      <dd>MS 윈도우에서의 개행문자가 문제되는 경우 </dd>
      <dd>개행문자를 윈도우에서는 CRLF 사용. mac, linux, 저장소는 LF 사용함 </dd>
    </dl>
  </li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[markdown processors]]></title>
    <link href="http://oddpoet.net/blog/2012/07/16/markdown-processors/"/>
    <updated>2012-07-16T03:41:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/16/markdown-processors</id>
    <content type="html"><![CDATA[<p>요즘 <a href="http://octopress.org (Octopress)">octopress</a>에 꽂혀서 <a href="http://daringfireball.net/projects/markdown/">markdown</a>관련 툴들을 이것 저것 보고 있는데 뭔가 많다. 
프리뷰 지원하는 전용 에디터와 전용 뷰어 등도 구입해서 써보는 중인데 
markdown 처리기가 많다보니 적절한 조합을 찾는 것도 쉽지 않다. 
이번 포스트에서는 markdown 처리기들을 정리해본다. </p>

<!--more-->

<p>John Gruber의 <a href="http://daringfireball.net/projects/markdown/">Standard markdown</a> 외에도 processor 구현체에 따른 확장 문법들이 존재하는데
그 수도 적지 않고 또한 자잘한 차이점들이 있다. </p>

<dl>
  <dt><a href="http://daringfireball.net/projects/markdown/">Standard Markdown</a></dt>
  <dd>John Gruber가 만든 오리지널이다. 홈페이지에 보면 markdown 문법에 대한 설명과 perl로 만든 html 변환 툴을 제공하고 있다. </dd>
  <dd>markdown은 간결한게 장점이라지만 definition list, table 등에 대한 syntax가 없는게 개인적으로 아쉽다. </dd>
  <dt><a href="http://michelf.com/projects/php-markdown/extra/ (PHP Markdown Extra)">PHP Markdown Extra</a></dt>
  <dd>표준 Markdown에 fenced code block, definition list, table, footnote 등의 문법을 확장했다. 이 후 대부분의 markdown processor들이 이것을 언급할 만큼 markdown 확장의 완전판에 가깝다. 
PHP 구현체라는 점 때문에 실제 많이 사용되고 있지는 않은듯.</dd>
  <dt><a href="http://fletcherpenney.net/multimarkdown/ (Multimarkdown)">Multimarkdown</a></dt>
  <dd>HTML 외에도 LaTex, PDF, OpenDocument, OPML 등 다양한 포맷으로 변환을 제공하는 markdown processor다. </dd>
  <dd>PHP Markdown extra 와 비슷한 수준의 확장 문법을 제공하는 듯.</dd>
  <dd>다양한 포맷 지원 외에도 c구현체의 command line 툴이라 많이 사용되는 듯하다. </dd>
  <dt><a href="http://github.github.com/github-flavored-markdown/ (Github Flavored Markdown)">Github Flavored Markdown</a></dt>
  <dd>Github에서 코드 snippet 들을 embed 하기 편하도록 확장한 문법 </dd>
  <dd>syntax highlighting을 위해서 fenced code block 문법에 언어를 명시할 수 있도록 했음</dd>
  <dt><a href="http://maruku.rubyforge.org/">Maruku</a></dt>
  <dd>입력으로는 PHP markdown extra 및 기타 확장을 포함 markdown 뿐만아니라 textile까지 지원하고, 출력은 HTML, LaTex, PDF, textile, markdown 을 지원하겠다는 야심찬 툴. </dd>
  <dd>많이 쓰이는지는 모르겠음. </dd>
  <dt><a href="http://kramdown.rubyforge.org/ (kramdown)">kramdown</a></dt>
  <dd>이 녀석도 왠만한 확장 문법은 다 지원하고 게다가 ruby 구현체 중에서 제일 빠르다는 markdown processor. </dd>
  <dd>markdown 관련 툴들 중에 ruby로 만들어진게 많은 편인데 느낌상으로는 이게 앞으로 대세가 될 듯. </dd>
  <dd>ruby 구현체로는 <a href="http://ruby.morphball.net/bluefeather/index_en.html (BlueFeather)">BlueFeather</a>, <a href="https://github.com/rtomayko/rdiscount/ (rdiscount)">rdiscount</a> 등도 있지만 이게 4~5배 빠르다니까 나머지는 생략. </dd>
  <dt><a href="https://github.com/mojombo/jekyll/wiki/">jekyll</a></dt>
  <dd>markdown 계의 완소툴!</dd>
  <dd>단순히 markdown 파일을 html로 변환해주는게 아니라 markdown으로 작성한 문서들을 처리해서 사이트를 만들 수 있게 해준다. </dd>
  <dd>요즘 댓글 등의 기능이 사이트 기능이 아니라 SNS 서비스 연동으로 가는게 대세이다 보니 jekyll을 이용해서 DB없이 static한 파일들로 site를 만들고 운영하는게 가능해졌다. </dd>
  <dd>보통 jekyll로 사이트를 생성하고 github page에 올려서 블로그를 운영하는게 요즘 geek들의 대세인듯. </dd>
  <dd>markdown 처리기로 <a href="https://github.com/rtomayko/rdiscount/ (rdiscount)">rdiscount</a>, redcarpet, kramdown 를 사용한다.</dd>
  <dt><a href="http://octopress.org (Octopress)">octopress</a></dt>
  <dd><strong>“Hacker들을 위한 블로그 프레임웍”</strong>이라는 부제를 달고 있는 툴</dd>
  <dd><a href="https://github.com/mojombo/jekyll/wiki/">jekyll</a> 기반으로 만들어진 툴인데 jekyll이 생성한 파일들을 배포하는 기능을 제공한다. GitHub page, Heroku 로의 배포를 지원하며 rsync 이용하여 호스팅 받는 서버로의 배포도 가능하다. </dd>
  <dd>검색해보면 Wordpress에서 Octopress로 갈아탄 사람이 꽤나 있는 것 같다 : <a href="http://codebrief.com/2012/01/25-reasons-to-switch-from-wordpress-to-octopress/">25 Reasons to Switch From WordPress to Octopress</a></dd>
  <dd>물론 다시 Wordpress로 돌아온 사람도 있다 ^^ : <a href="http://www.kevindangoor.com/2012/03/wordpress-to-octopress-and-back/">WordPress to Octopress and Back</a></dd>
</dl>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[bash tips]]></title>
    <link href="http://oddpoet.net/blog/2012/07/16/bash-tips/"/>
    <updated>2012-07-16T03:29:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/16/bash-tips</id>
    <content type="html"><![CDATA[<p>bash scripting 관련 조각팁 모음 (updated 2012-07-19)</p>

<!-- more -->

<h2 id="var1-var2"><code>${var1:-var2}</code></h2>

<p>bash script에 <code>${VAR1:-VAR2}</code> 의 표현을 종종 봤는데 오늘에야 뭔지 알았다. </p>

<p>다른 스크립트 언어에서 <code>var1 || var2</code> 형태와 같은 표현이다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="k">${</span><span class="nv">TM_MARKDOWN</span><span class="k">:-</span><span class="nv">markdown</span><span class="p">.pl</span><span class="k">}</span> <span class="nv">$filename</span>
</span><span class="line"><span class="c"># TM_MARKDOWN이 정의되어 있지 않으면 &#39;markdown.pl&#39;을 실행.</span>
</span><span class="line">
</span><span class="line"><span class="nv">INPUT</span><span class="o">=</span><span class="k">${</span><span class="nv">INPUT</span><span class="k">:-</span><span class="nv">$DEFAULT</span><span class="k">}</span>
</span><span class="line"><span class="c"># INPUT이 정의되어 있지 않으면 DEFAULT 값을 쓰라는.. </span>
</span><span class="line"><span class="c"># 다른 언어에서 &#39;input ||= default&#39; 과 같은 의미임. </span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="source-scriptsh-or--scriptsh"><code>source script.sh</code> or <code>. script.sh</code></h2>

<p><code>source</code> 명령은 현재 프로세스에서 다른 스크립트를 실행하는 명령이다. 
현재 프로세스에서 실행하므로 스크립트에 의한 환경변수들의 생성/수정에 영향을 받게 된다. 
주의할 점은 shebang<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>은 무시된다는 점이다. </p>

<p>일반적으로 다른 스크립트 파일을 include하는데에 사용한다. 혹은 <code>.bashrc</code> 같은 파일을 수정했을때 
다시 로그인 하지 않고 <code>source .bashrc</code> 와 같은 방법으로 다시 <code>.bashrc</code>를 로딩한다. </p>

<p><code>source script.sh</code>대신 <code>. script.sh</code>로 줄여 쓸 수 있다. </p>

<h2 id="cursor-">cursor 제어</h2>

<p><code>tput</code> 명령을 이용해서 cursor를 제어할 수 있다. </p>

<dl>
  <dt><code>tput sc</code></dt>
  <dd>현재 커서 위치 저장</dd>
  <dt><code>tput rc</code></dt>
  <dd>커서 위치 복구 (저장했던 위치로 커서가 이동함)</dd>
  <dt><code>tput cub1</code></dt>
  <dd>커서를 왼쪽으로 한칸 이동한다.</dd>
  <dt><code>tput cuf1</code></dt>
  <dd>커서를 오른쪽으로 한칸 이동한다. </dd>
  <dt><code>tput ll</code></dt>
  <dd>마지막 라인의 첫번째 위치로 이동한다. </dd>
  <dt><code>tput ed</code></dt>
  <dd>현재 위치부터 화면 마지막 까지 clear한다. </dd>
  <dt><code>tput lines</code></dt>
  <dd>터미널의 라인 수를 출력한다. </dd>
  <dt><code>tput cols</code></dt>
  <dd>터미널의 컬럼 수를 출력한다. </dd>
</dl>

<ul>
  <li>참고 : <a href="http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html">Colours and Cursor Movement With tput</a></li>
</ul>

<h2 id="color-">color 제어</h2>

<p><code>tput</code>을 이용해서도 터미널 컬러 제어가 가능하지만 <code>echo -e</code>와 escape를 이용하는것이 보다 편하다. </p>

<p><code>\x1b</code> 대신 <code>\e</code>나 <code>\033</code>를 사용해도 되지만 mac osx에서 <code>\e</code>는 정상작동하지 않는다. 
그리고 출력시에는 <code>echo -e</code>로 출력해야 한다. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nv">black</span><span class="o">=</span><span class="s2">&quot;\x1b[30m&quot;</span>
</span><span class="line"><span class="nv">red</span><span class="o">=</span><span class="s2">&quot;\x1b[31m&quot;</span>
</span><span class="line"><span class="nv">green</span><span class="o">=</span><span class="s2">&quot;\x1b[32m&quot;</span>
</span><span class="line"><span class="nv">yellow</span><span class="o">=</span><span class="s2">&quot;\x1b[33m&quot;</span>
</span><span class="line"><span class="nv">blue</span><span class="o">=</span><span class="s2">&quot;\x1b[34m&quot;</span>
</span><span class="line"><span class="nv">magenta</span><span class="o">=</span><span class="s2">&quot;\x1b[35m&quot;</span>
</span><span class="line"><span class="nv">cyan</span><span class="o">=</span><span class="s2">&quot;\x1b[36m&quot;</span>
</span><span class="line"><span class="nv">white</span><span class="o">=</span><span class="s2">&quot;\x1b[37m&quot;</span>
</span><span class="line"><span class="nv">color_off</span><span class="o">=</span><span class="s2">&quot;\x1b[39m&quot;</span>
</span><span class="line">
</span><span class="line"><span class="nv">bg_black</span><span class="o">=</span><span class="s2">&quot;\x1b[40m&quot;</span>
</span><span class="line"><span class="nv">bg_red</span><span class="o">=</span><span class="s2">&quot;\x1b[41m&quot;</span>
</span><span class="line"><span class="nv">bg_green</span><span class="o">=</span><span class="s2">&quot;\x1b[42m&quot;</span>
</span><span class="line"><span class="nv">bg_yellow</span><span class="o">=</span><span class="s2">&quot;\x1b[43m&quot;</span>
</span><span class="line"><span class="nv">bg_blue</span><span class="o">=</span><span class="s2">&quot;\x1b[44m&quot;</span>
</span><span class="line"><span class="nv">bg_magenta</span><span class="o">=</span><span class="s2">&quot;\x1b[45m&quot;</span>
</span><span class="line"><span class="nv">bg_cyan</span><span class="o">=</span><span class="s2">&quot;\x1b[46m&quot;</span>
</span><span class="line"><span class="nv">bg_white</span><span class="o">=</span><span class="s2">&quot;\x1b[47m&quot;</span>
</span><span class="line"><span class="nv">bg_color_off</span><span class="o">=</span><span class="s2">&quot;\x1b[49m&quot;</span>
</span><span class="line">
</span><span class="line"><span class="nv">bold</span><span class="o">=</span><span class="s2">&quot;\x1b[1m&quot;</span>
</span><span class="line"><span class="nv">bold_off</span><span class="o">=</span><span class="s2">&quot;\x1b[22m&quot;</span>
</span><span class="line"><span class="nv">underline</span><span class="o">=</span><span class="s2">&quot;\x1b[4m&quot;</span>
</span><span class="line"><span class="nv">underline_off</span><span class="o">=</span><span class="s2">&quot;\x1b[24m&quot;</span>
</span><span class="line">
</span><span class="line"><span class="nb">echo</span> -e <span class="s2">&quot;${red}Hello${color_off} ${bg_yellow}World${bg_color_off}&quot;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li>참고 : <a href="http://www.linuxselfhelp.com/howtos/Bash-Prompt/Bash-Prompt-HOWTO-6.html">ANSI Escape Sequences: Colours and Cursor Movement</a></li>
</ul>
<div class="footnotes">
  <ol>
    <li id="fn:1">
      <p>스크립트 첫 라인의 <code>#!/bin/bash</code> 같은 command를 지정하는 주석라인<a href="#fnref:1" rel="reference">&#8617;</a></p>
    </li>
  </ol>
</div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[HTML-to-Markdown]]></title>
    <link href="http://oddpoet.net/blog/2012/07/15/html-to-markdown/"/>
    <updated>2012-07-15T17:14:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/15/html-to-markdown</id>
    <content type="html"><![CDATA[<p>기존 블로그를 [octopress]로 이주할 때 사용할 만한 <strong>HTML to Markdown</strong> 변환 툴들. </p>

<!-- more -->

<dl>
  <dt><a href="http://heckyesmarkdown.com/">heckyesmarkdown</a></dt>
  <dd>웹에서 URL을 입력하면 변환 및 변환결과에 대한 프리뷰를 볼 수 있다. 
블로그의 경우 메뉴/사이드바를 제외한 컨텐츠 부분만 변환해준다. </dd>
  <dt><a href="http://www.aaronsw.com/2002/html2text/">html2text</a></dt>
  <dd>파이썬 스크립트. </dd>
  <dd><a href="http://heckyesmarkdown.com/">heckyesmarkdown</a>와 마찬가지로 웹 변환도 제공하지만, 
script를 받아서 로컬에서 수행할 수 있다는게 장점. 옮길 포스트가 많을 때 좋을 듯.</dd>
  <dd>메뉴/사이드바를 포함한 페이지 전체를 변환한다는게 아쉽다. </dd>
</dl>

<p>둘 다 original <a href="http://daringfireball.net/projects/markdown/">markdown</a> 포맷만 지원하는듯 하다. 
table 등은 정상적으로 변환하지 못한다. 어차피 변환 후 수작업을 피할 수 없으니 큰 문제는 아니다. </p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Gateway-kit]]></title>
    <link href="http://oddpoet.net/blog/2012/07/15/Gateway-kit/"/>
    <updated>2012-07-15T13:34:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/15/Gateway-kit</id>
    <content type="html"><![CDATA[<p>회사의 kerberos gateway 서버에서 사용하기 위한 bash script. 
<!-- more --></p>

<p>ruby 사용이 불가능한 환경의 서버라 bash script 로 작업했는데 익숙치 않아서 삽질을 많이함. </p>

<dl>
  <dt>Manage host list</dt>
  <dd>알고 있는 호스트 목록을 보여주고 커서를 움직여서 선택</dd>
  <dd>호스트 이름을 입력하면 목록 필터링됨</dd>
  <dd>Tab 자동완성 지원</dd>
  <dd>Bash key map 지원</dd>
  <dd>접속에 성공한 호스트는 목록에 자동으로 추가</dd>
  <dd>호스트에 대한 설명을 추가할 수 있음. </dd>
  <dt>Automatic kinit</dt>
  <dd>비밀번호를 <code>~/.kinit_passwd</code> 파일에 써놓으면 실행시 
  자동으로 kinit 인증을 통해 ticket을 발급받는다. </dd>
  <dd><strong>(주의)</strong> 비밀번호를 plain text로 기록하게 되므로 보안 문제가 있을 수 있다. </dd>
  <dd><code>~/.kinit_passwd</code> 파일이 없으면 비밀번호를 입력받음. </dd>
</dl>

<p><img src="http://oddpoet.net/downloads/images/gw-kit.png" /></p>

<p>스크립트는 gist에 있음. <a href="https://gist.github.com/3115022">https://gist.github.com/3115022</a></p>

<div><script src="https://gist.github.com/3115022.js"></script>
<noscript><pre><code>#!/bin/bash
#==============================================================================
# GW-Kit
# 	@author : yunsang.choi(oddpoet@gmail.com)
#	@version : 1.0.0
#
#==============================================================================

#========================================
# Constant for configuration
#========================================
HOST_LIST_FILE=~/.known_hosts
HOST_DISPLAY_COUNT=15
KINIT_PW_FILE=~/.kinit_passwd
USER_LIST_FOR_RLOGIN=(irteam irteamsu root)

VERSION=&quot;1.0.0&quot;
#========================================
# kinit
#========================================
exec_kinit() {
	if [ ! -f $KINIT_PW_FILE ];then
		message=&quot;kinit skipped : &#39;$KINIT_PW_FILE&#39; is not exist! &quot;
		return
	fi
	password=$( &lt; $KINIT_PW_FILE)
	echo &quot;Try to kinit ....&quot;	
	expect -c &quot;
		log_user 0
	        spawn -noecho kinit -V -f `whoami`
		expect \&quot;Password\&quot;
		send \&quot;$password\r\&quot;
	        expect {
			\&quot;Authenticated\&quot; { exit 0;}
			\&quot;Password incorrect\&quot; { exit 1;}
	        }
	        exit 2 
	&quot;
	case $? in
		0) message=&quot;success to kinit&quot;;;
		1) message=&quot;fail to kinit: password incorrect&quot;;;  
		2) message=&quot;fail to kinit: why?&quot;;;
	esac
	echo $message;
}


#========================================
# Read key input
#========================================
function read_key() {
	local kp
	local key
	local _return=$1
	
	# read key completely. 
	read -s -n1 key
	if [ &quot;$key&quot; == $&#39;\e&#39; ];then
		while read -sn1 -t1 kp; do
			key=$key$kp
			case $kp in
				[a-zA-NP-Z~]) break;;
			esac
		done
	fi
	
	# convert special key name
	case &quot;$key&quot; in
		$&#39;\e[A&#39; ) key=&#39;up&#39;;;
		$&#39;\e[B&#39; ) key=&#39;down&#39;;;
		$&#39;\e[C&#39; ) key=&#39;right&#39;;;
		$&#39;\e[D&#39; ) key=&#39;left&#39;;;
		$&#39;\e&#39; ) key=&#39;esc&#39;;;
		$&#39;\e[3~&#39; ) key=&#39;delete&#39;;;
		$&#39;\e[Z&#39; ) key=&#39;shift-tab&#39;;;
		?) key=$key;; # 일반키 
		*) key=&quot;&quot;;; # ignore
	esac
	
	# echo or return. 
	if [[ &quot;$_return&quot; ]]; then
		eval $_return=&quot;&#39;$key&#39;&quot;
	else
		echo $key
	fi
}
#========================================
# Control screen
#========================================
black=&quot;\x1b[30m&quot;
red=&quot;\x1b[31m&quot;
green=&quot;\x1b[32m&quot;
yellow=&quot;\x1b[33m&quot;
blue=&quot;\x1b[34m&quot;
magenta=&quot;\x1b[35m&quot;
cyan=&quot;\x1b[36m&quot;
white=&quot;\x1b[37m&quot;
reset=&quot;\x1b[39m&quot;

bg_black=&quot;\x1b[40m&quot;
bg_red=&quot;\x1b[41m&quot;
bg_green=&quot;\x1b[42m&quot;
bg_yellow=&quot;\x1b[43m&quot;
bg_blue=&quot;\x1b[44m&quot;
bg_magenta=&quot;\x1b[45m&quot;
bg_cyan=&quot;\x1b[46m&quot;
bg_white=&quot;\x1b[47m&quot;
bg_reset=&quot;\x1b[49m&quot;

bold=&quot;\x1b[1m&quot;
bold_off=&quot;\x1b[22m&quot;
underline=&quot;\x1b[4m&quot;
underline_off=&quot;\x1b[24m&quot;

save_cursor() {
	tput sc
}
restore_cursor() {
	tput rc
}
move_cursor() {
	tput cup $1 $2
}

#========================================
# Control user list
#========================================
# var: user list
user_list=(${USER_LIST_FOR_RLOGIN[@]})
# var: selected user
user=${user_list[0]}
# func: change user
next_user() {
	local index=0
	local cnt=${#user_list[@]}
	while true;do
		if [ &quot;$user&quot; == &quot;${user_list[index]}&quot; ]; then
			user=${user_list[(index+1) % cnt]}
			return
		fi
		let &quot;index=index+1 % cnt&quot;
	done
}

#========================================
# Control host list
#========================================
host_list=()
hostname_filter=
filtered_host_list=
selected_host= 

init_host_list() {
	if [ ! -f $HOST_LIST_FILE ];then
		touch $HOST_LIST_FILE
	fi
	host_list=( $( &lt; $HOST_LIST_FILE))
	host_list=($(printf &quot;%s\n&quot; ${host_list[*]} | sort | uniq ))
	filter_host_list
}

add_host_to_list() {
	local host=$1
	if [ -n &quot;$host&quot; ];then
		host_list=(&quot;${host_list[@]}&quot; &quot;$host&quot;)
		host_list=($(printf &quot;%s\n&quot; ${host_list[*]} | sort | uniq ))
	fi
}

save_host_list_file() {
	echo &quot;&quot; &gt; $HOST_LIST_FILE
	for host in ${host_list[@]};do
		echo &quot;$host&quot; &gt;&gt; $HOST_LIST_FILE
	done
}

filter_host_list() {
	filtered_host_list=($( 
		for i in ${host_list[@]};do 
			echo $i
		done | grep &quot;$hostname_filter&quot;
	))
	if [ -z `echo $selected_host | grep &quot;$hostname_filter&quot;` ];then
		selected_host=
	fi
}

host_index_of() {
	local host=$1
	local index=0
	while [ $index -lt ${#filtered_host_list[@]} ];do
		if [ &quot;${filtered_host_list[index]}&quot; == &quot;$host&quot; ];then
			echo $index
			return
		fi
		let &quot;index++&quot;
	done
	echo &quot;&quot;
}

select_next_host() {
	local index
	local cnt=${#filtered_host_list[@]}
	if [ $cnt == 0 ];then
		selected_host=
		return
	elif [ -z &quot;$selected_host&quot; ];then
		selected_host=&quot;${filtered_host_list[0]}&quot;
	else
		index=$(host_index_of $selected_host)
		let &quot;index++&quot;
		if [ $index -lt &quot;${#filtered_host_list[@]}&quot; ];then
			selected_host=&quot;${filtered_host_list[index]}&quot;
		fi
	fi
}

select_prev_host() {
	local index
	local cnt=${#filtered_host_list[@]}
	if [ $cnt == 0 ];then
		selected_host=
		return
	elif [ -z &quot;$selected_host&quot; ];then
		local cnt=${#filtered_host_list[@]}
		selected_host=&quot;${filtered_host_list[cnt-1]}&quot;
	else
		index=$(host_index_of $selected_host)
		let &quot;index--&quot;
		if [ $index -ge 0 ];then
			selected_host=&quot;${filtered_host_list[index]}&quot;
		fi
	fi
}

append_hostname_filter() {
	local ch=$1
	hostname_filter=$hostname_filter$ch
	filter_host_list
}
delete_tail_hostname_filter() {
	local len=${#hostname_filter}
	if [ $len -gt 0 ];then
		hostname_filter=${hostname_filter:0:(len-1)}
		filter_host_list
	fi
}
clear_hostname_filter() {
	hostname_filter=
	filter_host_list
}

#========================================
# Print host list
#========================================
begin_index=0
adjunst_begin_index() {
	local selected=$(host_index_of $selected_host)
	local last_index
	let last_index=begin_index+HOST_DISPLAY_COUNT-1
	if [ -z $selected ];then
		selected=0
	fi
	if [ $selected -lt $begin_index ];then
		begin_index=$selected
	fi
	if [ $selected -gt $last_index ];then
		let begin_index=selected-HOST_DISPLAY_COUNT+1
	fi
}

print_host_list() {
	adjunst_begin_index
	echo &quot;Known hosts (${#filtered_host_list[@]}/${#host_list[@]})&quot;
	local last_index
	let last_index=begin_index+HOST_DISPLAY_COUNT-1
	for (( i= $begin_index; i &lt;= $last_index; i++ ));do
		if [ $i -ge ${#filtered_host_list[@]} ];then
			break
		fi
		local host=&quot;${filtered_host_list[i]}&quot;
		local printmsg=&quot; - $host&quot;
		if [ -n &quot;$hostname_filter&quot; ];then
			local matched=`echo $host | grep -o &quot;$hostname_filter&quot; | head -n 1`
			printmsg=${printmsg/$matched/${underline}$matched${underline_off}} 
		fi
		if [ &quot;$selected_host&quot; == &quot;$host&quot; ];then
			printmsg=${printmsg/ -/&gt;&gt;}
			printmsg=${bg_yellow}${black}$printmsg${reset}${bg_reset}
		fi
		
		echo -e &quot;$printmsg&quot;
	done
}
#========================================
# Logo And Help
#========================================
print_logo(){
	echo -en &quot;${cyan}&quot;
	echo -e &quot;            __________      __           ____  __.______________               &quot;
	echo -e &quot;           /  _____/  \    /  \         |    |/ _|   \__    ___/               &quot;
	echo -e &quot;          /   \  __\   \/\/   /  ______ |      &lt; |   | |    |                  &quot;
	echo -e &quot;          \    \_\  \        /  /_____/ |    |  \|   | |    |                  &quot;
	echo -e &quot;           \______  /\__/\  /           |____|__ \___| |____|                  &quot;
	echo -e &quot;                  \/      \/                    \/           ver$VERSION       &quot;
	echo -e &quot;                                            By oddpoet@gmail.com               &quot;
	echo -en &quot;${reset}&quot;
	echo -e &quot;===============================================================================&quot;
	
}

print_help() {
	echo -en &quot; [up|down]: select prev/next host      &quot;
	echo -e  &quot; [/]: change user     &quot;
	echo -en &quot; [D|left]: delete 1 char from filter   &quot;
	echo -e  &quot; [C|delete]: clear filter       &quot;
	echo -en &quot; [enter]: execute                      &quot;
	echo -e  &quot; [Q|esc]: exit &quot;
	echo -e  &quot; - hostname will be auto save.($HOST_LIST_FILE)&quot;
	echo -e  &quot; - make &#39;$KINIT_PW_FILE&#39; to exec kinit automatically. &quot;
}

print_command() {
	local command
	command=&quot;${command}${magenta}`whoami`@gw-kit&gt; ${reset}&quot;
	command=&quot;${command}${white}rlogin -l ${bold}$user ${yellow}$hostname_filter${reset}${bold_off}&quot;
	
	echo &quot;-------------------------------------------------------------------------------&quot;	
	if [ -n &quot;$message&quot; ];then
		echo -e &quot;${red}${bold}[!] $message${bold_off}${reset}&quot;
		message=
	fi
	echo -en $command
	save_cursor
	echo
	echo &quot;-------------------------------------------------------------------------------&quot;	
}

print_screen() {
	clear
	print_logo
	print_help
	
	print_command
	print_host_list
	restore_cursor
}


#========================================
# Main 
#========================================
do_execute() {
	if [ -n &quot;$selected_host&quot; ];then
		hostname_filter=$selected_host
		selected_host=
		filter_host_list
		return
	fi
	if [ -z &quot;$hostname_filter&quot; ];then
		message=&quot;you should select a host to rlogin!&quot;
		return
	fi
	clear 
	local tmpfile=&quot;/tmp/$(basename $0).$$.tmp&quot;
	`rlogin -l $user $hostname_filter 2&gt; $tmpfile &gt; /dev/tty`
	if [ $? == 0 ];then 
		add_host_to_list $hostname_filter
		save_host_list_file
	else
		message=$( cat $tmpfile | head -n 1)
	fi
	filter_host_list
	clear
}
good_bye(){
	tput cud1
	tput cud1
	tput ed
	echo -e &quot;${reset}${red}Remember the team.${reset}&quot;
	exit
}
# init 
echo &quot;Gateway-kit ver$VERSION&quot;
exec_kinit
init_host_list
trap exit_to_shell INT
# main loop
while true;do
	print_screen
	k=$(read_key)
	case $k in
		left | D ) delete_tail_hostname_filter;;
		#right ) select_next_host;;
		up ) select_prev_host;;
		down ) select_next_host;;
		Q | esc ) good_bye;;
		/ ) next_user;;
		delete | C ) clear_hostname_filter;;
		[a-z0-9_\.\*] ) append_hostname_filter $k;;
		&#39;&#39; ) do_execute;;
	esac
done</code></pre></noscript></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[bash-completion]]></title>
    <link href="http://oddpoet.net/blog/2012/07/13/bash-completion/"/>
    <updated>2012-07-13T01:20:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/13/bash-completion</id>
    <content type="html"><![CDATA[<p>bash에서 키보드 Tab을 누르면 명령어를 자동완성해주는데 
그걸 만드는 방법을 간단히 정리한다.</p>

<!-- more -->

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line">_foo<span class="o">()</span>
</span><span class="line"><span class="o">{</span>
</span><span class="line">    <span class="nb">local </span>cur prev opts
</span><span class="line">    <span class="nv">COMPREPLY</span><span class="o">=()</span>
</span><span class="line">    <span class="nv">cur</span><span class="o">=</span><span class="s2">&quot;${COMP_WORDS[COMP_CWORD]}&quot;</span>
</span><span class="line">    <span class="nv">prev</span><span class="o">=</span><span class="s2">&quot;${COMP_WORDS[COMP_CWORD-1]}&quot;</span>
</span><span class="line">    <span class="nv">opts</span><span class="o">=</span><span class="s2">&quot;--help --verbose --version&quot;</span>
</span><span class="line">
</span><span class="line">    <span class="k">if</span> <span class="o">[[</span> <span class="k">${</span><span class="nv">cur</span><span class="k">}</span> <span class="o">==</span> -* <span class="o">]]</span> ; <span class="k">then</span>
</span><span class="line"><span class="k">        </span><span class="nv">COMPREPLY</span><span class="o">=(</span> <span class="k">$(</span><span class="nb">compgen</span> -W <span class="s2">&quot;${opts}&quot;</span> -- <span class="k">${</span><span class="nv">cur</span><span class="k">})</span> <span class="o">)</span>
</span><span class="line">        <span class="k">return </span>0
</span><span class="line">    <span class="k">fi</span>
</span><span class="line"><span class="o">}</span>
</span><span class="line"><span class="nb">complete</span> -F _foo foo
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>위와 같은 파일을 만든 후에 아래와 같이 실행하자. </p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nv">$ </span>. sample-completion.sh
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>이제 <code>foo --[TAB]</code>하면 <code>--help</code>, <code>--verbose</code>, <code>--version</code>이 자동완성 목록으로 나타난다. </p>

<h2 id="section">작동방식</h2>

<p><code>_foo(){...}</code>는 function 정의이고 <code>complete -F _foo foo</code>는 
<code>_foo</code> 함수를 <code>foo</code>에 대한 자동완성 처리를 위해 등록하는 명령이다. 
즉,  <code>foo</code> 이후에 [TAB]을 누르면 _foo 함수가 실행된다. </p>

<p>자동완성 처리 함수에는 입력변수 <code>COMP_WORD</code>, <code>COMP_CWORD</code>가 전달되고, 
자동완성 목록은 <code>COMPREPLY</code>로 리턴된다. </p>

<dl>
  <dt>COMPREPLY</dt>
  <dd>shell 함수에 의해서 만들어지는 배열 변수이고 
bash가 이 변수를 읽어서 자동완성 목록을 표시한다. </dd>
  <dt>COMP_WORDS</dt>
  <dd>현재 명령행에 있는 단어들에 대한 배열 변수이다. </dd>
  <dt>COMP_CWORD</dt>
  <dd>현재 커서가 있는 단어의 <code>${COMP_WORDS}</code> 상의 인덱스. </dd>
</dl>

<p>함수는 자동완성에 보여줄 목록을 가지고 적당한 조건으로 필터링한 후에 <code>COMPREPLY</code> 변수에 담기만 하면 된다. </p>

<h2 id="section-1">목록 필터링</h2>

<p>보통 목록 필터링에는 <code>compgen</code> command를 사용한다. </p>

<pre><code>compgen LIST -- INPUT
</code></pre>

<p>compgen은 <code>LIST</code> 중에서 <code>INPUT</code>으로 시작하는 단어만 돌려주는 command이다. 
아래 예를 보라. </p>

<pre><code>$ compgen -W "apple banana melon apple-iphone" -- app
apple
apple-iphone
</code></pre>

<p>compgen에 대한 자세한 내용은 <a href="http://linux.about.com/library/cmd/blcmdl1_compgen.htm">Unix Command: compgen</a>을 참고하도록 한다. </p>

<h2 id="references">References</h2>

<ul>
  <li><a href="http://www.debian-administration.org/articles/316">Bash Completion Part1</a></li>
  <li><a href="http://www.debian-administration.org/article/An_introduction_to_bash_completion_part_2">Bash Completion Part2</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[duplicity를 이용한 백업]]></title>
    <link href="http://oddpoet.net/blog/2012/07/12/duplicity/"/>
    <updated>2012-07-12T18:03:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/12/duplicity</id>
    <content type="html"><![CDATA[<p>증분 백업을 지원하는 간단한 command line 백업 툴인 <a href="http://www.nongnu.org/duplicity/index.html">duplicity</a>를 소개한다. </p>

<!-- more -->

<h2 id="section">백업 툴 선택</h2>

<p>백업 툴을 선택하는데 고려할 만한 요소는 아래와 같은 것들이 있다. </p>

<ul>
  <li>
    <p><strong>증분 백업(incremental backup)을 지원하는가?</strong> <br />
  증분 백업은 바뀐 부분에 대한 정보만 저장하므로써 백업 사이즈를 줄여준다. 
  보통 daily로 증분 백업, weekly로 full backup을 하게 된다. 
  주기적인 백업본이 필요한게 아니라면 rsync만으로도 충분하다. </p>
  </li>
  <li>
    <p><strong>백업 설정 및 복구를 위한 GUI를 제공하는가?</strong> <br />
  편리한게 좋은거다. 있으면 좋다. </p>
  </li>
  <li>
    <p><strong>백업 수행에 대한 스케쥴을 관리 기능을 제공하는가?</strong> <br />
  cron을 직접 설정할거냐의 얘기다. 있으면 편하다. </p>
  </li>
  <li>
    <p><strong>다른 시스템의 스토리지에 원격으로 백업할 수 있는가?</strong> <br />
  백업 파일을 다른 머신에 저장할거냐인데 상황에 따라 중요하다. </p>
  </li>
</ul>

<p>duplicity는 incremental backup과 remote backup을 지원하는 command line 툴이다. 
GUI와 스케쥴링 혹은 더 많은 기능들이 필요하다면 <a href="https://help.ubuntu.com/community/BackupYourSystem">백업 유틸리티들</a>의 목록을 보라. </p>

<h2 id="section-1">설치</h2>

<pre><code>sudo apt-get install duplicity
</code></pre>

<h2 id="section-2">백업</h2>

<p>백업은 아래와 같은 command line으로 수행할 수 있다. </p>

<pre><code>$ duplicity 원본경로 백업경로 
</code></pre>

<p>백업경로는 url 형식으로 써야한다. 로컬경로의 경우 <code>file:///path/to/backup</code> 형태로 쓴다. </p>

<p>예를 들어 <code>/etc</code> 디렉토리를 <code>/backup</code>로 백업하려면 shell에서 아래와 같이 한다. </p>

<pre><code>$ export PASSPHRASE=SomeLongGeneratedHardToCrackKey
$ duplicity /etc file:///backup
$ unset PASSPHRASE
</code></pre>

<p>실행되면 아래와 같은 형태의 결과가 출력될 것이다. </p>

<pre><code>--------------[ Backup Statistics ]--------------
StartTime 1342086679.23 (Thu Jul 12 18:51:19 2012)
EndTime 1342086689.32 (Thu Jul 12 18:51:29 2012)
ElapsedTime 10.09 (10.09 seconds)
SourceFiles 2145
SourceFileSize 3577250 (3.41 MB)
NewFiles 2145
NewFileSize 3577250 (3.41 MB)
DeletedFiles 0
ChangedFiles 0
ChangedFileSize 0 (0 bytes)
ChangedDeltaSize 0 (0 bytes)
DeltaEntries 2145
RawDeltaSize 2646680 (2.52 MB)
TotalDestinationSizeChange 924110 (902 KB)
Errors 0
-------------------------------------------------
</code></pre>

<h2 id="section-3">검증</h2>

<p>백업과 원본의 비교는 다음과 같이 한다. </p>

<pre><code>$ export PASSPHRASE=SomeLongGeneratedHardToCrackKey
$ duplicity verify file:///backup /etc
$ unset PASSPHRASE
</code></pre>

<p>결과는 다음과 같다 </p>

<pre><code>Verify complete: 3503 files compared, 2 differences found.
</code></pre>

<p><code>verbose</code> 레벨을 높여서 자세한 결과를 볼 수 도 있다. </p>

<pre><code>$ duplicity verify -v4 file:///backup /etc
Difference found: File . has mtime Fri Dec  2 05:58:42 2005, expected Wed Nov 30 11:42:01 2005
Difference found: File resolv.conf has mtime Fri Dec  2 05:58:42 2005, expected Mon Nov 28 18:58:28 2005
Verify complete: 3503 files compared, 2 differences found.
</code></pre>

<h2 id="section-4">확인</h2>

<p>백업 내용은 아래와 같이 확인할 수 있다. </p>

<pre><code>$ export PASSPHRASE=SomeLongGeneratedHardToCrackKey
$ duplicity list-current-files file:///backup
$ unset PASSPHRASE
</code></pre>

<p>파일 목록이 아래처럼 출력된다. </p>

<pre><code>...
Sat Nov 12 19:58:23 2005 rcS.d/S70xorg-common
Sat Nov 12 20:06:57 2005 rcS.d/S75sudo
Sat Nov 12 20:35:43 2005 readahead
Mon Nov 28 18:57:23 2005 readahead/readahead
Mon Nov 28 18:57:23 2005 readahead/readahead.new
Tue Jan 25 15:03:18 2005 reportbug.conf
Mon Nov 28 18:58:28 2005 resolv.conf
Sat Nov 12 19:55:51 2005 resolvconf
Sat Nov 12 20:06:57 2005 resolvconf/update-libc.d
Wed Aug 17 21:17:26 2005 resolvconf/update-libc.d/fetchmail
Mon Jun 27 07:16:38 2005 rmt
Mon Sep 19 06:41:04 2005 rpc
Sat Nov 19 13:08:21 2005 samba
Thu Jul 21 13:31:14 2005 samba/gdbcommands
Sat Nov 19 13:08:21 2005 samba/smb.conf
Sat Nov 19 13:05:32 2005 samba/smb.conf~
Sat Nov 12 20:00:22 2005 sane.d
Tue Sep 27 09:14:55 2005 sane.d/abaton.conf
Tue Sep 27 09:14:55 2005 sane.d/agfafocus.conf
...
</code></pre>

<h2 id="section-5">복구</h2>

<h3 id="section-6">파일 단위 복구</h3>

<pre><code>$ export PASSPHRASE=SomeLongGeneratedHardToCrackKey
$ duplicity --file-to-restore apt/sources.list file:///backup ~/restore/sources.list
$ unset PASSPHRASE
</code></pre>

<h3 id="section-7">전체 복구</h3>

<pre><code>$ export PASSPHRASE=SomeLongGeneratedHardToCrackKey
$ duplicity restore apt/sources.list file:///backup ~/restore/
$ unset PASSPHRASE
</code></pre>

<h3 id="section-8">특정 시간에 대한 복구</h3>

<p>아래는 4일전 파일 내용을 복구한다. </p>

<pre><code>$ export PASSPHRASE=SomeLongGeneratedHardToCrackKey
$ duplicity -t4D --file-to-restore apt/sources.list file:///backup ~/restore/sources.list
$ unset PASSPHRASE
</code></pre>

<h2 id="section-9">기타</h2>

<p>백업 기본 기능에 충실한 편이고 설정없이 command line으로 작동하는 툴이라 
scripting 및 cron scheduling을 활용하기 용이하다. </p>

<h2 id="reference">reference</h2>

<ul>
  <li><a href="https://help.ubuntu.com/community/DuplicityBackupHowto">Duplicity Backup HOWTO</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[favorite-ppa]]></title>
    <link href="http://oddpoet.net/blog/2012/07/12/favorite-ppa/"/>
    <updated>2012-07-12T17:20:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/12/favorite-ppa</id>
    <content type="html"><![CDATA[<p>Ubuntu 12.04 LTS 기준이다. </p>

<pre><code># install add-apt-repository
$ sudo apt-get install python-software-properties

# add ppa for oracle java
$ sudo add-apt-repository ppa:webupd8team/java

# add pass for airvideo server
$ sudo add-apt-repository ppa:rubiojr/airvideo
</code></pre>

<p>이제 oracle jdk 및 airvideo server를 설치할 수 있다. </p>

<pre><code>$ sudo apt-get update
$ sudo apt-get install oracle-jdk7-installer
$ sudo apt-get install airvideo-server
</code></pre>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[octopress-github-pages-tips]]></title>
    <link href="http://oddpoet.net/blog/2012/07/12/octopress-with-github/"/>
    <updated>2012-07-12T00:54:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/12/octopress-with-github</id>
    <content type="html"><![CDATA[<p><a href="http://octopress.com">octopress</a>로 github에 사이트를 퍼블리싱하는 것과 관련된 몇가지 팁들.</p>

<ol>
  <li>static site는 <code>gh-pages</code>라는 branch에 push된다. </li>
  <li><code>gh-pages</code> branch는 github에서 프로젝트 페이지가 있다고 가정하는 브랜치이기도 하다. </li>
  <li>octopress 및 markdown 문서는 <code>source</code> branch로 push하는게 관례인듯. <br />
 관리편의를 위해서 프로젝트 소스와 동일 repository에 
 <code>gh-pages</code>, <code>source</code> branch를 만들어서 문서 작성 및 퍼블리싱을 함. </li>
  <li>메타 정보에 <code>published: false</code>를 넣으면 generate 때 페이지가 생성되지 않음. 
 (draft 작업시 사용)</li>
  <li>github 프로젝트가 <code>https://github.com/USER/PROJECT</code>라면 <code>gh-pages</code> 브랜치에 있는 페이지들은 
 <code>http://USER.github.com/PROJECT</code>에서 호스팅된다. </li>
  <li>github repository 이름을 <code>USERNAME.github.com</code>으로 만들고 그 repository에 site를 publish하면 
 <code>http://USERNAME.github.com</code> 으로 호스팅됨. <br />
 즉, 프로젝트에 대한 사이트가 아니라 사용자(조직)에 대한 사이트가 됨. </li>
</ol>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[DB를 RDF로 덤프하기]]></title>
    <link href="http://oddpoet.net/blog/2012/07/11/dump-rdf-from-rdb/"/>
    <updated>2012-07-11T14:19:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/07/11/dump-rdf-from-rdb</id>
    <content type="html"><![CDATA[<p><a href="http://d2rq.org/">D2RQ</a>는 RDB 데이터베이스 위에서 Linked Data 서비스를 제공하는 시스템인데, 
이 시스템에 포함된 mapping 툴들을 사용하여 RDB의 데이터를 RDF로 dump 해보자. </p>

<!-- more -->
<p>## 데이터 모델 매핑 </p>

<p>RDB의 관계형 모델을 RDF 트리플로 매핑하기 위해 W3C의 <a href="http://www.w3.org/TR/r2rml">R2RML</a>이라는 
언어를 사용할 수 있다. 기본적으로 테이블의 각 Row를 하나의 Subject로 각 컬럼명과 컬럼값을 
predicate와 object로 매핑하게 되는데 R2RML은 DB의 스키마를 어떻게 RDF 트리플로 매핑할지 정의하는
언어라고 할 수 있다. </p>

<p><a href="http://d2rq.org/">D2RQ</a>에는 <a href="http://www.w3.org/TR/r2rml">R2RML</a>과 유사한 D2RQ Mapping Language가 있다. 
개념적인 부분은 R2RML과 유사하므로 여기서는 D2RQ의 툴을 사용하여 
데이터베이스를 RDF로 덤프해본다. </p>

<h2 id="d2rq-">D2RQ 설치</h2>

<ol>
  <li><a href="http://d2rq.org/">D2RQ</a>를 다운로드 받아서 적당한 곳에 압축을 푼다. </li>
  <li>Java 기반의 툴이므로 JAVA 환경을 준비한다. </li>
</ol>

<h2 id="mapping-rule-">mapping rule 생성</h2>

<ul>
  <li>참고 : <a href="http://d2rq.org/generate-mapping">D2RQ Generate mapping</a></li>
</ul>

<p>D2RQ를 이용하여 DB 스키마에서 매핑룰을 생성하는데 
DB 접속정보와 적합한 JDBC 드라이버를 준비하면 된다. </p>

<pre><code>generate-mapping -o outfile.ttl -u user -p pw -d DRIVER_CLASS_NAME JDBC_URL
</code></pre>

<p>위와 같이 수행하면 outfile.ttl 에 아래와 같은 DB-RDF 매핑 정보가 생성된다. 
테이블 별 s-p-o 매핑 정보 외에도 DB 접속정보도 포함되어 있음을 확인할 수 있다. </p>

<pre><code>@prefix map: &lt;#&gt; .
@prefix db: &lt;&gt; .
@prefix vocab: &lt;vocab/&gt; .
@prefix rdf: &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#&gt; .
@prefix rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt; .
@prefix xsd: &lt;http://www.w3.org/2001/XMLSchema#&gt; .
@prefix d2rq: &lt;http://www.wiwiss.fu-berlin.de/suhl/bizer/D2RQ/0.1#&gt; .
@prefix jdbc: &lt;http://d2rq.org/terms/jdbc/&gt; .

map:database a d2rq:Database;
	d2rq:jdbcDriver "com.mysql.jdbc.Driver";
	d2rq:jdbcDSN "jdbc:mysql://125.209.205.37/tahiti";
	d2rq:username "root";
	d2rq:password "lod";
	jdbc:autoReconnect "true";
	jdbc:zeroDateTimeBehavior "convertToNull";
	.

# Table nsp_user
map:nsp_user a d2rq:ClassMap;
	d2rq:dataStorage map:database;
	d2rq:uriPattern "nsp_user/@@nsp_user.user_id|urlify@@";
	d2rq:class vocab:nsp_user;
	d2rq:classDefinitionLabel "nsp_user";
	.
(이후 생략)
</code></pre>

<p><strong>주의</strong> : mysql은 기본 지원하지만 <a href="http://www.cubrid.com/">cubrid</a>는 문제가 좀 있다. </p>

<h2 id="mapping-rule--1">mapping rule 수정</h2>

<p>위에서 생성한 mapping rule은 컬럼이름이 predicate으로 그대로 사용되므로 
실제 적용시에는 이 파일을 수정하여 원하는 vocabulary를 사용하도록 변경하는 것이 필요하다.  </p>

<p>이 매핑 파일은 <a href="http://d2rq.org/d2rq-language" title="D2RQ Mapping Language">D2RQ mapping language</a>으로 기술되어 있지만, 
개념적으로는 R2RML과 유사하고 자동생성되는 파일이므로 
추후 R2RML로 매핑이 변경되더라도 RDF 덤프 관련 작업에 큰 변경은 없을 것으로 보인다. </p>

<p>(<a href="http://d2rq.org/d2rq-language" title="D2RQ Mapping Language">D2RQ language</a> 역시 RDF 이다)</p>

<p>데이터베이스를 RDF로 덤프할 때 대략 아래와 같은 매핑 정보 수정이 예상된다. </p>

<ol>
  <li>vocab을 적절한 이름으로 수정 </li>
  <li>외부키 등의 relation에 대한 매핑 확인 및 수정 </li>
</ol>

<h2 id="dump">dump!</h2>

<p>mapping rule을 수정했으면 dump만 남았다. </p>

<p>mapping rule에 DB접속정보가 포함되어 있으므로 
outfile.ttl 파일을 <a href="http://d2rq.org/dump-rdf">dump rdf</a> 툴을 사용하여 DB를 rdf로 덤프해보자 </p>

<pre><code>dump-rdf -b baseURI -o rdf_output mapping-file.ttl
</code></pre>

<p>__mapping-file.ttl__에 위에서 만든 mapping rule파일명을 쓰면 되는데, 
format을 지정하지 않으면 기본 값이 “N-TRIPLE” 포맷으로 출력된다. </p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SPDY 테스팅 서버 만들기]]></title>
    <link href="http://oddpoet.net/blog/2012/05/22/spdy-testing-environment/"/>
    <updated>2012-05-22T17:22:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/05/22/spdy-testing-environment</id>
    <content type="html"><![CDATA[<!-- TODO : redirect to http://oddpoet.net/archives/425 -->

<p>현재 SPDY 프로토콜 구현체는 <a href="http://dev.chromium.org/spdy">http://dev.chromium.org/spdy</a>에 언급된 대로 아파치 모듈이나 주요 언어들의 웹서버 구현 등이 있습니다. 그러나 SPDY가 SSL위에서 작동하는데다가, NPN(Next Protocol Neotiation) 확장을 필요로 하기 때문에 간단하게 SPDY의 작동을 확인할 목적으로 테스팅 환경을 만들 때 부담스러운 면이 많습니다.</p>

<!-- more -->

<p>실제 도입할 목적이 아니라 Lab Testing 목적이라면 node.js의 express를 사용하는 express-spdy를 사용하는게 가장 손쉽게 SPDY환경을 구축하는 방법일 것 같습니다. 이 글에서는 <a href="http://pragprog.com/book/csspdy/the-spdy-book">The SPDY Book</a> 에서 실습환경 부분 참고하여 linux와 mac에서의 SPDY 서버를 만들어 보겠습니다. 클라이언트는 Google Chrome을 사용하면 세션확인까지 가능합니다.</p>

<h2 id="edge-openssl-">Edge openssl 설치</h2>

<p>SPDY가 SSL 위에서 정상작동하기 위해서는 <a href="https://technotes.googlecode.com/git/nextprotoneg.html">NPN (Next Protocol Neogtiation)</a>확장이 필요합니다. NPN은 SSL의 다음 계층에서 HTTP를 쓸지, SPDY를 쓸지를 서버와 클라이언트가 협상할 수 있도록 해주는 것이라고 보시면 됩니다. NPN은 현재 edge openssl에서만 지원하고 있으므로 직접 openssl ftp server에서 소스를 받아서 컴파일 해야 합니다.</p>

<p>우선 <a href="ftp://ftp.openssl.org/snapshot/">openssl ftp server</a> 에서 <strong>openssl-SNAP-</strong>* 으로 시작하는 파일 중 가장 최신파일의 경로를 확인합니다.</p>

<pre><code>mkdir -p $HOME/src
cd $HOME/src
wget ftp://ftp.openssl.org/snapshot/openssl-SNAP-.tar.gz
tar xvzf openssl-SNAP-.tar.gz
cd openssl-SNAP-
</code></pre>

<p>이제 아래와 같이 설정을 합니다. (맥OS의 경우 <em>linux-elf</em> 대신에 <em>darwin64-x86_64-cc</em>를 씁니다. 64비트 linux라면 <em>linux-x86_64</em>를 쓰세요.)</p>

<pre><code>./Configure shared \
  --prefix=$HOME/local \
  no-idea no-mdc2 no-rc5 zlib \
  enable-tlsext \
  linux-elf
</code></pre>

<p>이제 컴파일하고 설치합니다.</p>

<pre><code>make depend
make
make install
</code></pre>

<h2 id="nodejs-">Node.js 설치</h2>

<p>기존에 이미 apt-get(ubuntu)이나 brew(osx)로 node.js를 설치했더라도 위에서 컴파일한 edge openssl을 사용하도록 재컴파일해서 별도 설치합니다.</p>

<pre><code>mkdir -p $HOME/src
cd $HOME/src/
wget http://nodejs.org/dist/v0.6.18/node-v0.6.18.tar.gz
tar xvzf node-v0.6.18.tar.gz
cd node
</code></pre>

<p>앞서 컴파일 설치한 openssl을 사용하도록 아래와 같이 설정하고 컴파일해서 설치합니다. 설치경로는 마찬가지로 <code>$HOME/local</code>입니다.</p>

<pre><code>./configure \
  --openssl-includes=$HOME/local/include \
  --openssl-libpath=$HOME/local/lib \
  --prefix=$HOME/local/node-v0.6.18
make
make install
</code></pre>

<h2 id="shell-">Shell 설정하기</h2>

<p>$HOME/.bashrc 에 아래 내용을 추가합니다.</p>

<pre><code># For locally installed binaries
export LD_LIBRARY_PATH=$HOME/local/lib
PATH=$HOME/local/bin:$PATH
PKG_CONFIG_PATH=$HOME/local/lib/pkgconfig
CPATH=$HOME/local/include
export MANPATH=$HOME/local/share/man:/usr/share/man
# For node.js work. For more info, see:
# http://blog.nodejs.org/2011/04/04/development-environment/
for i in $HOME/local/*; do
  [ -d $i/bin ] &amp;amp;&amp;amp; PATH="${i}/bin:${PATH}"
  [ -d $i/sbin ] &amp;amp;&amp;amp; PATH="${i}/sbin:${PATH}"
  [ -d $i/include ] &amp;amp;&amp;amp; CPATH="${i}/include:${CPATH}"
  [ -d $i/lib ] &amp;amp;&amp;amp; LD_LIBRARY_PATH="${i}/lib:${LD_LIBRARY_PATH}"
  [ -d $i/lib/pkgconfig ] &amp;amp;&amp;amp; PKG_CONFIG_PATH="${i}/lib/pkgconfig:${PKG_CONFIG_PATH}"
  [ -d $i/share/man ] &amp;amp;&amp;amp; MANPATH="${i}/share/man:${MANPATH}"
done
</code></pre>

<h2 id="npm">NPM설치</h2>

<pre><code>curl http://npmjs.org/install.sh | sh
</code></pre>

<p>이미 기존 node와 함께 npm이 설치되었다면 이 단계는 건너뛰어도 됩니다.</p>

<h2 id="express-spdy-">express-spdy 설치</h2>

<pre><code>npm install -g express-spdy
</code></pre>

<p>위와 같이 express-spdy를 설치합니다. express-spdy는 express에서 spdy를 사용할 수 있게 확장한 모듈입니다.</p>

<h2 id="app-">샘플 App 만들기</h2>

<p>express-spdy를 설치하였으면 application을 생성해봅니다.</p>

<pre><code>cd
mkdir $HOME/projects
cd $HOME/projects
express-spdy spdy-sample
cd spdy-sample
npm install
</code></pre>

<p>spdy-sample이라는 이름의 app이 생성되었습니다. app.js 파일을 열어보면 기본적인 app 골격이 나와있습니다. (생성된 코드를 보면 Ruby 개발자분들은 sinatra, rails의 느낌을 받으실듯)</p>

<pre><code>var app = module.exports = express.createServer({
  key: fs.readFileSync(__dirname %2B '/keys/spdy-key.pem'),
  cert: fs.readFileSync(__dirname %2B '/keys/spdy-cert.pem'),
  ca: fs.readFileSync(__dirname %2B '/keys/spdy-csr.pem'),
  NPNProtocols: ['spdy/2', 'http/1.1'],
  push: awesome_push
});
</code></pre>

<p>위 코드에서 key, cert, ca 부분은 SSL과 관련된 설정입니다. SSL관련 설정까지 템플릿으로 만들어주니 간단히 만들고 테스트하기에 좋습니다.</p>

<p>그리고 <strong><em>NPNProtocols</em></strong> 부분이 NPN 설정에 대한 부분입니다. Chrome 처럼 SPDY를 지원하는 브라우저와는 SPDY/2로 통신하고, 그 외에는 HTTP/1.1로 통신을 하게 됩니다. 즉, 여기에서 ‘spdy/2′ 부분을 제거하면 HTTP/1.1만 사용하게 되겠죠? 이렇게 SPDY를 ON/OFF하면서 비교테스트 하시면 되겠습니다. 그리고 <strong><em>awesome_push</em></strong> 는 함수 정의부를 보시면 설명이 있을텐데 SPDY의 server push에 대한 부분입니다.</p>

<h2 id="section">서버 실행하고 확인하기</h2>

<p>쉘에서 아래와 같이 실행하면 서버가 뜹니다. </p>

<pre><code>node app.js
</code></pre>

<p>자~ 이제 브라우저를 띄워볼까요? <a href="https://localhost:3000/">https://localhost:3000/</a>로 접근하시면 됩니다. <strong>http가 아니라 https입니다!</strong></p>

<p>뭐 별거 없는 썰렁한 샘플 페이지 입니다만, 구글 크롬으로 <a href="chrome://net-internals/#spdy">chrome://net-internals/#spdy</a> 페이지를 열어보시면 그 페이지에 대한 세션이 있음을 확인할 수 있습니다. 게다가 server push된게 1개 있다는 것도 볼 수 있습니다. (awesome_push함수에서 index 페이지에서 사용하는 css파일 하나를 push하도록 되어 있어요.)</p>

<h2 id="section-1">그리고… 흔적제거</h2>

<p>저만 그런지 모르겠지만 잠깐 테스팅하겠다고 구질구질하게 unstable lib들을 컴파일해서 설치하고 나면 나중에 뒷정리가 짜증이 납니다. apt-get, brew와 같은 패키지 관리툴에서 관리해주는게 아니라서 의존성이 꼬일 수도 있구요.</p>

<p>하지만 openssl, node를 모두 <code>$HOME/local</code> 밑에 설치했기 때문에, <code>$HOME/local</code> 디렉토리만 삭제하면 됩니다. 그리고 .bashrc에 추가했던 부분도 지우면 설치전과 동일한 상태가 됩니다. </p>

<h2 id="section-2">참고자료</h2>

<ul>
  <li><a href="http://pragprog.com/book/csspdy/the-spdy-book">The SPDY Book: Making Websites Fly</a></li>
  <li><a href="https://github.com/eee-c/express-spdy/blob/master/INSTALL.md">Install express-spdy</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SPDY : 더 빠른 웹을 위한 실험적인 프로토콜]]></title>
    <link href="http://oddpoet.net/blog/2012/05/17/spdy-an-experimental-protocol-for-a-faster-web/"/>
    <updated>2012-05-17T13:34:00+09:00</updated>
    <id>http://oddpoet.net/blog/2012/05/17/spdy-an-experimental-protocol-for-a-faster-web</id>
    <content type="html"><![CDATA[<!-- TODO : redirect to http://oddpoet.net/archives/409 -->

<p>이 글은 SPDY의 백서 “<em><a href="http://dev.chromium.org/spdy/spdy-whitepaper">SPDY : An experimental protocol for a faster web</a></em>“를 번역한 글입니다.</p>

<!-- more -->

<h2 id="section">개요</h2>

<p>“웹을 더 빠르게 하자(<a href="https://developers.google.com/speed/?hl=ko-KR">Let’s make the web faster</a>)” 계획의 부분으로써 우리는 웹페이지들의 반응속도를 높일 수 있는 대안 프로토콜을 실험 중에 있다. 이러한 실험 중에 하나가 SPDY(SPeeDY-스피디라고 읽는다)인데, SPDY는 웹에서 컨텐츠 전송을 위한 애플리케이션 레이어 프로토콜이다. 이 프로토콜 명세와 더불어 우리는 SPDY를 지원하는 구글 크롬 브라우저와 오픈소스 웹서버를 만들었다. 내부 테스트를 통해 우리는 HTTP와 SPDY 위의 어플리케이션의 성능을 비교한 결과 SPDY를 사용할 때 페이지 로드 타임을 64%정도 줄일 수 있었다. 우리는 오픈 소스 커뮤니티에 아이디어, 피드백, 코드, 테스트 결과들을 기여함으로써 보다 SPDY가 빠른 웹을 위한 차세대 애플리케이션 프로토콜이 되길 바란다.</p>

<h2 id="section-1">배경 : 웹 프로토콜과 웹 반응속도</h2>

<p>현재 웹에서 사용하는 프로토콜은 HTTP와 TCP이다. TCP는 일반적이고 신뢰성있는 전송프로토콜(transport protocol)로써 전송 보증(guaranteed delivery), 중복 제거(duplicate suppression), 순서유지(in-order delivery), 흐름제어(flow control), 혼잡 회피(congestion avoidance) 등을 제공한다. HTTP는 어플리케이션 레벨 프로토콜로써 기본적인 요청/응답의 시맨틱스를 제공한다. 우리는 전송계층(transport layer)에서 반응속도를 개선할 가능성이 더 있다고 생각하지만, 최초에는 어플리케이션 레이어(HTTP) 쪽을 중점적으로 조사를 했다. 불행히도 HTTP는 반응속도에 대해 특별히 고려된 프로토콜이 아니었다. 게다가 현재 네트웍 상에서 전송되는 웹페이지들은 10년 전의 웹페이지들과는 많이 달라졌고, 이에 따라 HTTP가 개발되던 시절에 예상할 수 없었던 개선이 필요하게 됐다. 다음은 HTTP가 최적의 성능을 내는데 저해가 되는 기능들이다.</p>

<ul>
  <li><strong>커넥션 당 하나의 요청</strong> <br />
HTTP가 한번에 하나의 리소스만 가져올 수 있기 때문에(HTTP piplelining이 도움이 되지만 여전히 FIFO queue라는 제약이 있다), 500ms의 서버 지연시간이 다른 요청에 대해서 TCP channel의 재사용을 어렵게 한다. 그래서 브라우저들은 다수의 컨넥션을 사용하여 이 문제를 우회하고 있다. 2008년 이후 대부분의 브라우저들은 결국 도메인 당 커넥션 수를 2개에서 6개로 늘렸다.</li>
  <li><strong>클라이언트만 가능한 요청</strong> <br /><br />
HTTP하에서 오직 클라이언트만 요청을 보낼 수 있다. 서버 측에서 클라이언트가 어떤 리소스가 필요하리라는 걸 알더라도 클라이언트에 알릴 메커니즘이 존재하지 않는다. 서버는 클라이언트가 요청할 때까지 기다려야만 한다.</li>
  <li><strong>압축되지 않는 요청/응답 헤더</strong><br />
요즘 요청 헤더의 크기는 200byte에서 2KB까지 다양해졌다. 어플리케이션에서 쿠키를 더 많이 사용하고, user agent의 기능이 확장됨에 따라 일벅으로 헤더 크기가 700~800 바이트가 되었다. 업링크 대역폭이 아주 작은 모뎀이나 ADSL 연결에서 헤더의 데이터를 줄이는 것은 요청을 보낼 때의 응답시간을 직접적으로 향상 시킬 수 있다.</li>
  <li><strong>중복 헤더들</strong><br />
같은 채널에서의 대부분의 헤더들은 모든 요청에 반복적으로 사용된다. 그러나 User-Agent, Host, Accept* 등의 헤더 값들은 고정된 값이며 일반적으로 재전송이 필요하지 않다.</li>
  <li><strong>선택적인 데이터 압축</strong> <br />
HTTP는 선택적으로 데이터에 대한 압축을 사용할 수 있다. 하지만 컨텐츠는 항상 압축되어 전송되는 것이 좋다.</li>
</ul>

<h3 id="section-2">기존의 접근방법들</h3>

<p>SPDY는 HTTP를 빠르게 하려는 유일한 연구는 아니다. 웹의 반응속도 개선에 대한 제안들 있었고, 그 대부분이 전송계층이나 세션계층에서의 연구였다.</p>

<dl>
  <dt>Stream Control Transmission Protocol (SCTP)</dt>
  <dd>TCP를 대체하는 전송계층 프로토콜로써 멀티플렉스 스트림과 스트림 기반 혼잡제어를 제공한다.</dd>
  <dt>HTTP over SCTP</dt>
  <dd>SCTP 위에 HTTP를 작동하자는 제안. Comparision of HTTP Over SCTP and TCP in High Delay Networks 는 두 전송프로토콜 위에서의 성능 비교 연구 결과를 보여준다.</dd>
  <dt>Structured Stream Transport (SST)</dt>
  <dd>“structured streams”는 일반적인 전송층 위에서 사용될 수 있는 가볍고, 독립적인 스트림을 만드는 프로토콜. 이것은 TCP를 대체하거나 UDP의 위에서 작동할 수 있다.</dd>
  <dt>MUX와 SMUX</dt>
  <dd>전송계층과 어플리케이션 레이어 사이에 작동하는 중간 레이어 프로토콜. 스트림의 멀티플렉싱을 지원한다. HTTP/1.1과 비슷한 시기에 제안됐다.</dd>
</dl>

<p>상기 제안들은 웹의 반응속도 문제에 대한 부분적인 해결책을 제공한다. HTTP의 압축, 우선순위 등의 고유의 문제는 여전히 남아있고, 그 아래의 전송계층과 별개의 문제다. 현실상황의 어떤 경우라도 전송계층을 변경하는 일은 매우 어렵다. 대신 우리는 어플리케이션 레이어에서의 문제들을 정리하면서 더 쉬운 해결방안 있다고 생각했다. 이 방법은 기존의 인프라스트럭쳐에 최소한의 변경만 필요로 하고, (우리 생각에는) 상당한 성능 향상을 얻어낼 수 있을 것이다.</p>

<h2 id="spdy-">SPDY의 목표</h2>

<p>SPDY 프로젝트는 웹의 반응속도를 개선할 수 있는 어플리케이션 프로토콜을 정의하고 구현한다. SPDY의 상위 수준 목표는 다음과 같다.</p>

<dl>
  <dt>페이지 로드 타임 50% 감소</dt>
  <dd>우리의 사전작업 결과는 이 목표에 이미 근접하고 있다. (아래)</dd>
  <dt>도입 복잡도를 최소화</dt>
  <dd>SPDY는 전송계층으로 TCP를 사용하여 기존 네트웍 인프라스트럭쳐에 어떤 변화도 필요로 하지 않는다.</dd>
  <dt>웹사이트의 컨테츠에 변경이 필요하지 않도록 함.</dt>
  <dd>SPDY는 오직 클라이언트 user agent와 웹서버만 변경하면 되도록 한다.</dd>
</dl>

<p>반응속도의 문제를 해결하기 위한 방법으로 프로토콜을 탐구하는 사람들이 함께 할 수 있도록 한다. 우리는 오픈 소스 커뮤니티와 산업계 전문가들의 협력하에서 이 새로운 프로토콜을 개발하길 원한다.</p>

<p>기타 기술적인 목표들은 다음과 같다.</p>

<ul>
  <li>하나의 TCP 세션을 통해 다수의 HTTP요청을 동시에 수행할 수 있게 한다.</li>
  <li>헤더에 대한 압축과 불필요한 헤더를 제거함으로써 HTTP가 현재 사용중인 대역폭을 줄인다.</li>
  <li>구현하기 쉬우면서 서버측에 효율적인 프로토콜을 정의한다. 우리는 예외적인 상황들을 줄이고 파싱하기 쉬운 메시지 포맷을 정의함으로써 HTTP의 복잡도를 낮추려고 한다.</li>
  <li>전송계층 위의 SSL을 보다 보안성있고 기존 네트웍 인프라스트럭쳐에 보다 호환성이 좋게 만든다. SSL은 반응속도 측면에서는 불리하지만, 장기적인 관점에서 웹은 결국 보안 네트웍 연결에 의존하게 될 것이라 믿는다. 추가적으로 SSL의 사용이 기존의 프락시들을 통한 통신이 깨지지 않도록 보장해야 한다.</li>
  <li>서버가 클라이언트와의 통신을 시작할 수 있게 하여, 가능한 상황에서는 클라이언트에 데이터를 push해줄 수 있도록 한다.</li>
</ul>

<h2 id="spdy--">SPDY 설계와 기능</h2>

<p>SPDY는 SSL 위에 세션계층을 추가했다. 이것은 하나의 TCP 연결 위에서 다수의, 동시수행되는 스트림을 교차배치할 수 있게 한다.</p>

<p>일반적인 HTTP GET과 POST 메시지 포맷은 동일하다. 그러나 SPDY는 연결위에서 데이터를 인코딩하고 전송하는 새로운 프레이밍 포맷을 정의한다.</p>

<p><img src="http://dev.chromium.org/_/rsrc/1258490355439/spdy/spdy-whitepaper/soarjOjSeS5hoFYvjtAnxCg.png" alt="SPDY protocol layer" /></p>

<p>스트림들은 양방향이다. 즉, 스트림은 클라이언트와 서버에 의해서 생성될 수 있다.</p>

<p>기본 기능(항상 활성화됨)과 고급 기능(선택적으로 활성화)을 통해서 SPDY는 빠른 반응속도를 얻을 수 있다.</p>

<h3 id="section-3">기본 기능</h3>

<dl>
  <dt>멀티플렉스 스트림</dt>
  <dd>SPDY는 하나의 TCP 연결 위에 무제한 동시 스트림을 지원한다. 하나의 채널에서 요청들이 교차배치되기 때문에 TCP의 효율은 높아진다. 적은 네트웍 연결만 필요하고, 더 적은 수의 패킷만 사용된다.</dd>
  <dt>요청 우선순위</dt>
  <dd>무제한으로 스트림을 병렬화 할 수 있기 때문에 직렬화 문제를 해결할 수 있지만, 이것은 다른 문제를 만든다. 채널의 대역폭이 제한되어 있다면 채널이 막힐 수 있기 때문에 클라이언트는 요청을 블록할 수 있다. 이 문제를 극복하기 위해서 SPDY는 요청 우선순위를 구현한다 : 클라이언트는 원하는 수 만큼 요청할 수 있고, 각 요청에 우선순위를 부여한다. 이것은 네트웍 채널이 중요하지 않은 리소스들 때문에 혼잡해져서 높은 우선순위의 요청이 지연되는 것을 막는다.</dd>
  <dt>헤더 압축</dt>
  <dd>SPDY는 요청과 응답의 HTTP 헤더를 압축한다. 결과적으로 더 적은 수의 패킷과 더 적은 수의 데이터가 전송된다.</dd>
</dl>

<h3 id="advanced-features">고급 기능 (advanced features)</h3>

<p>SPDY는 서버에서 스트림을 생성할 수 있는 기능을 제공한다. 서버에서 생성된 스트림은 클라이언트의 요청이 없어서도 컨텐츠를 클라이언트에 전송할 수 있다. 웹개발자는 이 옵션을 2가지 방식으로 설정할 수 있다.</p>

<dl>
  <dt>Server push</dt>
  <dd>SPDY는 서버가 클라이언트에 데이터를 푸쉬(push)하는 것을 X-Associated-Content 헤더를 통해서 실험했다. 이 헤더는 클라이언트가 요청하기 전에 서버가 리소스를 푸시하고 있다는 것을 클라이언트에 알린다. 사용자가 사이트에 처음으로 방문하여 첫 페이지를 다운로드할 때, 이 기능은 사용자 경험을 크게 향상시킬 수 있다.</dd>
  <dt>Server hint</dt>
  <dd>자동으로 클라이언트에 리소스를 push하는 대신, X-Subresources 헤더를 사용하여 클라이언트가 특정 리소스를 요청하도록 제안하지만, 컨텐츠를 보내기 전에 클라이언트의 요청을 기다리게 된다. 네트웍 연결이 느린 경우, 클라이언트가 필요한 리소스들을 파악하는 걸리는 수백 밀리세컨드의 시간을 줄여줄 수 있다. 이 옵션은 초기 페이지가 아닌 페이지에서 더 나은 선택이 될 것이다.</dd>
</dl>

<p>기술적인 세부사항은 <a href="http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft1">SPDY draft protocol specification</a>을 참조하라.</p>

<h2 id="spdy-----">SPDY 구현 : 우리가 만들어온 것들</h2>

<ul>
  <li>HTTP와 SPDY로 TCP와 SSL 위에서 효율적으로 응답할 수 있는 high-speed의 in-memory 서버. 우리는 조만간 오픈소스로 이 코드를 릴리즈할 예정이다.</li>
  <li>TCL와 SSL위에서 HTTP나 SPDY를 사용할 수 있는 수정된 구글 크롬. 소스 코드는 <a href="http://src.chromium.org/viewvc/chrome/trunk/src/net/spdy/">여기</a>에 있다. (주: 현재 내부적인 코드네임은 “flip”이지만, 조만간 바뀌게 될 것이다.)</li>
  <li>페이지들이 정확하게 복사되었는지 검증할 수 있는 테스팅과 벤치마킹 인프라스트럭처. SPDY는 원래의 서버 헤더들과 컨텐츠 인코딩, URLs 등을 그대로 유지한다. 우리는 곧 테스팅 툴과 우리의 결과를 재활용하기 위한 가이드를 릴리즈할 것이다.</li>
</ul>

<h3 id="section-4">사전 실험 결과</h3>

<p>우리가 개발한 구글 크롬 클라이언트와 웹서버를 가지고, SPDY의 HTTP 대비 성능을 벤치마크하기 위해서 많은 실험실 테스트를 수행했다.</p>

<p>우리는 1%의 패킷손실을 가지는 홈 네트워크 시뮬레이션 하에서 “Top 100″ 웹사이트 중 25개를 다운로드 해봤다. 각 사이트를 10번씩 다운로드했고, 평균 페이지 로딩 시간을 계산했다. 그 결과 HTTP 대비 TCP 하에서는 27%~60%정도, SSL하에서는 39%~55% 정도의 성능향상을 확인할 수 있었다.</p>

<p><em>표1 : Top 25 웹사이트들에 대한 평균 페이지 로딩 타임</em></p>

<table class="custom" border="1" cellspacing="0" cellpadding="3">
<tbody>
<tr>
<td width="20%" />
<td colspan="2">DSL 2 Mbps downlink,<br />
375 kbps uplink</td>
<td colspan="2">Cable 4 Mbps downlink,<br />
1 Mbps uplink</td>
</tr>
<tr>
<td width="20%" />
<td width="20%"><strong>A</strong><strong>verage ms</strong></td>
<td width="20%"><strong>Speedup</strong></td>
<td width="20%"><strong>Average ms</strong></td>
<td width="20%"><strong>Speedup</strong></td>
</tr>
<tr>
<td width="20%">HTTP</td>
<td width="20%">3111.916</td>
<td width="20%" />
<td width="20%">2348.188</td>
<td width="20%" />
</tr>
<tr>
<td width="20%">SPDY basic multi-domain* connection / TCP</td>
<td width="20%">2242.756</td>
<td width="20%">27.93%</td>
<td width="20%">1325.46</td>
<td width="20%">43.55%</td>
</tr>
<tr>
<td width="20%">SPDY basic single-domain* connection / TCP</td>
<td width="20%">1695.72</td>
<td width="20%">45.51%</td>
<td width="20%">933.836</td>
<td width="20%">60.23%</td>
</tr>
<tr>
<td width="20%">SPDY single-domain + server push / TCP</td>
<td width="20%">1671.28</td>
<td width="20%">46.29%</td>
<td width="20%">950.764</td>
<td width="20%">59.51%</td>
</tr>
<tr>
<td width="20%">SPDY single-domain + server hint / TCP</td>
<td width="20%">1608.928</td>
<td width="20%">48.30%</td>
<td width="20%">856.356</td>
<td width="20%">63.53%</td>
</tr>
<tr>
<td width="20%">SPDY basic single-domain / SSL</td>
<td width="20%">1899.744</td>
<td width="20%">38.95%</td>
<td width="20%">1099.444</td>
<td width="20%">53.18</td>
</tr>
<tr>
<td width="20%">SPDY single-domain + client prefetch / SSL</td>
<td width="20%">1781.864</td>
<td width="20%">42.74%</td>
<td width="20%">1047.308</td>
<td width="20%">55.40%</td>
</tr>
</tbody>
</table>

<ul>
  <li>많은 경우 요청 리소스가 속한 도메인의 수와 무관하게 SPDY는 하나의 연결로 모든 요청을 처리할 수 있다. 이 경우에는 모든 다운로드는 완전히 병렬로 진행될 수 있다. 그러나 모든 도메인을 하나의 도메인으로 줄일 수 없는 경우가 있는데, 이 경우에는 SPDY는 각 도메인별로 연결을 만들어야만한다. 이때 연결 생성마다 초기 RTT(round trip time)만큼의 시간이 필요하다.*</li>
</ul>

<p><em>그래서 우리는 2가지 모드에 대해서 테스트를 수행했다. 하나는 모든 도메인을 하나로 줄여놓은 경우(즉, 하나의 TCP 연결)와 리소스를 원래 속한 다수의 도메인으로 나누어놓은 경우(즉, 도메인 당 하나의 TCP 연결). 우리는 테스트 결과에 이를 각각 “single-domain”과 “multi-domain”이라고 표기해놓았다. 실제 환경에서의 결과는 이 결과의 사이값이 될 것이다.</em></p>

<h4 id="section-5">헤더 압축의 효과</h4>

<p>헤더 압축은 요청헤더의 크기를 88%, 응답헤더의 크기를 85%정도 줄인다. 낮은 대역폭을 갖는 DSL 환경에서(업로드가 375Kbps) 요청헤더 압축은 특정사이트에서 페이지 로딩 시간에 있어서 상당한 성능향상을 보여주었다(리소스 요청이 수가 많은 경우). 헤더압축만 해도 페이지 로딩 시간을 45~1142ms만큼 줄일 수 있었다.</p>

<h4 id="rttround-trip-time-">패킷 손실과 RTT(round-trip time)의 효과</h4>

<p>우리는 두번째 테스트로 패킷 손실율과 RTT가 결과에 어떤 영향을 주는지 확인하기 위한 테스트를 수행했다. 이 테스트를 위해서 패킷 손실율과 RTT를 실험변수가 아니라 케이블 연결에서 측정을 했다.</p>

<p>우리는 SPDY의 응답시간 향상효과가 패킷손실율이 높아질 수록 증가한다는 것을 알아냈다. 패킷손실율이 2%일때 최대 48%의 성능향상이 있었다. (패킷손실이 2%는 넘어서면 성능향상폭이 줄어들기 시작해서 2.5%가 되면 성능향상율이 떨어진다. 실제 미국내에서 패킷손실율은 일반적으로 1~2%이고, RTT는 평균 50-100ms이다.)</p>

<p>패킷손실율이 증가할 수록 SPDY가 더 나은 이유는</p>

<ul>
  <li>SPDY는 HTTP보다 40% 정도 적은 패킷을 전송한다. 즉 패킷손실율의 영향을 덜 받는다.</li>
  <li>SPDY는 보다 적은 TCP 컨넥션을 사용한다. 이것은 SYN packet이 손실될 가능성을 줄인다. 많은 TCP 구현에서 이것에 대한 지연은 의외로 크다. (3초)</li>
  <li>SPDY는 재전송 타이머보다는 TCP의 빠른 재전송을 이용하여 TCP를 보다 효율적으로 이용한다.</li>
</ul>

<p>SPDY는 RTT가 증가하면 더 나은 성능을 보였다. RTT가 200ms일때 최대 27%의 성능향상이 있었다. RTT가 증가할 때 SPDY가 HTTP 대비 더 나은 성능보이는 이유는 SPDY가 모든 요청을 병렬로 처리하기 때문이다. HTTP 클라이언트가 도메인당 4개의 연결을 사용하고, 20개의 리소스를 받아야 한다면, 대략 5RT(round trip)가 걸릴것이다. SPDY는 20개의 리소스를 한번의 RT만에 가져올 수 있다.</p>

<p><em>표2 : Top 25 웹사이트에 대한 평균 페이지로딩시간 (패킷손실율 별)</em></p>

<table class="custom" border="1" cellspacing="0" cellpadding="3">
<tbody>
<tr>
<td width="25%" />
<td colspan="2">Average ms</td>
<td width="25%">Speedup</td>
</tr>
<tr>
<td width="25%">Packet loss rate</td>
<td width="25%">HTTP</td>
<td width="25%">SPDY basic (TCP)</td>
<td width="25%" />
</tr>
<tr>
<td width="25%">0%</td>
<td width="25%">1152</td>
<td width="25%">1016</td>
<td width="25%">11.81%</td>
</tr>
<tr>
<td width="25%">0.5%</td>
<td width="25%">1638</td>
<td width="25%">1105</td>
<td width="25%">32.54%</td>
</tr>
<tr>
<td width="25%">1%</td>
<td width="25%">2060</td>
<td width="25%">1200</td>
<td width="25%">41.75%</td>
</tr>
<tr>
<td width="25%">1.5%</td>
<td width="25%">2372</td>
<td width="25%">1394</td>
<td width="25%">41.23%</td>
</tr>
<tr>
<td width="25%">2%</td>
<td width="25%">2904</td>
<td width="25%">1537</td>
<td width="25%">47.7%</td>
</tr>
<tr>
<td width="25%">2.5%</td>
<td width="25%">3028</td>
<td width="25%">1707</td>
<td width="25%">43.63%</td>
</tr>
</tbody>
</table>

<h2 id="spdy---------">SPDY 다음 단계 : 어떻게 우리를 도울 수 있는 방법</h2>

<p>우리의 초기 결과는 좋지만, 우리는 그것들을 어떻게 현실 세계에 적용해야 할지 잘 모르겠다. 게다가 SPDY는 더 개선할 여지들이 있다. 예를 들면</p>

<ul>
  <li>대역폭 효율성은 여전히 낮다. 전화망의 대역폭 효율이 90%에 근접한다고 하지만, 고속망의 효율은 약 32% 밖에 안된다.</li>
  <li>SSL은 추가적인 지연시간과 도입에 대한 난관들을 가지고 있다. SSL 핸드셰이킹를 위한 추가적인 RTT, 암호화, 프락시에서의 캐싱 문제 등이다. 우리는 SSL에 더 적합하게할 일들이 있다.</li>
  <li>우리의 패킷 손실율에 대한 실험결과로는 아직 결론을 내릴 수 었다. 패킷 손실율에 대한 연구들이 있었지만, 우리는 웹을 위한 현실적인 모델을 만들기 위한 충분한 데이터를 가지고 있지 않다. 패킷 손실율 시뮬레이션을 더 정교하게 할 수 있는 데이터들을 더 수집할 필요가 있다.</li>
  <li>SPDY의 단일 커넥션은 연결이 끊겨서 복구할 경우 다수 컨넥션보다 때로는 성능이 낮을 수 있다. 특히 RTT가 매우 높을 때는 단일 커넥션보다 멀티플 커넥션이 빠를 수 있다. 언제 SPDY 클라이언트가 새로운 커넥션을 만들거나 기존 커넥션을 끊을지, 이것이 서버에 어떤 영향을 줄지를 더 고민할 필요가 있다.</li>
  <li>서버는 우리가 만든 것보다 더 지능적으로 구현할 수 있을 것이다. 서버가 스트림을 생성하는 경우, 프리패칭 제안을 위해서 클라이언트 네트웍 정보를 수집하는 등의 케이스에 대해서 더 많은 연구가 필요하다.</li>
</ul>

<p>이런 문제에 도전하기 위해 당신이 참여하길 원한다면…</p>

<h2 id="spdy--faq">SPDY에 대한 FAQ</h2>

<p><strong>Q: HTTP piplelining이 이미 응답시간에 대한 문제를 해결하지 않았나요?</strong></p>

<p>A: No. 파이프라이닝이 다수의 요청을 하나의 TCP 연결하에서 병렬로 보내는 것을 지원하고 있지만, 여전히 단일 스트림입니다. 스트립 내의 과정에서 지연이 발생하면(첫 요청이 long request이거나 패킷 손실이 발생하거나해서) 전체 스트림이 지연됩니다. 파이프라이닝은 적용이 어렵기도 해서 대부분의 주요 브라우저에서 기본적으로 비활성화된 채로 남아있지요.</p>

<p><strong>Q: SPDY는 HTTP를 대체하는 겁니까?</strong></p>

<p>A: 아니오. SPDY는 HTTP의 일부분을 대체할 뿐이고, HTTP를 확장합니다. 어플리케이션 계층의 최상위 레벨에서 요청-응답 프로토콜은 여전히 동일합니다. SPDY는 여전히 HTTP 메소드, 헤더 등을 사용합니다. 그러나 SPDY는 커넥션 관리와 데이터 전송 포맷 등의 프로토콜의 다른 부분을 오버라이드합니다.</p>

<p><strong>Q: 왜 이름을 이렇게 지었습니까?</strong></p>

<p>A: 우리는 이름이 ‘속도’의 의미를 내포하길 원했습니다. SPDY(SPeeDY라고 읽음)는 그에 부합할 뿐만 아니라 압축이 어떻게 속도 향상에 도움되는지를 보여주기도 하지요.</p>

<p><strong>Q: SPDY는 전송계층을 변경해야합니까?</strong></p>

<p>A: 전송계층의 보완이 지연을 줄일 수 있는지는 더 연구해봐야 합니다. 그러나 전송계층의 교체는 복잡한 일이고, 어플리케이션 레이어에서 TCP와 HTTP의 비효율적인 부분들을 극복할 수 있다면 현실 적용이 더 쉬울거라고 봅니다.</p>

<p><strong>Q: TCP는 네트웍의 혼잡이나 붕괴를 유발하지 않는지 오랜 시간 사용되면서 검증되었습니다. SPDY가 인터넷을 깨뜨릴 수도 있습니까?</strong></p>

<p>A: 아니오. SPDY는 TCP 위에서 작동하므로 TCP의 혼잡제어 알고리즘들의 혜택을 받습니다. 게다가 혼잡 제어 방법을 수정한 HTTP가 인터넷에서 동작하고 있습니다. 예를 들면, 오늘날의 HTTP 클라이언트는 하나의 서버에 6개의 연결을 동시에 엽니다. 그리고 어떤 HTTP 서버는 초기 congestion window를 4 패킷으로 증가시켰습니다. TCP가 각각의 연결들을 독립적으로 속도를 올릴 수 있기 때문에 서버는 초기부터 효과적으로 24패킷을 보낼 수 있습니다. 다수 연결에서 TCP의 slow-start 문제를 회피한 것이죠. 반면 SPDY는 하나의 연결 위에 다수의 스트림을 구현한 겁니다.</p>

<p><strong>Q: SCTP는 뭡니까?</strong></p>

<p>A: SCTP는 재미있는 대한 전송 프로토콜이에요. 이것은 하나의 커넥션 위에 다수의 스트림을 제공합니다. 그러나 이건 전송계층의 변경을 필요로 해서 가정용 라우터에까지 적용하려면 매우 어렵운 일이 되죠. 또한 SCTP는 은총알(silver bullet)이 아니에요. 서버와 클라이언트 사이의 채널을 효율적으로 사용하려면 어플리케이션 계층의 변경 역시 필요하죠.</p>

<p><strong>Q: BEEP은 뭡니까?</strong></p>

<p>A: BEEP은 비슷한 여러가지 기능을 제공하는 재미있는 프로토콜이지만, 이것은 페이지 로드 타임을 줄이는 것을 목표로 하지 않습니다. 그걸 가능하게 할 몇몇 기능이 없지요. BEEP은 바이너리 프레이밍 대신에 텍스트 기반의 프레이밍을 사용합니다. 이것은 확장성에 있어서 경쟁력있는 좋은 프로토콜이지만, 몇몇 보안 문제들도 있고 정확히 파싱하기가 어렵다는 문제도 있습니다.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[TDD에 대한 조금 다른 생각]]></title>
    <link href="http://oddpoet.net/blog/2010/08/02/a-new-look-at-test-driven-development-kr/"/>
    <updated>2010-08-02T02:20:00+09:00</updated>
    <id>http://oddpoet.net/blog/2010/08/02/a-new-look-at-test-driven-development-kr</id>
    <content type="html"><![CDATA[<!-- TODO : redirect to http://oddpoet.net/archives/242 -->
<p>Dave Astels의 <a href="http://techblog.daveastels.com/2005/07/05/a-new-look-at-test-driven-development/">A New Look at Test Driven Development</a>라는 Article을 번역한 글입니다. 
BDD(Behavior Driven Development)의 시작점이라 할 만한 글이지요. 
2005년도에 씌여진 아티클이지만 개발조직에서 TDD의 수행지표로 code coverage를 사용하고 있는 작금의 현실에, 
TDD의 의미를 다시금 새겨보는데 도움이 될 듯 합니다.</p>

<p>참고로 BDD(Behaviour Driven Development)를 일반적으로 ‘행위주도개발’이라고 번역하는 듯하지만, 
문맥상 Behavior를 ‘행위’로 번역할 경우 의미전달이 힘든 부분이 있어서 ‘기대행동(Behavior)’라고 번역/표기했습니다.
사실 길지 않은 글이니 원문을 읽어보시는 걸 권장합니다. (발번역이라 죄송~)</p>

<!-- more -->
<p>## A New Look at Test Driven Development</p>

<p>Test Driven Development (TDD)는 요즘 전성기를 맞고 있다. 
큰 회사들은 개발자들이 TDD를 교육하는데 많은 돈을 쓰고 있으며, 컨퍼런스에도 TDD는 단골주제다. 
내 책(<a href="http://www.amazon.com/Test-Driven-Development-Practical-David-Astels/dp/0131016490">Test-Driven Development: A Practical Guide</a>)은 Jolt award를 수상했다. 
모든게 잘 되어 가는 것 같다. 
TDD를 적용한 모두가 TDD를 완전히 이해하고 있고, 그로 인한 혜택을 누리고 있다. 정말 그런가?</p>

<h3 id="fat-chance">우울한 현실 (Fat Chance!)</h3>

<p>내가 얘기해본 10% 정도의 사람들만 TDD가 무엇인지 이해하고 있다. 
사실 5% 정도밖에 안될지도… 어디서 잘못된걸까? 
사람들은 TDD를 <strong>test</strong>에 대한 것이라고 생각한다. <strong>case</strong>라고 생각하지 않는다는 거다.</p>

<p>조금 비슷하기는 하지만, 일반적으로 사람들은 TDD를 low level의 회귀테스트 정도로 본다. 
그런데 그건 TDD의 부수적인 이익 정도에 불과하다. 
왜 이런 일들이 비일비재하게 일어나는 걸까? 잠깐 과거로 돌아가보자. 
만약 <a href="http://bdn.borland.com/article/0,1410,29689,00.html">my old Coad Letter</a> 의 예전 이슈들을 봤다면, 그곳에서 TDD의 배경이 되는 것들을 찾을 수 있다.</p>

<pre><code>원래 XP는 깨질 수 있는 모든 것들을 테스트한다는 원칙을 가지고 있었다. 
그리고 XP의 테스팅 관습은 Test Driven Development로 진화했다.
XP originally had the rule to test everything that could possibly break. 
Now, however, the practice of testing in XP has evolved into Test-Driven Development.
</code></pre>

<p>그들이 테스트를 작성하는 것에 대해 얘기하던 그 시절을 생각해보면, 
왜 TDD가 테스트 중심의 어휘들을 가지게 되었는지를 상상할 수 있다. 
그리고 이게 사람들이 TDD가 테스트가 목적이라고 생각하게된 이유이다. 
그들이 TestCases, TestSuites, Tests 등에 대해 얘기했고, 
메소드 이름을 “test”로 시작하게 했기 때문이다. 
jUnit의 경우 그렇다. nUnit은 test로 메소드 이름이 시작해야 한다는 제약은 없다. 
(역주: JUnit도 annotation을 활용하게 되면서 이제 method 이름에 대한 명명제약은 없어졌지요.)</p>

<p>결국 TDD로 진화하면서 우리는 전혀 다른 종의 동물을 만들어 냈다. 
원래 XP 관습은 깨질 수 있는 모든 것들에 대한 테스트를 작성하는 것이었다. 
그래서 테스트를 먼저 작성하는 것에서 시작했고, 결국 TDD가 되었다. 
그러나 TDD는 사람들이 생각하는 것처럼 최종점이 아니다. 
TDD는 단지 다음 단계를 위한 발판일 뿐이다.</p>

<p>사실 <strong>unit</strong>이라는 단어가 가장 큰 문제다. 
그 이유는 첫번째로 <strong>unit</strong>은 막연한 용어라는 점이고, 
두번째로는 그 단어가 코드의 구조적 분할을 의미한다는 점이다. 
그래서 사람들은 method나 class를 테스트해야 한다고 생각한다. 
우리는 <em>unit</em>에 대해서 생각해야 하는게 아니고, <strong>기대행동(behavior)</strong>의 관점에서 생각해야 한다.</p>

<p>unit test에 대한 이런 생각들은 우리들이 코드의 구조에 따라 테스트를 나누도록 한다. 
예를 들면, product class들과 test class들이 1:1 관계를 만드는 것이다.</p>

<p>그건 우리가 원하는 게 아니다. 실제로는 기대행동(behaviour) 단위로 나누는 것이 필요하다. 
또한 일반적인 unit test보다 더 작은 단위의 레벨에서 작업하는 것이 필요하다. 
우리는 작은 단위의 기대행동들에 초점을 맞추어, 매우 작은 단위로 작업해야 한다. 
예를 들면, 
“리스트가 비어 있을 때<strong>[Given]</strong>, 
객체에 대해 add() 메소드가 호출되면<strong>[When]</strong>, 
리스트에는 하나만 들어있어야 한다<strong>[Then]</strong>.”에서 
메소드는 매우 특정 상황(context)에서 특정 매개변수와 함께 호출되고, 
매우 특정한 결과가 만들어지는… 이렇게 하나의 메소드에 대해 더 작은 단위로 작업해야 한다.</p>

<p>이것이 바로 “테스트”라는 관점에 의해서 가려져버린 고대의 아이디어이다. 
왜 이렇게 되었을까? 사람들이 “Test”에 대해 어떻게 생각하는지 보자.</p>

<p>개발자들은 보통 이렇게 생각한다: 
“난 모든 테스트를 작성할 생각은 없어”, 
“이건 매우 단순한 코드라서 테스트할 필요가 없거든”, 
“테스트는 시간 낭비야”, 
“난 이미 많이 해봤어”.</p>

<p>프로젝트 매니져들은 이렇게 생각한다: 
“테스트는 코드가 다 만들어 진 후 하는거야”, 
“우리는 테스팅할 사람들이 따로 있어”, 
“당장 거기에 쏟을 시간은 없어”….</p>

<p>사람들은 테스팅에 대한 생각은 이렇게도 부정적이고, 하지 않을 이유를 쉽게도 찾아낸다. 
특히나 시간이 없고, 압박이 심한 경우는 더욱….</p>

<h3 id="tdd----so-if-its-not-about-testing-whats-it-about">TDD가 테스트가 아니라면, 뭔데? (So if it’s not about testing, what’s it about?)</h3>

<p>TDD는 실제 코딩을 하기 전에 무엇을 만들 것인지 생각해보는 작업이다. 
즉, 간략하고, 모호하지 않으며, 실행가능한 형태으로 기대행동(behavior)들을 
보다 작은 단위로 명세화(specification)하는 과정이다. 
이 과정은 테스트를 작성하는 것일까? 아니다. 
이것은 코드가 무엇을 해야하는지에 대한, 명세(specification)을 작성하는 것이다. 
그래서 이것은 코드를 작성하기 전에 작성하는 것이 좋다. 
더 많은 명세들이 해야할 일을 더 명확히 해주기 때문이다. 
TDD의 기본 방식대로 조금씩 점진적으로 작업한다. 
기대행동(behavior)들을 작은 단위로 명세화하고, 그리고 그것을 구현한다.</p>

<p>이제 TDD가 테스트를 작성하는 것이 아니라, 
기대행동(behavior)에 대한 명세에 대한 것이라는 걸 알았다면 관점이 바뀔 것이다. 
각 product class마다 test class를 만든다거나, 
어떤 method에 대한 test method를 만들어서 테스트한다던가 하는 일들이 
얼마나 어이없는 일인지 느껴질 것이다.</p>

<h3 id="so-what-to-do">그럼 무엇을 할까?(So What to do?)</h3>
<p>test라는 관점에서 생각하지 않으려고 해도 JUnit은 그걸 어렵게 만든다. 
그럼 이런 일에 걸맞는 새로운 프레임웍이 있을까? 
ThoughtWorks의 Dan North는 jBehave 라는 프로젝트를 시작했다.</p>

<p>기대행동(behavior) 중심의 어휘와 개념들을 사용하는 행동 중심의 프레임웍을 사용하면, 기대행동(behavior)에 대한 명세로써 생각하는데 도움이 된다.</p>

<h3 id="a-behaviour-specification-framework">기대행동 명세 프레임웍(A Behaviour Specification Framework)</h3>
<p>기대행동(behavior)에 대한 명세는 어떤 모습이어야 할까? 
다음과 같은 부분에서는 jUnit과 같은 것들과 비슷해야 할 것이다.</p>

<ul>
  <li>충분히 잘 동작한다.</li>
  <li>모든 사람들에게 익숙한 형태이다.</li>
</ul>

<p>가장 큰 차이는 어휘(vocabulary)이다. 
<em>TestCase</em>를 상속받는 것이 아니라, <em>Context</em>를 상속받는다. 
그리고 method를 쓸때 <em>test</em>가 아니라, <em>should</em>로 시작하는 이름을 사용한다. 
검증에는 <code>assertions</code>(e.g. assertEquals(exp, act)) 대신, 
<code>shouldBeEqual</code>(act, exp) 같은 어휘들을 사용한다.</p>

<p>Smalltalk와 Ruby의 경우, class library 안으로 이런 framework을 내장시키는데 좀더 용이하다. 
예를 들면, <code>actual shouldEqual: expected</code> 나, <code>result shouldBeNull</code>, <code>[2/0] shouldThrow: DivideByZeroException</code> 과 같은 형태로 쓰는 것이 가능하다.</p>

<h3 id="what-now">현재 진행 상황은…(What Now?)</h3>
<p>앞에서 얘기한 대로 Dan North는 기대행동(behavior) 명세를 위한 junit 대체물로 
jBehave 프로젝트를 시작했다. 
나도 조금 다른 방법으로 그 프로젝트에 참여할 것이다. 
나는 Smalltalk와 Ruby에서 이러한 아이디어들을 적용해볼 계획을 가지고 있고, 
행위기반의 jUnit 버전을 위한 작업을 진행중이다.</p>

<h3 id="section">요약</h3>
<p>TDD에 대해 알고 있는 모든 것이 무용지물이 된다는 얘기인가? 아니다. 
TDD의 모든 기술을은 BDD에도 적용가능하다. 
행위를 기술한다는 접근에서 이것은 TDD와 같다. 
여전히 mock을 사용할 수도 있다. 
바뀐 것은 어휘와 관점이다. </p>

<p>관점은 매우 중요하다. 
프로세스의 가치가 어떻게 바뀌었는가? 
다수의 <strong>should</strong>를 사용하며, should method의 이름들은 보다 명세를 명확하게 해줄 것이다. 
예를 들면…</p>

<ul>
  <li><code>shouldAllowValidUser</code></li>
  <li><code>shouldCheckIsValidBeforeUpdate</code></li>
  <li><code>shouldUpdateRecordSet</code></li>
  <li><code>shouldWriteToUpdateLog</code></li>
</ul>

<p>요약하면…</p>

<ol>
  <li>TDD에 대한 문제는 우리들이 서로 다른 방향을 바라보고 있었다는 것이다. 틀린 방향을…</li>
  <li>우리는 검증 테스트(verification test)가 아니라, 기대행동 명세(behavior specifications)를 
 생각하는 것에서 시작할 필요가 있다.</li>
  <li>클래스나 메소드에 대한 테스트라는 관점보다, 각각의 기대행동(behavior)의 관점이 더 명확해지므로써, 
 실행가능한 문서를 가질 수 있게 된다는 잇점이 있다.</li>
  <li>TDD가 생겨난 이후 어느 누구도 그 이름의 의미를 바꾸거나, 우리가 기대하는 바를 바꾸지 않았다. 
 그래서 우리는 이 새로운 작업 방식에 <strong>BDD:”Behaviour Driven Development”</strong> 라는 
 새로운 이름을 사용하려고 한다.</li>
</ol>

]]></content>
  </entry>
  
</feed>
