<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
>

<channel>
	<title>EMSTONE Bebop &#187; CPAN</title>
	<atom:link href="http://bebop.emstone.com/tags/cpan/feed/" rel="self" type="application/rss+xml" />
	<link>http://bebop.emstone.com</link>
	<description>엠스톤 개발팀 블로그</description>
	<lastBuildDate>Sun, 29 Jan 2012 05:25:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<creativeCommons:license>http://creativecommons.org/licenses/by-nc-nd/2.0/kr/</creativeCommons:license>
		<item>
		<title>[Digest] 펄 스크립트로 상호 작용하는 Ack 쉘 만들기</title>
		<link>http://bebop.emstone.com/2009/02/18/ack-shell-with-perl-script/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ack-shell-with-perl-script</link>
		<comments>http://bebop.emstone.com/2009/02/18/ack-shell-with-perl-script/#comments</comments>
		<pubDate>Tue, 17 Feb 2009 17:31:57 +0000</pubDate>
		<dc:creator>keedi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[ack]]></category>
		<category><![CDATA[App::Ack]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[Digest]]></category>
		<category><![CDATA[grep]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[       주의 : [[Digest] 펄 스크립트로 상호 작용하는 Ack 쉘 만들기]의 가장 최근 판은  이곳 에서 확인할 수 있습니다.

 CPAN의  Andy Lester 가 제작한  App::Ack  모듈은 펄로 만든 강력한 문자열 검색 도구 입니다. 한 번 사용하면 즐겨 사용하던 GNU grep을 버려버릴 수 있을 정도로  grep 을 사용하면서 한 번쯤은 느낄 아쉬운 부분들을 보완하는 다양한 기능과 옵션을 가지고 있습니다.  App::Ack  모듈을 설치하면 명령줄 실행 프로그램인  ack 를 같이 설치하므로 특별히  App::Ac...
     <a href="http://bebop.emstone.com/2009/02/18/ack-shell-with-perl-script/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong>주의</strong>: [[Digest] 펄 스크립트로 상호 작용하는 Ack 쉘 만들기]의 가장 최근 판은 <a href="http://bebop.emstone.com/~keedi/timetolog/id/23">이곳</a>에서 확인할 수 있습니다.</p>
<p>CPAN의 <a href="http://search.cpan.org/~petdance/">Andy Lester</a>가 제작한 <a href="http://search.cpan.org/perldoc?App::Ack">App::Ack</a> 모듈은<br />
펄로 만든 강력한 문자열 검색 도구 입니다.<br />
한 번 사용하면 즐겨 사용하던 GNU grep을 버려버릴 수 있을 정도로<br />
<code>grep</code>을 사용하면서 한 번쯤은 느낄 아쉬운 부분들을 보완하는<br />
다양한 기능과 옵션을 가지고 있습니다.<br />
<code>App::Ack</code> 모듈을 설치하면 명령줄 실행 프로그램인 <code>ack</code>를 같이 설치하므로<br />
특별히 <code>App::Ack</code> 모듈을 이용한 프로그램을 작성할 것이 아니라면<br />
기본으로 제공하는 <code>ack</code> 프로그램만으로도 사용하는데는 지장이 없습니다.</p>
<p><code>ack</code>는 기본으로 버전 관리 프로그램에서 사용하는 내부 디렉터리를<br />
검색 대상에서 제외하기 때문에 프로그램 작성 중 CVS 또는 서브버전 저장소 안에서<br />
검색시 <code>grep</code>에 비해 아주 쾌적하게 사용할 수 있습니다.<br />
또한 펄 프로그래머라면 당연히 익숙할 <em>펄의 정규표현식</em> 을<br />
동일하게 지원하기 때문에 별도로 정규표현식 문법을<br />
익힐 필요가 없다는 것도 큰 장점입니다.<br />
<code>ack</code>의 자세한 기능과 다양한 옵션에 대해서는 <code>ack --help</code>를 참조하세요.</p>
<p>프로그램을 작성할 때 기존 코드를 검색해서 구조를 파악하거나<br />
호출 상관 관계를 살펴볼 때 <code>grep</code>과 같은 문자열 검색 프로그램은 매우 유용합니다.<br />
<code>ack</code>를 사용하면 효율을 더욱 높일 수 있는데 한 가지 아쉬운 점은<br />
매번 검색시 마다 <code>ack</code>와 원하는 옵션을 입력해야 한다는 것입니다.<br />
이것은 매번 사용자의 검색 질의어 입력을 받아서 <code>ack</code>를 호출하는<br />
간단한 펄 스크립트를 작성하면 쉽게 해결할 수 있습니다:</p>
<pre><code>  #!/usr/bin/perl

  use strict;
  use warnings;

  input();

  while (&lt;&gt;) {
      chomp;
      last if /^gg$/;
      next if !$_;

      print &lt;&lt;"END_MSG";

  ------------------------------------------------------------------------
  Search Keyword: [$_]
  ------------------------------------------------------------------------
  END_MSG

      if ( m{^/(.*)/$} ) {
          system 'ack', '--pager=more', '--match', $1, '.';
      }
      elsif ( m{^/(.*)/i$} ) {
          system 'ack', '--pager=more', '--ignore-case', '--match', $1, '.';
      }
      else {
          system 'ack', '--pager=more', '--literal', $_, '.';
      }

  } continue {
      input();
  }

  sub input {
      print &lt;&lt;"END_INPUT";

  * This is a Simple Ack shell.
  * /PATTERN/  : PATTERN will be interpreted as Perl's regular expression.
  * /PATTERN/i : PATTERN will be interpreted as Perl's regular expression
                 with ignore case option
  * PATTERN    : PATTERN will be interpreted as literal characters.
  * gg         : quit the program.

  END_INPUT

      print "Search Keyword: ";
  }
</code></pre>
<p>위의 펄 스크립트는 매우 간단한 구조를 가지고 있습니다.<br />
사용자의 입력을 받아들인 후 사용자의 입력을 이용해서<br />
정규표현식 검색을 할 것인지 또는<br />
대소문자 구분없는 정규표현식 검색을 할 것인지,<br />
단순 문자열 검색을 할 것인지, 프로그램을 종료할 것인지를 판단합니다.<br />
그 후 실제 검색은 <code>ack</code> 프로그램을 외부 호출을 통해 수행하고<br />
결과 역시 <code>ack</code>가 만들어내는 화면을 그대로 표준 출력에 보여줍니다.</p>
<p>규모가 있는 프로그램을 작성할 때 <code>ctags</code> 와 <code>cgvg</code><br />
그리고 간단하게 <code>ack</code>와 상호작용하는 펄 스크립트를 이용해서<br />
코드를 검색하는데 작업 능률면에서 매우 만족하고 있습니다. <img src='http://bebop.emstone.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://bebop.emstone.com/2009/02/18/ack-shell-with-perl-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by-nc-nd/2.0/kr/</creativeCommons:license>
	</item>
		<item>
		<title>Module::Pluggable 플러그인 프레임워크를 이용한 확장성 있는 프로그램 작성</title>
		<link>http://bebop.emstone.com/2009/02/17/module-pluggable-perl-plugin/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=module-pluggable-perl-plugin</link>
		<comments>http://bebop.emstone.com/2009/02/17/module-pluggable-perl-plugin/#comments</comments>
		<pubDate>Tue, 17 Feb 2009 01:53:07 +0000</pubDate>
		<dc:creator>keedi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[Module::Pluggable]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[       주의 : [Module::Pluggable 플러그인 프레임워크를 이용한 확장성 있는 프로그램 작성]의 가장 최근 판은  이곳 에서 확인할 수 있습니다.

 시작하며

 프로그램이 확장성이 좋다는 것은 변화에 유연하게 대처할 수 있으며 기능을 추가하는데 비용이 적게 든다는 것을 의미합니다. 확장성 있는 프로그램을 만드는 여러가지 방법이 있지만 플러그인 구조는 그 중에서도 널리 쓰이고 있는 기법 중 하나입니다. 보통 전산 분야에서 플러그인(plug-in)은 기능 확장용 소프트웨어를 지칭합니다. 플러그인 구조를 지원하는 소프트웨어는 글...
     <a href="http://bebop.emstone.com/2009/02/17/module-pluggable-perl-plugin/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong>주의</strong>: [Module::Pluggable 플러그인 프레임워크를 이용한 확장성 있는 프로그램 작성]의 가장 최근 판은 <a href="http://bebop.emstone.com/~keedi/timetolog/id/22">이곳</a>에서 확인할 수 있습니다.</p>
<h3>시작하며</h3>
<p>프로그램이 확장성이 좋다는 것은 변화에 유연하게 대처할 수 있으며<br />
기능을 추가하는데 비용이 적게 든다는 것을 의미합니다.<br />
확장성 있는 프로그램을 만드는 여러가지 방법이 있지만<br />
플러그인 구조는 그 중에서도 널리 쓰이고 있는 기법 중 하나입니다.<br />
보통 전산 분야에서 플러그인(plug-in)은 기능 확장용 소프트웨어를 지칭합니다.<br />
플러그인 구조를 지원하는 소프트웨어는 글자 그대로 플러그에 꽂듯이<br />
새로운 기능의 모듈을 제작한 후 원래의 소프트웨어에 꽂으면<br />
별다른 수고없이 해당 기능을 사용할 수 있는 것이 특징입니다.<br />
펄의 <a href="http://perldoc.perl.org/Module/Pluggable.html">Module::Pluggable</a> 모듈을 이용하면 플러그인 구조를<br />
손쉽게 지원할 수 있기 때문에 플러그인 구조를 만들기 위해 수고를 들일 필요없이<br />
최소한의 코드로도 가독성 있고 확장성 있는 프로그램을 작성할 수 있습니다.</p>
<h3>관련 연구</h3>
<p><a href="http://perldoc.perl.org/Module/Pluggable.html">Module::Pluggable</a>은 플러그인 형식의 서브 모듈을<br />
지원하는 모듈을 만들 수 있게 도와주는 간단한 프레임워크입니다.<br />
원래는 기본 내장 모듈이 아니었으나 5.9.5 개발 버전부터<br />
<a href="http://perldoc.perl.org/perldelta.html#New-modules">기본 내장 모듈로 포함</a>되어서<br />
근래에 배포되고 있는 5.10 안정 버전에서는 추가 설치없이 사용 가능합니다.<br />
만약 자신이 사용하고 있는 펄의 버전이 5.8 이하라면<br />
<a href="http://search.cpan.org/perldoc?Module::Pluggable">CPAN을 통해 설치</a>해야 합니다.</p>
<p>사용법은 간단합니다.<br />
플러그인 기능을 지원하려는 모듈에 다음의 예제처럼<br />
<code>Module::Pluggable</code>을 적재하는 코드를 넣어주면 됩니다:</p>
<pre><code>  package MyClass;

  use strict;
  use warnings;
  use Module::Pluggable require =&gt; 1;

  sub new {
      my ( $class, @args ) = @_;

      # ...
  }
</code></pre>
<p>너무 간단하지만 이것으로 모든 준비는 끝났습니다.<br />
<code>MyClass</code> 모듈은 이제 플러그인 모듈들을 마음껏 사용할 수 있습니다.<br />
<code>MyClass</code> 모듈이 자신의 모듈 하부에 적재되는<br />
플러그인 모듈을 사용하는 방법은 다음과 같습니다:</p>
<pre><code>  use strict;
  use warnings;
  use MyClass;

  my $mc = MyClass-&gt;new;
  my @plugins = $mc-&gt;plugins;
</code></pre>
<p><code>MyClass</code> 모듈에 <code>Module::Pluggable</code>을 적재하면 <code>Module::Pluggable</code>은<br />
<code>MyClass</code> 이름 공간에 <code>plugins</code> 함수를 자동으로 끼워넣습니다.<br />
일종의 이름 공간 침해라고 생각할 수도 있지만<br />
펄에서는 매우 유용하게 사용할 수 있는 기교 중 하나입니다.<br />
<code>plugins</code> 함수는 <code>MyClass::Plugin::*</code> 하부의 모든 모듈의 이름 목록을 반환합니다.</p>
<p><code>MyClass::Plugin::Add</code> 모듈의 예제는 다음과 같습니다:</p>
<pre><code>  package MyClass::Plugin::Add;

  use strict;
  use warnings;

  sub run {
      my ( $self, $num1, $num2 ) = @_;

      return $num1 + $num2;
  }

  1;
</code></pre>
<p><code>MyClass::Plugin::Add</code> 플러그인은 <code>run</code> 함수를 가지고 있으며<br />
<code>MyClass</code> 모듈 사용자는 다음처럼 이 플러그인을 사용할 수 있습니다:</p>
<pre><code>  use strict;
  use warnings;
  use MyClass;

  my $mc = MyClass-&gt;new;

  my $result;
  foreach my $plugin ($self-&gt;plugins) {
      next $plugin eq 'MyClass::Plugin::Add';
      last $plugin-&gt;can('run');
      $result = $plugin-&gt;run(4, 5);
      last;
  }
</code></pre>
<p>실제 플러그인 모듈을 어떤 식으로 구성해야 하는지에<br />
대해서는 어떠한 제약 사항도 없습니다.<br />
모든 것은 <code>MyClass</code> 모듈을 설계하는 프로그래머에게 달려있습니다.<br />
또한 <code>Module::Pluggable</code>은 다양한 옵션을 지원합니다.<br />
옵션을 이용해서 <code>plugins</code> 대신 자동으로 추가할 함수의 이름을 바꿀 수 있으며<br />
플러그인 하부 모듈의 위치도 <code>MyClass::Plugin::</code> 하부가 아닌 다른 위치로<br />
변경할 수 있습니다.<br />
그 외 다른 유용한 옵션들이 많으므로<br />
자세한 것은 <a href="http://perldoc.perl.org/Module/Pluggable.html">문서</a>를 참조하세요.</p>
<h3>구현</h3>
<h4>구성</h4>
<p>좀 더 실질적인 예제인 <code>Wiki</code> 모듈과 <code>Wiki::Plugin::*</code> 모듈을 작성해보겠습니다.<br />
위키를 만들다보면 해당 페이지의 체크썸을 확인해야할 일이 있습니다.<br />
이 때 MD5 해시라던가 SHA1 등 다양한 체크썸을 기호에 맞게 사용할 수 있으면<br />
유용할 것입니다. 이런 체크썸 기능을 플러그인 방식으로 사용할 수 있게 구성합니다.</p>
<p>또한 위키의 각 페이지는 보통 위키 문법을 이용해서 작성한 후 HTML로 변환합니다.<br />
취향에 따라 위키의 페이지를 위키 문법이 아닌 <a href="http://daringfireball.net/projects/markdown/">마크다운(markdown)</a>이나<br />
펄의 <a href="http://perldoc.perl.org/perlpod.html">POD</a> 문법을 사용하고 싶은 경우도 있습니다.<br />
이 뿐만 아니라 자신만의 즐겨쓰는 문법을 정의해서 사용할 수 있으면 유용할 것입니다.<br />
플러그인 구조를 이용해서 위키가 다양한 문법을 지원할 수 있게 구성합니다.</p>
<p><code>Wiki</code> 모듈의 플러그인 구조는 크게 <code>Wiki::Plugin::Digest::*</code> 모듈과<br />
<code>Wiki::Plugin::Renderer::HTML::*</code> 모듈로 구성합니다:</p>
<pre><code>  Wiki.pm
  Wiki/
    Plugin/
      Digest/
        HelloWorld.pm
        MD5.pm
        SHA1.pm
      Renderer/
        HTML/
          GoogleWiki.pm
          Markdown.pm
          MediaWiki.pm
          PlainText.pm
          Pod.pm
          WikiText.pm
</code></pre>
<h4>최상위 모듈</h4>
<p>다음은 각 모듈들의 코드입니다.<br />
먼저 최상위 모듈인 <code>Wiki</code> 모듈입니다:</p>
<pre><code>  package Wiki;

  use strict;
  use warnings;
  use Module::Pluggable require =&gt; 1;

  use base qw(Class::Accessor::Fast);
  Wiki-&gt;mk_ro_accessors(qw(digest renderer));

  sub new {
      my $class  = shift;
      my %params = (
          @_,
      );

      my $self = bless \%params, $class;
      $self-&gt;init;

      return $self;
  }

  sub init {
      my $self = shift;

      for my $plugin ( $self-&gt;plugins ) {
          if ($plugin =~ m/^Wiki::Plugin::Renderer::HTML::(.*)$/) {
              $self-&gt;{renderer}-&gt;{lc $1} = $plugin;
          }
          elsif ($plugin =~ m/^Wiki::Plugin::Digest::(.*)$/) {
              $self-&gt;{digest}-&gt;{lc $1} = $plugin;
          }
      }
  }

  sub handle_renderer {
      my ( $self, $src ) = @_;

      my ( $format, $content ) = split "\n", $src, 2;
      if ( $format =~ m/^#!(.*)/ ) {
          $format = lc $1;
      }
      else {
          $format = 'plaintext';
      }

      my $plugin = $self-&gt;renderer-&gt;{$format};

      my $dest;
      if ( $plugin &amp;&amp; $plugin-&gt;can('run') ) {
          $dest = $plugin-&gt;run($src);
      }
      else {
          $dest = "&lt;pre&gt;Error: Cannot find [$format] renderer plugin.&lt;/pre&gt;";
      }

      return $dest;
  }

  sub handle_digest {
      my ( $self, $type, $src ) = @_;

      my $plugin = $self-&gt;digest-&gt;{lc $type};
      return unless $plugin-&gt;can('run');

      my $dest = $plugin-&gt;run($src);

      return $dest;
  }

  1;
</code></pre>
<h4>체크썸 플러그인</h4>
<p><code>Wiki::Plugin::Digest::HelloWorld</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Digest::HelloWorld;

  use strict;
  use warnings;

  sub run {
      my $self = shift;
      my $msg  = shift || q{};

      return "Hello $msg!";
  }

  1;
</code></pre>
<p><code>Wiki::Plugin::Digest::MD5</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Digest::MD5;

  use strict;
  use warnings;
  use Digest;

  my $d = Digest-&gt;new("MD5");

  sub run {
      my $self = shift;
      my $msg  = shift || q{};

      $d-&gt;reset;
      $d-&gt;add( $msg );

      return $d-&gt;hexdigest;
  }

  1;
</code></pre>
<p><code>Wiki::Plugin::Digest::SHA1</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Digest::SHA1;

  use strict;
  use warnings;
  use Digest;

  my $d = Digest-&gt;new("SHA-1");

  sub run {
      my $self = shift;
      my $msg  = shift || q{};

      $d-&gt;reset;
      $d-&gt;add( $msg );

      return $d-&gt;hexdigest;
  }

  1;
</code></pre>
<h4>HTML 변환 플러그인</h4>
<p><code>Wiki::Plugin::Renderer::HTML::GoogleWiki</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Renderer::HTML::GoogleWiki;

  use strict;
  use warnings;
  use Text::GooglewikiFormat;

  sub run {
      my ( $self, $src ) = @_;

      my $dest = Text::GooglewikiFormat::format($src);

      return $dest;
  }

  1;
</code></pre>
<p><code>Wiki::Plugin::Renderer::HTML::Markdown</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Renderer::HTML::Markdown;

  use strict;
  use warnings;
  use Text::MultiMarkdown;

  my $parser = Text::MultiMarkdown-&gt;new(
      empty_element_suffix =&gt; ' /&gt;',
      tab_width            =&gt; 2,
      use_wikilinks        =&gt; 1,
      img_ids              =&gt; 1,
      heading_ids          =&gt; 1,
  );

  sub run {
      my ( $self, $src ) = @_;

      my $dest = $parser-&gt;markdown( $src );

      return $dest;
  }

  1;
</code></pre>
<p><code>Wiki::Plugin::Renderer::HTML::MediaWiki</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Renderer::HTML::MediaWiki;

  use strict;
  use warnings;
  use Text::MediawikiFormat qw(wikiformat);

  sub run {
      my ( $self, $src ) = @_;

      my $dest = wikiformat(
          $src,
          {},
          { implicit_links =&gt; 1, },
      );

      return $dest;
  }

  1;
</code></pre>
<p><code>Wiki::Plugin::Renderer::HTML::PlainText</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Renderer::HTML::PlainText;

  use strict;
  use warnings;

  sub run {
      my ( $self, $src ) = @_;

      my $dest = &lt;&lt;"END_HTML";
  &lt;pre&gt;
  $src
  &lt;/pre&gt;
  END_HTML

      return $dest;
  }

  1;
</code></pre>
<p><code>Wiki::Plugin::Renderer::HTML::Pod</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Renderer::HTML::Pod;

  use strict;
  use warnings;
  use Pod::Simple::HTML;
  use HTML::Entities;

  my $parser = Pod::Simple::HTML-&gt;new;
  $parser-&gt;index(1);
  $parser-&gt;html_header_before_title(0);
  $parser-&gt;html_header_after_title(0);
  $parser-&gt;html_footer(0);

  sub run {
      my ( $self, $src ) = @_;

      my $dest;
      $parser-&gt;output_string( \$dest );
      $parser-&gt;parse_string_document( $src );

      # http://chalow.net/2008-05-10-3.html
      $dest = decode_entities($dest);

      return $dest;
  }

  1;
</code></pre>
<p><code>Wiki::Plugin::Renderer::HTML::WikiText</code> 플러그인입니다:</p>
<pre><code>  package Wiki::Plugin::Renderer::HTML::WikiText;

  use strict;
  use warnings;
  use Text::WikiText;
  use Text::WikiText::Output::HTML;

  my $parser      = Text::WikiText-&gt;new;
  my $output      = Text::WikiText::Output::HTML-&gt;new;
  my %parser_opts = (
      full_page      =&gt; 0,
      heading_offset =&gt; 0,
  );

  sub run {
      my ( $self, $src ) = @_;

      my $document = $parser-&gt;parse($src);
      my $dest = $output-&gt;dump($document, \%parser_opts);

      return $dest;
  }

  1;
</code></pre>
<h4>사용</h4>
<p><code>Wiki</code> 모듈은 두 가지 객체 함수인 메소드를 지원합니다:</p>
<pre><code>  use strict;
  use warnings;
  use Wiki;

  my $wiki = Wiki-&gt;new;
  my $digest = $wiki-&gt;handle_digest  ( 'md5', $source );
  my $html   = $wiki-&gt;handle_renderer( $source        );
</code></pre>
<p>체크썸 플러그인과 HTML 변환 플러그인을 다루기 위해<br />
<code>handle_*</code> 함수를 만든 것에 주목하시기 바랍니다.<br />
체크썸 플러그인의 경우 호출하는 시점에 사용할 플러그인을 명시하며<br />
변환 플러그인의 경우 인자의 값을 이용해서 적절한 플러그인을<br />
동적으로 사용하고 있습니다.<br />
물론 어떻게 사용할지는 전적으로 프로그래머에게 달려있습니다.</p>
<h3>정리하며</h3>
<p>펄은 매우 유연하며 강력한 기능을 많이 가지고 있습니다.<br />
자유로운 이름 공간의 제약을 적극적으로 활용한<br />
<code>Module::Pluggable</code>의 기교도 그러한 예 중의 하나입니다.<br />
플러그인 구조를 지원하는 프로그램을 제작하는 것이 어렵지는 않다하더라도<br />
매번 비슷한 형태의 목업(mock-up) 코드를 작성하는 것은 성가신 일입니다.<br />
<code>Module::Pluggable</code>을 이용하면 최소한의 비용으로<br />
확장성있는 플러그인 구조의 장점을 적극 활용할 수 있습니다.<br />
더불어 여유로울때 <code>Module::Pluggable</code> 모듈의 소스 코드를 참고하면<br />
펄에 대해서 더 자세히 알 수 있는 좋은 기회가 될 것입니다.</p>
]]></content:encoded>
			<wfw:commentRss>http://bebop.emstone.com/2009/02/17/module-pluggable-perl-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by-nc-nd/2.0/kr/</creativeCommons:license>
	</item>
		<item>
		<title>CPAN 모듈 작성 #2 &#8211; Module::Install를 이용한 자동 빌드 생성</title>
		<link>http://bebop.emstone.com/2009/01/03/writing-cpan-module-with-module-install/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=writing-cpan-module-with-module-install</link>
		<comments>http://bebop.emstone.com/2009/01/03/writing-cpan-module-with-module-install/#comments</comments>
		<pubDate>Sat, 03 Jan 2009 02:24:32 +0000</pubDate>
		<dc:creator>keedi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[Log::Simple::Color]]></category>
		<category><![CDATA[Module::Install]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[       주의 : [CPAN 모듈 작성 #2 - Module::Install를 이용한 자동 빌드 생성]의 가장 최근 판은  이곳 에서 확인할 수 있습니다.

 시작하며

 CPAN 모듈로써 최소한의 외형적 요건을 갖추기 위해 필요한 것은 몇가지가 있습니다. 모듈 배포를 위해 필요한 파일 목록과  cpan  명령을 이용해서 자동으로 설치를 진행할 수 있도록 도와주는 자동화된 빌드 스크립트, 그리고 모듈의 정상 동작을 보장하기 위한 테스트 집합, CPAN 홈페이지에서 보여주기 위한 문서 등이 그것입니다. 이를 위해 필요한 작업은  지난 글 을...
     <a href="http://bebop.emstone.com/2009/01/03/writing-cpan-module-with-module-install/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong>주의</strong>: [CPAN 모듈 작성 #2 - Module::Install를 이용한 자동 빌드 생성]의 가장 최근 판은 <a href="http://bebop.emstone.com/~keedi/timetolog/id/12">이곳</a>에서 확인할 수 있습니다.</p>
<h3>시작하며</h3>
<p>CPAN 모듈로써 최소한의 외형적 요건을 갖추기 위해 필요한 것은 몇가지가 있습니다.<br />
모듈 배포를 위해 필요한 파일 목록과 <code>cpan</code> 명령을 이용해서 자동으로 설치를<br />
진행할 수 있도록 도와주는 자동화된 빌드 스크립트, 그리고 모듈의 정상 동작을<br />
보장하기 위한 테스트 집합, CPAN 홈페이지에서 보여주기 위한 문서 등이 그것입니다.<br />
이를 위해 필요한 작업은 <a href="http://bebop.emstone.com/~keedi/timetolog/id/8">지난 글</a>을 통해 살펴보았습니다.<br />
도움 모듈 및 스크립트를 이용해서 만든 뼈대 파일들을 이용해 자신의 모듈에<br />
적합하게 필요한 부분은 더하고, 그렇지 않은 부분은 제외하거나 고치도록 합니다.</p>
<h3>관련연구</h3>
<h4>준비물</h4>
<p><code>Module::Install</code>은 가능한 명료하고, 단순하고, 간단하게<br />
CPAN 배포 모듈용 인스톨러를 만들수 있게 도와주는 모듈입니다.<br />
<code>ExtUtils::MakeMaker</code>과 호환되며, 펄 5.004 이후의 버전에서<br />
모두 실행 가능한 것이 특징입니다.<br />
<code>Module::Install</code>을 이용하면 가능한 쉽게 CPAN 모듈을 작성할 수 있습니다.</p>
<ul>
<li><a href="http://search.cpan.org/perldoc?Module::Install">Module::Install</a> &#8211; Standalone, extensible Perl module installer</li>
</ul>
<p><code>Module::Install</code>을 이용해서 만든 <code>Makefile.PL</code>의 예시입니다:</p>
<pre><code>  Makefile.PL:
  use inc::Module::Install;

  # Define metadata
  name           'Your-Module';
  all_from       'lib/Your/Module.pm';

  # Specific dependencies
  requires       'File::Spec'  =&gt; '0.80';
  test_requires  'Test::More'  =&gt; '0.42';
  recommends     'Text::CSV_XS'=&gt; '0.50';
  no_index       'directory'   =&gt; 'demos';
  install_script 'myscript';

  WriteAll;
</code></pre>
<h4>디렉터리 구조</h4>
<p><code>Module::Starter</code>를 이용해서 뼈대를 만들었다면 자동으로 생성된<br />
디렉터리 및 파일 구조는 다음과 같을 것입니다:</p>
<pre><code>  Log-Simple-Color
  Log-Simple-Color/lib
  Log-Simple-Color/lib/Log/Simple
  Log-Simple-Color/lib/Log/Simple/Color.pm
  Log-Simple-Color/t
  Log-Simple-Color/t/perlcritic.t
  Log-Simple-Color/t/pod-coverage.t
  Log-Simple-Color/t/00.load.t
  Log-Simple-Color/t/pod.t
  Log-Simple-Color/.cvsignore
  Log-Simple-Color/Makefile.PL
  Log-Simple-Color/Build.PL
  Log-Simple-Color/Changes
  Log-Simple-Color/README
  Log-Simple-Color/MANIFEST
</code></pre>
<p>실제로 모듈 구현과 관련한 디렉터리는 <code>Log-Simple-Color/lib</code>이며<br />
나머지는 모두 배포 및 테스트와 상관있는 요소입니다.<br />
다음은 각 파일의 목록과 그에 대한 간략한 설명입니다:</p>
<pre><code>  Log-Simple-Color/lib                      : 모듈 저자가 구현한 파일을 담는 디렉터리
  Log-Simple-Color/lib/Log/Simple           : 이름 공간을 위해 자동 생성
  Log-Simple-Color/lib/Log/Simple/Color.pm  : Log::Simple::Color 모듈
</code></pre>
<p>테스트 집합은 모두 <code>Log-Simple-Color/t</code> 하부에 존재합니다.<br />
이 디렉터리에 존재하는 파일 중 확장자가 <code>.t</code> 인 파일은 모두<br />
<code>make test</code> 또는 <code>./Build test</code> 수행시 자동으로 모듈의 동작을<br />
점검하기 위한 테스트 파일입니다.<br />
다음은 각 파일의 목록과 그에 대한 간략한 설명입니다:</p>
<pre><code>  Log-Simple-Color/t                : 자동으로 테스트하기 위한 파일을 보관하는 디렉터리
  Log-Simple-Color/t/perlcritic.t   : 작성한 모듈이 PBP 규칙을 따르는지 점검
  Log-Simple-Color/t/pod-coverage.t : 작성한 모듈의 공개 API 가 문서를 가지는지 점검
  Log-Simple-Color/t/00.load.t      : 작성한 모듈의 적재 성공 여부를 점검
  Log-Simple-Color/t/pod.t          : 작성한 모듈에서 POD 문서 생성 성공 여부 점검
</code></pre>
<p>배포와 관련한 파일 중 자동 빌드를 위한 파일 목록은 다음과 같습니다:</p>
<pre><code>  Log-Simple-Color/Makefile.PL  : make 또는 dmake, nmake 을 위한 Makefile 생성기
  Log-Simple-Color/Build.PL     : make가 없는 환경을 위한 Build.PL 생성기
  Log-Simple-Color/MANIFEST     : 배포용 릴리즈 파일을 생성시 포함할 파일 목록
</code></pre>
<p>CPAN은 배포와 관련한 파일 중 여러분의 모듈을 사용자가 수동으로 다운로드 받은 후<br />
직접 설치시 도움이 될 만한 문서도 포함하는 것을 권장합니다.<br />
물론 이 문서 파일에 의미있는 내용을 넣을지 여부는 개발자에게 달려있습니다.<br />
다음은 배포시 필요한 문서 파일 목록입니다:</p>
<pre><code>  Log-Simple-Color/Changes      : 릴리즈 별로 모듈의 변경사항을 명시하는 문서
  Log-Simple-Color/README       : 사용자가 제일 먼저 읽기를 바라는 문서
</code></pre>
<p>그외 개발의 편의를 돕기 위한 파일 목록은 다음과 같습니다:</p>
<pre><code>  Log-Simple-Color/.cvsignore   : CVS 버전 관리 툴을 사용할 때 무시할 파일 목록
</code></pre>
<h3>구현</h3>
<h4>Build.PL 제거</h4>
<p><code>Log::Simple::Color</code> 모듈은 <code>perl ./Build.PL</code>를 이용한 자동 빌드를<br />
지원하지 않을 것이므로 사용하지 않을 <code>Build.PL</code> 파일을 제거합니다.<br />
<code>Build.PL</code>을 사용할지 <code>Makefile.PL</code>을 사용할지,<br />
또는 두 방법 모두 지원할 것인지는 전적으로 모듈 저자에게 달려있습니다:</p>
<pre><code>  $ rm Build.PL
</code></pre>
<h4>MANIFEST.SKIP 추가</h4>
<p><code>MANIFEST</code> 파일은 배포에 필요한 파일을 명시적으로 기록하는 용도로 사용합니다.<br />
반대로 <code>MANIFEST.SKIP</code> 파일을 이용하면<br />
배포시 넣지 않을 파일을 명시적으로 기록할 수 있습니다.<br />
버전관리와 관련한 파일이나, 임시 파일등을 기록해두면 편리합니다.<br />
<code>MANIFEST.SKIP</code> 파일의 예제는 다음과 같습니다.<br />
대부분의 경우 비슷한 <code>MANIFEST.SKIP</code> 파일을 가지므로 가장 아래 부분에<br />
모듈 이름을 명시한 부분만 자신의 모듈에 맞게 적당하게 변경해주면 됩니다:</p>
<pre><code>  MANIFEST.SKIP:
  # Avoid version control files.
  \bRCS\b
  \bCVS\b
  ,v$
  \B\.svn\b
  \B\.cvsignore$

  # Avoid Makemaker generated and utility files.
  \bMakefile$
  \bblib
  \bMakeMaker-\d
  \bpm_to_blib$
  \bblibdirs$
  ^MANIFEST\.SKIP$

  # Avoid Module::Build generated and utility files.
  \bBuild$
  \bBuild.bat$
  \b_build

  # Avoid Devel::Cover generated files
  \bcover_db

  # Avoid temp and backup files.
  ~$
  \.tmp$
  \.old$
  \.bak$
  \#$
  \.#
  \.rej$

  # Avoid OS-specific files/dirs
  #   Mac OSX metadata
  \B\.DS_Store
  #   Mac OSX SMB mount metadata files
  \B\._
  # Avoid archives of this distribution
  # 자신의 모듈에 맞게 수정합니다.
  \bLog-Simple-Color-[\d\.\_]+
</code></pre>
<h4>테스트 집합 작성</h4>
<h5>Perl::Critic 테스트 회피</h5>
<p>펄 프로그램을 작성시 <a href="http://search.cpan.org/perldoc?Perl::Critic">Perl::Critic</a> 모듈이 제안하는<br />
<a href="http://oreilly.com/catalog/9780596001735/">Perl Best Practices</a> 권고를 따르는 것은 좋은 습관입니다.<br />
그래서 <code>Module::Starter</code> 모듈은 디렉터리 구조를 생성할 때<br />
<a href="http://search.cpan.org/perldoc?Test::Perl::Critic">Test::Perl::Critic</a> 모듈을 사용해서 작성한 코드가<br />
커뮤니티가 권고하는 규칙을 잘 지키는지 점검하는 <code>t/perlcritic.t</code> 테스트를<br />
자동으로 추가합니다.</p>
<p>대부분의 경우 <code>t/perlcritic.t</code>이 알려주는 메시지는 매우 유용하며<br />
견고한 펄 프로그램을 작성할 수 있게 도와줍니다.<br />
하지만 시스템 환경에 따라 선별적으로 모듈을 적재하기 위해서<br />
<code>eval</code>을 써야 하는 상황이라던가, 또는 그렇지 않다하더라도<br />
좀 더 유연한 환경에 편하게 코드를 작성하고 싶을 수도 있습니다.<br />
이 경우는 해당 테스트를 강제로 제외해서 테스트 실패를 막을 수 있습니다.<br />
해당 테스트 파일을 지우거나 확장자를 <code>.t</code>가 아닌 다른 것으로 변경하면<br />
간단히 테스트를 자동으로 수행하지 않도록 할 수 있습니다:</p>
<pre><code>  $ mv t/perlcritic.t t/perlcritic.t.skip
</code></pre>
<p>자동 테스트 목록에서 테스트 파일을 뺐더라도<br />
언제든지 명령줄에서 <code>prove</code> 명령을 통해 수동으로 테스트할 수 있습니다:</p>
<pre><code>  $ prove t/perlcritic.t.skip
</code></pre>
<h5>테스트 파일 추가</h5>
<p>작성한 모듈의 동작을 점검할 수 있는 두 가지 테스트 파일을 추가합니다.<br />
파일의 이름의 아스키문자 순서대로 자동으로 테스트를 수행하므로<br />
먼저 테스트하기를 원하는 파일은 작은 숫자를 붙이도록 합니다.</p>
<p>의존성있는 모듈을 제대로 적재할 수 있는지 점검하는<br />
<code>t/01.dependency.t</code> 파일은 다음과 같이 구성합니다:</p>
<pre><code>  t/01.dependency.t:
  use Test::More tests =&gt; 3;

  BEGIN {
  use_ok( 'Log::Simple::Color' );
  }

  diag( "Testing Log::Simple::Color $Log::Simple::Color::VERSION" );

  SKIP: {
      skip( 'Term::ANSIColor is not required for win32 systems', 1 ) if $^O eq 'MSWin32';
      use_ok( 'Term::ANSIColor' );
  }

  SKIP: {
      skip( 'Win32::Console is not required for non win32 systems', 1 ) if $^O ne 'MSWin32';
      use_ok( 'Win32::Console' );
  }
</code></pre>
<p>모듈의 동작을 점검하는 <code>t/10.logging.t</code> 파일은 다음과 같이 구성합니다:</p>
<pre><code>  t/10.logging.t:
  use strict;
  use warnings;

  use Test::More tests =&gt; 6;
  use Log::Simple::Color;

  diag( "Testing Log::Simple::Color API" );

  my $log = Log::Simple::Color-&gt;new;
  isa_ok( $log, 'Log::Simple::Color', 'instance check' );
  is( $log-&gt;VERSION, '0.0.1', 'version check' );

  is( $log-&gt;level, 'info', 'log level check' );
  is( $log-&gt;debug('This is a debug message'), undef, 'debug is lower level than info' );

  is( $log-&gt;level('unknown level'), 'info', 'unknown log level must be info level' );
  is( $log-&gt;level('debug'), 'debug', 'set log level to debug' );
</code></pre>
<h4>MANIFEST</h4>
<p>앞에서 배포에 필요한 파일을 추가하거나 제거, 또는 이름을 변경했으므로<br />
변경사항에 맞게 <code>MANIFEST</code> 파일을 갱신하도록 합니다:</p>
<pre><code>  제거할 항목:
  Build.PL
  t/perlcritic.t

  추가할 항목:
  MANIFEST.SKIP
  t/01.dependency.t
  t/10.logging.t
  t/perlcritic.t.skip
</code></pre>
<p>변경 후의 <code>MANIFEST</code> 파일은 다음과 같습니다:</p>
<pre><code>  MANIFEST:
  Changes
  MANIFEST
  MANIFEST.SKIP
  Makefile.PL
  README
  lib/Log/Simple/Color.pm
  t/00.load.t
  t/01.dependency.t
  t/10.logging.t
  t/perlcritic.t.skip
  t/pod-coverage.t
  t/pod.t
</code></pre>
<h4>Makefile.PL 재구성</h4>
<p><code>ExtUtils::MakeMaker</code> 또는 <code>Module::Install</code> 모듈을 이용해서<br />
<code>Makefile.PL</code>을 생성하면 사용자는 명령줄에서 다음 명령을 사용해서<br />
여러분의 모듈을 빌드하고, 테스트 한 후 모든 것이 성공적이라면<br />
자동으로 설치까지 할 수 있습니다:</p>
<pre><code>  $ perl Makefile.PL
  $ make
  $ make test
  $ sudo make install
</code></pre>
<p>GNU <code>make</code>가 아니더라도 <code>dmake</code>, <code>nmake</code> 등 시스템에 설치되어 있는 <code>make</code><br />
유틸리티를 사용해서 자동으로 펄 모듈을 빌드 및 테스트, 설치할 수 있습니다.</p>
<p><code>Log::Simple::Color</code> 모듈을 위해 만든 <code>Makefile.PL</code>은 다음과 같습니다:</p>
<pre><code>  Makefile.PL:
  use 5.008_001;
  use strict;
  use inc::Module::Install 0.77;

  # Define metadata
  name          'Log-Simple-Color';
  license       'perl';
  author        'Keedi Kim - 김도형 &lt;keedi@cpan.org&gt;';
  all_from      'lib/Log/Simple/Color.pm';

  # Specific dependencies
  perl_version  '5.008_001';
  requires      'version'         =&gt; 0;
  requires      'File::Spec'      =&gt; '0.80';
  requires      'Module::Install' =&gt; '0.77';
  requires      'Term::ANSIColor' =&gt; '1.12' unless win32;
  requires      'Win32::Console'  =&gt; '0.09' if win32;
  requires      'Perl6::Say'      =&gt; '0.12' if $] &lt; 5.010;
  test_requires 'Test::More'      =&gt; '0.42';

  #homepage       '';
  #bugtracker     '';
  #repository     '';

  WriteAll;
</code></pre>
<h4>모듈의 문서화</h4>
<h5>Changes 내용 추가</h5>
<p><code>Changes</code> 파일과 <code>README</code> 파일에 꼭 의미있는 정보를 기입해야하는 것은 아니지만,<br />
여러분의 모듈 사용자들은 아마도 이 파일을 통해 설치하기 전에 모듈의 변경사항이나<br />
중요한 API 사용법이나 설치 방법 등을 확인하고 싶어할 것입니다.</p>
<p><code>Changes</code> 파일은 버전 업 할때마다 변경된 사항을 정리하도록 합니다.<br />
<code>Log::Simple::Color</code> 모듈의 변경사항은 다음과 같습니다:</p>
<pre><code>  Changes:
  Revision history for Log-Simple-Color

  0.0.1
      Wed Dec 31 10:48:10 KST 2008
      Initial release.
</code></pre>
<p>새로운 버전 정보를 추가할 때는 0.0.1 보다 아랫부분에 추가하지 말고<br />
위에 추가해서 사용자가 쉽게 변경사항을 추적할 수 있도록 배려합니다:</p>
<pre><code>  Changes:
  Revision history for Log-Simple-Color

  0.0.2
      Fri Jan  2 18:49:55 KST 2009
      Added changing color API Bug #4
      Fixed windows install failure Bug #12

  0.0.1
      Wed Dec 31 10:48:10 KST 2008
      Initial release.
</code></pre>
<h5>README 내용 추가</h5>
<p><code>README</code> 파일은 그 이름처럼 모듈 사용자가<br />
가장 처음 열어 볼 확률이 높은 파일입니다.<br />
이 파일에는 모듈에 대한 개략적인 설명 뿐만 아니라,<br />
설치 방법, 알려진 버그, 라이센스 등과 관련한 정보가<br />
들어있는 것이 관례입니다.</p>
<p>이 파일 역시 <code>Changes</code> 파일 처럼 수동으로 생성할 수도 있지만<br />
<code>Log::Simple::Color</code> 모듈의 POD 문서를 꼼꼼하게 작성한다면<br />
이 문서를 그대로 이용하는 것도 좋은 방법입니다.<br />
<code>README</code> 파일을 자동으로 생성하려면 <code>Makefile.PL</code>의<br />
적당한 위치에 다음 내용을 추가합니다:</p>
<pre><code>  Makefile.PL:
  ...
  if ( -e 'MANIFEST.SKIP' ) {
      system( 'pod2text lib/Log/Simple/Color.pm &gt; README' );
  }
  ...
</code></pre>
<h5>lib/Log/Simple/Color.pm 에 POD 추가</h5>
<p>마지막으로 대부분의 CPAN 모듈이 그러하듯이<br />
<code>lib/Log/Simple/Color.pm</code> 파일 내부에 POD 문서도 추가해야 합니다.<br />
모듈 내부에 작성한 POD 문서를 이용해서 CPAN에서 그 내용을 열람하거나,<br />
설치후 <code>perldoc</code> 을 통해 참고할 수 있으므로 번거롭지만 꼼꼼하게<br />
문서를 작성하는 것은 중요한 일입니다.</p>
<p><code>Module::Starter</code> 모듈이 대부분의 외형을 작성해주기 때문에<br />
처음부터 작성할 필요없이 해당 섹션에 필요한 내용만 추가하면 됩니다.<br />
또한 <code>t/pod.t</code>와 <code>t/pod-coverage.t</code> 테스트는 모듈에 있는 POD 문서가<br />
올바른 문법을 지켜서 작성되었는지, 공개 API 에 대해서 명시적으로<br />
문서화를 하고 있는지 점검해주므로 작성 중간중간에 이들 테스트를 활용해서<br />
올바르게 문서화를 하고 있는지 확인하면 편리합니다.</p>
<p>물론 POD 관련 테스트를 회피하려면 <code>t/perlcritic.t</code> 테스트를 회피하듯이<br />
테스트 목록에서 제거할 수 있지만 그다지 좋은 생각은 아닙니다. <img src='http://bebop.emstone.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<h3>설치 및 배포</h3>
<p>완성한 모듈을 CPAN에 올리기 위해서는 CPAN이 요구하는 형식에 맞게<br />
모듈이름과 버전 번호를 명시해서 압축 파일을 만들어야 합니다.<br />
또한 이 압축 파일에는 지금까지 작업한 관련있는 파일이 모두 들어가야합니다.<br />
<code>Makefile.PL</code>을 이용해서 <code>Makefile</code>을 생성하면 릴리스와 관련한<br />
일련의 작업을 자동으로 해주므로 특별한 예외사항이 없다면<br />
이부분에 대해서 고민할 필요가 없습니다.<br />
명령줄에서 다음 명령을 실행하면 자동으로 CPAN에 업로드하기 위한<br />
압축 파일을 생성합니다:</p>
<pre><code>  $ perl Makefile.PL
  $ make dist
</code></pre>
<p>물론 이렇게 제작한 펄 모듈을 꼭 CPAN에 업로드해야 하는 것은 아닙니다.<br />
사내 배포용으로 사용할 수도 있고, 개인용으로 사용할 수도 있습니다.<br />
(하지만 CPAN에 업로드 하는 편이 훨씬 낫다는 것을 곧 깨달을 것입니다. <img src='http://bebop.emstone.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /><br />
어느 쪽을 선택하든 일련의 과정을 거친 펄 모듈은 완전한 자동 빌드 환경과<br />
테스트 환경, 문서를 가지고 있는 CPAN에 있는 모듈과 다를 바가 없으므로<br />
펄에 익숙한 대부분의 사용자들은 거부감 없이 여러분의 모듈을 즐길 것입니다.</p>
<h3>정리하며</h3>
<p>지금까지 CPAN 모듈을 작성하는 방법을 알아보았습니다.<br />
사실 <code>package</code> 지시어를 이용해서 특정 기능을 수행할 수 있는<br />
API를 제공한다면 어떤 형태든 모두 펄 모듈이라고 할 수 있습니다.<br />
하지만 관례적으로 제대로 모양새를 갖춘 펄 모듈은<br />
CPAN 모듈이라고 생각하기 때문에 어짜피 제작할 펄 모듈이라면<br />
CPAN 에서 요구하는 최소한의 요건을 갖추는 것이 좋습니다.<br />
CPAN이 요구하는 몇가지 사항(문서화, 자동 빌드, 테스트 집합)은<br />
비단 펄로 개발 할 때 뿐만 아니라 다른 언어로 개발할 때 역시<br />
도움이 될 수 있는 좋은 참고 자료가 될것입니다.</p>
<h3>관련 글</h3>
<ul>
<li><a href="http://bebop.emstone.com/~keedi/timetolog/id/8">CPAN 모듈 작성 #1 &#8211; Module::Starter로 시작하기</a></li>
<li><a href="http://bebop.emstone.com/~keedi/timetolog/id/12">CPAN 모듈 작성 #2 &#8211; Module::Install를 이용한 자동 빌드 생성</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://bebop.emstone.com/2009/01/03/writing-cpan-module-with-module-install/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by-nc-nd/2.0/kr/</creativeCommons:license>
	</item>
		<item>
		<title>CPAN 모듈 작성 #1 &#8211; Module::Starter로 시작하기</title>
		<link>http://bebop.emstone.com/2008/12/29/writing-cpan-module-with-module-starter/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=writing-cpan-module-with-module-starter</link>
		<comments>http://bebop.emstone.com/2008/12/29/writing-cpan-module-with-module-starter/#comments</comments>
		<pubDate>Mon, 29 Dec 2008 02:57:04 +0000</pubDate>
		<dc:creator>keedi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[Log::Simple::Color]]></category>
		<category><![CDATA[Module::Starter]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[       주의 : [CPAN 모듈 작성 #1 - Module::Starter로 시작하기]의 가장 최근 판은  이곳 에서 확인할 수 있습니다.

 시작하며

 대부분 처음에는 간단한 문제를 해결하기 위해 가벼운 스크립트를 작성하곤 합니다. 하지만 이내 기능을 추가하기 시작하면서 크기는 금방 방대해져 버립니다. 게다가 여러분이 작성한 스크립트의 팬이 나타난다면, 아마도 여러분은 기대에 부응하기 위해 기꺼이 오류를 고치고 새로운 기능을 추가하기 시작할 것입니다.

 여러분의 프로그램은 점점 커지고 있으며, 사용자들은 늘어가고, 이제는 새로운 릴...
     <a href="http://bebop.emstone.com/2008/12/29/writing-cpan-module-with-module-starter/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong>주의</strong>: [CPAN 모듈 작성 #1 - Module::Starter로 시작하기]의 가장 최근 판은 <a href="http://bebop.emstone.com/~keedi/timetolog/id/8">이곳</a>에서 확인할 수 있습니다.</p>
<h3>시작하며</h3>
<p>대부분 처음에는 간단한 문제를 해결하기 위해 가벼운 스크립트를 작성하곤 합니다.<br />
하지만 이내 기능을 추가하기 시작하면서 크기는 금방 방대해져 버립니다.<br />
게다가 여러분이 작성한 스크립트의 팬이 나타난다면,<br />
아마도 여러분은 기대에 부응하기 위해<br />
기꺼이 오류를 고치고 새로운 기능을 추가하기 시작할 것입니다.</p>
<p>여러분의 프로그램은 점점 커지고 있으며, 사용자들은 늘어가고,<br />
이제는 새로운 릴리스 버전을 알려주는 것만해도 쉬운 일이 아닙니다.<br />
이쯤되면 규모의 문제지만 흔히 말하는 소프트웨어 개발 주기를 가지고 있는 셈입니다.<br />
작성한 스크립트를 모듈로 분리하고, CPAN 에서 요구하는 형식에 맞는<br />
자동화 빌드와 문서 및 테스트 집합까지 포함해서 배포까지 고려한 온전한 펄 모듈을 작성한다면<br />
여러분의 작업은 일원화되고, 사용화 친화적인 여러분의 프로그램에 팬들은 기뻐할 것입니다.</p>
<h3>관련 연구</h3>
<h4>준비물</h4>
<p>펄(Perl) 모듈을 작성하는 일은 여느 언어에서 그러하듯이 어렵지 않습니다.<br />
하지만 새로운 모듈의 문서화를 위해 디렉터리 구조를 고려하고, 라이센스를 명시하고,<br />
README 파일을 생성하고, 설치를 고려한 Makefile 작성 및 테스트 집합까지 포함하는 일은<br />
비단 펄에서 뿐만이 아니라 어떤 언어를 사용한다 할지라도 번거로운 일임에는 틀림없습니다.<br />
다행히 CPAN에는 이런 반복적이고, 유사한 작업을 자동으로 해주는,<br />
즉, CPAN 모듈을 작성하기 위한 뼈대 구조(mock-up, skeleton)를 생성해주는 편리한 자동화 모듈이 많습니다.<br />
<code>Module::Starter</code> 역시 그런 자동화 모듈 중 하나로 노련한 펄 해커인 <a href="http://search.cpan.org/~petdance/">Andy Lester</a>가 작성한 모듈입니다.</p>
<ul>
<li><a href="http://search.cpan.org/perldoc?Module::Starter">Module::Starter</a> &#8211; a simple starter kit for any module</li>
</ul>
<h4>Module::Starter</h4>
<p><code>Module::Starter</code> 모듈은 명령행에서 실행할 수 있는 스크립트도 포함하고 있기 때문에<br />
이 모듈을 설치하고 나면 <code>module-starter</code> 프로그램을 실행할 수 있습니다.<br />
스크립트의 자세한 사용법은 다음 명령을 통해서 확인할 수 있습니다:</p>
<pre><code>  $ module-starter --help
</code></pre>
<p><code>--help</code> 명령에서 확인할 수 있는 몇 가지 선택 사항을 소개합니다:</p>
<pre><code>  --module=module  모듈 이름 (필수, 반복 가능)
  --distro=name    배포시 이름, 모듈 이름을 여러개 명시할 경우 첫 번째 항목으로 사용 (선택 사항)
  --dir=dirname    새로운 모듈을 위한 디렉터리 구조 생성할 위치
                   (선택 사항, 명시하지 않을 경우 현재 디렉터리 사용)

  --builder=module 'ExtUtils::MakeMaker' 나 'Module::Build', 'Module::Install'
                   중 하나를 고를 수 있음 (명시하지 않을 경우 'ExtUtils::MakeMaker'를 사용)
  --eumm           --builder=ExtUtils::MakeMaker 와 동일
  --mb             --builder=Module::Build 와 동일
  --mi             --builder=Module::Install 와 동일

  --author=name    저자 이름 (필수)
  --email=email    저자 전자우편 주소 (필수)
  --license=type   배포한 모듈의 라이센스
                   (기본은 perl과 같은 라이센스)

  --verbose        작업 중 진행 메시지를 출력
  --force          필요하다면 동일한 이름의 파일이 존재할 경우 덮어씀

  --help           도움말
</code></pre>
<h3>Log::Simple::Color 모듈 작성</h3>
<p><a href="http://bebop.emstone.com/~keedi/timetolog/id/6">Log::Simple::Color</a> 모듈을 예로 들어<br />
이 모듈을 CPAN에 올릴 수 있도록 적합한 형태로 바꾸기 위해 명령줄에서 다음 명령을 실행합니다:</p>
<pre><code>  module-starter \
      --module=Log::Simple::Color \
      --author="Keedi Kim - 김도형" \
      --email=keedi@perl.kr \
      --builder=Module::Install \
      --verbose
</code></pre>
<p>명령을 실행하면 <code>--module</code>에 명시한 이름을 기준으로 <code>Log-Simple-Color</code> 디렉터리를 생성하고<br />
하부에 소스 코드 작성을 위한 <code>lib</code>와 테스트 코드를 위한 <code>t</code>, 자동 빌드를 위한 <code>Makefile.PL</code> 또는<br />
<code>Build.PL</code> 등의 필수 파일들을 자동으로 만들어줍니다:</p>
<pre><code>  $ module-starter \
  &gt;     --module=Log::Simple::Color \
  &gt;     --author="Keedi Kim - 김도형" \
  &gt;     --email=keedi@perl.kr \
  &gt;     --builder=Module::Install \
  &gt;     --verbose
  Created Log-Simple-Color
  Created Log-Simple-Color/lib/Log/Simple
  Created Log-Simple-Color/lib/Log/Simple/Color.pm
  Created Log-Simple-Color/t
  Created Log-Simple-Color/t/perlcritic.t
  Created Log-Simple-Color/t/pod-coverage.t
  Created Log-Simple-Color/t/00.load.t
  Created Log-Simple-Color/t/pod.t
  Created Log-Simple-Color/.cvsignore
  Builders 'Module::Install' and 'ExtUtils::MakeMaker' are mutually exclusive.  Using 'ExtUtils::MakeMaker'. at /usr/local/share/perl/5.8.8/Module/Starter/Simple.pm line 827
  Created Log-Simple-Color/Makefile.PL
  Created Log-Simple-Color/Build.PL
  Created Log-Simple-Color/Changes
  Created Log-Simple-Color/README
  Created Log-Simple-Color/MANIFEST
  Created starter directories and files
  $
</code></pre>
<p>여기까지 작업이 끝나면 <code>Log-Simple-Color/lib/Log/Simple/Color.pm</code> 파일에<br />
이전에 작성한 모듈의 실제 코드를 붙여 넣으면 CPAN에 업로드하기 위한 모듈 작성의<br />
첫 단계를 완료하게 됩니다.</p>
<h3>정리하며</h3>
<p>CPAN 저장소는 업로드하려는 모듈이 문서를 제대로 가지고 있는지, 자동화 빌드가 가능한지,<br />
테스트 집합을 가지고 있는지, 또한 테스트에 통과하는지를 모두 자동으로 점검 후 이러한<br />
기본적인 요소를 모두 충족한 경우에만 모듈을 받아들이고 CPAN을 통해 출판해줍니다.<br />
이런 자연스러운 CPAN의 최소한의 강제를 통해 CPAN 모듈은 대부분 비교적 훌륭한 문서를 내장하고 있으며,<br />
또한 <code>cpan</code> 프로그램을 통해 자동으로 설치할 수 있고, 설치시 테스트를 통해 문제점을 쉽게 발견할 수 있습니다.<br />
비록 일련의 과정이 개발자에게 조금 번거로울 수는 있지만 사용자 친화적임에는 틀림없을 것입니다.<br />
<code>Module::Starter</code>와 같은 모듈을 이용하면 이것 마저 단 한줄의 명령으로 해결할 수 있습니다.</p>
<p>다음 글에서는 <code>Module::Starter</code>가 생성한 파일을 토대로 <code>Log::Simple::Color</code>에 맞도록<br />
수정해서 더욱 완전한 모듈 패키지를 완성해보도록 합니다.</p>
<h3>관련 글</h3>
<ul>
<li><a href="http://bebop.emstone.com/~keedi/timetolog/id/8">CPAN 모듈 작성 #1 &#8211; Module::Starter로 시작하기</a></li>
<li><a href="http://bebop.emstone.com/~keedi/timetolog/id/12">CPAN 모듈 작성 #2 &#8211; Module::Install를 이용한 자동 빌드 생성</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://bebop.emstone.com/2008/12/29/writing-cpan-module-with-module-starter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by-nc-nd/2.0/kr/</creativeCommons:license>
	</item>
		<item>
		<title>콘솔 프로그램의 출력에 색깔 입히기 &#8211; Log::Simple::Color</title>
		<link>http://bebop.emstone.com/2008/12/27/print-colorful-console-message-with-log-simple-color/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=print-colorful-console-message-with-log-simple-color</link>
		<comments>http://bebop.emstone.com/2008/12/27/print-colorful-console-message-with-log-simple-color/#comments</comments>
		<pubDate>Fri, 26 Dec 2008 16:39:30 +0000</pubDate>
		<dc:creator>keedi</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[Term::ANSIColor]]></category>
		<category><![CDATA[Win32::Console]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[       주의 : [콘솔 프로그램의 출력에 색깔 입히기 - Log::Simple::Color]의 가장 최근 판은  이곳 에서 확인할 수 있습니다.

 시작하며

 거의 모든 작업을 GUI 환경에서 처리하는 요즘에도 업무의 자동화나 일괄 처리 때문에 터미널의 명령줄에서 실행하는 프로그램의 사용의 빈도는 여전히 잦습니다. 보통 이런 프로그램은 작업의 진행 내역을 알려주기 위해 표준 출력이나 표준 에러에 유용한 정보를 출력하거나 또는 로그에 기록을 남깁니다. 이 중에서도 특히 표준 출력이나 표준 에러에 정보를 출력하는 경우 글자나 배경에 색깔을...
     <a href="http://bebop.emstone.com/2008/12/27/print-colorful-console-message-with-log-simple-color/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong>주의</strong>: [콘솔 프로그램의 출력에 색깔 입히기 - Log::Simple::Color]의 가장 최근 판은 <a href="http://bebop.emstone.com/~keedi/timetolog/id/6">이곳</a>에서 확인할 수 있습니다.</p>
<h3>시작하며</h3>
<p>거의 모든 작업을 GUI 환경에서 처리하는 요즘에도 업무의 자동화나 일괄 처리 때문에<br />
터미널의 명령줄에서 실행하는 프로그램의 사용의 빈도는 여전히 잦습니다.<br />
보통 이런 프로그램은 작업의 진행 내역을 알려주기 위해 표준 출력이나 표준 에러에<br />
유용한 정보를 출력하거나 또는 로그에 기록을 남깁니다.<br />
이 중에서도 특히 표준 출력이나 표준 에러에 정보를 출력하는 경우<br />
글자나 배경에 색깔을 입혀서 가독성을 높힐 수 있습니다.<br />
ANSI 를 지원하는 리눅스 계열의 시스템과 윈도우즈 시스템에서<br />
출력물에 색깔을 입혀서 사용자 친화적인 프로그램을 만듭니다.</p>
<h3>준비물</h3>
<p>명령줄 프로그램을 작성하기 위해 많이 사용하는 스크립트 언어로는 펄(Perl)이 있습니다.<br />
CPAN 모듈을 이용해서 리눅스와 윈도우즈 환경 모두에서 동작하는 프로그램의 뼈대를 작성합니다.</p>
<ul>
<li><a href="http://search.cpan.org/perldoc?Term::ANSIColor">Term::ANSIColor</a></li>
<li><a href="http://search.cpan.org/perldoc?Win32::Console">Win32::Console</a></li>
</ul>
<h3>관련 연구</h3>
<h4>Term::ANSIColor</h4>
<p>Term::ANSIColor 는 ANSI 회피 문자를 사용해서 화면에 출력하는 글자 또는 배경에 색을 입히는 모듈입니다.<br />
당연히 ANSI를 지원하는 터미널에서 동작하며 지원하지 않는 터미널에서는 ANSI 회피 문자열 그 자체가<br />
화면에 나타납니다.</p>
<p>다음은 녹색 배경에 밑줄과 굵은 속성을 가진 노란색 글씨와 굵은 속성의 파란색 글씨를 출력하는 예제입니다:</p>
<pre><code>  #!/usr/bin/perl

  use 5.010;
  use strict;
  use warnings;
  use Term::ANSIColor qw(:constants);

  say BOLD, UNDERLINE, YELLOW, ON_GREEN, "ugly", RESET;
  say "This text is normal.";

  say BOLD, BLUE, "This text is in bold blue.", RESET;
  say "This text is normal.";
</code></pre>
<h4>Win32::Console</h4>
<p>윈도우즈 콘솔은 ANSI 회피 문자를 지원하지 않기 때문에 다른 기능을 사용해야합니다.<br />
Win32::Console 모듈은 윈도우즈 콘솔과 문자 모드를 다룰 수 있는 기능을 제공합니다.<br />
윈도우즈 콘솔은 굵은 글씨나 밑줄 등의 속성을 지원하지 않으므로 색상만 변경할 수 있습니다.</p>
<p>다음은 녹색 배경에 노란색 글씨와 파란색 글씨를 출력하는 예제입니다:</p>
<pre><code>  #!/usr/bin/perl

  use 5.010;
  use strict;
  use warnings;
  use Win32::Console;

  my $console = Win32::Console-&gt;new(STD_OUTPUT_HANDLE);
  my $reset = $console-&gt;Attr;

  $console-&gt;Attr($FG_YELLOW | $BG_GREEN);
  say "ugly";

  $console-&gt;Attr($reset);
  say "This text is normal.";

  $console-&gt;Attr($FG_BLUE);
  say BOLD, BLUE, "This text is in bold blue.", RESET;

  $console-&gt;Attr($reset);
  say "This text is normal.";
</code></pre>
<h3>구현</h3>
<p>다음은 Term::ANSIColor 과 Win32::Console 모듈을 이용해 작성한<br />
간단한 로그 모듈 Log::Simple::Color 입니다:</p>
<pre><code>  package Log::Simple::Color;

  use strict;
  use warnings;

  sub say { print @_, "\n" }

  my $console;
  my %color_of;
  my %msg;

  my $default_level = 'info';
  my %log_level_of = (
      debug   =&gt; 0,
      info    =&gt; 1,
      warning =&gt; 2,
      error   =&gt; 3,
  );

  my %default_msg = (
      debug =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{debug} &lt; $log_level_of{$self-&gt;level};
          say @args;
      },
      info =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{info} &lt; $log_level_of{$self-&gt;level};
          say @args;
      },
      warning =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{warning} &lt; $log_level_of{$self-&gt;level};
          say @args;
      },
      error =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{error} &lt; $log_level_of{$self-&gt;level};
          say @args;
      },
      default =&gt; sub {
          my ( $self, $mode, @args ) = @_;
          return if $log_level_of{default} &lt; $log_level_of{$self-&gt;level};
          say "[$mode] ", @args;
      },
  );

  my %linux_msg = (
      debug =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{debug} &lt; $log_level_of{$self-&gt;level};
          say @{ $color_of{debug} }, @args, @{ $color_of{default} };
      },
      info =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{info} &lt; $log_level_of{$self-&gt;level};
          say @{ $color_of{info} }, @args, @{ $color_of{default} };
      },
      warning =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{warning} &lt; $log_level_of{$self-&gt;level};
          say @{ $color_of{warning} }, @args, @{ $color_of{default} };
      },
      error =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{error} &lt; $log_level_of{$self-&gt;level};
          say @{ $color_of{error} }, @args, @{ $color_of{default} };
      },
      default =&gt; sub {
          my ( $self, $mode, @args ) = @_;
          return if $log_level_of{default} &lt; $log_level_of{$self-&gt;level};
          say "[$mode] ", @args;
      },
  );

  my %window_msg = (
      debug =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{debug} &lt; $log_level_of{$self-&gt;level};
          $console-&gt;Attr(@{ $color_of{debug} });
          say @args;
          $console-&gt;Attr(@{ $color_of{default} });
      },
      info =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{info} &lt; $log_level_of{$self-&gt;level};
          $console-&gt;Attr(@{ $color_of{info} });
          say @args;
          $console-&gt;Attr(@{ $color_of{default} });
      },
      warning =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{warning} &lt; $log_level_of{$self-&gt;level};
          $console-&gt;Attr(@{ $color_of{warning} });
          say @args;
          $console-&gt;Attr(@{ $color_of{default} });
      },
      error =&gt; sub {
          my ( $self, @args ) = @_;
          return if $log_level_of{error} &lt; $log_level_of{$self-&gt;level};
          $console-&gt;Attr(@{ $color_of{error} });
          say @args;
          $console-&gt;Attr(@{ $color_of{default} });
      },
      default =&gt; sub {
          my ( $self, $mode, @args ) = @_;
          return if $log_level_of{default} &lt; $log_level_of{$self-&gt;level};
          say "[$mode] ", @args
      },
  );

  if ($^O eq 'linux') {
      eval 'use Term::ANSIColor qw(:constants);';
      if (!$@) {
          eval(
                '$color_of{default} = [ RESET          ];'
              . '$color_of{debug}   = [ CYAN           ];'
              . '$color_of{info}    = [ YELLOW         ];'
              . '$color_of{warning} = [ WHITE, ON_BLUE ];'
              . '$color_of{error}   = [ WHITE, ON_RED  ];'
          );
          $msg{debug}   = $linux_msg{debug};
          $msg{info}    = $linux_msg{info};
          $msg{warning} = $linux_msg{warning};
          $msg{error}   = $linux_msg{error};
          $msg{default} = $linux_msg{default};
      }
      else {
          say "Recommand CPAN Perl Module: [Win32::Console]";
          $msg{debug}   = $default_msg{debug};
          $msg{info}    = $default_msg{info};
          $msg{warning} = $default_msg{warning};
          $msg{error}   = $default_msg{error};
          $msg{default} = $default_msg{default};
      }
  }
  elsif ($^O eq 'MSWin32') {
      eval 'use Win32::Console;';
      if (!$@) {
          eval(
                '$console = Win32::Console-&gt;new(STD_OUTPUT_HANDLE);'
              . '$color_of{default} = $console-&gt;Attr;'
              . '$color_of{debug}   = [ $FG_CYAN             ];'
              . '$color_of{info}    = [ $FG_YELLOW           ];'
              . '$color_of{warning} = [ $FG_WHITE | $BG_BLUE ];'
              . '$color_of{error}   = [ $FG_WHITE | $BG_RED  ];'
          );
          $msg{debug}   = $window_msg{debug};
          $msg{info}    = $window_msg{info};
          $msg{warning} = $window_msg{warning};
          $msg{error}   = $window_msg{error};
          $msg{default} = $window_msg{default};
      }
      else {
          say "Recommand CPAN Perl Module: [Win32::Console]";
          $msg{debug}   = $default_msg{debug};
          $msg{info}    = $default_msg{info};
          $msg{warning} = $default_msg{warning};
          $msg{error}   = $default_msg{error};
          $msg{default} = $default_msg{default};
      }
  }
  else {
      $msg{debug}   = $default_msg{debug};
      $msg{info}    = $default_msg{info};
      $msg{warning} = $default_msg{warning};
      $msg{error}   = $default_msg{error};
      $msg{default} = $default_msg{default};
  }

  sub new {
      my ($class, %param) = @_;

      my $self = bless {
          level =&gt; $default_level,
      }, $class;

      $self-&gt;level($param{level}) if exists $param{level};

      return $self;
  }

  sub debug   { $msg{debug}-&gt;(@_) }
  sub info    { $msg{info}-&gt;(@_) }
  sub warning { $msg{warning}-&gt;(@_) }
  sub error   { $msg{error}-&gt;(@_) }

  sub level {
      my ( $self, $level ) = @_;

      return $self-&gt;{level} unless $level;

      if    ( $level =~ m/^debug$/i )   { $self-&gt;{level} = 'debug'; }
      elsif ( $level =~ m/^info$/i )    { $self-&gt;{level} = 'info'; }
      elsif ( $level =~ m/^warning$/i ) { $self-&gt;{level} = 'warning'; }
      elsif ( $level =~ m/^error$/i )   { $self-&gt;{level} = 'error'; }
      else                              { $self-&gt;{level} = 'info'; }
  }

  1;
