forked from manuelkiessling/nodebeginner.org
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index-kr.html
3561 lines (3181 loc) · 241 KB
/
index-kr.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!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="icon" href="favicon.png" type="image/png" />
<link rel="stylesheet" type="text/css" href="olddefault.css" />
<link rel="canonical" href="http://www.nodebeginner.org/index-kr.html" />
<script type="text/javascript">
// Google Analytics
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-2127388-6']);
_gaq.push(['_setDomainName', 'www.nodebeginner.org']);
_gaq.push(['_setAllowLinker', true]);
_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="tw-flag.png" width="24" height="24" alt="traditional 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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>