<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
	<channel>
		<title>Extremely Agile</title>
		<link>http://www.buggymind.com/</link>
		<description>버그투성이 소프트웨어 프로젝트와 화해하는 방법</description>
		<language>ko</language>
		<pubDate>Tue, 14 Oct 2008 09:24:22 +0900</pubDate>
		<generator>Tattertools 1.1.3 : Transition</generator>
		<item>
			<title>jQuery를 사용한 Rails REST 프로그래밍 (1)</title>
			<link>http://www.buggymind.com/168</link>
			<description>&lt;P&gt;잘 알려진 이야기입니다만, Rails는 REST를 꽤 우아하게 지원하는 플랫폼입니다. 그리고 Prototype을 사용한 JavaScript 연동 기능도 잘 지원하고 있죠. 그래서 프로그래밍하기에 따라서는 JavaScript를 전혀 할 줄 몰라도 AJAX 프로그래밍을 할 수 있습니다. &lt;br /&gt;&lt;br /&gt;그런데 Rails가 제공하는 JavaScript 지원 기능들을 사용해 프로그래밍을 하다 보면, 이런 생각이 듭니다. 본질적으로 JavaScript는 CLIENT쪽 제어를 위한 기능이거든요. 이런 기능들이 서버쪽에서 생성하는 코드에 묻어들어가게 되면, 요즘 한참 뜨고 있는 &quot;무간섭 자바스크립트&quot; 코딩 철학에 맞는 프로그래밍을 하기는 좀 어려워지죠. 거기다, 클라이언트쪽 코드를 서버측 기술에 독립적인 형태로 구현하기도 어려워집니다. &quot;서로 다른 티어(tier) 사이에 존재하는 종속성을 최소화한다&quot;라는 것을 프로그래밍의 대원칙으로 삼는다면, 그 원칙에 맞게 코딩하기가 어려워진다는 겁니다.&lt;br /&gt;&lt;br /&gt;그렇다면, 그 원칙을 지키면서 코딩을 하려면 어떻게 해야 하나요? 가급적 HTML과 JavaScript 코드는 서버쪽 스크립팅 기술과는 독립적으로 작성해야 할 겁니다. 그렇게 하면 다음과 같은 부수적 효과를 누릴 수 있습니다.&lt;br /&gt;&lt;br /&gt;1. 서버측 기능이 풍부하지 않아도 된다. (REST만 지원해도 상관없다.)&lt;br /&gt;2. 서버측에서 어떤 언어나 스크립팅 태그를 지원하건, 그에 무관한 코드를 작성할 수 있다. &lt;br /&gt;3. 클라이언트 JavaScript 코드를 서버와는 상관없이 간단하게 교체/수정할 수 있다.&lt;br /&gt;&lt;br /&gt;웹 서버가 REST만 지원해도 된다는 것은 어떤 의미일까요? RESTful 코딩 패턴만을 사용하여 프로그래밍을 하면 웹 서버를&amp;nbsp; 마치 클라이언트 측 Resource에 대한 Persistency layer로 추상화한 코딩을 할 수 있다는 것에 주목합시다. 물론, 이렇게 하게 되면 MVC 패턴에 있어서 Model, View, Controller 로직이 전부 클라이언트 쪽으로 이동하게 됩니다. 그게 과연 옳은 일인지는 논쟁의 소지가 있으므로 언급하고 싶지는 않습니다만, 최근 jQuery등의 라이브러리 등장과 JavaScript에 관한 좋은 교과서들이 나옴에 따라, 그런 코드를 만들기가 쉬워지긴 했습니다.&lt;br /&gt;&lt;br /&gt;예를 들어서 한번 살펴보죠. 여러분이 클라이언트 측에 임의의 Element를 동적으로 생성하고, 그 Element가 만들어질 때 마다 그 절대 위치를 Location이라는 이름의 웹 서버 리소스에 저장하고 싶다고 해 봅시다. 그런 일을 하려면, 우선 rails 서버에 Location이라는 이름의 리소스를 만들어 줘야 합니다. &lt;br /&gt;&lt;br /&gt;$~ /work/rails/foo$ script/generate scaffold location elementid:string x:integer y:integer width:integer height:integer&lt;br /&gt;&lt;br /&gt;위와 같이 실행하면 location이라는 resource에 대한 scaffold 코드가 쫙 만들어질겁니다. 그 컨트롤러 코드의 일부분을 잠깐 보시면...&lt;br /&gt;&lt;br /&gt;class LocationsController &amp;lt; ApplicationController&lt;br /&gt;&amp;nbsp; ...&lt;br /&gt;&amp;nbsp; # POST /locations&lt;br /&gt;&amp;nbsp; # POST /locations.xml&lt;br /&gt;&amp;nbsp; def create&lt;br /&gt;&amp;nbsp; &amp;nbsp; @location = Location.new(params[:location])&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; respond_to do |format|&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; if @location.save&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; flash[:notice] = &#039;Location was successfully created.&#039;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; format.html { redirect_to(@location) }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; format.xml&amp;nbsp; { render :xml =&amp;gt; @location, :status =&amp;gt; :created, :location =&amp;gt; @location }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;FONT color=#ff0000&gt;format.json { head :ok }&lt;/FONT&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; format.js { head :ok }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; format.html { render :action =&amp;gt; &quot;new&quot; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; format.xml&amp;nbsp; { render :xml =&amp;gt; @location.errors,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; :status =&amp;gt; :unprocessable_entity }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;FONT color=#d41a01&gt;format.json { render :json =&amp;gt; @location.errors,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; :status =&amp;gt; :unprocessable_entity }&lt;br /&gt;&lt;/FONT&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; format.js &amp;nbsp; { render :json =&amp;gt; @location.errors,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; :status =&amp;gt; :unprocessable_entity }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; end&lt;br /&gt;&amp;nbsp; &amp;nbsp; end&lt;br /&gt;&amp;nbsp; end&lt;br /&gt;&amp;nbsp; ...&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;생성된 코드 그대로는 아니고, 수정을 좀 했습니다. (몇줄 안됩니다. 코드라고 보기엔 좀 그렇고, &#039;설정(configuration)&#039;에 가까운 코드죠. Ruby 프로그래밍을 할 줄 몰라도 짤 수 있는, 그런 수준의 코드입니다.) 본 예제에서는 클라이언트에서 json 형태의 회신을 요구하는 것으로 가정을 했습니다. 서버는 클라이언트에서 POST 형태로 전달된 파라메터를 분석해서 Location 객체를 만들고, 그 객체를 database에 성공적으로 저장하고 나면 HTTP OK 메시지를 응답으로 전달해야 합니다. 그래서 format.json {head:ok} 코드를 넣었구요. 제대로 저장되지 않았을 경우도 처리할 수 있도록 else 블럭 안에 적절히 코드를 삽입했습니다. (붉은색으로 표시된 부분을 보시기 바랍니다.)&lt;br /&gt;&lt;br /&gt;자. 그러면 이것으로 서버측에서는 클라이언트가 날리는 데이터를 가지고 Location이라는 모델 객체(리소스)를 만들어 database에 저장할 준비를 끝냈습니다. 남은 일은 클라이언트에서 제대로 된 json 형태의 request를 날려주는 것 뿐입니다. 그럼 클라이언트 측 코드를 보시죠. jQuery로 작성한 코드입니다. &lt;br /&gt;&lt;br /&gt;var createLocationAjax = function(options) {&lt;/P&gt;