</code></pre>
<h3>사용</h3>
<p>Log::Simple::Color 를 사용하기 위해서는 생성자를 이용해서 객체를 먼저 만듭니다:</p>
<pre><code>  use Log::Simple::Color;

  my $log = Log::Simple::Color-&gt;new;
</code></pre>
<p>객체가 지원하는 메소드 종류는 다음과 같습니다:</p>
<ul>
<li>$log-&gt;level</li>
<li>$log-&gt;debug</li>
<li>$log-&gt;info</li>
<li>$log-&gt;warning</li>
<li>$log-&gt;error</li>
</ul>
<p>완전한 예제는 다음과 같습니다:</p>
<pre><code>  #!/usr/bin/perl 

  use strict;
  use warnings;
  use Log::Simple::Color;

  my $log = Log::Simple::Color-&gt;new;

  for my $level (qw/ incorrect_mode debug info warning error /) {
      print "$level: [", $log-&gt;level, "] -&gt; [", $log-&gt;level($level), "]\n";

      $log-&gt;debug("This is a debug message");
      $log-&gt;info("This is an info message");
      $log-&gt;warning("This is a warning message");
      $log-&gt;error("This is an error message");
  }
</code></pre>
<h3>정리하며</h3>
<p>로그메시지는 프로그램의 동작 상태를 확인할 수 있는 효과적인 방법입니다.<br />
특이사항이나 문제가 발생한 부분처럼 중요하기 때문에 눈에 띄어야 한다면<br />
중요도 별로 색깔을 서로 다르게 입혀서 효율적으로 로그를 관리할 수도 있습니다.<br />
비록 윈도우즈 기본 터미널은 ANSI 를 지원하지 않기 때문에 서로 다른 두 가지 모듈을<br />
사용해서 구현했지만 모듈로 한번 만들어놓으면 간단한 콘솔 스크립트를 작성할 때<br />
편리하게 사용할 수 있습니다.</p>
]]></content:encoded>
			<wfw:commentRss>http://bebop.emstone.com/2008/12/27/print-colorful-console-message-with-log-simple-color/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by-nc-nd/2.0/kr/</creativeCommons:license>
	</item>
	</channel>
</rss>

