Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 080b6d5cc3
Fetching contributors…

Cannot retrieve contributors at this time

3558 lines (3177 sloc) 246.155 kb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The Node Beginner Book (Korean version) » A comprehensive Node.js tutorial</title>
<meta name="description" content="A comprehensive Node.js tutorial for beginners (Korean version): Learn how to build a full blown web application with server-side JavaScript" />
<link rel="stylesheet" type="text/css" href="default.css" />
<script type="text/javascript">
// Google Analytics
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-2127388-6']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
// Disqus
var disqus_shortname = 'nodebeginner';
var disqus_identifier = 'nodebeginner-book';
var disqus_url = 'http://www.nodebeginner.org/index-kr.html';
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
</head>
<body>
<img style="display: none;" src="the_node_beginner_book_cover_medium.png" height="256" width="171" />
<div id="forkmeongithub">
<a href="https://github.com/ManuelKiessling/NodeBeginnerBook"><img src="fork_me_on_github.png" width="149" height="149" alt="Fork me on GitHub" /></a>
</div>
<div id="translations">
<table>
<tr>
<td>
<a href="index-jp.html">
<div class="flag"><img src="jp-flag.png" width="24" height="24" alt="japanese flag" /></div>
<div class="text">日本語で読む</div>
</a>
</td>
<td>
<a href="index-es.html">
<div class="flag"><img src="es-flag.png" width="24" height="24" alt="spanish flag" /></div>
<div class="text">Lee este tutorial en Español</div>
</a>
</td>
<td>
<a href="./">
<div class="flag"><img src="us-flag.png" width="24" height="24" alt="usa flag" /></div>
<div class="text">Read this tutorial in english</div>
</a>
</td>
</tr>
<tr>
<td>
<a href="index-zh-cn.html">
<div class="flag"><img src="cn-flag.png" width="24" height="24" alt="chinese flag" /></div>
<div class="text">阅读本书中文版</div>
</a>
</td>
<td>
<a href="index-zh-tw.html">
<div class="flag"><img src="cn-flag.png" width="24" height="24" alt="chinese flag" /></div>
<div class="text">阅读本书繁体中文版</div>
</a>
</td>
<td>
<a href="http://www.nodebeginner.ru">
<div class="flag"><img src="ru-flag.png" width="24" height="24" alt="russian flag" /></div>
<div class="text">Читать этот учебник на русском</div>
</a>
</td>
</tr>
</table>
</div>
<div class="buybox">
<div class="buy-the-bundle">
<div class="cover">
<p>
The perfect introduction plus the perfect reference in one bundle!
</p>
<a href="buy-bundle/index.html"><img src="the_node_beginner_book_cover_small.png" height="86" width="57" /></a>
<a href="buy-bundle/index.html"><img src="hands-on_node.js_cover.png" height="86" width="57" /></a>
</div>
<div class="description">
<p>
LeanBundle currently offers<br />
the final version of
<br />
<strong>The Node Beginner Book</strong>
<br />
plus Pedro Teixeira's excellent
<br />
<strong>Hands-on Node.js</strong> for only
<br />
<br />
<strong class="price dollarsign">$</strong><strong class="price">9.99</strong>
<br />
(regular price <del>$21.98</del>)
</p>
</div>
<div class="buy">
<p>
226 pages in total
<br />
PDF, ePub & MOBI
<br />
Direct download
<br />
Free updates
</p>
<a class="buttonlink" href="buy-bundle/index.html">
<div class="button">Buy this<br />bundle now</div>
</a>
</div>
</div>
</div>
<div id="book">
<h1>The Node Beginner Book</h1>
<div id="author">A Node.js tutorial by <a href="http://twitter.com/manuelkiessling">Manuel Kiessling</a><br/>
translated into korean by <a href="http://facebook.com/stoneshim">심형석</a>, <a href="http://blog.doortts.com/">채수원</a></div>
<a name="about"></a>
<h2>이 문서에 대하여</h2>
<p>
본 문서의 목표는 Node.js용 애플리케이션 개발을 시작을 할 수
있게 만드는 것입니다. 그리고 함께 알아야 하는 “고급”
JavaScript에 관한 모든 것을 다룹니다. 본 문서는 전형적인
“Hello World” 튜토리얼 보다는 더 많이 다룹니다.
</p>
<a name="status"></a>
<h3>상태</h3>
<p>
당신은 현재 이 책의 최종버전을 읽고 있습니다. 즉, 새로운
버전의 Node.js에 있는 변경사항들을 반영하거나 오류를 수정할
때만 업데이트 합니다.
</p>
<p>
이 책에 있는 코드 예제들은 Node.js 0.6.11에서 동작하는지
테스트 되었습니다.
</p>
<a name="intended-audience"></a>
<h3>대상 독자</h3>
<p>
이 문서는 저와 비슷한 배경을 가진 독자들에게 가장 잘 맞을 겁니다.
적어도 객체지향 언어 –루비, 파이선, PHP, 혹은 자바 같은 언어- 하나
정도에는 경험이 있고, JavaScript에는 약간의 경험만 있으며,
Node.js는 이번이 처음인 분들 말입니다.
</p>
<p>
이미 다른 언어에 대한 경험을 가진 개발자들을 대상으로 한다는 말은
데이터 타입이나 변수, 제어구조 같은 것들을 이 문서에서 다루지 않다는
뜻입니다. 본 문서를 이해하기 위해서는 그런 기본적인 것들은 미리 알고
있어야 합니다.
</p>
<p>
하지만, JavaScript에서의 객체나 함수들은 다른 대부분의 언어들에
대응되는 것과 다르기 때문에, 좀 더 자세히 설명하겠습니다.
</p>
<a name="structure"></a>
<h3>이 문서의 구조</h3>
<p>
이 문서를 마치는 시점에, 유저들에게 웹페이지를 보여주고 파일들을
업로드 할 수 있는 완성된 웹 애플리케이션을 가지게 될 것입니다.
</p>
<p>
이 유스케이스를 만족하기 위해 “충분한 정도”까지만 코드를 만드는
것에서 조금 더 나아가서, “세상을 바꾸는 정도”는 아닙니다만,
간결하지만 완결성있는 프레임워크를 만들어서 우리의 애플리케이션의
다른 부분들로부터 깔끔하게 분리할 겁니다.
</p>
<p>
우리는 Node.js에서 JavaScript 개발을 하는 것이 브라우저에서
JavaScript를 개발하는 것과 어떻게 다른지를 살펴보는 것으로
시작하려 할겁니다.
</p>
<p>
다음으로, “Hello World” 애플리케이션을 작성하는 훌륭한 오랜 전통을
따를 생각입니다. 그리고 그건 “무언가를 하는” 아주 기본이 되는
Node.js 애플리케이션이 될 겁니다.
</p>
<p>
그리고 나서는, 우리가 만들기 원하는 “실제” 애플리케이션의 종류가
무엇인지에 대해 논의하고, 이 애플리케이션을 조립하기 위해 구현해야
하는 다른 부분들을 자세히 살펴보고, 하나씩 이 각각의 부분들에 대해
작업을 시작할 생각입니다.
</p>
<p>
약속드렸듯이, 우리는 JavaScript의 고급 개념 몇 가지와, 그것을
어떻게 사용하는지, 그리고 우리가 아는 다른 프로그래밍 언어의 개념과
이 개념이 어떻게 다른지 살펴볼 것입니다.
</p>
<p>
완성된 애플리케이션의 소스코드는 아래 링크를 통해 다운 받으실 수
있습니다.
<a href="https://github.com/ManuelKiessling/NodeBeginnerBook/tree/master/code/application">
the NodeBeginnerBook Github repository</a>.
</p>
<div id="table-of-contents-headline">차례</div>
<div id="table-of-contents">
<ul>
<li><a href="#about">이 문서에 대하여</a>
<ul>
<li><a href="#status">상태</a></li>
<li><a href="#intended-audience">대상 독자</a></li>
<li><a href="#structure">이 문서의 구조</a></li>
</ul>
</li>
<li><a href="#javascript-and-nodejs">JavaScript와 Node.js</a>
<ul>
<li><a href="#javascript-and-you">JavaScript와 당신</a></li>
<li><a href="#a-word-of-warning">주의 사항</a></li>
<li><a href="#server-side-javascript">서버사이드 JavaScript</a></li>
<li><a href="#hello-world">"Hello World"</a></li>
</ul>
</li>
<li><a href="#a-full-blown-web-application-with-nodejs">Node.js로 활짝 핀 웹 애플리케이션</a>
<ul>
<li><a href="#the-use-cases">유스케이스</a></li>
<li><a href="#the-application-stack">애플리케이션 스택</a></li>
</ul>
</li>
<li><a href="#building-the-application-stack">애플리케이션 스택 구축하기</a>
<ul>
<li><a href="#a-basic-http-server">기본 HTTP 서버</a></li>
<li><a href="#analyzing-our-http-server">우리의 HTTP 서버를 분석하기</a></li>
<li><a href="#passing-functions-around">함수를 전달하기</a></li>
<li><a href="#how-function-passing-makes-our-http-server-work">어떻게 함수 전달이
HTTP 서버를 동작하게 하는가</a></li>
<li><a href="#event-driven-callbacks">Event-driven callbacks</a></li>
<li><a href="#how-our-server-handles-requests">우리 서버는 요청을 어떻게 처리하는가?</a></li>
<li><a href="#finding-a-place-for-our-server-module">server 모듈의 위치 잡기</a>
</li>
<li><a href="#whats-needed-to-route-requests">요청을 "route" 하려면?</a></li>
<li><a href="#execution-in-the-kongdom-of-verbs">동사들(verbs)의 나라에서의 실행(execution)</a></li>
<li><a href="#routing-to-real-request-handlers">실제 request handler로 라우팅(routing)하기</a></li>
<li><a href="#making-the-request-handlers-respond">request handler가 응답하게 만들기</a>
<ul>
<li><a href="#how-to-not-do-it">해서는 안되는 것</a></li>
<li><a href="#blocking-and-non-blocking">Blocking 과 non-blocking</a></li>
<li><a href="#responding-request-handlers-with-non-blocking-operations">request handler가
non-blocking 방식으로 동작하면서 응답하기</a>
</li>
</ul>
</li>
<li><a href="#serving-something-useful">유용한 것을 제공하기</a>
<ul>
<li><a href="#handling-post-requests">POST 요청 처리하기</a></li>
<li><a href="#handling-file-uploads">파일 업로드 처리하기</a></li>
</ul>
</li>
<li><a href="#conclusion-and-outlook">결론과 개관</a></li>
</ul>
</li>
</ul>
</div>
<a name="javascript-and-nodejs"></a>
<h2>JavaScript와 Node.js</h2>
<a name="javascript-and-you"></a>
<h3>JavaScript와 당신</h3>
<p>
기술적인 부분들에 대해 이야기 하기 앞서, 잠깐 짬을 내서 당신과
JavaScript와의 관계에 대해 이야기 해 봅시다. 이번 챕터를 읽고
나면 이 문서를 계속 읽어야 할지 판단할 수 있을 겁니다.
</p>
<p>
만약 당신이 나와 같다면, 당신은 오래 전에 HTML 문서 작성을 계기로
HTML “개발”을 시작했습니다. 당신은 이 재밌는 일을 JavaScript와
함께 했습니다. 하지만 매우 기초적인 방식으로 사용했죠. 웹페이지
이곳 저곳에 상호작용 기능을 추가하는 식으로 말입니다.
</p>
<p>
당신이 정말 원했던 것은 “실제의 것”이었습니다. 당신은 어떻게 하면
복잡한 웹 사이트를 만들 수 있는지 알고 싶었습니다. 그래서 PHP나
루비, 자바 같은 프로그래밍 언어를 배웠고, “백엔드”코드를 작성하기
시작했습니다.
</p>
<p>
그러면서도, JavaScript에 눈은 계속 두고 있었죠. jQuery나
Prototype같은 것들의 소개를 보면서 말입니다. 그것들은 JavaScript
영토내에서 좀 더 진보해나갔습니다. 그러면서 JavaScript가 실제로는
<em>window.open()</em> 함수 이상이라는 것을 보았습니다.
</p>
<p>
하지만, 여전히 이 언어는 프론트앤드에 머물러 있었고, 웹페이지를
꾸미고 싶을 때 마음대로 다룰 수 있는 jQuery가 있다는 것이 좋긴
했지만, 결국 당신은 JavaScript <em>사용자</em>였을 뿐입니다.
결코 JavaScript <em>개발자</em>는 아니었죠.
</p>
<p>
그러다 서버 위에서 동작하는 JavaScript, Node.js가 나왔습니다.
너무 멋지지 않습니까?
</p>
<p>
이제 비로소 오래되었지만 새로운 JavaScript를 살펴봐야 할
시기라고 마음먹습니다. 하지만 잠시만요. Node.js 애플리케이션을
작성하는 것과 왜 그런 방식으로 작성해야만 하는 것인지, 즉
Javascript를 이해하는 것은 다른 이야기 입니다.
</p>
<p>
문제는 이렇습니다. JavaScript는 두 개, 혹은 세 개의
삶(90년대 중반부터 시작된 작고 우스운 DHTML 헬퍼, jQuery나 그
비슷한 종류의 좀 더 진지한 프론트앤드 도구, 그리고 지금은
서버사이드)을 살았기 때문에 JavaScript를 “올바른” 방식으로
배우는 것을 도와줄 정보를 찾는 것이 쉽지는 않습니다.JavaScript를
단순히 사용하는 것이 아니라, 개발하고 있다는 느낄 수 있도록
Node.js 애플리케이션을 작성하기 위해서 말입니다.
</p>
<p>
바로 그겁니다. 당신은 이미 경험있는 개발자이고, 여기저기 해킹하듯
새로운 기술을 배우고 나서 엉뚱하게 사용하는 것을 원하지 않습니다.
당신은 올바른 각도로 이것에 접근하고 있다는 것을 확신하고 싶을
겁니다.
</p>
<p>
물론, 훌륭한 문서들이 널려 있습니다. 하지만 때로는 문서만으로는
충분하지 않습니다. 필요한 것은 바로 올바른 안내입니다.
</p>
<p>
저의 목표가 당신에게 그런 가이드를 제공하는 것입니다.
</p>
<a name="a-word-of-warning"></a>
<h3>주의 사항</h3>
<p>
정말 뛰어난 JavaScript 개발자들이 있습니다. 저는 그런 사람이 아닙니다.
</p>
<p>
저는 단지 앞에서 말했던 그런 수준의 사람입니다. 저는 백앤드 웹
애플리케이션 개발에 관해 한 두 개쯤은 알고 있습니다만, 여전히
“진정한” JavaScript에 대해서는 신참이고, Node.js에 대해서는
마찬가지입니다. JavaScript의 좀 더 고급스러운 내용에 대해서는
최근에서야 배웠습니다. 경험 많은 사람이 아니죠.
</p>
<p>
그게 바로 이 책에 “초보에서 전문가로”의 책이 아닌 이유입니다.
그 보다는 “아주 초보에서 발전된 초보로”에 더 가까운 책입니다.
</p>
<p>
제가 실패하지 않는다면, 이 책은 제가 Node.js를 시작할 때 가지고
있었다면 좋았겠다 싶은 생각이 드는 그런 종류의 문서가 될 것입니다.
</p>
<a name="server-side-javascript"></a>
<h3>서버 사이드 JavaScript</h3>
<p>
첫 번째 JavaScript의 구현체는 브라우저안에 살았습니다. 하지만
단순히 환경에 불과했습니다. JavaScript로 무엇을 할 수 있는지
정의하였습니다만, 해당 언어 자체가 할 수 있는 것이 무엇인지에
대해서는 그다지 많이 알려주지 않았습니다.
JavaScript는 “완결성을 가진” 언어입니다. 당신은 이 언어를 수 많은
환경속에서 사용할 수 있고 다른 “완결성을 가진” 언어들과 마찬가지로
무엇이든지 만들어 낼 수 있습니다.
</p>
<p>
Node.js는 실제로 단지 다른 환경일 뿐입니다. Node.js는 브라우저 밖,
백앤드에서 JavaScript를 실행할 수 있게 해줍니다.
</p>
<p>
백앤드에서 당신이 지정한 JavaScript를 수행하기 위해서는, 잘 해석되고
실행되어야 합니다. Node.js가 하는 것이 바로 그 일입니다.
그리고 그 일은 구글 크롬 브라우저가 사용하는 JavaScript 실행환경과
동일한, 구글의 V8 가상머신을 사용해 이루어집니다.
</p>
<p>
거기에 더해서, Node.js는 많은 유용한 모듈을 탑재하고 있습니다.
그렇기 때문에 string을 console로 뿌리는 것 같은 것을 바닥부터
모두 작성할 필요가 없습니다.
</p>
<p>
요컨대, Node.js는 서버사이드 JavaScript 실행환경과 라이브러리,
이렇게 두 가지로 이루어져 있습니다.
</p>
<p>
이것들을 사용하기 위해서는 Node.js를 설치해야 합니다.
<a href="https://github.com/joyent/node/wiki/Installation" title="Building and Installing Node.js">
공식 설치 안내 사이트</a>를
방문해서 설치를 마친 다음에 다시 오시길 정중히 권합니다.
</p>
<a name="hello-world"></a>
<h3>"Hello World"</h3>
<p>
좋습니다. 바로 차가운 물로 뛰어들어보죠.
우리의 첫 번째 Node.js 애플리케이션, “Hello World”를 작성해 봅시다.
</p>
<p>
즐겨쓰는 에디터를 실행해서 <em>helloworld.js</em>라는 파일을
만드세요. STDOUT(표준출력)으로 “Hello World” 문자열을 출력할
생각입니다.
그 일을 하는 코드는 다음과 같습니다.
</p>
<pre class="prettyprint lang-js"><span class="pln">console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span></pre>
<p>
파일을 저장하고 Node.js를 통해서 실행해 보겠습니다.
</p>
<pre>node helloworld.js</pre>
<p>
터미널에 <em>Hello World</em>라고 출력될 겁니다.
</p>
<p>
좋아요! 조금 심심하죠? 좀 더 리얼한 걸 만들어 봅시다.
</p>
<a name="a-full-blown-web-application-with-nodejs"></a>
<h2>Node.js로 활짝 핀 웹 애플리케이션</h2>
<a name="the-use-cases"></a>
<h3>유스케이스</h3>
<p>
간단하지만 리얼하게 해 봅시다.
</p>
<ul>
<li>
사용자는 웹 브라우저로 우리의 웹 애플리케이션을 이용할 수 있다.
</li>
<li>
사용자가 http://<em>domain</em>/start를 요청하면 파일 업로드
폼이 들어있는 웰컴페이지를 볼 수 있어야 한다.
</li>
<li>
업로드할 이미지 파일을 선택해서 폼으로 전송하면,
해당 이미지는 http://<em>domain</em>/upload로 업로드 되어야
하며, 업로드가 끝나면 해당 페이지에 표시된다.
</li>
</ul>
<p>
이 정도면 충분합니다. 이제, 당신은 구글링을 하고 코드를 좀 만지면
위 요구사항을 달성할 수 있습니다.
하지만 우리가 원하는 건 그게 아닙니다.
</p>
<p>
더욱이, 우리가 원하는 것은 목표를 만족하는 가장 기초적인 코드를
작성하는 것이 아니라, 우아하고 적절한 코드를 만드는 것입니다.
우린 의도적으로 좀 더 추상화된 부분을 넣어서 좀 더 복잡한 Node.js
애플리케이션을 만들고 있다는 느낌을 갖게 해볼 예정입니다.
</p>
<a name="the-application-stack"></a>
<h3>애플리케이션 스택</h3>
<p>
우리의 애플리케이션을 면밀하게 살펴봅시다. 유스케이스를 만족시키기
위해서 구현되어야 하는 부분은 어떤 부분인가요?
</p>
<ul>
<li>
우리는 웹페이지를 제공해야 한다. 따라서
<strong>HTTP 서버</strong>가 필요하다.
</li>
<li>
우리는 서버는 어떤 URL 요청(request)을 받았는지에 따라 다르게
응답해야 한다. 따라서, 요청과 요청을 처리할 핸들러들을 연결짓기
위한 <strong>라우터(router)</strong> 같은 것이 필요하다.
</li>
<li>
서버로 도착한 요청들, 그리고 라우터를 이용해서 라우팅된 요청들을
만족시키기 위해서 실제적인
<strong>요청 핸들러(request handlers)</strong>가 필요하다.
</li>
<li>
라우터는 아마도 들어오는 어떠한 POST 데이터들도 다룰 수 있어야
한다. 그리고 해당 데이터를 다루기 편한 형태로 만들어
request handler 들에게 넘겨야 한다. 따라서
<strong>요청 데이터 핸들링(request data handling)</strong>이
필요하다.
</li>
<li>
URL에 대한 요청을 다루는 것뿐 아니라 URL이 요청되었을 때 내용을
표시할 필요도 있다. 이 말은 즉, request handler 들이
사용자 브라우저로 콘텐트를 보내기 위해 사용할 수 있는
<strong>뷰 로직(view logic)</strong>이 필요하다는 이야기다.
</li>
<li>
마지막이지만 중요한 것으로는, 사용자가 이미지들을 업로드 할 수
있어야 하니까, 세부 사항을 다루는
<strong>업로드 핸들링(upload handling)</strong>이 필요할 것이다.
</li>
</ul>
<p>
PHP를 이용해서 이런 스택을 구축하는 방법에 대해 잠시 생각해 보는
시간을 가져봅시다. 전형적으로 mod_php5를 가진 아파치 HTTP 서버를
설치하는 것을 생각해 볼 수 있습니다.
<br>
이것은 “웹페이지를 제공하고 HTTP 요청을 받을 수 있어야 한다”는
요구의 전체를 PHP만으로는 이룰 수가 없다는 것을 의미합니다.
</p>
<p>
그런데 node의 경우는 조금 다릅니다. Node.js로는 우리는
애플리케이션뿐 아니라, HTTP 서버를 통째로 구현할 것이기 때문입니다.
사실 따지고보면, 우리의 웹 애플리케이션과 웹 서버는 기본적으로
동일합니다.
</p>
<p>
해야 할 일이 많은 것처럼 들릴 수 있는데,
Node.js를 이용하면 그렇지 않다는 걸 바로 보게 될 것입니다.
</p>
<p>
자 우리 애플리케이션 스택의 첫 번째 파트인
HTTP 서버 구현을 시작해 봅시다.
</p>
<a name="building-the-application-stack"></a>
<h2>애플리케이션 스택 구축하기</h2>
<a name="a-basic-http-server"></a>
<h3>기본 HTTP 서버</h3>
<p>
“리얼한” Node.js 애플리케이션을 만들어야겠다고 생각했을 때,
어떻게 코드를 작성할지 뿐 아니라 어떻게 코드를 구조화 시킬지에 대해
고민을 했습니다.
<br>
한 파일에 다 넣어야 하나? 웹에 있는 대부분의 튜터리얼들은
Node.js에서 하나의 기본 HTTP 서버에서 한 곳에 모든 로직을 갖도록
작성하는 법을 알려주었습니다.
내가 당장 구현하는 것보다 더 많은 것을 해야 할 때에도
내 코드가 가독성이 좋으려면 어떻게 해야 할까요?
</p>
<p>
관심사가 다른 코드들을 분리해서 모듈로 넣어 유지하는 것이 상대적으로
쉬운 방법이라는 것을 알게 되었습니다.
</p>
<p>
이렇게 하면 Node.js를 가지고 실행할 때 사용하는 main 파일을 깔끔하게
유지할 수 있고, main 파일과 다른 파일에서 서로 가져다 사용하는
모듈들을 깔끔하게 유지할 수 있습니다.
</p>
<p>
자 그럼 우리의 애플리케이션을 실행하는 데 쓸 main 파일과 HTTP 서버
코드가 거주하게 될 모듈 파일을 만들어 보겠습니다.
</p>
<p>
제가 보기에는 main 파일의 꽤 표준적인 이름은 index.js 입니다.
서버 모듈을 <em>server.js</em>라는 파일로 만드는 것은
괜찮을 것 같습니다.
</p>
<p>
자, 서버모듈을 만들어 봅시다. <em>server.js</em> 라는 파일을
프로젝트 루트 디렉토리에 만든 다음 아래 코드를 타이핑해 넣습니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br>http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="kwd">function</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; response</span><span
class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span
class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span
class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span
class="pln"><br>&nbsp; response</span><span class="pun">.</span><span class="pln">write</span><span
class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span><span
class="pln"><br>&nbsp; response</span><span class="pun">.</span><span class="pln">end</span><span
class="pun">();</span><span class="pln"><br></span><span class="pun">}).</span><span
class="pln">listen</span><span class="pun">(</span><span class="lit">8888</span><span
class="pun">);</span></pre>
<p>
이게 끝입니다. 잘 동작하는 HTTP서버를 방금 만들었습니다.
실행해서 확인해 보겠습니다.
Node.js로 방금 만든 스크립트를 실행합니다.
</p>
<pre>node server.js</pre>
<p>
이제 브라우저를 열고
<a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a>
로 접속합니다. “Hello World”가 찍히는 웹페이지가 보일 겁니다.
</p>
<p>
매우 흥미롭습니다. 그렇죠? 지금은 여기서 무슨 일이 일어났는 지를
먼저 살펴봅시다. 프로젝트를 어떻게 구조화 하는 지에 대한 질문은
잠시 미루고, 뒤에서 다시 다루겠습니다.
</p>
<a name="analyzing-our-http-server"></a>
<h3>우리의 HTTP 서버를 분석하기</h3>
<p>
자, 그럼 실제로 뭐가 어떻게 된 건지 살펴봅시다.
</p>
<p>
첫 줄은 Node.js에 기본으로 포함된 <em>http</em> 모듈을 읽어 들인
다음, http 라는 이름의 변수를 통해 접근할 수 있게 만들었습니다.
</p>
<p>
그 다음 http 모듈에서 제공하는 함수 중 하나인
<em>createServer</em>를 호출합니다. 해당 함수는 객체를 리턴하고,
그 리턴된 객체는 <em>listen</em> 이라는 이름의 함수를 가지고
있습니다. 이 listen 함수는 HTTP서버에서 요청대기할 포트 번호를
나타내는 숫자를 받습니다.
</p>
<p>
<em>http.createServer</em>의 괄호 뒤에 나오는 함수 선언에 대해서는
잠시 무시합시다.
</p>
<p>
다음과 같은 식으로 8888 port를 listen 하는 서버를 만들 수도 있습니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">var</span><span
class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">();</span><span
class="pln"><br>server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span></pre>
<p>
8888포트를 Listen 하는 HTTP 서버를 시작한 다음 아무일도 안 하는
코드입니다. 어떤 요청이 들어오더라도 말입니다.
테스트 해보면 웹 브라우저는 대기상태에 빠집니다.
</p>
<p>
<em>createServer()</em> 호출의 첫 번째 파라미터가 있어야 하는
부분에 함수의 정의가 나온 부분이 매우 흥미롭습니다.(당신의
background가 php처럼 더 보수적인 언어라면 이상하게 보일 겁니다).
</p>
<p>
이 함수정의는 <em>createServer()</em>에 넘기는 첫 번째 파라미터입니다.
JavaScript에서는 함수도 다른 값처럼 파라미터로 넘길 수 있습니다.
</p>
<a name="passing-functions-around"></a>
<h3>함수를 전달하기</h3>
<p>
예를 들면 다음과 같은 것이 가능합니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> say</span><span
class="pun">(</span><span class="pln">word</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span
class="pln">word</span><span class="pun">);</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br></span><span class="kwd">function</span><span class="pln"> execute</span><span
class="pun">(</span><span class="pln">someFunction</span><span class="pun">,</span><span class="pln"> value</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; someFunction</span><span
class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>execute</span><span
class="pun">(</span><span class="pln">say</span><span class="pun">,</span><span
class="pln"> </span><span class="str">"Hello"</span><span class="pun">);</span></pre>
<p>
주의해서 보세요! 우리는 여기서 <em>say</em> 라는 함수를
<em>execute</em> 함수의 첫 번째 파라미터로 넘기고 있습니다.
<em>say</em>함수의 return 값을 넘기는 것이 아니라
<em>say</em> 그 자체를 넘기는 겁니다.
</p>
<p>
따라서, <em>say</em>는 <em>execute</em> 함수 내에서
<em>someFunction</em> 이라는 변수가 되며 execute는
이 변수에 담긴 함수를 <em>someFunction()</em> 이라고 표시함으로써
호출할 수 있습니다.(변수에 괄호를 치면서)
</p>
<p>
<em>say</em>는 한 개의 파라미터를 가지므로 <em>execute</em>는
<em>someFunction</em>을 호출하면서 파라미터를 넘길 수 있습니다.
</p>
<p>
방금 한 것처럼 우리는 함수를 그 이름으로 다른 함수의 파라미터로
넘길 수 있습니다. 하지만 이렇게 먼저 정의하고 그것을 넘기는 간접적인
방법을 사용할 필요가 없습니다. 우리는 함수를 정의하면서 동시에
다른 함수의 파라미터로 넘길 수 있습니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> execute</span><span
class="pun">(</span><span class="pln">someFunction</span><span class="pun">,</span><span class="pln"> value</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; someFunction</span><span
class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>execute</span><span
class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span
class="pln">word</span><span class="pun">){</span><span class="pln"> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span
class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">},</span><span class="pln"> </span><span class="str">"Hello"</span><span
class="pun">);</span></pre>
<p>
우린 <em>execute</em>의 첫 번째 파라미터 위치에
<em>execute</em>에게 넘기려고 하는 함수를 정의했습니다.
</p>
<p>
이때 우리는 함수 이름을 줄 필요가 없습니다.
이것이 바로 <em>anonymous function</em> 이라고 불리는 이유입니다.
</p>
<p>
이것으로 제가 “advanced” JavaScript 라고 부르고 싶은 것에 대해
처음으로 살짝 맛을 봤습니다. 이제 이것을 기억합시다.
JavaScript 에서는 우리는 함수를 다른 함수의 파라미터로 넘길 수 있다.
이것을 함수를 변수에 할당한 후에 넘길 수도 있고,
정의하는 동시에 넘길 수도 있다.
</p>
<a name="how-function-passing-makes-our-http-server-work"></a>
<h3>어떻게 함수 전달이 HTTP 서버를 동작하게 하는가</h3>
<p>
우리의 미니멀리즘적인 HTTP 서버로 되돌아가 봅시다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br>http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="kwd">function</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; response</span><span
class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span
class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span
class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span
class="pln"><br>&nbsp; response</span><span class="pun">.</span><span class="pln">write</span><span
class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span><span
class="pln"><br>&nbsp; response</span><span class="pun">.</span><span class="pln">end</span><span
class="pun">();</span><span class="pln"><br></span><span class="pun">}).</span><span
class="pln">listen</span><span class="pun">(</span><span class="lit">8888</span><span
class="pun">);</span></pre>
<p>
이제 위 코드가 어떤 일을 하는지 명확해집니다. anonymous 함수가 <em>createServer</em> 함수로 전달되었습니다.
</p>
<p>
위 코드를 리팩터링 하면 아래와 같이 만들 수 있습니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> onRequest</span><span class="pun">(</span><span class="pln">request</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; response</span><span
class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span
class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span
class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span
class="pln"><br>&nbsp; response</span><span class="pun">.</span><span class="pln">write</span><span
class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span><span
class="pln"><br>&nbsp; response</span><span class="pun">.</span><span class="pln">end</span><span
class="pun">();</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span></pre>
<p>
이제 다음과 같은 질문을 할 차례입니다.
</p>
<a name="event-driven-callbacks"></a>
<h3>Event-driven callbacks</h3>
<p>
답은 a) 그리 쉽지 않고(최소한 저에게는),
b) Node.js가 동작하는 방식이 어떤가에 있습니다.
Node.js가 굉장히 빠른 이유가 바로 이벤트 드리븐 때문입니다.
</p>
<p>
배경 설명을 위해 Felix Geisendörfer의 뛰어난 포스트
<a href="http://debuggable.com/posts/understanding-node-js:4bd98440-45e4-4a9a-8ef7-0f7ecbdd56cb">
Understanding node.js</a>
를 읽어 보시는 것도 좋을 것 같네요.
</p>
<p>
이것은 모두 결국 Node.js가 이벤트 드리븐으로 동작한다는 사실로
귀결됩니다. 오, 저 역시 이것이 정확히 무슨 뜻인지 모릅니다.
하지만 Node.js로 웹 기반 애플리케이션을 만드는 우리들에게 이게 왜
의미가 있는지 한번 살펴봅시다.
</p>
<p>
<em>http.createServer</em> 메소드를 호출할 때, 우리는 서버가
특정 포트를 listen 할 뿐 아니라 HTTP 요청이 올 때
뭔가를 하기를 기대합니다.
</p>
<p>
문제는 이것(HTTP 요청)이 비동기적으로 일어난다는 점입니다.
HTTP 요청은 언제든지 일어날 수 있지만
우리에게는 하나의 프로세스밖에 없습니다.
</p>
<p>
PHP 애플리케이션을 만들때 우린 이런것은 신경 쓰지 않죠.
HTTP 요청이 올 때마다 웹서버(보통 Apache)가 이 요청을 위해 새
프로세스를 포크하고 해당하는 PHP 스크립트를 처음부터 끝까지
실행합니다.
</p>
<p>
제어흐름의 관점에서 보면, 새로운 요청이 8888 포트로 들어왔을 때
Node.js의 제어 안에 있습니다.(역주: PHP의 경우 새로운 요청이
들어왔을때 제어흐름은 Apache에 있고, Apache가 알아서 처리하죠)
이것을 도대체 어떻게 해결하죠?
</p>
<p>
이것이 바로 Node.js/JavaScript의 이벤트 드리븐 디자인이 빛을
발하는 부분입니다. 다만 몇 가지 새로운 개념을 익히긴 해야합니다.
이 개념들을 우리 서버 코드에 어떻게 적용할지 한번 봅시다.
</p>
<p>
서버를 생성할 때 서버 생성 메소드의 파라미터로 함수를 넘깁니다.
요청이 올 때마다 파라미터로 넘긴 함수가 호출됩니다.
</p>
<p>
요청이 언제 발생할 지는 모르지만 이제 들어오는 요청을 처리할 곳
생겼습니다. 파라미터로 넘긴 함수입니다.
함수를 먼저 정의한 후 넘겼든 anonymous function으로 넘겼든 말이죠.
</p>
<p>
이 개념을 <em>callback</em> 이라고 합니다.
우리는 메소드에 함수를 넘기고, 메소드는 관련된 이벤트가 발생하면
이 함수를 <em>거꾸로 호출(call back)</em> 합니다.
</p>
<p>
최소한 저에게는 이것을 이해하는 데 좀 시간이 걸렸습니다.
확실히 이해가 되지 않으면 Felix의 블로그 포스트를 다시 읽어보세요.
</p>
<p>
이 새로운 개념을 가지고 동작을 시켜 볼까요?
서버를 생성한 후 아무런 HTTP 요청이 발생하지 않아서 우리가 넘긴
callback 함수도 호출되지 않고 있다면, 우리 코드가 동작한다는 것을
어떻게 알 수 있을까요? 아래와 같이 해 봅시다:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> onRequest</span><span class="pun">(</span><span class="pln">request</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request received."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br>http</span><span class="pun">.</span><span class="pln">createServer</span><span
class="pun">(</span><span class="pln">onRequest</span><span class="pun">).</span><span class="pln">listen</span><span
class="pun">(</span><span class="lit">8888</span><span class="pun">);</span><span class="pln"><br><br>console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span></pre>
<p>
<em>onRequest</em> 함수 (callback)가 호출될 때 텍스트를 찍기 위해서
<em>console.log</em>를 사용했습니다. 또 HTTP 서버를
<em>시작하자마자</em> 다른 텍스트를 하나 찍었습니다.
</p>
<p>
이것을 실행하면(<em>node server.js</em>), “Server has started”라고
커맨드라인에 찍힙니다. 서버에 요청을 할 때마다(브라우저로
<a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a>
을 열어서) “Request received.” 라고 커맨드 라인에 찍힙니다.
</p>
<p>
callback을 가진 이벤트 드리븐 비동기 서버사이드 JavaScript가
동작하다! :-)
</p>
<p>
(한번의 브라우저 요청에 “Request received.” 메시지가 두번 STDOUT으로
찍히는 것은 대부분의 브라우저가 http://localhost:8888/을 요청할 때
http://localhost:8888/favicon.ico를 로드하려고 하기 때문이에요).
</p>
<a name="how-our-server-handles-requests"></a>
<h3>우리 서버는 요청을 어떻게 처리하는가?</h3>
<p>
네. 이제 나머지 코드를 얼른 분석해 봅시다. 바로 callback 함수인
<em>onRequest()</em> 입니다.
</p>
<p>
callback으로 <em>onRequest()</em> 함수가 호출될때 두 개의 파라미터가
넘어옵니다. <em>reqeust</em>와 <em>response</em> 죠.
</p>
<p>
이것들은 객체인데요, HTTP 요청을 자세히 핸들링하거나
응답을 보내는데에 이 객체들의 메소드를 사용할 수 있습니다.
</p>
<p>
우리 코드는 요청이 올 때마다 <em>response.writeHead()</em> 함수를
사용해서 HTTP status 200 과 content-type을 응답 헤더로 보내고,
<em>response.write()</em> 함수로 “Hello World” 텍스트를
HTTP 응답 바디로 보냅니다.
</p>
<p>
마지막으로 <em>response.end()</em>로 응답을 마무리하죠.
</p>
<p>
여기서는 요청의 자세한 내용에 대해서 신경 쓰지 않았습니다.
그래서 <em>request</em> 객체는 사용하지 않았죠.
</p>
<a name="finding-a-place-for-our-server-module"></a>
<h3>server 모듈의 위치 잡기</h3>
<p>
네. 제가 나중에 애플리케이션을 구조화 하겠다고 약속했었죠?
지금 우리는 기본적인 HTTP 서버 코드가 있는 <em>server.js</em> 파일과
모듈들을 적재하고 애플리케이션을 시작시키는 main file인
<em>index.js</em> 파일을 가지고 있습니다.
</p>
<p>
server.js를 Node.js 모듈로 만들어서 아직 작성하지 않은
<em>index.js</em> main 파일에서 사용하도록 해 봅시다.
</p>
<p>
아시는 것처럼, 아래와 같이 우리 코드에서 이미 모듈을 사용하고 있지요.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="pun">...</span><span
class="pln"><br><br>http</span><span class="pun">.</span><span class="pln">createServer</span><span
class="pun">(...);</span></pre>
<p>
Node.js의 내부 어딘가에 “http” 라는 모듈이 있으며,
이를 require 하고 지역 변수에 할당해서 사용할 수 있습니다.
</p>
<p>
이렇게 하면 지역변수가 <em>http</em> 모듈이 제공하는 모든
public 메소드를 사용할 수 있는 객체가 됩니다.
</p>
<p>
모듈의 이름으로 지역변수의 이름을 선택하는 것이 일반적이지만,
우리가 원하는 이름으로 지역변수 이름을 선택할 수도 있습니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> foo </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="pun">...</span><span
class="pln"><br><br>foo</span><span class="pun">.</span><span class="pln">createServer</span><span
class="pun">(...);</span></pre>
<p>
좋습니다. Node.js의 내부모듈을 사용하는 것은 확실히 알겠군요.
그럼 모듈을 어떻게 직접 만들고 사용할 수 있을까요?
</p>
<p>
<em>server.js</em> 스크립트를 모듈로 만들면서 알아봅시다.
</p>
<p>
사실 별로 수정할 것도 없습니다. 코드를 모듈로 만든다는 것은
모듈을 필요로 하는 스크립트에 제공할 기능의 일부를
<em>export</em> 하는 것입니다.
</p>
<p>
export 할 필요가 있는 우리 HTTP 서버의 기능은 단순합니다.
우리 서버모듈을 require 하는 스크립트는
단순히 서버를 시작하는 것이 필요한 거죠.
</p>
<p>
이렇게 하기 위해, 서버 코드를 <em>start</em> 라는 함수 안에 넣고
이 함수를 export 하겠습니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br>&nbsp; </span><span class="kwd">function</span><span class="pln"> onRequest</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request received."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br><br>&nbsp; http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
이런 방식으로 main 파일인 <em>index.js</em>를 만들고,
HTTP 서버를 시작시킬 수 있습니다.
server에 대한 코드는 <em>server.js</em> 파일에 있지만요.
</p>
<p>
<em>index.js</em> 파일을 만들고 내용을 아래와 같이 만듭시다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> server </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"./server"</span><span
class="pun">);</span><span class="pln"><br><br>server</span><span class="pun">.</span><span class="pln">start</span><span
class="pun">();</span></pre>
<p>
보시다시피, 우리 서버 모듈을 다른 내장 모듈과 똑같이 사용할 수
있습니다: 파일을 require 하고, 지역변수에 할당하면 export 된
함수를 쓸 수 있게 되지요.
</p>
<p>
됐습니다. 이제 main 스크립트를 통해 애플리케이션을 구동해서 기존과
완전히 동일하게 동작하는지 봅시다.
</p>
<pre>node index.js</pre>
<p>
대단하죠. 이제 애플리케이션의 다른 부분을 다른 파일에 넣고 그것들을
모듈로 만들어서 서로 엮을 수 있게 되었습니다.
</p>
<p>
아직 우리는 애플리케이션의 아주 초기 부분만을 만들었습니다.
단지 HTTP 요청을 받는거죠. 이제 요청을 가지고 뭔가를 할 필요가 있죠.
브라우저가 요청한 URL에 따라 반응을 다르게 하는 겁니다.
</p>
<p>
아주 단순한 애플리케이션이라면 이 작업을 <em>onRequest()</em>
callback 함수에 구현할 수 있죠. 하지만 우리 애플리케이션이 좀 더
재밌기 위해서 좀 더 추상화를 해보자구요.
</p>
<p>
다른 HTTP 요청이 코드의 다른 부분을 가리키도록 하는 것을
“라우팅(routing)” 이라고 합니다.
자 그럼 <em>router</em>를 만들어 봅시다.
</p>
<a name="whats-needed-to-route-requests"></a>
<h3>요청을 "route" 하려면?</h3>
<p>
요청 URL과 GET/POST 파라미터를 router로 전달하면 router는 어떤
코드를 실행할지 결정할 수 있어야 합니다.
(이 실행할 코드는 우리 애플리케이션의 세 번째 부분이에요:
요청을 받았을 때 실제 일을 하는 request handler 들의 집합입니다.)
</p>
<p>
따라서, HTTP 요청에서 URL과 GET/POST 파라미터를 뽑아내야 합니다.
이것을 router 부분에서 구현해야 하는지 server 부분(혹은 server 모듈)
부분에서 구현해야 하는지는 논란의 여지가 있습니다.
일단 지금은 HTTP server의 일부로 만들어 봅시다.
</p>
<p>
우리에게 필요한 모든 정보는 <em>request</em> 객체를 - callback 함수인
<em>onRequest()</em> 함수의 첫 번째 파라미터로 전달받은 - 통해
접근할 수 있습니다. 하지만 이 정보를 얻어내기 위해 <em>url</em> 과
<em>querystring</em> 이라는 모듈이 추가로 필요합니다.
</p>
<p>
<em>url</em> 모듈은 URL의 각각의 부분
(예를들면 URL path와 query string)
을 뽑아낼 수 있는 메소드를 제공합니다.
<em>querystring</em>은 query string을 request 파라미터로 파싱
하는데 사용합니다.
</p>
<pre> url.parse(string).query
|
url.parse(string).pathname |
| |
| |
------ -------------------
http://localhost:8888/start?foo=bar&amp;hello=world
--- -----
| |
| |
querystring(string)["foo"] |
|
querystring(string)["hello"]
</pre>
<p>
나중에 보겠지만 <em>querystring</em> 모듈을 POST 요청의 body를
파싱하는데에도 사용할 수 있습니다.
</p>
<p>
브라우저가 요청한 URL path가 무엇인지 알 수 있도록 하는 로직을
<em>onRequest()</em> 함수에 추가해 봅시다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br></span><span
class="kwd">var</span><span class="pln"> url </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span
class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br>&nbsp; </span><span class="kwd">function</span><span class="pln"> onRequest</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">var</span><span class="pln"> pathname </span><span class="pun">=</span><span class="pln"> url</span><span
class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span
class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span
class="pun">).</span><span class="pln">pathname</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname </span><span
class="pun">+</span><span class="pln"> </span><span class="str">" received."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br><br>&nbsp; http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
좋아요. 이제 URL path를 기준으로 요청을 구분할 수 있게 되었습니다.
이걸 이용하면 URL path를 기반으로 요청을 request handler로 매핑하는
router(아직 안 만들었죠)를 만들 수 있겠네요.
</p>
<p>
우리 애플리케이션 관점에서 보면, <em>/start</em> 요청과
<em>/upload</em> 요청을 각각 다른 코드에서 처리하도록
할 수 있다는 것입니다.
곧 모든 것이 어떻게 맞아떨어지는지 보게 될겁니다.
</p>
<p>
좋아요. 이제 진짜로 router를 만듭시다. <em>router.js</em> 라는 새
파일을 만들고 내용을 아래와 같이 입력합니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> route</span><span
class="pun">(</span><span class="pln">pathname</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"About to route a request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">route </span><span class="pun">=</span><span
class="pln"> route</span><span class="pun">;</span></pre>
<p>
네. 이 코드는 기본적으로 아무것도 안 하죠.
하지만 지금은 이걸로 좋습니다. router에 좀 더 로직을 추가하기 전에
일단 이 router를 server와 어떻게 엮을지 봅시다.
</p>
<p>
HTTP server가 router를 사용한다는 것을 알게 해야 합니다.
이 의존성을 강하게 만들 수도 있지만 다른 프로그래밍 언어를 통해
배운 대로 dependency injection을 통해 server와 router를 느슨하게
결합할 겁니다.(배경지식을 위해 Martin Fowler 의
<a href="http://martinfowler.com/articles/injection.html"> Dependency Injection에 대한 좋은 포스트</a>를
읽어보는걸 추천합니다.)
</p>
<p>
먼저 router 함수를 파라미터로 넘길 수 있도록 server의
<em>start()</em> 함수를 확장합니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br></span><span
class="kwd">var</span><span class="pln"> url </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span
class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">(</span><span class="pln">route</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br>&nbsp; </span><span class="kwd">function</span><span class="pln"> onRequest</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">var</span><span class="pln"> pathname </span><span class="pun">=</span><span class="pln"> url</span><span
class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span
class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span
class="pun">).</span><span class="pln">pathname</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname </span><span
class="pun">+</span><span class="pln"> </span><span class="str">" received."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; &nbsp; route</span><span class="pun">(</span><span
class="pln">pathname</span><span class="pun">);</span><span
class="pln"><br><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br><br>&nbsp; http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
그리고 <em>index.js</em>를 확장합니다.
여기서 router 함수를 server로 주사(inject) 합니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> server </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"./server"</span><span
class="pun">);</span><span class="pln"><br></span><span class="kwd">var</span><span
class="pln"> router </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"./router"</span><span class="pun">);</span><span class="pln"><br><br>server</span><span
class="pun">.</span><span class="pln">start</span><span class="pun">(</span><span
class="pln">router</span><span class="pun">.</span><span class="pln">route</span><span
class="pun">);</span><span class="pln"><br></span></pre>
<p>
여기서도 다시 함수를 넘기고 있지요.
이런 것은 이제 우리에게 새로울 것도 없습니다.
</p>
<p>
이제 애플리케이션을 시작하고(<em>node index.js</em>),
URL을 요청하면, 애플리케이션의 출력으로 HTTP server가 router를
사용하고 pathname을 넘기는 것을 볼 수 있습니다.
</p>
<pre>bash$ node index.js
Request for /foo received.
About to route a request for /foo</pre>
<p>
(다소 짜증나는 /favicon.ico 요청 관련 출력은 생략했어요).
</p>
<a name="execution-in-the-kongdom-of-verbs"></a>
<h3>동사들(verbs)의 나라에서의 실행(execution)</h3>
<p>
잠깐 functional programming 에 대해 이야기를 해볼게요.
</p>
<p>
함수를 전달하는 것은 단지 기술적인 고려사항이 아니에요.
소프트웨어 디자인의 관점에서 보면 이것은 거의 철학적인 이야기입니다.
잠깐 생각해 봅시다. index 파일에서 <em>router</em> 객체를 server로
전달할 수도 있고, server 는 이 객체의 <em>route</em> 함수를
호출할 수도 있습니다.
</p>
<p>
이 방식으로 우리는 물건(thing)을 전달하고 server는 무슨 일인가를
하는데에 이 물건(thing)을 사용합니다.
헤이 router야~~, 이것 좀 보내(route) 줄래?
</p>
<p>
하지만 server는 그런 것(thing)이 필요 없죠. 다만 뭔가를 <em>할</em>
필요가 있는 것 뿐 입니다. 그리고 뭔가를 하려면 물건(things)이
필요한 게 아니라 <em>행동(actions)</em>이 필요하죠.
<em>명사(nouns)</em>는 필요 없고 <em>동사(verbs)</em>가 필요합니다.
</p>
<p>
이 아이디어의 핵심에 있는 근본적인 정신변화를 이해하니
functional programming을 진정으로 이해할 수 있었습니다.
</p>
<p>
Steve Yegge의 걸작
<a href="http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html">Execution in the Kingdom of Nouns</a>
을 읽었을 때 이것을 이해했습니다. 지금 읽어보세요.
이것은 제가 본 소프트웨어와 관련된 최고의 저술 중에 하나입니다.
</p>
<a name="routing-to-real-request-handlers"></a>
<h3>실제 request handler로 라우팅(routing) 하기</h3>
<p>
비즈니스로 돌아옵시다. 이제 우리가 의도한 대로 HTTP server와
router는 친구가 되어서 서로 이야기를 나눕니다.
</p>
<p>
당연히, 이걸로는 부족합니다.
“Routing” 은 다른 URL을 다르게 처리하고 싶다는 말이죠.
<em>/start</em> 요청에 대한 “비즈니스 로직”은
<em>/upload</em> 요청과는 다른 함수에서 처리하고 싶습니다.
</p>
<p>
router는 요청에 대해 실제로 뭔가를 할 만한 곳이 아닙니다.
애플리케이션이 더 복잡해지면 router를 확장하기가 쉽지가 않아집니다.
</p>
<p>
요청이 route 될 함수를 request handler 라고 부르기로 하고
곧바로 뛰어들어가 봅시다.
</p>
<p>
새로운 애플리케이션 부분이니까 새 모듈로 만들어야겠습니다.
requestHandlers 라는 모듈을 만들고, 각각의 request handler를 위한
껍데기(placeholder) 함수들을 추가한 후
이 모듈의 메소드로 export 합니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> start</span><span
class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span
class="pln"><br><br></span><span class="kwd">function</span><span class="pln"> upload</span><span
class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
class="pln"> upload</span><span class="pun">;</span></pre>
<p>
이제 router와 request handler를 엮었으므로 우리 router가
어딘가로 route 할 수 있게 되었습니다.
</p>
<p>
이 시점에 결정이 필요합니다: requestHandler 모듈을 router에
하드코딩 된 방식으로 묶을 것이냐, 아니면 의존관계 주입(dependency injection)으로 좀 더 처리할 것이냐?
모든 패턴과 마찬가지로 의존 관계 주입은 그 자체를 위해
사용하는 것이 아닙니다.
여기에서는 router와 request handler를 느슨하게 묶어서
router의 재사용을 크게 높입니다.
</p>
<p>
이렇게 하려면 request handler를 server에서 router로 전달해야 하는데,
이건 더 잘못된 느낌을 줍니다. 왜냐하면 main 파일에서 server로 보내고
server에서 router로 보내야 하기 때문이죠.
</p>
<p>
그럼 어떻게 전달하는 게 좋을까요? 지금은 두 개의 handler가 있지만
실제 애플리케이션에서는 handler의 수가 늘어나고 다양해 질 겁니다.
그리고 새 URL request handler가 추가될 때마다 router에서
request와 handler를 매핑하는 일을 하고 싶진 않습니다.
게다가 <em>if request == x then call handler y</em>와 같은 코드는
추악함 이상이 될겁니다.
</p>
<p>
다양한 항목이 있고, 각각은 (요청된 URL)문자열에 매핑된다?
오, 연관배열이 딱 맞아떨어질것 같긴 합니다만..
</p>
<p>
음. 조금 실망스럽게도 JavaScript는 연관배열을 지원하지 않는답니다.
그런데, 알고보니 연관배열이 필요한 경우에는 객체를 사용하면
된다는군요.
</p>
<p>
여기 좋은 소개가 있습니다.
<a href="http://msdn.microsoft.com/en-us/magazine/cc163419.aspx">http://msdn.microsoft.com/en-us/magazine/cc163419.aspx</a>,
관련된 부분을 인용해 보면:
</p>
<blockquote>
<p>
C++과 C#에서, 객체는 class나 struct의 인스턴스를 지칭합니다.
객체는 그것이 어떤 템플릿(이를테면, class)으로 인스턴스화 되었느냐에 따라
다른 프로퍼티와 메소드를 가집니다. (하지만) JavaScript 객체는 다릅니다.
JavaScript에서 객체는 단지 이름/값 쌍의 컬렉션에 불과합니다.
- JavaScript 객체는 키 값이 문자열인 사전(dictionary)이라고 생각하세요.
</p>
</blockquote>
<p>
만약 JavaScript 객체가 단순히 이름/값 쌍의 컬렉션이라면 어떻게
메소드를 가질 수 있죠? 네, 값은 문자열 일수도 숫자일수도
함수!일 수도 있습니다.
</p>
<p>
오케이, 다시 코드로 돌아와서 requestHandlers의 리스트를 객체로
넘기고, 느슨한 연결을 위해 이 객체를
<em>route()</em>로 주사(inject) 하려고 합니다.
</p>
<p>
이 객체를 main 파일인 <em>index.js</em>에 넣어 봅시다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> server </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"./server"</span><span
class="pun">);</span><span class="pln"><br></span><span class="kwd">var</span><span
class="pln"> router </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"./router"</span><span class="pun">);</span><span class="pln"><br></span><span class="kwd">var</span><span
class="pln"> requestHandlers </span><span class="pun">=</span><span class="pln"> require</span><span
class="pun">(</span><span class="str">"./requestHandlers"</span><span class="pun">);</span><span
class="pln"><br><br></span><span class="kwd">var</span><span class="pln"> handle </span><span
class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span
class="pln"><br>handle</span><span class="pun">[</span><span class="str">"/"</span><span
class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
class="pun">.</span><span class="pln">start</span><span class="pun">;</span><span class="pln"><br>handle</span><span
class="pun">[</span><span class="str">"/start"</span><span class="pun">]</span><span
class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
class="pun">.</span><span class="pln">start</span><span class="pun">;</span><span class="pln"><br>handle</span><span
class="pun">[</span><span class="str">"/upload"</span><span class="pun">]</span><span
class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
class="pun">.</span><span class="pln">upload</span><span class="pun">;</span><span class="pln"><br><br>server</span><span
class="pun">.</span><span class="pln">start</span><span class="pun">(</span><span
class="pln">router</span><span class="pun">.</span><span class="pln">route</span><span
class="pun">,</span><span class="pln"> handle</span><span class="pun">);</span></pre>
<p>
<em>handle</em>은 "사물(thing)" (여기에선 request handler들의 컬렉션)에
가깝지만 동사처럼 이름을 지었습니다.
곧 보게 되겠지만 결과적으로 router의 멋진 표현(방식)이 될 겁니다.
</p>
<p>
보시는 바와 같이, 다른 URL을 동일한 request handler에 매핑하는 것은 매우
쉽습니다. 키/값 쌍에 <em>"/"</em>와 <em>requestHandlers.start</em>
를 추가하면 <em>/start</em> 요청뿐 아니라 <em>/</em> 요청도
<em>start</em> handler로 연결됩니다.
</p>
<p>
객체를 정의한 후에 server에게 별도의 파라미터로 전달합니다.
<em>server.js</em>를 다음과 같이 바꿉시다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br></span><span
class="kwd">var</span><span class="pln"> url </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span
class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">(</span><span class="pln">route</span><span
class="pun">,</span><span class="pln"> handle</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; </span><span class="kwd">function</span><span
class="pln"> onRequest</span><span class="pun">(</span><span class="pln">request</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">var</span><span class="pln"> pathname </span><span class="pun">=</span><span class="pln"> url</span><span
class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span
class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span
class="pun">).</span><span class="pln">pathname</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname </span><span
class="pun">+</span><span class="pln"> </span><span class="str">" received."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; &nbsp; route</span><span class="pun">(</span><span
class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span class="pun">);</span><span
class="pln"><br><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br><br>&nbsp; http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
<em>start()</em> 함수에 <em>handle</em> 파라미터를 추가했고 handle
객체를 <em>route()</em> callback 에 첫 번째 파라미터로 넘겼습니다.
</p>
<p>
이제 <em>route()</em> 함수를 수정해야죠.
<em>router.js</em> 파일입니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> route</span><span
class="pun">(</span><span class="pln">handle</span><span class="pun">,</span><span
class="pln"> pathname</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span class="str">"About to route a request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">if</span><span
class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span
class="pln"> handle</span><span class="pun">[</span><span class="pln">pathname</span><span
class="pun">]</span><span class="pln"> </span><span class="pun">===</span><span
class="pln"> </span><span class="str">'function'</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; handle</span><span
class="pun">[</span><span class="pln">pathname</span><span class="pun">]();</span><span class="pln"><br>&nbsp; </span><span
class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"No request handler found for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">route </span><span class="pun">=</span><span
class="pln"> route</span><span class="pun">;</span></pre>
<p>
여기에 우리가 해야 할 일은, 주어진 pathname에 해당하는 request handler가 있는지
체크하고, 존재하면 그 함수를 호출하는 겁니다.
연관배열에서 요소에 접근하는 것처럼 객체에서 request handler 함수에
접근할 수 있으므로, “<em>pathname</em>을 <em>handle</em> 해줘~”로 읽히는 멋진 <em>handle[pathname]();</em> 함수를 갖게 되었습니다.
</p>
<p>
좋군요. 이것이 server와 router 그리고 request handler를 함께 묶기 위해 필요한
전부입니다. 애플리케이션을 실행하고 브라우저로
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>
를 요청하면 정확한 request handler가 호출된 것을 확인할 수 있습니다.
</p>
<pre>Server has started.
Request for /start received.
About to route a request for /start
Request handler 'start' was called.</pre>
<p>
브라우저로
<a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a>
을 열면 역시 <em>start</em> request handler가 동작합니다.
</p>
<pre>Request for / received.
About to route a request for /
Request handler 'start' was called.</pre>
<a name="making-the-request-handlers-respond"></a>
<h3>request handler가 응답하게 만들기</h3>
<p>
아름답지요? 이제 오직 request handler만이 브라우저에게 뭔가를
보낼 수 있게 된다면 한결 좋아지겠죠. 그렇죠?
</p>
<p>
기억해 봅시다. 브라우저에 찍히는 “Hello World” 는 여전히
<em>server.js</em> 파일의 <em>onRequest</em> 함수에서 오는거죠.
</p>
<p>
결국 “request 처리”란 “request에 응답하는 것”입니다.
이제 <em>onRequest</em>가 하는 것처럼 request handler가
브라우저에 이야기할 수 있게 해줘야 합니다.
</p>
<a name="how-to-not-do-it"></a>
<h4>해서는 안되는 것</h4>
<p>
PHP나 Ruby 배경을 가진 개발자처럼 직관적인 접근 방식을 따르고 싶을
수 있는데, 매력적이고 상당히 그럴듯해 보이지만,
예상치 못한 때에 갑자기 망쳐버릴 수 있습니다.
</p>
<p>
“직관적인 접근 방식”이란 이런 겁니다.
requesthandler는 사용자에게 보여주려는 내용을
<em>return()</em>하고, 그러면
<em>onRequest</em> 함수는 이 응답 데이터를 user에게 보내는 방식입니다.
</p>
<p>
이렇게 한번 해보고, 이게 왜 그리 좋지 않은 아이디어인지 봅시다.
</p>
<p>
우리는 request handler로부터 시작해서, 브라우저에 표시하고자 하는 내용을 request handler가 return하도록 만듭니다.
<em>requestHandlers.js</em>를 다음과 같이 고칠 필요가 있습니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> start</span><span
class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">return</span><span
class="pln"> </span><span class="str">"Hello Start"</span><span class="pun">;</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> upload</span><span class="pun">()</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span
class="str">"Request handler 'upload' was called."</span><span class="pun">);</span><span
class="pln"><br>&nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span
class="str">"Hello Upload"</span><span class="pun">;</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br>exports</span><span class="pun">.</span><span class="pln">start </span><span
class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
class="pln"> upload</span><span class="pun">;</span></pre>
<p>
좋군요. 마찬가지로 router는 request handler가 return 한 것을
server에게 return 해야 합니다. 따라서, <em>router.js</em>를 다음과 같이 고칩니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> route</span><span
class="pun">(</span><span class="pln">handle</span><span class="pun">,</span><span
class="pln"> pathname</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span class="str">"About to route a request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">if</span><span
class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span
class="pln"> handle</span><span class="pun">[</span><span class="pln">pathname</span><span
class="pun">]</span><span class="pln"> </span><span class="pun">===</span><span
class="pln"> </span><span class="str">'function'</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">return</span><span class="pln"> handle</span><span class="pun">[</span><span class="pln">pathname</span><span
class="pun">]();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span class="str">"No request handler found for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">return</span><span
class="pln"> </span><span class="str">"404 Not found"</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span
class="pun">}</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">route </span><span class="pun">=</span><span
class="pln"> route</span><span class="pun">;</span></pre>
<p>
보시다시피, route 못하는 요청이 있으면
약간의 텍스트를 return 하게 했습니다.
</p>
<p>
마지막으로, <em>server.js</em>를 아래와 같이 바꿔서
request handler가 router를 통해 return 한 내용을
브라우저에 응답하도록 수정합니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br></span><span
class="kwd">var</span><span class="pln"> url </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span
class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">(</span><span class="pln">route</span><span
class="pun">,</span><span class="pln"> handle</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; </span><span class="kwd">function</span><span
class="pln"> onRequest</span><span class="pun">(</span><span class="pln">request</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">var</span><span class="pln"> pathname </span><span class="pun">=</span><span class="pln"> url</span><span
class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span
class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span
class="pun">).</span><span class="pln">pathname</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname </span><span
class="pun">+</span><span class="pln"> </span><span class="str">" received."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; &nbsp; response</span><span
class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span
class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span
class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span
class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> content </span><span
class="pun">=</span><span class="pln"> route</span><span class="pun">(</span><span
class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span
class="pun">)</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="pln">content</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br><br>&nbsp; http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
수정한 애플리케이션을 다시 구동하면 모든 것이 매력적으로 동작합니다.
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>를 요청하면 “Hello Start”가 출력되고,
<a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>는 “Hello Upload”가,
<a href="http://localhost:8888/foo" rel="nofollow">http://localhost:8888/foo</a>는 “404 Not found”가 나옵니다.
</p>
<p>
좋아요. 그런데 이게 왜 문제라는 거죠? 짧게 대답하자면 이렇습니다. 나중에 request handler 중
하나가 non-blocking 동작을 해야할 때 문제에 생기기 때문입니다.
</p>
<p>
좀 더 긴 대답을 들어 봅시다.
</p>
<a name="blocking-and-non-blocking"></a>
<h4>Blocking 과 non-blocking</h4>
<p>
이야기 했듯이, 문제는 request handler에 non-blocking 동작을
포함시킬 때 발생합니다. 먼저 blocking 동작에 대해 이야기를 하고,
그 후에 non-blocking 동작에 대해 이야기 할게요.
</p>
<p>
그리고 “blocking” 과 “non-blocking”의 뜻을 설명하는 대신에
request handler에 blocking 동작을 추가할 때 무슨 일이 생기는지
직접 보여 드리겠습니다.
</p>
<p>
이를 위해, <em>start</em> request handler에서 “Hello Start” 문자열을
return 하기 전에 10초 동안 기다리도록 수정할 겁니다.
JavaScript에 <em>sleep()</em> 같은 건 없기 때문에
다른 짓을 해야 합니다.
</p>
<p>
<em>requestHandlers.js</em>를 아래와 같이 고칩시다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> start</span><span
class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">function</span><span
class="pln"> sleep</span><span class="pun">(</span><span class="pln">milliSeconds</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">var</span><span class="pln"> startTime </span><span class="pun">=</span><span
class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span
class="typ">Date</span><span class="pun">().</span><span class="pln">getTime</span><span
class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">while</span><span
class="pln"> </span><span class="pun">(</span><span class="kwd">new</span><span
class="pln"> </span><span class="typ">Date</span><span class="pun">().</span><span
class="pln">getTime</span><span class="pun">()</span><span class="pln"> </span><span
class="pun">&lt;</span><span class="pln"> startTime </span><span class="pun">+</span><span class="pln"> milliSeconds</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br><br>&nbsp; sleep</span><span class="pun">(</span><span class="lit">10000</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">return</span><span
class="pln"> </span><span class="str">"Hello Start"</span><span class="pun">;</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> upload</span><span class="pun">()</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span
class="str">"Request handler 'upload' was called."</span><span class="pun">);</span><span
class="pln"><br>&nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span
class="str">"Hello Upload"</span><span class="pun">;</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br>exports</span><span class="pun">.</span><span class="pln">start </span><span
class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
class="pln"> upload</span><span class="pun">;</span></pre>
<p>
이게 무엇을 하는 것인지 살펴보면: <em>start()</em> 함수가 호출되면
Node.js는 10초를 기다리고 “Hello Start”를 return 합니다.
<em>upload()</em>를 호출하면 기존처럼 곧바로 return 합니다.
</p>
<p>
(10초를 sleep 하는 것 말고 다른 것을 상상할 수도 있겠죠. 오랜 시간이
걸리는 계산과 같은 blocking operation 이 start() 안에 존재할 수 있습니다.)
</p>
<p>
이 변화가 무엇을 의미하는지 봅시다.
</p>
<p>
애플리케이션을 재시작 한 후에 무슨 일이 일어나는지 보기 위해 조금
복잡한 절차를 따라주세요. 첫째, 브라우저 두 개나 탭 두 개를 열어주세요.
첫 번째 브라우저의 주소창에
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>를
입력만 하고 아직은 이 URL로 이동하진 마세요.
</p>
<p>
두 번째 브라우저의 주소창에
<a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>를
입력하고 역시 엔터를 치지는 말아주세요.
</p>
<p>
이제 다음과 같이 합니다.: 첫 번째 윈도우(“/start”)에 엔터를 치고
재빨리 두 번째 윈도우(“/upload”)로 가서 엔터를 칩니다.
</p>
<p>
우리가 예상한 대로 /start URL은 로드하는데 10초가 걸립니다.
하지만 /upload 의 request handler에는 <em>sleep()</em>이 없음에도
불구하고 /upload URL <em>또한</em> 로드하는 데 10초가 걸립니다.
</p>
<p>
왜요? <em>start()</em>가 blocking 동작을 포함하기 때문이죠.
마치 “다른 모든 것을 일하지 못하게 막는 것”과 같죠.
</p>
<p>
<em>“node에서는 모든 게 병렬로 수행된다. 당신 code만 빼고.”</em>라는
말처럼 이건 문제입니다.
</p>
<p>
Node.js는 다수의 동시작업을 처리할 수 있지만 thread를 나누는
방식으로 하지 않습니다. 사실 Node.js는 단일 thread입니다.
대신, Node.js는 동시작업을 event loop을 실행해서 처리하며 개발자들은
이것을 사용할 수 있습니다.
우리는 blocking 동작을 피하고 non-blocking 동작을 사용해야만 합니다.
</p>
<p>
그러기 위해서는 callback으로 함수를 다른 함수에게 넘겨야 합니다.
언젠가 어떤 이벤트가 발생하면 (예를들면 10초 sleep을 한다거나,
database에 질의를 하거나, 값비싼 계산을 하거나)
이 함수를 호출하게 되겠지요.
</p>
<p>
이런식으로 이야기 하는거죠. <em>“헤이, probablyExpensiveFunction(),
니 일을 해줘. 하지만 나 Single Node.js 쓰레드는 네가 끝낼 때까지
여기서 기다리지 않을거야. 네 아래에 있는 코드 라인을 계속 실행할거야.
그러니 여기 이 callbackFunction()을 가져가서 네가 너의 비싼 일을
모두 끝냈을 때 호출해 주겠니? 고마워!”</em>
</p>
<p>
(이것에 대해 더 자세히 읽고 싶다면 Mixu의
<a href="http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/">Understanding the node.js event loop</a>.
라는 포스트를 살펴보세요)
</p>
<p>
이제 우리가 만들었던 “request handler가 response를 handling” 하는
식의 방법이 왜 non-blocking 동작을 제대로 하지 못하도록 하는지
살펴봅시다.
</p>
<p>
다시 한번, 먼저 애플리케이션을 수정해서 문제가 발생하는 것을
경험해 봅시다.
</p>
<p>
이번에도 <em>start</em> request handler를 사용합니다.
아래와 같이 수정해 주세요(<em>requestHandlers.js</em> 파일이에요).
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> exec </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"child_process"</span><span
class="pun">).</span><span class="pln">exec</span><span class="pun">;</span><span
class="pln"><br><br></span><span class="kwd">function</span><span class="pln"> start</span><span
class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">var</span><span class="pln"> content </span><span
class="pun">=</span><span class="pln"> </span><span class="str">"empty"</span><span class="pun">;</span><span
class="pln"><br><br>&nbsp; exec</span><span class="pun">(</span><span class="str">"ls -lah"</span><span
class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span
class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span
class="pun">,</span><span class="pln"> stdout</span><span class="pun">,</span><span
class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br>&nbsp; &nbsp; content </span><span class="pun">=</span><span
class="pln"> stdout</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span
class="pun">});</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">return</span><span
class="pln"> content</span><span class="pun">;</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br></span><span class="kwd">function</span><span class="pln"> upload</span><span
class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">return</span><span
class="pln"> </span><span class="str">"Hello Upload"</span><span class="pun">;</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
class="pln"> upload</span><span class="pun">;</span></pre>
<p>
보시는 것처럼, 우리는 새로운 Node.js 모듈인 <em>child_process</em>를
사용했습니다. 매우 단순하지만 쓸모가 많은 non-blocking 동작인
<em>exec()</em>을 사용하기 위해서 입니다.
</p>
<p>
<em>exec()</em>은 shell 커맨드를 Node.js 안에서 실행시킵니다.
이 예에서는 현재 디렉토리에 있는 모든 파일 리스트를 가져오는데
사용해서 브라우저가 <em>/start</em> URL을 요청할 때 이 리스트를
출력할 수 있습니다.
</p>
<p>
코드가 매우 직관적이죠. <em>content</em> 라는 새 변수를
생성하고(초기값은 “empty”로), “ls -alh”를 실행하고,
이 결과로 변수를 채우고 변수를 return 합니다.
</p>
<p>
언제나처럼 애플리케이션을 구동시키고
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>를 방문합니다.
</p>
<p>
멋진 웹페이지가 로드되고 “empty” 문자열이 출력됩니다.
뭐가 잘못된 거죠?
</p>
<p>
짐작하셨겠지만, <em>exec()</em>이 non-blocking 방식으로 마법을
부렸습니다. blocking <em>sleep</em> operation 처럼 애플리케이션을
정지시키지 않고 상당히 비싼 shell operation
(매우 큰 파일을 복사한다거나 하는)을 실행할 수 있게 해주기 때문에
exec()은 매우 좋은 겁니다.
</p>
<p>
(이것을 증명하고 싶다면 “ls -lah”를 “find /” 처럼
더 비싼 operation 으로 바꿔 보세요)
</p>
<p>
하지만 브라우저에 그 결과를 출력하지 못하기 때문에 이 우아한
non-blocking operation을 좋아할 수만은 없습니다. 그렇죠?
</p>
<p>
그러면, 고쳐 봅시다. 그러면서 왜 지금의 아키텍쳐는 제대로 동작하지
않는지 이해해 보자구요.
</p>
<p>
문제는 <em>exec()</em>이 non-blocking 으로 동작하기 위해서
callback 함수를 사용해야 한다는 것입니다.
</p>
<p>
우리 예에서는 <em>exec()</em> 함수의 두 번째 파라미터로 전달한
anonymous 함수입니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> </span><span
class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span
class="pln"> stdout</span><span class="pun">,</span><span class="pln"> stderr</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; content </span><span
class="pun">=</span><span class="pln"> stdout</span><span class="pun">;</span><span
class="pln"><br></span><span class="pun">}</span></pre>
<p>
그리고 여기에 문제의 뿌리가 놓여있습니다: 우리의 코드는
동기적으로 동작합니다. 즉 <em>exec()</em>을 호출하자 마자 Node.js는
이어서 <em>return content;</em>를 실행합니다.
바로 이 시점에 <em>content</em>는 여전히 “empty” 입니다.
왜냐하면 <em>exec()</em>은 비동기적으로 동작하기 때문에
<em>exec()</em>에 전달된 callback 함수는 아직 호출되지 않았던 것이죠.
</p>
<p>
“ls -lah” 는 매우 싸고 빠른 operation 입니다.
(디렉토리에 수백만 개의 파일이 존재하지 않는다면)
따라서 callback이 상대적으로 빠르게 호출됩니다.
그럼에도 불구하고 이 일은 비동기적으로 일어납니다.
</p>
<p>
이것을 더욱 확실하게 보여주는 더 비싼 커맨드를 생각해 봅시다:
“find /” 는 제 장비에서 대략 1분이 걸리지만 request handler에서
“ls -lah”를 “find /”로 바꾸어도 /start URL을 요청할 때 여전히
즉시 HTTP 응답을 받게 됩니다.
이제 Node.js가 애플리케이션을 계속 실행하는 중에 <em>exec()</em>은
백그라운드에서 수행한다는 것이 확실합니다.
그리고 <em>exec()</em>에 넘긴 callback 함수는
“find /” 커맨드의 동작이 끝났을 때 호출될 것입니다.
</p>
<p>
그럼 우리는 어떻게 현재 디렉토리의 파일 리스트를 사용자에게
보여주려는 우리 목표를 이룰 수 있을까요?
</p>
<p>
어떻게 하면 <em>안</em> 되는지를 배웠으니까, 이제 request handler가
브라우저에게 올바른 방식으로 응답하는 방법을 논의해 봅시다.
</p>
<a name="responding-request-handlers-with-non-blocking-operations"></a>
<h4>request handler가 non-blocking 방식으로 동작하면서 응답하기</h4>
<p>
제가 방금 “올바른 방식” 이라는 표현을 사용했는데. 위험한 말입니다.
대부분은 “올바른 방식”이 하나만 존재하는 것이 아닙니다.
</p>
<p>
이번 경우에는 함수를 전달하는 것이 한 방법입니다. 한번 살펴봅시다.
</p>
<p>
현재 우리 애플리케이션은 사용자에게 보여주고 싶은 content를 request handler에서 HTTP server로 전달할 수 있습니다. 다음과 같은 여러 애플리케이션 레이어들을 거쳐 넘기는 식으로 식으로 말입니다.
(request handler -&gt; router -&gt; server).
</p>
<p>
새로운 접근 방법은 다음과 같습니다: content를 server로 보내는 대신
server를 content로 보낼겁니다. 좀 더 자세히 이야기 하면,
<em>response</em> 객체 (server의 callback 함수인 <em>onRequest()</em>
에서 얻은)를 router를 통해 request handler에게 주사(inject) 합니다.
이제 handler는 이 객체가 가진 함수들을 이용해서 스스로 요청에 응답할 수 있게
되었습니다.
</p>
<p>
설명은 충분합니다. 이제 하나씩 애플리케이션을 고쳐 봅시다.
</p>
<p>
<em>server.js</em>부터 시작합니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br></span><span
class="kwd">var</span><span class="pln"> url </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span
class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">(</span><span class="pln">route</span><span
class="pun">,</span><span class="pln"> handle</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; </span><span class="kwd">function</span><span
class="pln"> onRequest</span><span class="pun">(</span><span class="pln">request</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">var</span><span class="pln"> pathname </span><span class="pun">=</span><span class="pln"> url</span><span
class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span
class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span
class="pun">).</span><span class="pln">pathname</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname </span><span
class="pun">+</span><span class="pln"> </span><span class="str">" received."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; &nbsp; route</span><span class="pun">(</span><span
class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span
class="pun">}</span><span class="pln"><br><br>&nbsp; http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
<em>route()</em> 함수가 값을 return 하길 기다리는 대신 세 번째 파라미터로
<em>response</em> 객체를 전달했습니다. 또 <em>onRequest()</em>에서는
<em>response</em>의 메소드 호출을 모두 제거했습니다.
이제 <em>route</em>가 처리해야 하니까요.
</p>
<p>
다음은 router.js 입니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> route</span><span
class="pun">(</span><span class="pln">handle</span><span class="pun">,</span><span
class="pln"> pathname</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"About to route a request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">if</span><span
class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span
class="pln"> handle</span><span class="pun">[</span><span class="pln">pathname</span><span
class="pun">]</span><span class="pln"> </span><span class="pun">===</span><span
class="pln"> </span><span class="str">'function'</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; handle</span><span
class="pun">[</span><span class="pln">pathname</span><span class="pun">](</span><span class="pln">response</span><span
class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span class="str">"No request handler found for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">404</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"404 Not found"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">route </span><span class="pun">=</span><span
class="pln"> route</span><span class="pun">;</span></pre>
<p>
비슷한 패턴입니다: request handler가 return 하도록 하는 대신
<em>response</em> 객체를 전달했습니다.
</p>
<p>
마땅한 request handler를 찾지 못하는 경우 직접 “404” header와
body를 응답하도록 처리해야 합니다.
</p>
<p>
이제 마지막으로 <em>requestHandlers.js</em> 입니다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> exec </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"child_process"</span><span
class="pun">).</span><span class="pln">exec</span><span class="pun">;</span><span
class="pln"><br><br></span><span class="kwd">function</span><span class="pln"> start</span><span
class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; exec</span><span class="pun">(</span><span
class="str">"ls -lah"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span
class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span
class="pun">,</span><span class="pln"> stdout</span><span class="pun">,</span><span
class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="pln">stdout</span><span
class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">});</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> upload</span><span class="pun">(</span><span class="pln">response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello Upload"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br>exports</span><span class="pun">.</span><span class="pln">start </span><span
class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
class="pln"> upload</span><span class="pun">;</span></pre>
<p>
handler 함수가 response를 파라미터로 받았고 request에 응답하기 위해
이 객체를 직접 사용하고 있습니다.
</p>
<p>
<em>start</em> handler는 <em>exec()</em> 함수의 anonymous callback
에서 응답합니다. 그리고 <em>upload</em> handler는 여전히 단순하게
“Hello Upload”를 응답하지만 이번에는 직접 <em>response</em> 객체를
사용하지요.
</p>
<p>
이제 애플리케이션을 다시 시작하면(<em>node index.js</em>)
제대로 동작할 겁니다.
</p>
<p>
<em>/start</em> 요청에 있는 비싼 동작에도 불구하고
<em>/upload</em> 요청이 곧바로 응답되는 것을 (block 되지 않고)
확인하고 싶다면, 아래와 같이 <em>requestHandlers.js</em>를
고쳐보세요.
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> exec </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"child_process"</span><span
class="pun">).</span><span class="pln">exec</span><span class="pun">;</span><span
class="pln"><br><br></span><span class="kwd">function</span><span class="pln"> start</span><span
class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; exec</span><span class="pun">(</span><span
class="str">"find /"</span><span class="pun">,</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="pun">{</span><span class="pln"> timeout</span><span class="pun">:</span><span
class="pln"> </span><span class="lit">10000</span><span class="pun">,</span><span
class="pln"> maxBuffer</span><span class="pun">:</span><span class="pln"> </span><span
class="lit">20000</span><span class="pun">*</span><span class="lit">1024</span><span
class="pln"> </span><span class="pun">},</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span
class="pln">error</span><span class="pun">,</span><span class="pln"> stdout</span><span
class="pun">,</span><span class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span
class="pln">stdout</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span
class="pun">});</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br></span><span
class="kwd">function</span><span class="pln"> upload</span><span class="pun">(</span><span class="pln">response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
class="pun">);</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello Upload"</span><span
class="pun">);</span><span class="pln"><br>&nbsp; response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br>exports</span><span class="pun">.</span><span class="pln">start </span><span
class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
class="pln"> upload</span><span class="pun">;</span></pre>
<p>
이렇게 하면
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>
에 대한 요청이 최소한 10초가 걸리게 되지만
/start가 여전히 동작 중일 때에도
<a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>
요청은 곧바로 응답합니다.
</p>
<a name="serving-something-useful"></a>
<h3>유용한 것을 제공하기</h3>
<p>
지금까지 우리가 한 일은 멋지긴 했습니다만,
우리의 멋진 사이트로 고객에게 가치를 제공하지는 못했습니다.
</p>
<p>
server, router, request handlers가 있으니 이제 사이트에 컨텐트를
추가해서 사용자가 파일을 선택하고, 이 파일을 업로드하고,
업로드한 파일을 보는 유스케이스를 제공할 수 있습니다.
단순하게 하기 위해 이미지 파일만 업로드 하고 볼 수 있다고
가정하겠습니다.
</p>
<p>
네. 이제 하나씩 해 봅시다. 대부분 지금까지 다룬 JavaScript 테크닉과
이론을 가지고 할 겁니다. 그리고 조금 새로운 것도 나올 겁니다.
</p>
<p>
하나씩이라고 했는데 두 단계입니다:
첫 번째로 POST 요청(파일 업로드는 아니지만)을 처리하는 방법을
살펴볼 겁니다.
두 번째는 파일 업로드를 처리하기 위해 Node.js의 외부 모듈을 사용할
겁니다. 제가 이런 방식을 선택한 이유는 두 가지가 입니다.
</p>
<p>
첫째, Node.js로 기본적인 POST 요청을 처리하는 것은 상대적으로
쉽습니다만 연습해 볼 가치는 충분합니다.
<br>
둘째, Node.js로 파일 업로드( 즉 multipart POST 요청)를 처리하는 것은
<em>간단하지 않아서</em> 이 튜토리얼의 범위를 넘어갑니다.
하지만 외부 모듈을 사용하는 것은 그 자체로 의미가 있어서
beginner 튜토리얼에 포함할만 합니다.
</p>
<a name="handling-post-requests"></a>
<h4>POST 요청 처리하기</h4>
<p>
뻔하고 단순한 것으로 합시다: textarea를 하나 제공해서 사용자가
내용을 채우고 submit 해서 POST 요청을 서버로 보냅니다.
이 요청을 받아서 textarea 내부의 내용을 출력하겠습니다.
</p>
<p>
<em>/start</em> 요청에서 textarea를 위한 HTML을 뿌려줘야 하니까
<em>requestHandler.js</em>에 추가해 봅시다.
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> start</span><span
class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">var</span><span
class="pln"> body </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;html&gt;'</span><span
class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><sp