&lt;P&gt;&amp;nbsp; $.ajax( {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; url: options.url,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; type: &#039;post&#039;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; data: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#039;location[elementid]&#039;: options.elementId,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#039;location[x]&#039;: options.pageX,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#039;location[y]&#039;: options.pageY,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#039;location[width]&#039;: this.width,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#039;location[height]&#039;: this.height&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; dataType: &#039;json&#039;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; success: (options.success || function(data) {}),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; error: (options.error || function(data) {})&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; );&lt;br /&gt;&amp;nbsp; return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;createLocationAjax라는 함수를 하나 정의하고 있는데, 이 함수는 options라는 프라퍼티 객체를 인자로 받는 함수입니다. 이 프라퍼티 객체를 통해 url, elementId, pageX, pageY, width, height 등의 값을 인자로 전달할 수 있고, ajax 호출이 정상적으로 수행되었을 경우의 핸들러와 실패했을 경우의 핸들러도 인자로 전달할 수 있습니다. 위의 함수는 전달받은 인자들을 가지고 json 객체를 만들어, 그 객체에 대한 참조를 data라는 프라피터의 값으로 두고 $.ajax 함수를 호출합니다. &lt;br /&gt;&lt;br /&gt;유의할 것은 서버측에서 params[:location]과 같은 형태로 클라이언트가 전송한 인자들을 가져와 분석하여 객체를 생성하기 때문에, 클라이언트측에서도 그에 맞게 json 프라퍼티 이름을 설정해 주어야 한다는 겁니다. rails의 form_for 태그가 생성하는 HTML form 코드를 보셨다면 아마 쉽게 이해하실 수 있을 듯. 따라서 모든 json 프라퍼티 이름은 location[xxx]의 형태로 지정하였습니다. 그래야 서버쪽에서 :location을 키로 사용하여 클라이언트가 전송한 모든 파라메터들을 한번에 가져올 수 있죠. &lt;br /&gt;&lt;br /&gt;각설하고, 이렇게 하는 것 만으로 클라이언트쪽에서 생성한 모델 객체(!)의 위치 정보를 서버에 지속적(persistent)으로 저장할 수 있습니다. 물론 웹 프로그래밍을 하는 사람이 자바 스크립트도 알아야 하고, rails도 쓸 줄 알아야 한다는 부담이 있긴 합니다만, 이런 패턴을 지키면 오히려 JavaScript만 아는 사람도 굉징히 간단하게(?) 웹 프로그래밍을 할 수 있게 됩니다. 웹 서버를 그냥 persistency API를 제공하는 서버 정도로 생각해버려도 되는데다, 사실 Ruby에 대해서 잘 몰라도 되거든요.&lt;br /&gt;&lt;br /&gt;물론, 이 방법이 100% 완벽한 것은 아닙니다. 최신 버전의 rails에는 클라이언트 측 공격을 막기 위한 feature가 들어 있는데, (그에 관해서는 &lt;A href=&quot;http://www.buggymind.com/148&quot;&gt;http://www.buggymind.com/148&lt;/A&gt;&amp;nbsp;를 참고하세요) 이 기능과 연동하기가 어렵습니다. rails API를 사용해 폼을 생성하거나 하지 않기 때문이죠. 따라서, 위의 예제가 제대로 돌아가기 위해서는 locations_controller.rb 파일의 맨 위쪽에 다음과 같은 코드를 추가해 주어야 합니다. &lt;br /&gt;&lt;br /&gt;class LocationsController &amp;lt; ApplicationController&lt;br /&gt;&amp;nbsp; &lt;FONT color=#ff0000&gt;skip_before_filter :verify_authenticity_token&lt;br /&gt;&lt;/FONT&gt;&lt;br /&gt;&amp;nbsp; ...&lt;br /&gt;end&lt;br /&gt;&lt;/P&gt;</description>
			<category>Rails</category>
			<category>jQuery</category>
			<category>json</category>
			<category>REST</category>
			<category>RESTful</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/168</guid>
			<comments>http://www.buggymind.com/168#entry168comment</comments>
			<pubDate>Mon, 13 Oct 2008 13:39:19 +0900</pubDate>
		</item>
		<item>
			<title>Ruby의 inject를 활용한 코드 사례</title>
			<link>http://www.buggymind.com/167</link>
			<description>&lt;P&gt;Ruby를 주마간산식으로 배운 사람에게는 감동적인 코드...&lt;br /&gt;&lt;br /&gt;module ActionController&lt;br /&gt;&amp;nbsp; module StatusCodes #:nodoc:&lt;br /&gt;&amp;nbsp; &amp;nbsp; # Defines the standard HTTP status codes, by integer, with their&lt;br /&gt;&amp;nbsp; &amp;nbsp; # corresponding default message texts.&lt;br /&gt;&amp;nbsp; &amp;nbsp; # Source: &lt;A href=&quot;http://www.iana.org/assignments/http-status-codes&quot;&gt;http://www.iana.org/assignments/http-status-codes&lt;/A&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; STATUS_CODES = {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 100 =&amp;gt; &quot;Continue&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 101 =&amp;gt; &quot;Switching Protocols&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 102 =&amp;gt; &quot;Processing&quot;,&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 200 =&amp;gt; &quot;OK&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 201 =&amp;gt; &quot;Created&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 202 =&amp;gt; &quot;Accepted&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 203 =&amp;gt; &quot;Non-Authoritative Information&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 204 =&amp;gt; &quot;No Content&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 205 =&amp;gt; &quot;Reset Content&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 206 =&amp;gt; &quot;Partial Content&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 207 =&amp;gt; &quot;Multi-Status&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 226 =&amp;gt; &quot;IM Used&quot;,&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 300 =&amp;gt; &quot;Multiple Choices&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 301 =&amp;gt; &quot;Moved Permanently&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 302 =&amp;gt; &quot;Found&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 303 =&amp;gt; &quot;See Other&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 304 =&amp;gt; &quot;Not Modified&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 305 =&amp;gt; &quot;Use Proxy&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 307 =&amp;gt; &quot;Temporary Redirect&quot;,&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 400 =&amp;gt; &quot;Bad Request&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 401 =&amp;gt; &quot;Unauthorized&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 402 =&amp;gt; &quot;Payment Required&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 403 =&amp;gt; &quot;Forbidden&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 404 =&amp;gt; &quot;Not Found&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 405 =&amp;gt; &quot;Method Not Allowed&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 406 =&amp;gt; &quot;Not Acceptable&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 407 =&amp;gt; &quot;Proxy Authentication Required&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 408 =&amp;gt; &quot;Request Timeout&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 409 =&amp;gt; &quot;Conflict&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 410 =&amp;gt; &quot;Gone&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 411 =&amp;gt; &quot;Length Required&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 412 =&amp;gt; &quot;Precondition Failed&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 413 =&amp;gt; &quot;Request Entity Too Large&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 414 =&amp;gt; &quot;Request-URI Too Long&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 415 =&amp;gt; &quot;Unsupported Media Type&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 416 =&amp;gt; &quot;Requested Range Not Satisfiable&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 417 =&amp;gt; &quot;Expectation Failed&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 422 =&amp;gt; &quot;Unprocessable Entity&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 423 =&amp;gt; &quot;Locked&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 424 =&amp;gt; &quot;Failed Dependency&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 426 =&amp;gt; &quot;Upgrade Required&quot;,&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 500 =&amp;gt; &quot;Internal Server Error&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 501 =&amp;gt; &quot;Not Implemented&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 502 =&amp;gt; &quot;Bad Gateway&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 503 =&amp;gt; &quot;Service Unavailable&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 504 =&amp;gt; &quot;Gateway Timeout&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 505 =&amp;gt; &quot;HTTP Version Not Supported&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 507 =&amp;gt; &quot;Insufficient Storage&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 510 =&amp;gt; &quot;Not Extended&quot;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; # Provides a symbol-to-fixnum lookup for converting a symbol (like&lt;br /&gt;&amp;nbsp; &amp;nbsp; # :created or :not_implemented) into its corresponding HTTP status&lt;br /&gt;&amp;nbsp; &amp;nbsp; # code (like 200 or 501).&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;FONT color=#d41a01&gt;SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) do |hash, (code, message)|&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; hash[message.gsub(/ /, &quot;&quot;).underscore.to_sym] = code&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; end&lt;br /&gt;&lt;/FONT&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; # Given a status parameter, determine whether it needs to be converted&lt;br /&gt;&amp;nbsp; &amp;nbsp; # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup&lt;br /&gt;&amp;nbsp; &amp;nbsp; # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE&lt;br /&gt;&amp;nbsp; &amp;nbsp; # hash to convert it.&lt;br /&gt;&amp;nbsp; &amp;nbsp; def interpret_status(status)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; case status&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; when Fixnum then&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;#{status} #{STATUS_CODES[status]}&quot;.strip&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; when Symbol then&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; interpret_status(SYMBOL_TO_STATUS_CODE[status] ||&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;500 Unknown Status #{status.inspect}&quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; status.to_s&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; end&lt;br /&gt;&amp;nbsp; &amp;nbsp; end&lt;br /&gt;&amp;nbsp; &amp;nbsp; private :interpret_status&lt;/P&gt;
&lt;P&gt;&amp;nbsp; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;해쉬 hash는 처음에 {}로 초기화된다. STATUS_CODES 내의 아이템은 하나씩 (code, message)로 전달된다. 이 때 message에 포함된 공백을 전부 &quot;&quot;로 대체하고, 소문자로 바꾼 다음, 심볼 (:ok와 같은)로 바꾸고 hash 객체의 key로 삼는다. 해당 심볼에 매핑되는 값은 code가 된다.&lt;br /&gt;&lt;br /&gt;&lt;/P&gt;</description>
			<category>Rails</category>
			<category>inject</category>
			<category>Ruby</category>
			<category>사례</category>
			<category>코드</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/167</guid>
			<comments>http://www.buggymind.com/167#entry167comment</comments>
			<pubDate>Fri, 10 Oct 2008 11:00:12 +0900</pubDate>
		</item>
		<item>
			<title>프로그래밍 jQuery (jQuery in Action)</title>
			<link>http://www.buggymind.com/166</link>
			<description>&lt;P&gt;어제 &quot;프로그래밍 jQuery&quot; (인사이트. 원제 jQuery in Action)를 완독했습니다. 개인 프로젝트로 시작한 웹 사이트 구축에 필요할까 해서요. 이 책을 읽으면서 존 레식의 &quot;프로 자바스크립트 테크닉&quot;을 띄엄띄엄 보기도 했는데, 자바스크립트라는 언어와 자바스크립트 라이브러리에 대해 새로운 것을 깨달은 느낌입니다.&lt;br /&gt;&lt;br /&gt;&quot;프로그래밍 jQuery&quot;라는 책의 최대 강점은 굉장히 알기쉽게 쓰여져 있고, 예제가 잘 구축되어 있다는 점입니다. 예제 파일들이 하나로 패키징되어 있는데, 덕분에 예제를 참고하고 공부하기가 쉽습니다. 프로그래밍을 배우는 사람들이 쉽고 잘 짜여져 있는 예제에 목마르다는 점을 감안한다면, 아주 대단한 장점이라고 할 수 있겠습니다. 거기다 설명까지 잘 되어 있으니, 금상첨화라고도 할 수 있겠죠.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;imageblock center&quot; style=&quot;text-align: center; clear: both;&quot;&gt;&lt;img src=&quot;http://www.buggymind.com/attach/1/1082405595.jpg&quot; alt=&quot;사용자 삽입 이미지&quot; height=&quot;475&quot; width=&quot;371&quot; /&gt;&lt;/div&gt;&lt;br /&gt;그런데 이 책이 갖는 그런 장점은 jQuery라는 라이브러리가 갖는 장점 때문에 생기는 것이기도 합니다. jQuery 라이브러리는 일관된(consistent), 잘 조직되고(well-organized) 잘 짜여진(well-written) 라이브러리거든요. 사용법도 간단해서 (복잡하게 사용하는 것도 물론 가능하겠지만 말입니다) 프로그래머가 신경쓸 일이 대폭 줄어듭니다.&lt;br /&gt;&lt;br /&gt;jQuery가 오랫만에 만나는 잘 짜인 라이브러리라고 한다면, 이 책은 그야말로 오랫만에 읽어보는, 모든 면에서 균형이 잘 잡힌 책입니다. 번역까지도요.&lt;br /&gt;&lt;br /&gt;&lt;/P&gt;</description>
			<category>JavaScript</category>
			<category>jQuery in Action</category>
			<category>인사이트</category>
			<category>프로그래밍 jQuery</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/166</guid>
			<comments>http://www.buggymind.com/166#entry166comment</comments>
			<pubDate>Thu, 02 Oct 2008 16:18:35 +0900</pubDate>
		</item>
		<item>
			<title>The Street Lawyer - John Grisham</title>
			<link>http://www.buggymind.com/165</link>
			<description>&lt;P&gt;존 그리샴은 국내에서는 소설보다 아마 영화로 더 잘 알려진 작가일겁니다. 그의 소설을 원작으로한 많은 영화들이 제법 히트를 쳤으니까요. 저도 그의 소설을 단 한권도 읽어본 적이 없었고, 영화로만 접했습니다. 영어공부를 한다는 핑계로 The Street Lawyer (거리의 변호사) 를 페이퍼백 원서로 구입하기 전까지는요. (이 책은 국내에 이미 번역출간되어 있습니다.)&lt;br /&gt;&lt;br /&gt;Street라는 단어는 마치 &#039;법이 제대로 적용되지 못하는 지대&#039;라는 인상을 풍깁니다. 영어의 경우에는 더더욱 그렇죠. 하지만 이 소설이 무대로 하는 &#039;거리&#039;를 방황하는 홈리스(Homeless)들에게 제대로 적용되지 못하는 것은 비단 법 뿐만이 아닙니다.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;imageblock center&quot; style=&quot;text-align: center; clear: both;&quot;&gt;&lt;img src=&quot;http://www.buggymind.com/attach/1/1098550028.jpg&quot; alt=&quot;사용자 삽입 이미지&quot; height=&quot;475&quot; width=&quot;311&quot; /&gt;&lt;/div&gt;&lt;br /&gt;흔히 없는 사람에게는 여름보다 겨울이 더 고단하다고 합니다. 그래서인지, 이 소설이 배경으로 삼는 거리의 계절도 겨울입니다. 눈이 내리고, 살을 에일듯한 바람이 붑니다. 그리고 그 거리에서, 쉼터에서조차 가족을 누일 자리를 찾지 못한 한 여자가, 어린 자식들과 함께 죽습니다.&lt;br /&gt;&lt;br /&gt;그 전 과정을 목도한 한 변호사가, 마침내 거리의 변호사가 되기로 작심합니다. 물론, 그가 그런 마음을 먹은 데는 이 글에서는 다 밝힐 수 없는 복잡한 전후사정들이 있습니다. (다 밝혀버리면 재미 없으니까요.) 어쨌거나, 거리로 간 그는 제대로 작동하지 않는 미국의 복지 시스템에 경악합니다. 그리고 그 복지 시스템을 조금이나마 움직이게 만들려면 전화기를 붙잡고 고함을 지르거나 협박을 하는 단순한 방법이 가장 효과적이라는 사실을 깨닫고는 다시 절망합니다. &lt;br /&gt;&lt;br /&gt;미국의 경제 위기가 드디어 7000억 대의 달러 구제금융 조치를 마련하는 데 까지 왔습니다. 신용이 그다지 좋지 못한 사람의 부동산을 담보로 하던 서브프라임 모기지 사업이 연이어 파산하게 되자, 시장에 대한 정부의 개입을 그다지 달가와햐지 않던 공화당조차 어쩔 수 없음을 자인하고 연일 강도높은 안정책을 정부에 주문하고 있습니다. 대형 금융회사들도 자빠지고 있는 판국에, 재주 있나요. 덕분에 오바마의 지지도는 아직도 매캐인을 소폭이나마 앞서고 있습니다. 배고픔 앞에 강한 사람은 없으니까요. &lt;br /&gt;&lt;br /&gt;이번 서브프라임 모기지 사태는 집이 있던 사람이 집이 없는 상태로 바뀌는 것이 얼마나 간단한 일인지를 사람들에게 실제로 보여주었습니다. 이 소설은 그 들이 어떤 삶을 살아가게 되는지, 단편적이나마 보여줍니다. 이 블로그에서 사회적 안전장치에 대한 이야기를 여러번 했습니다만, 저자는 공화당 집권 이후 미국의 사회적 안전장치가 이전보다 더 허술해졌다는 비판을 변호사들의 입을 빌어 가감없이 내놓습니다. 효율이라는 이름으로 절감하기 쉬운 부분부터 쳐나갔습니다만, 지금 경제 상황은 그들이 그토록 추구했던 효율이나 성장과는 거리가 멉니다. &lt;br /&gt;&lt;br /&gt;미국의 경제 상황에 그다지 자유롭지 못한 한국의 경제와 그 안전 장치들은 미국보다 나은 수준일까요? 아마 아닐 겁니다. 대한민국의 식품 안전 시스템과 그 시스템을 통제하는 정부가 얼마나 안일한 생각을 가지고 있는지는 이미 지난 소고기 파동이나, 이번 멜라민 사태 등으로 입증됐습니다. 또한 이 정부는 가뜩이나 돈 들어갈 일 많은 경제 상황은 눈에 보이지 않는지, 2%를 대상으로 하는 종부세 감면안을 원안대로 통과시키겠다고 으름장을 부리고 있습니다. 신문의 시론 난에는 비록 &quot;본 사외 기고는 본 신문사의 ... 와 아무런 관련이 없습니다&quot;라는 문구로 위장을 하고는 있지만 벌써부터 &quot;종부세 폐지가 당연한 수순&quot;이라는 기사들이 실리고 있습니다. &lt;br /&gt;&lt;br /&gt;거기다 누가 현재 경제 시스템을 총관리하고 있는 수장에 대해 문제제기를 하면, 행정부의 수장이 앞장서 나서서 &quot;신뢰가 중요하다고 생각한다&quot;는 식의 일방통행식 이야기를 해 대고 있구요. 한때 실용과 효율, 결과를 중요하게 생각한다고 주장하던 사람의 입에서 나오기는 힘든 이야기죠. &#039;결과가 좋으리라는 믿음이 있으므로 신뢰한다&#039;는 이야기인데, 지금 굴러가는 꼴을 보면 도무지 그런 말을 믿어 줄 수가 없거든요.&lt;br /&gt;&lt;br /&gt;이런 상황에서 복지라는 건 이미 물건너 간 이야기죠. 좋은 쪽으로 보자면, 그나마 노무현 정부때는 복지 정책에 관한 이야기들이 꽤 자주 신문에 오르긴 했습니다. (물론 그럴 때 마다 그 정책들은 심히 난타당했죠.) 그런데 요즘 신문을 보면 그런 이야기는 별로 찾아 볼 수가 없어요. 복지라는 단어가 이미 사람들의 관심사가 아니라는 뜻이겠죠.그러니, 주가 지수가 1100대 부근으로 주저앉기라도 하면, 저는 The Street Lawyer라는 책에 나온 이야기가 많은 사람들에게 현실이 되지는 않을까, 걱정이 많이 됩니다. &lt;br /&gt;&lt;br /&gt;윌 스미스가 출연했던 The pursuit of happiness라는 영화의 주인공도 홈리스입니다. 그 영화의 주인공은 아내와 헤어진 상태에서 아이를 제대로 키우기 위해 정말로 눈물나게 고생하죠. 그리고 마침내 성공합니다. 영화를 보면, 그들이 이용하는 쉼터(shelter)가 가끔 배경으로 나옵니다. 그 쉼터를 집 삼아 아이를 씻기고, 먹이고, 재우고, 공부를 하죠. (The street laywer에 나오는 쉼터들은 그 보다 훨씬 더 열악합니다.) 이 영화의 문제점은 딱 한가지인데, 그들이 머무르는 쉼터의 나머지 공간을 차지하는 홈리스들이 완전히 타자화되어 있다는 점입니다. 윌 스미스가 성취한 성공신화만이 부각되는 것이죠. 그러다보면 &#039;너희도 하려고만 들면 할 수 있어&#039; 식의 불건전한 믿음이 소위 &#039;영화를 볼만한 여력이 있는 사람들&#039; 사이에 퍼지기 쉽습니다. 감동적인 &#039;남의 이야기&#039;가 끼치는 해악은 바로 이런 부분입니다. 한 번도 바닥이었던 적이 없는 사람들이, 바닥인, 혹은 바닥이었던 사람들의 절망이나 슬픔에 대해 &#039;오해&#039;를 하게 되는 거죠.&lt;br /&gt;&lt;br /&gt;지금 집권한 정부 여당은, 소위 이런 류의 &#039;성공신화&#039;에 굉장히 강하게 계도되어 있는 사람들입니다. 술자리에 가서 술을 마시다 보면 느끼게 되는 &#039;자수성가형&#039; 인간들의 한가지 공통점은, &quot;고통은 견딜만 한 것이다&quot;라는 잣대를 누구에게나 강요하려고 든다는 것이에요. 이런 분들은 다른 사람들의 이야기에 잘 귀를 기울이지 않습니다. &#039;노력하면 누구나 성공할 수 있으니&#039;까요. 하지만 &#039;그들의 신화&#039;가 &#039;우리 모두의 신화&#039;가 될 수 있으려면, 이 사회에는 더 많은 안전장치가 필요합니다. 문제는 그분들이 과연 그 사실을 깨달을 수 있을지, 도무지 알수가 없다는 사실이죠. &lt;br /&gt;&lt;br /&gt;이런 모든 것들에 대해 한 번쯤 생각해 볼 기회를 갖고 싶은 분들은, 한번 이 책을 읽어보시는 것도 좋겠습니다. 저도 그럴 기회를 가질 수 있었으니까요. 원서로 읽으셔도 그다지 어렵지는 않습니다. 페이퍼백 가격은 국내에서 구입할 경우 한 팔구천원 하는 것 같네요. 존 그리샴의 다른 소설은 모르겠지만, 이 소설은 단순한 &#039;읽을거리&#039;의 수준은 뛰어 넘는 것 같습니다. &lt;br /&gt;&lt;br /&gt;&lt;/P&gt;</description>
			<category>Thoughts</category>
			<category>John Grisham</category>
			<category>The Street Lawyer</category>
			<category>거리의 변호사</category>
			<category>존 그리샴</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/165</guid>
			<comments>http://www.buggymind.com/165#entry165comment</comments>
			<pubDate>Fri, 26 Sep 2008 14:16:24 +0900</pubDate>
		</item>
		<item>
			<title>불확실성</title>
			<link>http://www.buggymind.com/164</link>
			<description>&lt;P&gt;&lt;FONT color=#d41a01&gt;불확실성(Uncertainty) : &lt;/FONT&gt;&lt;SPAN class=text13&gt;&lt;FONT color=#d41a01&gt;장래 일어날 수 있는 사상(&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;事 : 일 사, ㉠일 ㉡섬기다 ㉢부리다 ㉣일삼다 ㉤찌르다 ㉥꽂다&#039;)&quot; onmouseout=tooltip_off()&gt;事&lt;/SPAN&gt;&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;象 : 코끼리 상, ㉠코끼리 ㉡꼴 ㉢모양 ㉣무악 이름 ㉤법 ㉥길 ㉦도 ㉧상아 ㉨통변 ㉩통역 ㉪징후 ㉫조짐 ㉬본뜨(받)다&#039;)&quot; onmouseout=tooltip_off()&gt;象&lt;/SPAN&gt;)에 관해서 인간이 가진 정보의 정확성에 대한 하나의 구분. (출처 : 네이버 백과사전)&lt;/FONT&gt;&lt;br /&gt;&lt;br /&gt;네이버 백과사전에 나온 불확실성의 정의에 따르면, 의사결정자가 가지고 있는 정보의 정확성은 ㉠ &lt;A href=&quot;http://100.naver.com/100.nhn?docid=173258&quot;&gt;&lt;FONT color=#096ab5&gt;확실성&lt;/FONT&gt;&lt;/A&gt;, ㉡ &lt;A href=&quot;http://100.naver.com/100.nhn?docid=718228&quot;&gt;&lt;FONT color=#096ab5&gt;리스크&lt;/FONT&gt;&lt;/A&gt;(risk), ㉢ &lt;A href=&quot;http://terms.naver.com/item.nhn?dirId=2&amp;amp;docId=7615&quot;&gt;&lt;FONT color=#458a08&gt;불확실성&lt;/FONT&gt;&lt;/A&gt;, ㉣ 무지(&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;무 : 없을 무, ㉠없다 ㉡아니다(부정) ㉢말다(금지)&#039;)&quot; onmouseout=tooltip_off()&gt;無&lt;/SPAN&gt;&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;지 : 알 지, ㉠알다 ㉡주관하다 ㉢맡다 ㉣사귐 ㉤대접 ㉥앎&#039;)&quot; onmouseout=tooltip_off()&gt;知&lt;/SPAN&gt;)의 4종류로 분류할 수 있다고 합니다. 관련 부분을 인용해보죠.&lt;br /&gt;&lt;br /&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;㉠의 확실성은 무엇이 일어날지 확정적으로 알고 있는 경우를 말한다. ㉡의 리스크는 무엇이 일어날지 확정적으로는 알 수 없으나, 일어날 수 있는 상태는 알고 있고, 또 그 &lt;A href=&quot;http://100.naver.com/100.nhn?docid=173253&quot;&gt;&lt;FONT color=#096ab5&gt;확률분포&lt;/FONT&gt;&lt;/A&gt;(&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;확 : 굳을 확, ㉠굳다 ㉡단단하다 ㉢확실하다&#039;)&quot; onmouseout=tooltip_off()&gt;確&lt;/SPAN&gt;&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;률 : 율 률(율), 거느릴 솔, 우두머리 수, ㉠율, 비례 ㉡제한 ⓐ거느리다 (솔) ⓑ쫓다 (솔) ⓒ따르다 (솔) ⓓ소탈하다 (솔) ⓔ꾸밈없다 (솔) ⓕ경솔하다 (솔) ⓖ가볍다 (솔) ⓗ거칠다 (솔) ⓘ대략 (솔) ⓙ대강 (솔) ⓚ우두머리 (수) ⓛ장수 (수)&#039;)&quot; onmouseout=tooltip_off()&gt;率&lt;/SPAN&gt;&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;분 : 나눌 분, ㉠나누다 ㉡구별 ㉢길이, 무게, 시간, 각도, 화폐 따위의 단위 ㉣신분, 직분&#039;)&quot; onmouseout=tooltip_off()&gt;分&lt;/SPAN&gt;&lt;SPAN onmouseover=&quot;tooltip_on(&#039;&#039;,&#039;포 : 베 포, ㉠베 ㉡돈 ㉢베풀다 ㉣벌이다 ㉤펴다&#039;)&quot; onmouseout=tooltip_off()&gt;布&lt;/SPAN&gt;)도 알고 있는 경우를 말한다. 이에 대하여 ㉢의 불확실성은 일어날 수 있는 상태는 알고 있으나, 그 확률분포를 알지 못하는 경우를 말한다. ㉣의 무지란 무엇이 일어날지, 어떠한 상태가 일어날지, 전혀 예견할 수 없는 경우를 말한다. 한편, 넓은 뜻의 불확실성이란 ㉡의 리스크와 ㉢의 불확실성의 양자를 가리킨다.&lt;/BLOCKQUOTE&gt;
&lt;P&gt;이런 개념을 최초로 정립한 사람들은 경제학자들입니다. 돈이 걸리면 어떻게든 위험성을 줄이는 것이 좋으니까, 당연하겠죠. 소프트웨어 프로젝트에도 돈이 걸려 있기 때문에, 무슨 방법을 쓰던 위험성을 줄이면 줄일수록 이득입니다. &lt;br /&gt;&lt;br /&gt;하지만 &#039;확실성&#039;이 갖는 정의 - 무엇이 일어날 지 확정적으로 알고 있는 경우 - 에 비추어보면, 확실성이란 달성이 불가능한 꿈과 같습니다. 확실성을 성취하려면 타임머신을 발명하던가 해야 합니다. &#039;아무 일도 하지 않으면 되지 않느냐?&#039;고 물으실 분도 계실 것 같은데, 아무 일도 하지 않으면 그 순간부터 유보수익이 사라지고, 기회비용 문제가 발생하기 시작합니다. &lt;br /&gt;&lt;br /&gt;하지만 &#039;불확실성&#039;에 대한 &#039;확실한 사실&#039;이 한가지 있긴 합니다. 우리가 알고자 하는 것이 미래의 특정 시점에 일어날 사건에 대한 불확실성의 정도라고&amp;nbsp; 한다면, 이 불확실성은 그 시점에 가까이 가면 갈수록 감소하는 경향을 보인다는 것이죠. 가령 우리 아기가 몇개월때 첫 걸음마를 뗄 것인지를 알고 싶다고 해 봅시다. 그리고 18개월쯤에는 첫걸음마를 뗄 것으로 예상한다고 해 봅시다. 이 예상이 갖는 불확실성은 시간이 흘러 18개월 근처로 가면 갈수록 감소합니다. 17개월이 되었는데도 엎드려 고개를 드는 것이 고작이라고 한다면? 아마 18개월에 첫 걸음마를 떼기는 힘들어지겠죠. 예상이 맞건 틀리건, 불확실성은 이런 식으로 감소 추세를 보이기 시작합니다. (물론 아이가 예상하지 못한 병에 걸린다거나 하는 불행한 일이 생기면 불확실성이 급상승하는 일도 생길 수 있긴 하겠습니다.)&lt;br /&gt;&lt;br /&gt;불확실성이 갖는 이러한 속성과, 확실성, 위험, 지식이 갖는 속성들을 종합해 보면, 우리는 분명해 보이는 몇 가지 결론에 이르게 됩니다. &lt;br /&gt;&lt;br /&gt;1. 시간이 흐르면 불확실성은 차츰 감소한다.&lt;br /&gt;2. 지식이 늘어나면 불확실성은 차츰 감소한다.&lt;br /&gt;&lt;br /&gt;이 지식에는 &#039;확률적 지식&#039;도 포함됩니다. 시간이 흐르면 사람들은 &#039;어떤 사실&#039;의 &#039;발생 가능성&#039;에 대한 지식을 자연스럽게 취득하게 됩니다. 확률적 지식이 100% 정확하다고는 볼 수 없습니다만, 적어도 한 잣대는 되어줄 수 있기 때문에, 불확실성은 그에 따라 차츰 감소하게 됩니다. &lt;br /&gt;&lt;br /&gt;이런 불확실성의 개념이 소프트웨어 개발 프로젝트 관리 방법론의 한 근간으로 등장한 것은 사실 오래된 일입니다. 1981년 베리 뵘은 이후 스티브 멕코넬이 &#039;불확실성 원추(Cone of Uncertainty)&#039;라고 부르게 되는 그래프를 하나 그렸는데, 이것이 시발이라고 봐도 무방할 것 같습니다. 이 그래프가 보여주는 것은 사실 위에 제시했던 두 가지 뻔한 결론에 다름 아닙니다. 하지만 전통적인 개발 방법론이 이 자명한 사실을 인정하게 되는 데는 약간의 시간이 필요했죠. 애자일 선언문이 발표된 것이 2001년도이니까, 그 사람들이 지금은 &#039;애자일&#039;이라고 불리는 방법론을 가다듬는 데 필요했을 10년 정도의 세월을 감안하면, 불확실성이 화두로 등장하는 데는 10년 정도의 시간이 필요했다고 봐도 될 것 같네요. &lt;br /&gt;&lt;br /&gt;그럼&amp;nbsp; 불확실성이 프로젝트 관리에 있어서 가장 중요한 문제중 하나로 등장한 이유는 뭘까요? 아마 다 아시겠지만, 그것은 초기 계획대로 굴러가는 프로젝트가 별로 없기 때문이며, 초기 설계대로 우아하게 마무리되는 프로그램이 별로 없기 때문이고, 그런 일이 빈번하게 생기면 프로젝트 팀원들의 &#039;꼭지가 돌아버리기&#039; 때문입니다. 그리고 그런 프로젝트는 고객에게 &#039;최선의 가치를 전달&#039;하기가 점점 더 어려워지죠. 전통적인 방법론들은 프로젝트 도중에도 불쑥 뿔쑥 등장하곤 하는 이런 &#039;꼭지도는 일들&#039;을 어떻게 처리해야 하는지에 대해서는 별 이야기가 없습니다.&lt;br /&gt;&lt;br /&gt;이쯤에서 이미 식상해졌을 지도 모를 애자일 이야기를 조금만 더 하자면... 애자일 방법론은 사실 불확실성에 관한 방법론입니다. 흔한 오해중에 한가지는 &#039;애자일 방법론은 고객 중심 방법론이다&#039;라는 것인데, 사실 이 이야기는 반 정도는 맞고 반 정도는 틀립니다. 사용자를 무조건 만족시키는 것이 애자일 방법론의 목표는 아니기 때문이죠. 고객이 중요한 것은, 사용자가 프로젝트의 성패에 관련된 가장 중요한 이해 당사자 중 하나이기 때문입니다. 고객을 중요하게 생각하는 것은, 그들과 어떻게 의사소통하느냐에 따라 프로젝트의 불확실성 정도가 굉장히 많이 달라질 수 있기 때문입니다. &lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;STRONG&gt;흔히 보는 장면&lt;br /&gt;&lt;/STRONG&gt;A : 이번 프로젝트의 요구사항 명세표를 분석해야 하는데요.&lt;br /&gt;B : 누가 작성한 명세표죠?&lt;br /&gt;C : 영업팀에서 작성한 겁니다.&lt;br /&gt;D : 그거 영업 팀에서 분석해야 하는거 아닌가?&lt;br /&gt;A : 이전 프로젝트 결과를 토대로 우리가 추가해줬음 하는 것도 있어서요.&lt;br /&gt;B : 그런걸 요구사항이라고 부를 수 있나요?&lt;br /&gt;A : 엇비슷한 프로젝트니까 일단은 그렇게 하고 프로젝트를 진행해달라는 게 &#039;영업팀의&#039; 요구사항이죠. &lt;br /&gt;B : 왜?&lt;br /&gt;C : 받아온 프로젝트 기간이 좀 빡빡해서요.&lt;br /&gt;(2달 후)&lt;br /&gt;A : 우리가 구현한 기능들 중 한 열개 항목 정도가 잘못되었다는 군요.&lt;br /&gt;B : 누가 그래?&lt;br /&gt;A : 고객이요.&lt;/BLOCKQUOTE&gt;
&lt;P&gt;보통 개발자들이 고객을 미워하는 이유는 개발자들이 보기에 고객이란 사람들이 &#039;뭘 잘 모르는 사람들&#039;처럼 보이기 때문도 있습니다만, 가장 큰 이유는 프로젝트 진행 중에 요구사항을 변경하는 사람들이 대부분 고객이기 때문입니다. 그런 일이 자주 생기면, 개발자들의 인생은 급속도로 피폐해집니다. &lt;br /&gt;&lt;br /&gt;그런데 사실 그건 고객 잘못은 아니에요. 위의 &#039;흔히 보는 장면&#039; (진짜로 이런 일이 흔하면 큰일나겠습니다만 ㅋㅋ)에서도 느끼실 수 있겠지만, 애초에 프로젝트를 진행하는 팀이 고객과 의사소통을 할 기회가 없었다는 것이 가장 큰 문제죠. 자뻑용 프로젝트를 한다면야 모르곘지만, 개발팀을 고객과 떼놓고 프로젝트를 하면 득보다는 실이 더 많아질 수도 있습니다. 고객/사용자는 종종 시장 그 자체이며, 시장의 변화를 외면하면서 시장에 &#039;최선의 가치를 전달할&#039; 방법 같은 것은 없습니다. 시장의 변화라는 것은 곧 &#039;불확실성&#039;이기 때문에, 우리는 어떻게든 불확실성을 끌어안을 방법을 찾아내야 합니다.&lt;br /&gt;&lt;br /&gt;애자일 방법론이 의사소통의 문제를 가끔 비 이성적으로 보일 때 까지 깊이 탐구하는 것은 아마 그래서 일 겁니다. 애자일 방법론은 구두로 나눈 대화와, 그 자리에서 합의된 사항을 &#039;개발 계획서&#039;보다 더 중시하는 경향이 있습니다. (이 점이 전통적인 방법론을 옹호하는 사람들의 심기를 굉장히 불편하게 만들곤 합니다만...) 이건 제 개인적인 경험입니다만, 지식을 늘리는 가장 간단한 방법른 사실 구글 검색도 아니고 독서도 아닙니다. &#039;대화&#039;죠. 대화가 확대할 수 있는 지식의 영역은 특별히 어딘가로 제한되지 않습니다. 하지만 프로젝트 진행에 있어 가장 도움되는 부분은 다음의 몇 가지 일 것 같군요. (무순)&lt;br /&gt;&lt;br /&gt;1. 프로젝트 자체에 대한 지식&lt;br /&gt;2. 팀원에 대한 지식&lt;br /&gt;3. 고객/사용자에 대한 지식&lt;br /&gt;&lt;br /&gt;앞서도 언급했었습니다만, 지식이 증가하면 불확실성은 감소합니다. 지식을 늘리는 방법으로 스파이크(spike) 같은 것도 있습니다만, 아무래도 고수와의 커피타임만큼 짧고 효율적이진 않습니다. 요구사항 변화에서 오는 야근을 줄이는 방법으로 &#039;무조건적인 퇴근시간 준수&#039;같은 것도 있겠습니다만, 아무래도 요구사항을 낸 이해 당사자와의 전화통화만큼 효과적이지는 못합니다.&lt;br /&gt;&lt;br /&gt;의사소통을 자주 그리고 효율적으로 하게 되면 프로젝트 중반에 발생하는 이런 저런 일들에 보다 능동적으로 대처할 수 있게 됩니다. 그리고 팀이 지금 어디쯤 와 있는지 좀 더 분명히 알 수 있게 되고, 앞으로 발생할 지 모를 &#039;꼭지도는&#039; 일들이 팀의 항로를 어떻게 바꾸어 놓게 될지 좀 더 명료하게 알 수 있게 됩니다. &lt;br /&gt;&lt;br /&gt;의사소통을 강조하는 것이 자기 파티션 안에 처박혀 있기를 좋아하는 개발자들의 습성과 배치되긴 합니다만, 그런 개발자의 모습은 개발이라는 것을 종종 예술(art)과 비슷한 것으로 묘사하는 너드(nerd) 문화의 클리셰일 가능성이 높습니다. 개발자들은 프로그래밍이 오타쿠들이 좋아하는 저패니메이션같은 것과는 질적으로 다르다는 사실을 깨달을 필요가 있어요. 개발은 현실이고, 현실은 가혹합니다. 개발자들이 의사소통을 무시하고 파티션 안에 처박혀 있으려는 습성은 어찌보면 개발자들이 처한 현실이 주는 문제를 계속해서 재생산하는 측면이 있습니다. 그냥 그 안에 처박혀서 &#039;사용자들은 아무것도 몰라&#039;라고 해서는 인간다운 생활을 영위할 수 없다는 것이 문제라는 거죠. 불확실성을 감소시키지 않으면, 야근 일수는 줄어들지 않는다, 뭐 그런 이야기죠.&lt;br /&gt;&lt;br /&gt;하지만 굳이 불확실성 이야기의 외연을 개발자 각각의 삶으로 넓혀놓지 않더라도, 불확실성을 감소시키는 것은 중요합니다. 소프트웨어 프로젝트의 목표가 &#039;고객에게 최선의 가치를 전달&#039;하는 것이라고 보면, &#039;최선의 가치&#039;라는 것의 정의는 시간이 지나면 어떻게든 변화하게 될 처지이니 (시장 상황은 자고 일어나면 바뀌죠) 불확실성이라는 것이 존재한다는 사실을 인정하고 그것과 &#039;화해할 수 있는&#039; 전략을 수립하도록 애쓰는 것이 프로젝트를 관리하는 사람이 해야 하는 일이 아닐까요?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/SPAN&gt;&lt;/P&gt;</description>
			<category>General</category>
			<category>Agile</category>
			<category>기민성</category>
			<category>불확실성</category>
			<category>애자일</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/164</guid>
			<comments>http://www.buggymind.com/164#entry164comment</comments>
			<pubDate>Wed, 24 Sep 2008 11:49:53 +0900</pubDate>
		</item>
		<item>
			<title>Erlang for C Programmers (7) : 병렬 프로그래밍 (2)</title>
			<link>http://www.buggymind.com/163</link>
			<description>&lt;P&gt;앞선 글을 통해 Erlang에서 병렬 프로그래밍이 어떻게 이루어지는지 대략이나마 감을 잡으실 수 있었을 거라고 믿습니다. 다시 정리해보자면, 얼랭에서 병렬 프로그램은 프로세스의 집합으로 만들어지고, 프로세스 끼리 메시지를 주고받아 통신하는 방식을 사용해 구현됩니다. 메시지를 보낼때는 Pid ! Message 이런 형식으로 보내고, 받는 쪽에서는 receive 를 사용해 메시지를 받아 처리합니다.&lt;br /&gt;&lt;br /&gt;그러면 지금부터는 앞선 글에서 만든 프로그램을 진짜 클라이언트/서버 모델을 만족하도록 바꿔 보겠습니다. 클라이언트가 서버에게 요청을 보내면, 서버는 그 요청을 해석해서 클라이언트에게 답을 보내주어야 합니다. 다음의 코드를 보시죠.&lt;br /&gt;&lt;br /&gt;-module(concur).&lt;br /&gt;-export([start_server/0, send_request/2, send_stop/1]).&lt;/P&gt;
&lt;P&gt;start_server() -&amp;gt; spawn( fun loop/0 ).&lt;/P&gt;
&lt;P&gt;loop() -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; receive&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {request, Pid, L} -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Result = odds_and_evens_acc(L),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Pid ! {response, self(), Result},&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loop();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; stop -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; exit&lt;br /&gt;&amp;nbsp; &amp;nbsp; end.&lt;/P&gt;
&lt;P&gt;send_request(Pid, L) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Pid ! {request, self(), L},&lt;br /&gt;&amp;nbsp; &amp;nbsp; receive&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {response, _SPid, {{odd, Ov},{even,Ev}}} -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; io:format(&quot;odd: ~p, even: ~p~n&quot;, [Ov,Ev])&lt;br /&gt;&amp;nbsp; &amp;nbsp; end.&lt;br /&gt;&lt;br /&gt;send_stop(Pid) -&amp;gt; Pid ! stop.&lt;br /&gt;&lt;br /&gt;odds_and_evens_acc(L) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; odds_and_evens_acc(L, 0, 0).&lt;/P&gt;
&lt;P&gt;odds_and_evens_acc([H|T], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; case ( H rem 2 ) of&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1 -&amp;gt; odds_and_evens_acc(T, Odds + H, Evens);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0 -&amp;gt; odds_and_evens_acc(T, Odds, Evens + H)&lt;br /&gt;&amp;nbsp; &amp;nbsp; end;&lt;br /&gt;odds_and_evens_acc([], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; { {odd, Odds}, {even, Evens} }.&lt;br /&gt;&lt;br /&gt;클라이언트/서버 모델을 따르는 통신을 하기 위해, 서버에게 보내는 메시지의 형식이 좀 바뀌었습니다. 메시지가 투플 형태로 간다는 것은 종전과 똑같습니다만, 그 투플의 두 번째 항목에 요청을 보내는 클라이언트 프로세스의 PID가 들어간다는 점이 달라졌습니다. 이 PID가 있어야 서버는 어느 클라이언트에게 응답을 보내야 할 지를 알 수&amp;nbsp; 있죠. 서버는 요청이 오면 odds_and_evens_acc를 호출해서 결과를 계산한 다음에, 요청을 보낸 프로세스에게 응답을 전송합니다. 전송되는 응답 투플 맨 앞에는 &#039;응답&#039;임을 알리기 위해 response라는 아톰이 들어가고, 투플 두번째 항목에는 서버 프로세스의 PID가 들어가고(self()를 호출하면 자기 자신의 PID를 얻을 수 있습니다) 세 번째 항목에는 보낼 계산 결과값이 들어갑니다.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;클라이언트는 서버에게 요청을 보내기 위해 send_request 함수를 이용해야 합니다. 이 함수의 첫번째 인자로는 서버의 PID가 오고, 두 번째 인자로는 리스트가 전달됩니다. send_request는 해당 인자들을 사용해 서버에 메시지를 전송한 다음, 스스로 receive를 통해 그 서버가 보내는 응답을 전송받습니다. 그런 다음 응답을 해석해 화면에 결과값을 뿌리죠. (서버가 보내는 PID는 사용하지 않을 것이기 때문에 _SPid와 같은 이름의 변수를 썼다는 것에 주의합시다. _가 앞에 붙는 변수는 설사 이후의 코드에서 사용되지 않아도 컴파일러가 ok합니다.) &lt;br /&gt;&lt;br /&gt;위의 코드는 다음과 같이 사용합니다.&lt;br /&gt;&lt;br /&gt;14&amp;gt; c(concur).&lt;br /&gt;{ok,concur}&lt;br /&gt;15&amp;gt; Pid = concur:start_server().&lt;br /&gt;&amp;lt;0.90.0&amp;gt;&lt;br /&gt;16&amp;gt; concur:send_request(Pid, [1,2,3,4,5,6]).&lt;br /&gt;odd: 9, even: 12&lt;br /&gt;ok&lt;br /&gt;17&amp;gt;&lt;/P&gt;이렇게 해서 클라이언트/서버 모델을 따르는 간단한 예제까지 만들어 봤습니다. 이해하기 쉬우셨는지 모르겠군요. 다음 글에서는 실제로 메시지 송수신 과정에서 발생할 수 있는 문제에 대처할 수 있는 코드를 만드는 방법을 포함해, 조금 더 심화된 주제들을 살펴보겠습니다. &lt;br /&gt;&lt;br /&gt;머리가 터질거같은 상태에서 억지로 글을 쓰다보니 더 이상 길게는 못쓰겠군요. 머리 터질때 코드 작성하는 일 말고는 할 수 있는게 없는 저 자신도 참 한심하기 짝이 없습니다만....&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;</description>
			<category>General</category>
			<category>Erlang</category>
			<category>병렬</category>
			<category>서버</category>
			<category>얼랭</category>
			<category>클라이언트</category>
			<category>프로그래밍</category>
			<category>프로세스</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/163</guid>
			<comments>http://www.buggymind.com/163#entry163comment</comments>
			<pubDate>Mon, 22 Sep 2008 17:19:37 +0900</pubDate>
		</item>
		<item>
			<title>Erlang for C Programmers (6) : 병렬 프로그래밍 (1)</title>
			<link>http://www.buggymind.com/162</link>
			<description>&lt;P&gt;앞선 다섯 번의 글을 통해, Erlang의 가장 기본적인 면들을 살펴 봤습니다. 물론 다 살펴본 것은 아닙니다만, 미처 살펴보지 못한 주제들(binary, record...)은 Erlang의 가장 기본적인 부분이라기 보다는 양념에 가깝고, 특별히 심각한 프로그래밍을 할 생각이 없다면 일단은 제처놓고 사용하지 않을 수도 있습니다. 그러니, 나중에 필요할 때 더 살펴보도록 하죠. &lt;br /&gt;&lt;br /&gt;그러니, 오늘은 바로 병행성(concurrency)으로 점프해 보도록 하겠습니다. 사실 얼랭을 배우겠다고 작심하신 분들 중 상당수는 Erlang의 병렬 처리 성능에 이끌려 오신 분들일 거에요. CPU를 꽂으면 꽂는 대로 성능이 graceful 하게 upgrader되는 시스템은 모든 개발자가 꿈꾸는 시스템이긴 합니다만, 그렇다고 모든 사람들이 그런 프로그램을 짤 수 있는 것은 아니었죠. (언어의 가상 머신 시스템이 발전되어 가면서 그 장벽이 점점 낮아지고 있는 것은 사실입니다만... )&lt;br /&gt;&lt;br /&gt;Erlang의 병행성은 spawn이라는 fork() call과 메시지 send / receive 메커니즘을 톻해 얻어집니다. 그 메커니즘이 동작하는 방식이 굉장히 간단하고 알기 쉽기 떄문에, C에서 fork()를 써 보신 분이나, 병행 프로그래밍에 대해 아주 간단한 수준의 지식만 가지고 있더라도 프로그래밍을 할 수가 있어요. &lt;br /&gt;&lt;br /&gt;우선, 예전에 만들었던 함수들을 가지고, 그 함수들을 사용해 병행성 프로그램을 만드는 실습을 한번 해 보겠습니다. 다음 코드를 보시죠.&lt;br /&gt;&lt;br /&gt;-module(concur).&lt;br /&gt;-export([odds_and_evens_acc/1]).&lt;/P&gt;
&lt;P&gt;odds_and_evens_acc(L) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; odds_and_evens_acc(L, 0, 0).&lt;/P&gt;
&lt;P&gt;odds_and_evens_acc([H|T], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; case ( H rem 2 ) of&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1 -&amp;gt; odds_and_evens_acc(T, Odds + H, Evens);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0 -&amp;gt; odds_and_evens_acc(T, Odds, Evens + H)&lt;br /&gt;&amp;nbsp; &amp;nbsp; end;&lt;br /&gt;odds_and_evens_acc([], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; { {odd, Odds}, {even, Evens} }.&lt;br /&gt;&lt;br /&gt;이 함수를 컴파일하고 실행하면 다음과 같은 결과를 얻습니다.&lt;br /&gt;&lt;br /&gt;Eshell V5.5.5&amp;nbsp; (abort with ^G)&lt;br /&gt;1&amp;gt; c(concur).&lt;br /&gt;{ok,concur}&lt;br /&gt;2&amp;gt; concur:odds_and_evens_acc([1,2,3,4,5,6,7,8,9,10]).&lt;br /&gt;{{odd,25},{even,30}}&lt;br /&gt;3&amp;gt;&lt;br /&gt;&lt;br /&gt;뭐 여기까지는 단순한데요. 자. 그러면 이 함수를 별개의 프로세스로 실행 시킬 수 있도록, 코드를 변경해 보도록 하죠. 목표는 odds_and_evens_acc 함수가 서버로서 계속해서 동작할 수 있도록 만드는 것입니다. 클라이언트 측에서는 이 서버에 메시지를 전달하는 것만으로 리스트의 홀수합과 짝수합을 계산할 수 있어야 하죠.&lt;br /&gt;&lt;br /&gt;우선 서버라는 것은 뭔가가 계속해서 끊임없이 돌아야 하는 것이니까, 그 끊임없이 도는 함수를 한 번 만들어 보겠습니다. 메시지를 수신할 때 쓰는 receive 구문이 포함되어 있으니까 주의해서 보시기 바랍니다.&lt;br /&gt;&lt;br /&gt;-module(concur).&lt;br /&gt;-export([start/0]).&lt;/P&gt;
&lt;P&gt;start() -&amp;gt; spawn( fun loop/0 ).&lt;/P&gt;
&lt;P&gt;loop() -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; receive&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; L -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {{odd, Ov}, {even, Ev}} = odds_and_evens_acc(L),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; io:format(&quot;~p, ~p~n&quot;, [Ov, Ev]),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loop()&lt;br /&gt;&amp;nbsp; &amp;nbsp; end.&lt;/P&gt;
&lt;P&gt;odds_and_evens_acc(L) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; odds_and_evens_acc(L, 0, 0).&lt;/P&gt;
&lt;P&gt;odds_and_evens_acc([H|T], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; case ( H rem 2 ) of&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1 -&amp;gt; odds_and_evens_acc(T, Odds + H, Evens);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0 -&amp;gt; odds_and_evens_acc(T, Odds, Evens + H)&lt;br /&gt;&amp;nbsp; &amp;nbsp; end;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;odds_and_evens_acc([], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; { {odd, Odds}, {even, Evens} }.&lt;br /&gt;&lt;br /&gt;이 코드는 다음과 같이 실행합니다.&lt;br /&gt;&lt;br /&gt;23&amp;gt; c(concur).&lt;br /&gt;{ok,concur}&lt;br /&gt;24&amp;gt; Q = concur:start().&lt;br /&gt;&amp;lt;0.95.0&amp;gt;&lt;br /&gt;25&amp;gt; Q ! [1,2,3].&lt;br /&gt;[1,2,3]&lt;br /&gt;4, 2&lt;br /&gt;27&amp;gt;&lt;br /&gt;&lt;br /&gt;concur 모듈에 정의되어 있는 start() 함수를 부르면, spawn 함수가 호출되면서 새로운 프로세스가 만들어지고, 그 프로세스의 PID가 반환됩니다. spawn 함수의 인자로 fun loop/0을 넘겼기 때문에, 해당 함수가 그 프로세스의 이미지(image)가 됩니다. 이 함수는 메시지가 도착할 때 마다 (이 메시지는 리스트입니다) 그 메시지를 인자로 하여 odds_and_evens_acc 함수를 호출하고, 그 결과를 화면에 출력합니다. 서버는 계속해서 돌아야 하기 때문에, 실행이 끝나면 loop()로 자기 자신을 호출하여 다음 메시지를 기다립니다. 간단하죠? 클라이언트 측에서는 이 서버에 메시지를 전송하려면 서버의 PID에다가 !를 주고 메시지를 인자로 넘기기만 하면 됩니다. 그러면 해당 PID를 갖는 서버 프로세스에 메시지가 날아가죠.&lt;br /&gt;&lt;br /&gt;그런데 이 함수에는 좀 미심쩍은 구석이 있습니다. 메시지로 받은 L이 리스트가 아니면 어떻게 되는거죠?&lt;br /&gt;&lt;br /&gt;그런 미심쩍음을 좀 해소하기 위해, 이번에는 리스트와 함께 아톰으로 된 헤더를 붙여 전달해야 처리되도록 코드를 바꿔 보죠. loop()의 코드만 다음과 같이 바꿔보겠습니다.&lt;br /&gt;&lt;br /&gt;loop() -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; receive&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {list, L} -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {{odd, Ov}, {even, Ev}} = odds_and_evens_acc(L),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; io:format(&quot;~p, ~p~n&quot;, [Ov, Ev]),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loop();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; stop -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; exit&lt;br /&gt;&amp;nbsp; &amp;nbsp; end.&lt;br /&gt;&lt;br /&gt;그렇게 한 다음에 다음과 같이 실행해 보죠.&lt;br /&gt;&lt;br /&gt;27&amp;gt; f().&lt;br /&gt;ok&lt;br /&gt;28&amp;gt; c(concur).&lt;br /&gt;{ok,concur}&lt;br /&gt;29&amp;gt; Q = concur:start().&lt;br /&gt;&amp;lt;0.104.0&amp;gt;&lt;br /&gt;30&amp;gt; Q ! [1,2,3].&lt;br /&gt;[1,2,3]&lt;br /&gt;31&amp;gt; Q ! {list, [1,2,3]}.&lt;br /&gt;{list,[1,2,3]}&lt;br /&gt;4, 2&lt;br /&gt;32&amp;gt; Q ! stop.&lt;br /&gt;stop&lt;br /&gt;33&amp;gt; Q ! {list, [1,2,3]}.&lt;br /&gt;{list,[1,2,3]}&lt;br /&gt;34&amp;gt;&lt;br /&gt;&lt;br /&gt;이제 서버의 함수가 정상적으로 실행되도록 하려면, 메시지 앞에 list라는 아톰을 붙여야 합니다. 다른 형태의 메시지를 보내면 (list가 아닌 다른 아톰을 헤더로 사용하거나 아예 형식이 다른 메시지를 보내면) 그 메시지는 아예 loop 함수에 의해 처리가 되지 않는다는 것도 볼 수 있습니다. (위의 30&amp;gt;번 실행 결과를 보세요.) 서버의 동작에 영향을 미치지 못하는 거죠. (이 점이 Erlang 서버의 구현을 좀 더 편하게 만든다고도 볼 수 있습니다. 명시적으로 무시하는 로직을 작성할 수도 있습니다만, 그렇게 하지 않아도 어쨌든 되긴 된다는 거죠. )&lt;br /&gt;&lt;br /&gt;서버의 동작을 중지시키려면 stop이라는 메시지를 보내면 되고, 그런 다음에는 concur:start()를 호출한 결과로 받은 PID에 대고 메시지를 전송시켜 봐야 아무일도 하지 않습니다. 서버의 loop() 함수 수행이 이미 끝났거든요. (32&amp;gt;와 32&amp;gt;을 보세요.) 어쨌거나, 위의 코드에는 아직도 L이 리스트인지 아닌지를 검사하는 부분은 없습니다. 다음과 같이 한번 바꿔보죠.&lt;br /&gt;&lt;br /&gt;loop() -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; receive&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {list, L} -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; case is_list(L) of&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; true -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {{odd, Ov}, {even, Ev}} = odds_and_evens_acc(L),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; io:format(&quot;~p, ~p~n&quot;, [Ov, Ev]),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loop();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; false -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; io:format(&quot;second argument is not a list&quot;),&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loop()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; end;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; stop -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; exit&lt;br /&gt;&amp;nbsp; &amp;nbsp; end.&lt;br /&gt;&lt;br /&gt;이렇게 한 다음에 다음과 같이 실행해 보겠습니다.&lt;br /&gt;&lt;br /&gt;3&amp;gt; c(concur).&lt;br /&gt;{ok,concur}&lt;br /&gt;4&amp;gt; Pid = concur:start().&lt;br /&gt;&amp;lt;0.46.0&amp;gt;&lt;br /&gt;5&amp;gt; Pid ! { list, [1,2,3,4,5] }.&lt;br /&gt;{list,[1,2,3,4,5]}&lt;br /&gt;9, 6&lt;br /&gt;6&amp;gt; Pid ! { list, 3 }.&lt;br /&gt;{list,3}&lt;br /&gt;second argument is not a list&lt;br /&gt;7&amp;gt;&lt;br /&gt;&lt;br /&gt;어떤 멍청한 인간이 리스트가 아닌 다른 무엇을 보내도 사고가 생기지 않는 코드를 만들고자 이렇게 했습니다. 종전 코드에서는, 클라이언트가 만일 리스트가 아닌 다른 무엇을 서버에 보냈더라면 실행 시간에 오류가 발생했을 겁니다. 이처럼, 얼랭으로 프로그램을 짤 때 문제가 될 수 있는 부분은, 대응규칙(matching rule)으로 처리될 수 없는 비정상적인 어떤 일이 벌어졌을 때가 대부분입니다. 그런 부분을 막도록 프로그램을 잘 짜야 괴상망칙한 일이 벌어지는 것을 미연에 방지할 수 있죠.&lt;br /&gt;&lt;br /&gt;자. 그렇다면 이제 list라는 아톰 헤더는 필요가 없는거군요? 그렇습니다. list라는 아톰 대신, 뭔가 다른 그럴싸한 아톰이 오면 좋을 것 같습니다. 어쩄든 &#039;이 메시지가 무슨 메시지인지를&#039; 표현하는 헤더는 필요한 것이니까요. 다음 글에서 예제를 그에 맞게 조금 수정하도록 하겠습니다. 그리고 지금까지의 예제는 클라이언트가 메시지를 보내면 서버가 그 실행 결과를 화면에 뿌리는 것이었습니다만, 다음 글에서는 이제 진짜 클라이언트-서버 모델에 맞게 프로그램을 더 고쳐보겠습니다. 그러려면 고려해야 할 것들이 좀 더 많습니다. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/P&gt;</description>
			<category>General</category>
			<category>concurrency</category>
			<category>Erlang</category>
			<category>병렬 프로그래밍</category>
			<category>얼랭</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/162</guid>
			<comments>http://www.buggymind.com/162#entry162comment</comments>
			<pubDate>Mon, 22 Sep 2008 13:46:02 +0900</pubDate>
		</item>
		<item>
			<title>불확실성과 화해하는 프로젝트 추정과 계획</title>
			<link>http://www.buggymind.com/161</link>
			<description>이 책을 번역하면서 올해의 반을 보낸거같네요. 드디어 Agile Estimating &amp;amp; Planning의 출간을 앞두고 있습니다. 한글판 제목은 &lt;A href=&quot;http://blog.insightbook.co.kr/entry/신간소개-불확실성과-화해和解하는-프로젝트-추정과-계획&quot; target=_blank&gt;&quot;불확실성과 화해하는 프로젝트 추정과 계획&quot;&lt;/A&gt; 입니다. &lt;br /&gt;&lt;br /&gt;Agile 에 입문해서 공부하고 이것 저것 배우는 과정에서 저에게 주어졌던 가장 큰 기회를 꼽으라면, 바로 이 책을 번역했던 것이라고 하겠습니다. 책의 모든 부분을 꼼꼼히 살필수 있는 기회를 얻었고, 애자일을 배우는 좋은 사람들을 만났고 (제가 워낙 두문불출하는 스타일이라 많이는 뵙지 못했습니다만) 인사이트 출판사의 좋은 분들과 함께 일할 수 있는 기회를 얻었습니다. 이 자리를 빌어 감사의 말씀을 드립니다. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;imageblock center&quot; style=&quot;text-align: center; clear: both;&quot;&gt;&lt;img src=&quot;http://www.buggymind.com/attach/1/1219752348.jpg&quot; alt=&quot;사용자 삽입 이미지&quot; height=&quot;626&quot; width=&quot;492&quot; /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;저 개인적으로는 애자일을 공부하면서 다음과 같은 것들을 알게 되어 보람있었습니다.&lt;br /&gt;&lt;br /&gt;1. &lt;A href=&quot;http://agile.egloos.com&quot; target=_blank&gt;김창준님의 블로그를 &lt;/A&gt;알게 된 것&lt;br /&gt;2. 프로그래밍을 하는 방법 만큼이나 프로그래밍을 잘 하는 방법을 깨우치는 것도 중요하다는 것&lt;br /&gt;3. 배우는 것은 언제나 즐겁다는 것&lt;br /&gt;4. 세상은 넓고 고수는 많다는 것&lt;br /&gt;5. 생각했던 것 보다 내 번역 실력은 그다지 좋지 못하다는 것&lt;br /&gt;&lt;br /&gt;
&lt;DIV style=&quot;TEXT-ALIGN: center&quot;&gt;* * *&lt;/DIV&gt;&lt;br /&gt;&lt;br /&gt;확실한 출간 일정은 아직 모릅니다만, 곧 서점에서 만나실 수 있을 것 같습니다. 부끄럽지 않은 번역이 되었어야 하는데 참 큰일이군요. 출간을 앞두고 이렇게 마음이 콩닥콩닥하기는 또 처음인것 같습니다. 한두권 번역했던 것도 아닌데... &lt;br /&gt;&lt;br /&gt;부디 냉정한 평가를 해 주시길. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;</description>
			<category>General</category>
			<category>Agile</category>
			<category>Agile Estimating &amp; Planning</category>
			<category>불확실성과 화해하는 프로젝트 추정과 계획</category>
			<category>애자일</category>
			<category>인사이트</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/161</guid>
			<comments>http://www.buggymind.com/161#entry161comment</comments>
			<pubDate>Tue, 09 Sep 2008 12:11:04 +0900</pubDate>
		</item>
		<item>
			<title>Erlang for C programmers (5) : 함수</title>
			<link>http://www.buggymind.com/160</link>
			<description>&lt;P&gt;Erlang은 함수형 언어이고, 모든 프로그래밍 행위가 함수를 정의하는 것에서부터 출발합니다. Erlang 함수에는 두 가지 종류가 있습니다. &lt;br /&gt;&lt;br /&gt;1. 이름 있는 함수&lt;br /&gt;2. 이름 없는 함수&lt;br /&gt;&lt;br /&gt;이름 있는 함수의 예제는 지금까지 보셨으니 잘 아실테구요. (이름 있는 함수들은 C나 Java에서도 흔한 것이니까 이해하기도 쉬운 편이죠.) 이번 글에서는 이름 없는 함수를 살펴보겠습니다.&lt;br /&gt;&lt;br /&gt;이름 없는 함수의 사례는 JavaScript 같은 프로그래밍 언어에서 쉽게 찾아볼 수 있습니다. 제가 예전에 JavaScript 관련 글을 적으면서 JavaScript에서는 함수도 객체다라는 이야기를 한 적이 있었는데요. 다음 예제를 한 번 보시죠. &lt;br /&gt;&lt;br /&gt;function addGenerator( num ) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return function( toAdd ) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; return num + toAdd;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var addFive = addGenerator( 5 );&lt;br /&gt;&lt;br /&gt;alert( addFive(4) == 9 );&lt;br /&gt;&lt;br /&gt;위의 예제를 보면 함수가 함수를 반환하고 있고, 반환된 함수를 다시 호출하고 있습니다. Erlang에서도 당연히 이런 프로그래밍이 가능합니다. 가령 위와 똑같은 코드를 Erlang으로 작성해 본다면...&lt;br /&gt;&lt;br /&gt;4&amp;gt; AddGenerator = fun(Num) -&amp;gt; ( fun(X) -&amp;gt; Num + X end ) end.&lt;br /&gt;5&amp;gt; AddFive = AddGenerator(5).&lt;br /&gt;6&amp;gt; AddFive(4).&lt;br /&gt;9.&lt;br /&gt;&lt;br /&gt;뭐 이렇게 되는 겁니다. 여기서 중요하게 보셔야 할 부분이 바로 fun()의 쓰임새인데요. JavaScript에서 function()이 익명의 함수 객체를 만들어 반환하는 데 쓰이는 것 처럼, fun()도 Erlang에서 같은 쓰임새를 가지고 있습니다. &lt;br /&gt;&lt;br /&gt;Erlang에서 fun 정의는 fun 키워드로 시작해서 end로 끝납니다. 그러니까 이런 식이죠.&lt;br /&gt;&lt;br /&gt;1&amp;gt; Mult = fun(X, Y) -&amp;gt; X * Y end.&lt;br /&gt;2&amp;gt; Mult(3, 4)&lt;br /&gt;12&lt;br /&gt;&lt;br /&gt;정의한 fun을 변수에 대응시켜 변수 이름을 통해 호출하고 있습니다. fun도 함수인지라 일반 Erlang 함수와 같이 절을 가질 수 있습니다. 근데 fun에는 함수 이름이 없기 떄문에, 그 문법은 일반 함수와 조금 틀립니다. 가령, 다음의 fun을 한번 보시죠.&lt;br /&gt;&lt;br /&gt;1&amp;gt; Max = fun(X, Y) when X&amp;gt;Y -&amp;gt; X; (X, Y) -&amp;gt; Y end.&lt;br /&gt;#Fun&amp;lt;erl_eval.12.102015280&amp;gt;&lt;br /&gt;2&amp;gt; Max(2,3).&lt;br /&gt;3&lt;br /&gt;&lt;br /&gt;이전 글에서 배웠던 Max 함수의 정의외 바교해 보면, 차이가 분명합니다. 적어줄 함수 이름이 없기 때문에, 괄호와 인자 리스트만 절 앞부분에 적어주는 것이죠. &lt;br /&gt;&lt;br /&gt;이렇게 해서 만들어진 fun은 함수의 인자로 넘길 수 있습니다. 물론 이름 있는 함수들도 다른 함수의 인자로 전달할 수 있습니다. 하지만 fun을 사용하면 좀 더 재미있게(fun!) 코드를 작성할 수 있습니다. 예를 한번 살펴보죠. Erlang의 표준 라이브러리 lists라는 모듈에는 map이라는 함수가 정의되어 있습니다. 이 함수는 인자를 두 개 받는데요. 첫 번째 인자는 함수이고, 두 번째 인자는 리스트입니다. map은 두 번째 인자에 보관된 각각의 원소에 첫 번째 인자로 주어진 함수를 적용한 결과를 리스트 형태로 반환합니다. 다음의 코드를 보시죠.&lt;br /&gt;&lt;br /&gt;5&amp;gt; Double = fun(X) -&amp;gt; 2 * X end.&lt;br /&gt;#Fun&amp;lt;erl_eval.6.49591080&amp;gt;&lt;br /&gt;6&amp;gt; L = [1, 2, 3, 4, 5].&lt;br /&gt;[1,2,3,4,5]&lt;br /&gt;7&amp;gt; lists:map(Double, L).&lt;br /&gt;[2,4,6,8,10]&lt;br /&gt;&lt;br /&gt;예상했던 결과죠? 그런데 저렇게 코드를 작성하는 대신 다음과 같이 해도 됩니다.&lt;br /&gt;&lt;br /&gt;8&amp;gt; lists:map(fun(X) -&amp;gt; 2*X end, L).&lt;br /&gt;[2,4,6,8,10]&lt;br /&gt;9&amp;gt;&lt;br /&gt;&lt;br /&gt;jQuery 써서 자바스크립트 프로그래밍 많이들 하시는 분은 아마 다음과 같은 형식에 꽤나 익숙하실텐데, 비슷한 형태의 문법이라고 할 수 있죠.&lt;br /&gt;&lt;br /&gt;$(function() {&lt;br /&gt;&amp;nbsp;&amp;nbsp; ...&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;자. 지금까지 Erlang 함수가 fun이나 함수를 인자로 받을 수 있음을 살펴봤습니다. 뿐만 아니라, Erlang 함수는 fun을 반환(return)할 수도 있습니다. 이에 대한 예제는 맨 위의 AddGenerator 예제를 통해 살펴봤었죠. 그런데 Erlang 함수에서 다른 이름 붙은 Erlang 함수를 반환할 수는 있을까요? &lt;br /&gt;&lt;br /&gt;됩니다. (첨엔 안된다고 썼었는데 죄송.)&lt;br /&gt;&lt;br /&gt;다음과 같이 하면 됩니다. &lt;br /&gt;&lt;br /&gt;-module(test)&lt;br /&gt;-export([get_sum/0]).&lt;br /&gt;&lt;br /&gt;sum(L) -&amp;gt; sum(L, 0).&lt;/P&gt;
&lt;P&gt;sum([H|T], S) -&amp;gt; sum(T, H + S);&lt;br /&gt;sum([], S) -&amp;gt; S.&lt;/P&gt;
&lt;P&gt;get_sum() -&amp;gt;&lt;FONT color=#d41a01&gt; fun sum/1.&lt;/FONT&gt;&lt;br /&gt;&lt;br /&gt;위와 같이 코드를 작성하셨으면 컴파일 해서 실행을 한 번 해 보죠.&lt;br /&gt;&lt;br /&gt;1&amp;gt; c(test).&lt;br /&gt;{ok,test}&lt;br /&gt;2&amp;gt; X = test:get_sum().&lt;br /&gt;#Fun&amp;lt;test.0.55022140&amp;gt;&lt;br /&gt;3&amp;gt; X([1,2,3,4,5,6]).&lt;br /&gt;21&lt;br /&gt;4&amp;gt;&lt;br /&gt;&lt;br /&gt;어쨌던 fun이라는 키워드를 사용하는 것이 요령입니다. /1과 같이 arity를 명시해 주는 부분도 잊으면 안되겠죠.&lt;br /&gt;&lt;br /&gt;&lt;/P&gt;</description>
			<category>General</category>
			<category>Erlang</category>
			<category>fun</category>
			<category>함수</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/160</guid>
			<comments>http://www.buggymind.com/160#entry160comment</comments>
			<pubDate>Mon, 08 Sep 2008 20:05:57 +0900</pubDate>
		</item>
		<item>
			<title>Erlang for C programmers (4) : 순환문</title>
			<link>http://www.buggymind.com/159</link>
			<description>&lt;P&gt;C같은 프로그래밍 언어에는 순환문 지원을 위한 다양한 문법들을 제공하고 있습니다. for, while, do-while 등등이 그것이죠. 물론 순환문을 위한 &#039;특별한&#039; 문법을 사용하지 않고도 프로그래밍을 할 수 있긴 합니다. 가령 C의 다음과 같은 프로그램을 한번 생각해 보죠.&lt;br /&gt;&lt;br /&gt;int sum(int[] array, size_t n) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; int sum = 0;&lt;br /&gt;&amp;nbsp; &amp;nbsp; for ( int i = 0; i &amp;lt; n; ++i ) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sum += array[i];&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;for를 사용해 배열에 있는 모든 값들을 합산하고 있습니다. 그런데 for라는 키워드를 사용할 수가 없고, 그냥 함수 개념만 사용해 프로그래밍을 해야 한다면, 문제를 어떻게 해결할 수 있을까요?&lt;br /&gt;&lt;br /&gt;int sum(int[] array, size_t n) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return sum_array(array, n, 0, 0);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int sum_array(int[] array, size_t n, int start_index, int sum) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if ( n == 0 ) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return sum;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; return sum_array(array, n-1, start_index + 1, sum + array[start_index]);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;뭐 이렇게도 할 수 있지 않았을까요? 여기서 주목해 볼 것은, 인자 sum을 통해 최종적으로 반환될 합계 값을 추적한다는 점입니다. 이런 프로시저는 &quot;컴퓨터 프로그램의 구조와 해석&quot;이라는 책에서 쓰인 용어를 빌려 말하자면, &quot;되도는 프로시저&quot;이고 &quot;반복적인 프로세스&quot;이죠. (이에 대해서는 해당 책을 참조하시길. ㅋㅋ) 반복적인 프로세스는 재귀적인 프로세스와는 달리 유한한 메모리를 사용해 문제를 풀 수 있습니다. (재귀적인 프로세스는 그 계산과정을 추적하기 위해 재귀 호출이 뻗어나간 각각의 가지에 대한 정보를 계산이 끝나서 그 결과가 &#039;말아올려질 때 까지&#039; 들고 있어야 합니다.) &lt;br /&gt;&lt;br /&gt;하지만 C나 Java같은 프로그래밍 언어는 설사 프로세스가 풀려나가는 과정이 반복적(iterative)이라고 하더라도 일단 재귀적인 프로시저를 통해 구현했다면 전부 똑같이 처리합니다. 가령, 위의 sum_array 함수는 네 개의 변수만 사용하면 그 계산과정 전부를 온전히 추적할 수 있음에도, C 컴파일러는 냉정하게 함수가 새로 호출될 때 마다 그에 필요한 스택을 강제로 할당해 버립니다. 결국 배열이 길어지면 stack overflow가 나고 말죠. 그래서 for나 whie, do-while 같은 별도의 문법이 존재하는 겁니다.&lt;br /&gt;&lt;br /&gt;하지만 Erlang이나 Scheme같은 프로그래밍 언어는 순환문을 위한 별도의 문법을 제공하지 않습니다. Erlang에서 위의 문제를 어떻게 푸는지 한번 살펴보도록 하죠. 재귀적 프로시저를 사용해 코드를 작성해야 합니다.&lt;br /&gt;&lt;br /&gt;sum([H|T]) -&amp;gt; H + sum(T);&lt;br /&gt;sum([]) -&amp;gt; 0.&lt;br /&gt;&lt;br /&gt;위의 sum/1 함수는 리스트 하나를 인자로 받습니다. 리스트가 비어 있지 ([]) 않을 경우, 대응 규칙에 따라 이 리스트는 위의 첫 번째 절로 전달됩니다. 첫 번째 절에서 인자는 머리 (H)와 나머지(T) 부분으로 분리됩니다. 그러므로 sum이 반환해야 할 값은 H와 sum(T)의 합이 됩니다. 간단하죠?&lt;br /&gt;&lt;br /&gt;그런데 위의 함수는 되도는 프로세스 이지 반복적인 프로세스는 아닙니다. 그다지 효율적으로 메모리를 사용하지 못한다는 뜻이 되겠군요. 이 프로세스를 반복적 프로세스로 바꿀려면, 다음과 같이 해야 할겁니다.&lt;br /&gt;&lt;br /&gt;sum(L) -&amp;gt; sum(L, 0).&lt;/P&gt;
&lt;P&gt;sum([H|T], S) -&amp;gt; sum(T, H + S);&lt;br /&gt;sum([], S) -&amp;gt; S.&lt;br /&gt;&lt;br /&gt;이렇게 하고 Erlang 셸에서 다음과 같이 해 보면 결과가 제대로 출력되는 것을 볼 수 있습니다.&lt;br /&gt;&lt;br /&gt;14&amp;gt; L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].&lt;br /&gt;[1,2,3,4,5,6,7,8,9,10]&lt;br /&gt;15&amp;gt; c(test).&lt;br /&gt;...&lt;br /&gt;20&amp;gt; test:sum(L).&lt;br /&gt;55&lt;br /&gt;21&amp;gt;&lt;br /&gt;&lt;br /&gt;결국 Erlang에서 순환문은 함수를 재귀적으로 작성하여 만들게 된다는 이야기입니다. 간단한 이야기를 하려던 것이었는데 말이 너무 길어졌군요 ㅜㅜ 어쨌건 순환문을 만드는 데 있어서 재귀적 프로세스와 반복적 프로세스에 대한 차이는 반드시 알아두는 것이 좋습니다. Erlang에서는 그 차이를 정확하게 이해해야 효율적인 프로그램을 짤 수 있을 것이거든요.&lt;br /&gt;&lt;br /&gt;&quot;컴퓨터 프로그램의 구조와 해석&quot;이 좀 비싸긴 합니다만 한권 장만해두시는 것도 좋겠습니다. ㅋㅋㅋㅋㅋㅋ&lt;br /&gt;&lt;br /&gt;그럼 보는 김에 &quot;프로그래밍 얼랭&quot;에 나오는 예를 하나 더 살펴볼까요? 완전히 같은건 아니고 제가 좀 바꿨습니다. &lt;br /&gt;&lt;br /&gt;odds_and_evens_acc(L) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; odds_and_evens_acc(L, 0, 0).&lt;/P&gt;
&lt;P&gt;odds_and_evens_acc([H|T], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; case ( H rem 2 ) of&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1 -&amp;gt; odds_and_evens_acc(T, Odds + H, Evens);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0 -&amp;gt; odds_and_evens_acc(T, Odds, Evens + H)&lt;br /&gt;&amp;nbsp; &amp;nbsp; end;&lt;br /&gt;odds_and_evens_acc([], Odds, Evens) -&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; { {odd, Odds}, {even, Evens} }.&lt;/P&gt;
&lt;P&gt;&lt;br /&gt;29&amp;gt; c(test).&lt;br /&gt;{ok,test}&lt;br /&gt;30&amp;gt; test:odds_and_evens_acc(L).&lt;br /&gt;{{odd,25},{even,30}}&lt;br /&gt;31&amp;gt;&lt;br /&gt;&lt;br /&gt;홀수끼리의 합, 짝수끼리의 합을 구해서 투플 형태로 반환하는 예입니다. case 문도 사용했고, 투플에 아톰을 넣어뒀기 때문에 반환값의 의미를 좀 명확하게 볼 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/P&gt;</description>
			<category>General</category>
			<category>Erlang</category>
			<category>순환문</category>
			<category>재귀</category>
			<category>프로세스</category>
			<category>프로시저</category>
			<author> (낭만고양이)</author>
			<guid>http://www.buggymind.com/159</guid>
			<comments>http://www.buggymind.com/159#entry159comment</comments>
			<pubDate>Fri, 05 Sep 2008 14:11:17 +0900</pubDate>
		</item>
	</channel>
</rss>
