本书状态
你正在阅读的已经是本书的最终版。因此,只有当进行错误更正以及针对新版本Node.js的改动进行对应的修正时,才会进行更新。
本书中的代码案例都在Node.js 0.6.11版本中测试过,可以正确工作。
读者对象
本书最适合与我有相似技术背景的读者: 至少对一门诸如Ruby、Python、PHP或者Java这样面向对象的语言有一定的经验;对JavaScript处于初学阶段,并且完全是一个Node.js的新手。
这里指的适合对其他编程语言有一定经验的开发者,意思是说,本书不会对诸如数据类型、变量、控制结构等等之类非常基础的概念作介绍。要读懂本书,这些基础的概念我都默认你已经会了。
然而,本书还是会对JavaScript中的函数和对象作详细介绍,因为它们与其他同类编程语言中的函数和对象有很大的不同。
本书结构
读完本书之后,你将完成一个完整的web应用,该应用允许用户浏览页面以及上传文件。
当然了,应用本身并没有什么了不起的,相比为了实现该功能书写的代码本身,我们更关注的是如何创建一个框架来对我们应用的不同模块进行干净地剥离。 是不是很玄乎?稍后你就明白了。
本书先从介绍在Node.js环境中进行JavaScript开发和在浏览器环境中进行JavaScript开发的差异开始。
紧接着,会带领大家完成一个最传统的“Hello World”应用,这也是最基础的Node.js应用。
最后,会和大家讨论如何设计一个“真正”完整的应用,剖析要完成该应用需要实现的不同模块,并一步一步介绍如何来实现这些模块。
可以确保的是,在这过程中,大家会学到JavaScript中一些高级的概念、如何使用它们以及为什么使用这些概念就可以实现而其他编程语言中同类的概念就无法实现。
该应用所有的源代码都可以通过 本书Github代码仓库:https://github.com/ManuelKiessling/NodeBeginnerBook/tree/master/code/application.
JavaScript与Node.js
JavaScript与你
抛开技术,我们先来聊聊你以及你和JavaScript的关系。本章的主要目的是想让你看看,对你而言是否有必要继续阅读后续章节的内容。
如果你和我一样,那么你很早就开始利用HTML进行“开发”,正因如此,你接触到了这个叫JavaScript有趣的东西,而对于JavaScript,你只会基本的操作——为web页面添加交互。
而你真正想要的是“干货”,你想要知道如何构建复杂的web站点 —— 于是,你学习了一种诸如PHP、Ruby、Java这样的编程语言,并开始书写“后端”代码。
与此同时,你还始终关注着JavaScript,随着通过一些对jQuery,Prototype之类技术的介绍,你慢慢了解到了很多JavaScript中的进阶技能,同时也感受到了JavaScript绝非仅仅是window.open() 那么简单。 .
不过,这些毕竟都是前端技术,尽管当想要增强页面的时候,使用jQuery总让你觉得很爽,但到最后,你顶多是个JavaScript用户,而非JavaScript开发者。
然后,出现了Node.js,服务端的JavaScript,这有多酷啊?
于是,你觉得是时候该重新拾起既熟悉又陌生的JavaScript了。但是别急,写Node.js应用是一件事情;理解为什么它们要以它们书写的这种方式来书写则意味着——你要懂JavaScript。这次是玩真的了。
问题来了: 由于JavaScript真正意义上以两种,甚至可以说是三种形态存在(从中世纪90年代的作为对DHTML进行增强的小玩具,到像jQuery那样严格意义上的前端技术,一直到现在的服务端技术),因此,很难找到一个“正确”的方式来学习JavaScript,使得让你书写Node.js应用的时候感觉自己是在真正开发它而不仅仅是使用它。
因为这就是关键: 你本身已经是个有经验的开发者,你不想通过到处寻找各种解决方案(其中可能还有不正确的)来学习新的技术,你要确保自己是通过正确的方式来学习这项技术。
当然了,外面不乏很优秀的学习JavaScript的文章。但是,有的时候光靠那些文章是远远不够的。你需要的是指导。
本书的目标就是给你提供指导。
简短申明
业界有非常优秀的JavaScript程序员。而我并非其中一员。
저는 이전 섹션에서 설명한 사람입니다. 저는 백엔드 웹 애플리케이션 개발에는 익숙했지만 "실제" JavaScript와 Node.js는 처음이었습니다. 저는 최근에야 JavaScript의 일부 고급 개념을 배웠으며 실제 경험이 없습니다.
그러므로 이 책은 '입문부터 마스터까지' 책이 아니라 '초급부터 고급 입문까지' 책에 가깝습니다.
성공한다면 이 책은 내가 Node.js를 배우기 시작했을 때 가장 갖고 싶었던 튜토리얼이 될 것입니다.
서버측 JavaScript
JavaScript는 처음에 브라우저에서 실행되었습니다. 그러나 브라우저는 JavaScript를 사용하여 수행할 수 있는 작업을 정의하는 컨텍스트만 제공했을 뿐 JavaScript 언어 자체가 수행할 수 있는 작업에 대해서는 많이 "설명"하지 않았습니다. 실제로 JavaScript는 "완전한" 언어입니다. 다양한 상황에서 사용될 수 있으며 그 기능은 다른 유사한 언어만큼 뛰어납니다.
Node.js는 실제로 JavaScript 코드가 백엔드(브라우저 환경 외부)에서 실행될 수 있도록 하는 또 다른 컨텍스트입니다.
백그라운드에서 자바스크립트 코드를 실행하려면 먼저 코드를 해석한 후 올바르게 실행해야 합니다. 이것이 Node.js의 원리입니다. Google의 V8 가상 머신(Google의 Chrome 브라우저에서 사용하는 JavaScript 실행 환경)을 사용하여 JavaScript 코드를 해석하고 실행합니다.
또한 Node.js에는 터미널에 문자열을 출력하는 등 많은 반복 작업을 단순화할 수 있는 유용한 모듈이 많이 제공됩니다.
따라서 Node.js는 실제로 런타임 환경이자 라이브러리입니다.
Node.js를 사용하려면 먼저 Node.js를 설치해야 합니다. Node.js 설치 방법에 대해서는 여기서 자세히 설명하지 않겠습니다. 공식 설치 가이드를 직접 참조하세요. 설치가 완료된 후 계속해서 다시 돌아와서 이 책의 나머지 부분을 읽으십시오.
“안녕하세요”
자, 더 이상 "말도 안되는 소리"는 하지 마세요. 첫 번째 Node.js 애플리케이션인 "Hello World"를 시작해 보겠습니다.
좋아하는 편집기를 열고 helloworld.js 파일을 만듭니다. 우리가 해야 할 일은 "Hello World"를 STDOUT으로 출력하는 것입니다. 다음은 이 함수를 구현하는 코드입니다.
파일을 저장하고 Node.js를 통해 실행하세요.
정상이라면 터미널에 Hello World가 출력됩니다.
알겠습니다. 이 애플리케이션이 약간 지루하다는 점은 인정합니다. 그러니 '건조한 것'을 좀 알아봅시다.
Node.js 기반의 완전한 웹 애플리케이션
사용 사례
단순하지만 충분히 현실적인 목표를 세우자:
1. 사용자는 브라우저를 통해 애플리케이션을 사용할 수 있습니다.
2. 사용자가 http://domain/start를 요청하면 파일 업로드 양식이 포함된 환영 페이지가 표시됩니다.
3. 사용자가 이미지를 선택하고 양식을 제출하면 파일이 http://domain/upload에 업로드됩니다. 페이지 업로드가 완료되면 이미지가 페이지에 표시됩니다.
그렇습니다. 지금 Google로 이동하여 기능을 완료할 수 있는 항목을 찾을 수도 있습니다. 하지만 지금은 그러지 말자.
게다가 이 목표를 달성하는 과정에서는 코드의 우아함 여부와 관계없이 기본 코드 이상의 것이 필요합니다. 또한 더 복잡한 Node.js 애플리케이션을 구축하는 방법을 찾으려면 이를 추상화해야 합니다.
분석을 위해 다양한 모듈 적용
위의 사용 사례를 구현하려면 어떤 부분을 구현해야 합니까?
1. 웹페이지를 제공해야 하므로 HTTP 서버가 필요합니다
2. 다양한 요청의 경우 서버가 요청된 URL에 따라 다른 응답을 제공해야 하므로 요청을 라우팅할 경로가 필요합니다. 요청 핸들러
3. 요청이 서버에 수신되어 경로를 통과한 후에는 이를 처리해야 하므로 최종 요청 핸들러가 필요합니다
4. 데이터를 POST하고, 데이터를 보다 친숙한 형식으로 캡슐화하여 요청 처리 프로그램에 전달하므로 데이터 처리 기능을 요청해야 합니다
5. URL에 해당하는 요청을 처리해야 할 뿐만 아니라 콘텐츠를 표시합니다. 즉, 요청 핸들러가 콘텐츠를 사용자의 브라우저로 보내려면 보기 로직이 필요합니다.
6. 마지막으로 사용자는 이미지를 업로드해야 하므로 이 먼저 해보자 PHP를 사용하여 이 구조를 어떻게 구축할지 생각해 보세요. 일반적으로 우리는 mod_php5 모듈과 함께 Apache HTTP 서버를 사용합니다.
이러한 관점에서 전체 "HTTP 요청 수신 및 웹페이지 제공" 요구 사항은 PHP에서 전혀 처리할 필요가 없습니다.
할 일이 많은 것처럼 들리지만, Node.js에서는 이것이 귀찮은 일이 아니라는 것을 점차 깨닫게 될 것입니다.
이제 첫 번째 부분인 HTTP 서버부터 구현 과정을 시작하겠습니다.
빌딩 애플리케이션용 모듈
기본 HTTP 서버
첫 번째 "실제" Node.js 애플리케이션 작성을 시작하려고 했을 때 Node.js 코드 작성 방법을 몰랐을 뿐만 아니라 코드를 구성하는 방법도 몰랐습니다.모든 것을 하나의 파일에 넣어야 하나요? 인터넷에는 Node.js로 작성된 기본 HTTP 서버에 모든 로직을 넣는 방법을 알려주는 튜토리얼이 많이 있습니다. 하지만 코드를 계속 읽을 수 있게 유지하면서 더 많은 콘텐츠를 추가하려면 어떻게 해야 할까요?
이 접근 방식을 사용하면 Node.js로 실행할 수 있는 깔끔한 메인 파일과 메인 파일 및 기타 모듈에서 호출할 수 있는 깔끔한 모듈을 가질 수 있습니다.
이제 애플리케이션을 시작하는 기본 파일과 HTTP 서버 코드를 보유하는 모듈을 만들어 보겠습니다.
제 생각엔 메인 파일 index.js를 호출하는 것이 거의 표준 형식입니다. server.js라는 파일에 서버 모듈을 넣는 것은 이해하기 쉽습니다.
서버 모듈부터 시작해 보겠습니다. 프로젝트의 루트 디렉터리에 server.js라는 파일을 생성하고 다음 코드를 작성합니다.
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World") ;
response.end();
}).listen(8888);
완료! 방금 HTTP 서버 작동을 완료했습니다. 이를 증명하기 위해 이 코드를 실행하고 테스트해 보겠습니다. 먼저 Node.js로 스크립트를 실행하세요.
node server.js
다음으로 브라우저를 열고 http://localhost:8888/을 방문하면 "Hello World"라고 적힌 웹 페이지가 표시됩니다.
재미있지 않나요? 먼저 HTTP 서버에 대해 이야기하고 프로젝트 구성 방법은 따로 남겨두겠습니다. 그건 나중에 알아내겠다고 약속해요.
HTTP 서버 분석
그럼 다음으로 이 HTTP 서버의 구성을 분석해 보겠습니다.
첫 번째 줄은 Node.js와 함께 제공되는 http 모듈을 요청하고 이를 http 변수에 할당합니다.
다음으로 http 모듈에서 제공하는 함수인 createServer를 호출합니다. 이 함수는 객체를 반환합니다. 이 객체에는 HTTP 서버가 수신하는 포트 번호를 지정하는 숫자 매개변수가 있습니다.
지금은 http.createServer 괄호 안의 함수 정의를 무시하겠습니다.
다음과 같은 코드를 사용하여 서버를 시작하고 포트 8888에서 수신할 수 있습니다.
var server = http.createServer();
server.listen(8888);
가장 흥미로운(그리고 PHP와 같은 좀 더 보수적인 언어에 익숙하다면 이상한) 부분은 함수 정의인 createSever()의 첫 번째 인수입니다.
사실 이 함수 정의는 createServer()의 첫 번째이자 유일한 매개변수입니다. 왜냐하면 JavaScript에서는 함수가 다른 변수처럼 전달될 수 있기 때문입니다.
기능 이전 수행
예를 들어 다음과 같이 할 수 있습니다.
함수 실행(someFunction, value) {
someFunction(value);
}
execute("안녕하세요"라고 말하세요);
이 코드를 주의 깊게 읽어주세요! 여기서는 실행 함수의 첫 번째 변수로 say 함수를 전달합니다. 여기서 반환되는 것은 say의 반환 값이 아니라, say 자체입니다!
이런 식으로 say는 실행 시 someFunction이 로컬 변수가 됩니다. someFunction()(괄호 포함)을 호출하여 say 함수를 사용할 수 있습니다.
물론 say에는 변수가 있으므로, someFunction을 호출할 때 Execute는 그러한 변수를 전달할 수 있습니다.
방금 했던 것처럼 함수를 이름으로 변수로 전달할 수 있습니다. 하지만 이 "먼저 정의한 다음 전달"하는 순환을 우회할 필요는 없습니다. 이 함수를 다른 함수의 괄호 안에 직접 정의하고 전달할 수 있습니다.
execute(function(word){ console.log(word) }, "Hello");
execute가 첫 번째 매개변수를 허용하는 경우 실행을 위해 전달할 함수를 직접 정의합니다.
이렇게 하면 함수에 이름을 붙일 필요도 없기 때문에 익명 함수라고 부릅니다.
이것은 제가 "고급" JavaScript라고 생각하는 것을 처음으로 가까이서 접한 것이지만 여전히 단계별로 진행해야 합니다. 지금은 이것을 받아들이겠습니다. JavaScript에서 함수는 매개변수를 다른 함수로 받을 수 있습니다. 먼저 함수를 정의한 다음 이를 전달하거나 매개변수가 전달되는 위치에서 직접 함수를 정의할 수 있습니다.
함수 전달로 HTTP 서버가 작동하는 방식
이 지식을 바탕으로 간단하지만 단순하지 않은 HTTP 서버를 살펴보겠습니다.
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World") ;
response.end();
}).listen(8888);
다음과 같은 코드로 동일한 목적을 달성할 수 있습니다.
function onRequest(요청, 응답) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
이벤트 기반 콜백
이 질문에 대답하기는 쉽지 않지만(적어도 나에게는) 이것이 Node.js가 작동하는 기본 방식입니다. 이벤트 중심이므로 속도가 매우 빠릅니다.
잠시 시간을 내어 배경 지식을 제공하는 Felix Geisendörfer의 걸작 Node.js 이해를 읽어보세요.
이 모든 것은 "Node.js가 이벤트 기반"이라는 사실로 귀결됩니다. 글쎄요, 사실 저는 이 문장이 무슨 뜻인지 정확히 모르겠습니다. 하지만 Node.js로 웹 기반 애플리케이션을 작성하는 것이 왜 의미가 있는지 설명하려고 노력할 것입니다.
http.createServer 메소드를 사용할 때 물론 서버가 특정 포트를 수신 대기하는 것뿐만 아니라 서버가 HTTP 요청을 수신할 때 무언가를 수행하기를 원하기도 합니다.
문제는 이것이 비동기식이라는 점입니다. 요청은 언제든지 도착할 수 있지만 서버는 단일 프로세스에서 실행되고 있습니다.
PHP 애플리케이션을 작성할 때 우리는 이에 대해 전혀 걱정하지 않습니다. 요청이 들어올 때마다 웹 서버(일반적으로 Apache)는 이 요청에 대한 새 프로세스를 생성하고 해당 프로세스를 처음부터 끝까지 실행하기 시작합니다. PHP 스크립트.
그럼 Node.js 프로그램에서 새 요청이 포트 8888에 도착하면 프로세스를 어떻게 제어합니까?
여기서 Node.js/JavaScript의 이벤트 중심 디자인이 실제로 도움이 될 수 있습니다. 하지만 이를 익히려면 몇 가지 새로운 개념을 배워야 합니다. 이러한 개념이 서버 코드에 어떻게 적용되는지 살펴보겠습니다.
서버를 생성하고 서버를 생성한 메서드에 함수를 전달했습니다. 서버가 요청을 받을 때마다 이 함수가 호출됩니다.
언제 일어날지 모르지만 이제 요청을 처리할 수 있는 장소가 생겼습니다. 요청을 전달한 함수입니다. 미리 정의된 함수인지 익명 함수인지는 중요하지 않습니다.
전설의 콜백입니다. 메소드에 함수를 전달하고, 이 메소드는 해당 이벤트가 발생할 때 콜백을 수행하기 위해 이 함수를 호출합니다.
적어도 저에게는 그것을 알아내는 데 약간의 노력이 필요했습니다. 아직도 확실하지 않다면 Felix의 블로그 게시물을 읽어보세요.
이 새로운 개념을 다시 생각해 보겠습니다. 서버를 생성한 후 HTTP 요청이 들어오지 않고 콜백 함수가 호출되지 않는 경우에도 코드가 계속 유효하다는 것을 어떻게 증명할 수 있나요? 이것을 시도해 봅시다:
function onRequest(request, response) {
console.log("Request received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("서버가 시작되었습니다.");
평소처럼 node server.js를 실행하면 명령줄에 "서버가 시작되었습니다."라는 메시지가 즉시 출력됩니다. 서버에 요청을 하면(브라우저에서 http://localhost:8888/ 방문) 명령줄에 "Request received."라는 메시지가 나타납니다.
이벤트 기반 비동기 서버측 JavaScript와 콜백입니다!
(서버에서 웹페이지에 접속할 때 서버에서 "Request received."를 두 번 출력할 수 있다는 점에 유의하세요. 이는 대부분의 서버가 http://localhost:8888/ Get http에 접속할 때 읽기를 시도하기 때문입니다. ://localhost:8888/favicon.ico )
서버가 요청을 처리하는 방법
좋아, 콜백 함수 onRequest()의 주요 부분인 서버 코드의 나머지 부분을 간략하게 분석해 보겠습니다.
콜백이 시작되고 onRequest() 함수가 트리거되면 요청과 응답이라는 두 가지 매개변수가 전달됩니다.
HTTP 요청의 세부 정보를 처리하고 요청에 응답하는 데 사용할 수 있는 메서드(예: 요청 브라우저로 무언가를 다시 보내는 등)가 포함된 개체입니다.
그래서 우리 코드는 다음과 같습니다. 요청을 받을 때 response.writeHead() 함수를 사용하여 HTTP 상태 200과 HTTP 헤더의 콘텐츠 유형(content-type)을 보내고 response.write()를 사용합니다. HTTP 해당 본문을 추가하는 함수에 "Hello World"라는 텍스트를 보냅니다.
마지막으로 response.end()를 호출하여 응답을 완료합니다.
지금은 요청의 세부 사항에 관심이 없으므로 요청 개체를 사용하지 않습니다.
서버 모듈 배치 위치
좋아, 약속한 대로 이제 지원서를 정리하는 방법으로 돌아갈 수 있습니다. 이제 server.js 파일에 매우 기본적인 HTTP 서버 코드가 있으며 일반적으로 애플리케이션의 다른 모듈(예: Boot.js의 HTTP 서버 모듈)을 호출하는 index.js라는 파일이 있다고 언급했습니다. 그리고 응용 프로그램을 실행합니다.
이제 (아직 시작되지 않은) index.js 메인 파일에서 사용할 수 있도록 server.js를 실제 Node.js 모듈로 변환하는 방법에 대해 이야기하겠습니다.
아마도 우리가 코드에 모듈을 사용했다는 것을 눈치채셨을 것입니다. 이렇게:
...
http.createServer(...);
이렇게 하면 로컬 변수가 http 모듈에서 제공하는 모든 공개 메소드를 포함하는 객체로 전환됩니다.
이러한 로컬 변수에 모듈 이름과 동일한 이름을 지정하는 것이 관례이지만 원하는 대로 사용할 수도 있습니다.
...
foo.createServer(...);
server.js를 실제 모듈로 변환해보면 알게 될 것입니다.
사실 너무 많은 변화를 줄 필요는 없습니다. 코드 조각을 모듈로 변환한다는 것은 모듈을 요청하는 스크립트에 제공하려는 기능의 일부를 내보내야 함을 의미합니다.
현재 HTTP 서버가 내보내야 하는 기능은 매우 간단합니다. 서버 모듈을 요청하는 스크립트는 서버를 시작하기만 하면 되기 때문입니다.
서버 스크립트를 start라는 함수에 넣은 다음 내보냅니다.
function start() {
function onRequest(request, response) {
console.log("Request received.");
response.writeHead(200, {"Content-Type": " text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("서버가 시작되었습니다.");
}
exports.start = 시작;
index.js 파일을 생성하고 다음 내용을 작성합니다.
server.start();
알겠습니다. 이제 이전과 동일하게 메인 스크립트에서 애플리케이션을 시작할 수 있습니다.
아직 전체 애플리케이션의 초기 부분만 남아 있습니다. HTTP 요청을 수신할 수 있습니다. 하지만 우리는 뭔가를 해야 합니다. 서버는 다양한 URL 요청에 다르게 응답해야 합니다.
아주 간단한 애플리케이션의 경우 콜백 함수 onRequest()에서 직접 이 작업을 수행할 수 있습니다. 하지만 제가 말했듯이, 예제를 좀 더 흥미롭게 만들려면 몇 가지 추상적인 요소를 추가해야 합니다.
다양한 HTTP 요청을 처리하는 것은 "라우팅"이라는 코드의 다른 부분입니다. 따라서 다음으로 라우팅이라는 모듈을 만들어 보겠습니다.
요청을 "라우팅"하는 방법
경로에 대해 요청된 URL과 기타 필수 GET 및 POST 매개변수를 제공해야 합니다. 그런 다음 경로는 이러한 데이터를 기반으로 해당 코드를 실행해야 합니다(여기서 "코드"는 전체 애플리케이션의 세 번째 부분에 해당함). 일련의 요청은 실제로 작동하는 핸들러를 통해 수신됩니다.
따라서 HTTP 요청을 살펴보고 요청된 URL과 GET/POST 매개변수를 추출해야 합니다. 이 기능이 라우팅에 속하는지 서버에 속하는지(또는 모듈 자체의 기능으로) 실제로 논의할 가치가 있지만 여기서는 임시적으로 HTTP 서버의 기능으로 간주됩니다.
필요한 모든 데이터는 onRequest() 콜백 함수의 첫 번째 매개변수로 전달되는 요청 객체에 포함됩니다. 하지만 이 데이터를 구문 분석하려면 url 및 querystring 모듈인 추가 Node.JS 모듈이 필요합니다.
이제 브라우저에서 요청한 URL 경로를 찾기 위해 onRequest() 함수에 몇 가지 로직을 추가해 보겠습니다.
var http = require("http");
var url = require("url");
function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("" pathname "에 대한 요청이 수신됨 .");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("서버가 시작되었습니다.");
}
exports.start = start;
자, 이제 우리 애플리케이션은 요청의 URL 경로로 서로 다른 요청을 구별할 수 있습니다. 이를 통해 라우팅(아직 완료되지 않음)을 사용하여 요청을 URL 경로로 라우팅할 수 있습니다. 기준선은 핸들러에 매핑됩니다.
우리가 구축 중인 애플리케이션에서 이는 /start 및 /upload의 요청이 다른 코드로 처리될 수 있음을 의미합니다. 나중에 이것이 어떻게 결합되는지 살펴보겠습니다.
이제 router.js라는 파일을 생성하고 다음 콘텐츠를 추가할 수 있습니다.
function Route(pathname) {
console.log("pathname에 대한 요청 라우팅 정보);
}
exports.route = Route;
보시다시피 이 코드는 아무 작업도 수행하지 않지만 지금은 그대로 유지됩니다. 로직을 추가하기 전에 먼저 라우팅과 서버를 통합하는 방법을 살펴보겠습니다.
우리 서버는 경로의 존재를 인식하고 이를 효과적으로 사용해야 합니다. 물론 이 종속성을 서버에 하드 코딩할 수도 있지만 다른 언어로 프로그래밍한 경험에 따르면 이것이 어려울 것이므로 종속성 주입을 사용하여 경로를 느슨하게 모듈을 추가할 것입니다(Martin Fowlers를 읽을 수 있음). ' 배경 지식에 대한 의존성 주입에 관한 걸작).
먼저 라우팅 함수를 매개변수로 전달하도록 서버의 start() 함수를 확장해 보겠습니다.
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log(" " pathname "에 대한 요청 받았습니다.");
경로(경로명);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("서버가 시작되었습니다.");
}
exports.start = start;
server.start(router.route);
지금 애플리케이션을 시작한 다음(node index.js, 항상 이 명령줄을 기억하세요) URL을 요청하면 애플리케이션이 해당 정보를 출력하는 것을 볼 수 있습니다. 이는 HTTP 서버가 이미 라우팅을 사용하고 있음을 나타냅니다. 모듈, 그리고 요청된 경로가 경로에 전달됩니다:
행동 중심 실행
여기서는 다시 주제에서 벗어나 함수형 프로그래밍에 대해 이야기해보겠습니다.
함수를 인수로 전달하는 것은 단지 기술적인 이유만은 아닙니다. 소프트웨어 설계의 경우 이는 실제로 철학적인 질문입니다. 다음 시나리오를 생각해 보십시오. 인덱스 파일에서 라우터 개체를 전달할 수 있으며, 그런 다음 서버는 이 개체의 경로 기능을 호출할 수 있습니다.
이와 같이 우리가 무언가를 전달하면 서버는 이를 사용하여 무언가를 완료합니다. 안녕하세요. 라우팅이라는 항목입니다. 라우팅을 도와주실 수 있나요?
하지만 서버에는 실제로 그런 것이 필요하지 않습니다. 실제로 일을 완료하려면 일이 전혀 필요하지 않으며 조치가 필요합니다. 즉, 명사가 필요하지 않고 동사가 필요합니다.
이 개념의 가장 핵심적이고 기본적인 이념적 변용을 이해한 후 자연스럽게 함수형 프로그래밍을 이해하게 되었습니다.
스티브 예게(Steve Yegge)의 걸작 명사의 왕국에서의 사형(Death Penalty in the Kingdom of Nouns)을 읽고 함수형 프로그래밍을 이해하게 되었습니다. 이 책도 꼭 읽어보세요, 정말. 이 책은 나에게 독서의 즐거움을 선사한 소프트웨어에 관한 책 중 하나입니다.
실제 요청 핸들러로 라우팅됨
주제로 돌아가서 이제 HTTP 서버와 요청 라우팅 모듈은 한 쌍의 가까운 형제처럼 예상대로 서로 통신할 수 있습니다.
물론 이것만으로는 충분하지 않습니다. 라우팅은 이름에서 알 수 있듯이 다양한 URL을 다양한 방식으로 처리해야 함을 의미합니다. 예를 들어, /start 처리를 위한 "비즈니스 로직"은 /upload 처리를 위한 "비즈니스 로직"과 달라야 합니다.
현재 구현에서 라우팅 프로세스는 라우팅 모듈에서 "종료"되며 라우팅 모듈은 요청에 대해 실제로 "조치를 취하는" 모듈이 아닙니다. 그렇지 않으면 애플리케이션이 더 복잡해지면 라우팅 모듈을 사용할 수 없습니다. 저울에 잘 맞습니다.
우리는 일시적으로 요청 처리기로서 라우팅 대상 기능을 참조합니다. 요청 핸들러가 준비되지 않은 경우 라우팅 모듈을 개선할 의미가 거의 없기 때문에 지금 라우팅 모듈 개발을 서두르지 말자.
애플리케이션에는 새 위젯이 필요하므로 새 모듈을 추가하세요. 더 이상 새로울 필요가 없습니다. requestHandlers라는 모듈을 만들고 각 요청 핸들러에 대해 자리 표시자 함수를 추가한 다음 이러한 함수를 모듈 메서드로 내보냅니다.
function upload() {
console.log("요청 핸들러 'upload'가 호출되었습니다.");
}
exports.start = 시작;
exports.upload = 업로드;
여기서 결정을 내려야 합니다. requestHandlers 모듈을 사용할 경로에 하드코딩해야 할까요, 아니면 약간의 종속성 주입을 추가해야 할까요? 다른 패턴과 마찬가지로 종속성 주입을 단지 용도로만 사용해서는 안 되지만, 이 경우 종속성 주입을 사용하면 경로와 요청 처리기 간의 결합이 느슨해져서 경로를 더 쉽게 재사용할 수 있습니다.
이는 요청 핸들러를 서버에서 경로로 전달해야 함을 의미하지만 이는 훨씬 더 터무니없는 느낌입니다. 메인 파일에서 서버까지 여러 요청 핸들러를 전달한 다음 전달해야 합니다. 서버에서 경로로.
그렇다면 이러한 요청 핸들러를 어떻게 전달합니까? 실제 애플리케이션에는 핸들러가 2개만 있으므로 새 요청이 있을 때마다 요청 핸들러 수가 계속해서 증가할 필요는 없습니다. URL 또는 요청 핸들러를 핸들러에 매핑하고 반복해서 던지는 것입니다. 또한 라우팅에서 if request == x then call 핸들러 y가 많아 시스템을 보기 흉하게 만듭니다.
잘 생각해 보세요. 각각 문자열(즉, 요청된 URL)에 매핑되어야 하는 것들이 많이 있습니다. 연관 배열이 완벽하게 작동하는 것 같습니다.
그러나 결과는 다소 실망스럽습니다. JavaScript는 연관 배열을 제공하지 않습니다. 제공한다고 말할 수 있습니까? 실제로 JavaScript에서는 이러한 기능을 실제로 제공할 수 있는 것이 객체입니다.
이와 관련하여 http://msdn.microsoft.com/en-us/magazine/cc163419.aspx에 좋은 소개가 있으니 여기서 발췌하겠습니다.
C나 C#에서는 객체에 관해 이야기할 때 클래스나 구조체의 인스턴스를 참조합니다. 객체는 인스턴스화되는 템플릿(소위 클래스)에 따라 다른 속성과 메서드를 갖습니다. 하지만 JavaScript에서는 객체가 이러한 개념이 아닙니다. JavaScript에서 개체는 키/값 쌍의 모음입니다. JavaScript 개체를 문자열 키가 있는 사전으로 생각할 수 있습니다.
하지만 JavaScript 객체가 단지 키/값 쌍의 모음인 경우 어떻게 메소드를 가질 수 있습니까? 음, 여기의 값은 문자열, 숫자 또는... 함수일 수 있습니다!
자, 드디어 코드로 돌아가겠습니다. 이제 객체를 통해 일련의 요청 핸들러를 전달하기로 결정했으므로 이 객체를 느슨하게 결합된 방식으로 Route() 함수에 삽입해야 합니다.
먼저 이 객체를 기본 파일 index.js에 소개합니다.
var handler = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers. 업로드;
server.start(router.route, handler);
보시다시피 서로 다른 URL을 동일한 요청 핸들러에 매핑하는 것은 쉽습니다. requestHandlers.start에 해당하는 "/" 키가 있는 속성을 객체에 추가하기만 하면 간결하게 구성된 /start 및 /는 시작 핸들러에 의해 처리됩니다.
객체 정의를 완료한 후 이를 추가 매개변수로 서버에 전달합니다. 이를 위해 server.js를 다음과 같이 수정합니다.
function start(route, handler) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " 경로명 " 받았습니다.");
경로(핸들, 경로명);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("서버가 시작되었습니다.");
}
exports.start = start;
그런 다음 그에 따라 Route.js 파일의 Route() 함수를 수정합니다.
exports.route = Route;
이를 통해 서버, 경로 및 요청 처리기가 함께 구성됩니다. 이제 애플리케이션을 시작하고 브라우저에서 http://localhost:8888/start를 방문합니다. 다음 로그는 시스템이 올바른 요청 핸들러를 호출함을 보여줍니다.
요청 핸들러가 응답하도록 합니다.
아주 좋아요. 그러나 이제 요청 핸들러가 단지 "Hello World" 대신 의미 있는 정보를 브라우저에 반환할 수 있다면 좋을 것입니다.
여기서 기억해야 할 점은 브라우저가 요청한 후에 얻어지고 표시되는 "Hello World" 정보는 여전히 server.js 파일의 onRequest 함수에서 나온다는 것입니다.
사실 "요청 처리"는 단순히 "요청에 응답"을 의미합니다. 따라서 onRequest 함수처럼 브라우저와 "대화"할 수 있도록 요청 핸들러를 활성화해야 합니다.
잘못된 구현
PHP 또는 Ruby 기술 배경을 가진 우리와 같은 개발자의 경우 가장 간단한 구현 방법은 실제로 그다지 신뢰할 수 없습니다. 효과적인 것처럼 보이지만 실제로는 그렇지 않을 수도 있습니다.
여기서 "간단한 구현"이란 onRequest 함수를 통해 사용자에게 표시하려는 정보를 요청 핸들러가 직접 반환(return())하도록 하는 것입니다.
먼저 이렇게 구현해 보고, 왜 구현하는 것이 좋은 방법이 아닌지 살펴보겠습니다.
요청 핸들러가 브라우저에 표시해야 하는 정보를 반환하도록 하는 것부터 시작하겠습니다. requestHandler.js를 다음 형식으로 수정해야 합니다.
function upload() {
console.log("요청 핸들러 '업로드'가 호출되었습니다.");
return "Hello Upload";
}
exports.start = 시작;
exports.upload = 업로드;
exports.route = Route;
마지막으로 다음과 같이 요청 경로를 통해 요청 핸들러가 반환한 콘텐츠로 브라우저에 응답하도록 server.js를 리팩토링해야 합니다.
function start(route, handler) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " 경로명 " 받았습니다.");
response.writeHead(200, {"Content-Type": "text/plain"});
var content = Route(handle, pathname)
response.write(content);
응답 .end();
}
http.createServer(onRequest).listen(8888);
console.log("서버가 시작되었습니다.");
}
exports.start = start;
그래, 뭐가 문제야? 간단히 말해서, 요청 핸들러가 나중에 비차단 작업을 수행해야 할 경우 애플리케이션이 "중단"됩니다.
이해가 안 되시나요? 상관없습니다. 아래에서 자세히 설명하겠습니다.
차단 및 비차단
앞서 언급했듯이 요청 핸들러에 비차단 작업을 포함하면 문제가 발생합니다. 하지만 이에 대해 이야기하기 전에 먼저 차단 작업이 무엇인지부터 살펴보겠습니다.
'차단'과 '비차단'의 구체적인 의미를 설명하고 싶지는 않습니다. 요청 핸들러에 차단 작업을 추가하면 어떤 일이 발생하는지 살펴보겠습니다.
여기서는 "Hello Start"를 반환하기 전에 10초 동안 기다리도록 시작 요청 핸들러를 수정하겠습니다. JavaScript에는 sleep()과 같은 작업이 없기 때문에 약간의 Hack만 사용하여 구현을 시뮬레이션할 수 있습니다.
requestHandlers.js를 다음 형식으로 수정해 보겠습니다.
function sleep(milliSeconds) {
var startTime = new Date().getTime();
while (new Date().getTime() < startTime milliSeconds);
}
sleep(10000);
"Hello Start" 반환;
}
function upload() {
console.log("요청 핸들러 '업로드'가 호출되었습니다.");
return "Hello Upload";
}
exports.start = 시작;
exports.upload = 업로드;
(물론 10초 동안 자는 시뮬레이션일 뿐입니다. 실제 시나리오에서는 일부 장기 계산 연산 등 차단 연산이 많이 있습니다.)
저희의 변화가 어떤 변화를 가져왔는지 살펴보겠습니다.
평소와 마찬가지로 먼저 서버를 다시 시작해야 합니다. 효과를 보려면 비교적 복잡한 작업을 수행해야 합니다(따라오세요). 먼저 두 개의 브라우저 창이나 탭을 엽니다. 첫 번째 브라우저 창의 주소 표시줄에 http://localhost:8888/start를 입력하고 아직 열지 마세요!
두 번째 브라우저 창의 주소 표시줄에 http://localhost:8888/upload를 입력하세요. 아직 열지 마세요.
다음으로 다음을 수행하세요. 첫 번째 창("/start")에서 Enter 키를 누른 다음 빠르게 두 번째 창("/upload")으로 전환하고 Enter 키를 누르세요.
무슨 일이 일어났는지 확인하세요. /start URL을 로드하는 데 10초가 걸렸는데, 이는 우리가 예상했던 것입니다. 그런데 /upload URL은 실제로 10초가 걸렸고, 해당 요청 핸들러에는 sleep()과 유사한 연산이 없었습니다!
이게 왜요? 그 이유는 start()에 차단 작업이 포함되어 있기 때문입니다. 비유적으로 말하면 "다른 모든 처리 작업을 차단합니다."
이것은 분명히 문제가 됩니다. Node는 항상 다음과 같이 자신을 광고했기 때문입니다. "노드에서는 코드를 제외한 모든 것이 병렬로 실행됩니다."
이 문장이 의미하는 바는 Node.js가 추가 스레드를 추가하지 않고도 작업을 병렬로 처리할 수 있다는 것입니다. Node.js는 단일 스레드입니다. 이벤트 루프를 통해 병렬 작업을 구현하며 이를 최대한 활용해야 합니다. 가능한 한 차단 작업을 피하고 대신 비차단 작업을 사용해야 합니다.
그러나 비차단 작업을 사용하려면 처리하는 데 시간이 걸리는 다른 함수에 함수를 매개변수로 전달하여 콜백을 사용해야 합니다(예: 10초 동안 휴면, 데이터베이스 쿼리 또는 대규모 작업 수행). 계산 횟수).
Node.js의 경우 다음과 같이 처리됩니다. "야, 아마도ExpensiveFunction()(역자 주: 처리하는 데 시간이 걸리는 함수를 의미함), 너는 계속해서 네 일을 처리해, 나(Node.js 스레드) 지금은 기다리지 않고 계속해서 코드를 처리하겠습니다. callbackFunction()을 제공해 주세요. 처리가 끝나면 콜백 함수를 호출하겠습니다.”
(이벤트 폴링에 대해 더 자세히 알고 싶다면 Mixu의 블로그 게시물 - node.js 이벤트 폴링 이해를 읽어보세요.)
다음으로 비차단 작업을 사용하는 잘못된 방법을 소개하겠습니다.
지난번과 마찬가지로 문제가 노출되도록 애플리케이션을 수정했습니다.
이번에도 시작 요청 핸들러를 사용하여 "작동"합니다. 다음 형식으로 수정하세요.
function start() {
console.log("요청 핸들러 'start'가 호출되었습니다.");
var content = "empty";
exec("ls -lah", 함수(오류, stdout, stderr) {
content = stdout;
});
콘텐츠 반환;
}
function upload() {
console.log("요청 핸들러 '업로드'가 호출되었습니다.");
return "Hello Upload";
}
exports.start = 시작;
exports.upload = 업로드;
exec()는 무엇을 하나요? Node.js에서 쉘 명령을 실행합니다. 위의 예에서는 이를 사용하여 현재 디렉터리("ls -lah")의 모든 파일을 가져온 다음 /startURL이 요청될 때 파일 정보를 브라우저에 출력합니다.
위 코드는 매우 직관적입니다. 새 변수 content(초기값은 "empty")를 만들고, "ls -lah" 명령을 실행하고, 결과를 content에 할당하고, 마지막으로 content를 반환합니다.
평소와 마찬가지로 서버를 시작하고 "http://localhost:8888/start"에 액세스합니다.
'empty'라는 콘텐츠가 포함된 아름다운 웹페이지가 로드됩니다. 무슨 일이야?
이쯤 되면 exec()가 논블로킹에서 마법 같은 역할을 한다는 것을 대충 짐작하셨을 겁니다. 실제로 좋은 점은 애플리케이션을 강제로 중지하고 작업을 기다리지 않고도 시간이 많이 걸리는 쉘 작업을 수행할 수 있다는 것입니다.
(이를 증명하려면 "ls -lah"를 "find /"와 같이 시간이 더 많이 소요되는 작업으로 대체하여 효과를 얻을 수 있습니다.)
그러나 브라우저에 표시된 결과로 볼 때 우리의 논블로킹 작업은 만족스럽지 못하죠?
자, 다음은 이 문제를 해결해 보겠습니다. 이 과정에서 현재 접근 방식이 작동하지 않는 이유를 살펴보겠습니다.
문제는 non-blocking 작업을 수행하기 위해 exec()가 콜백 함수를 사용한다는 것입니다.
우리의 경우 콜백 함수는 exec()에 두 번째 매개변수로 전달된 익명 함수입니다.
여기서 "ls -lah" 작업은 실제로 매우 빠릅니다(현재 디렉터리에 수백만 개의 파일이 없는 경우). 이것이 콜백 함수가 빠르게 실행되는 이유입니다. 하지만 어쨌든 여전히 비동기적입니다.
효과를 더 명확하게 하기 위해 좀 더 시간이 많이 걸리는 명령인 "find /"를 상상해 보겠습니다. 이 명령은 내 컴퓨터에서 실행하는 데 약 1분이 걸립니다. 그러나 요청 핸들러에는 "ls - lah"를 입력했습니다. "find /"로 대체됩니다. /start URL을 열 때 여전히 HTTP 응답을 즉시 얻을 수 있습니다. 분명히 exec()가 백그라운드에서 실행될 때 Node.js 자체는 다음 코드를 계속 실행합니다. 그리고 여기에서는 exec()에 전달된 콜백 함수가 "find /" 명령이 실행된 후에만 호출된다고 가정합니다.
그렇다면 현재 디렉토리의 파일 목록을 사용자에게 어떻게 표시할 수 있을까요?
이제 우리는 이 잘못된 구현을 이해했으므로 요청 핸들러가 브라우저 요청에 올바른 방식으로 응답하도록 만드는 방법을 소개하겠습니다.
비차단 작업을 통한 요청에 대한 응답
방금 '올바른 길'이라는 문구를 언급했습니다. 사실, "올바른 길"은 대개 간단하지 않습니다.
그러나 Node.js를 사용하는 구현 솔루션인 함수 전달이 있습니다. 이를 구체적으로 구현하는 방법을 살펴보겠습니다.
지금까지 우리 애플리케이션은 요청 핸들러에서 반환한 콘텐츠(요청 핸들러가 결국 사용자에게 콘텐츠를 표시함)를 HTTP 서버로 전달할 수 있었습니다.
이제 우리는 다음과 같은 새로운 구현 방법을 채택합니다. 콘텐츠를 서버에 전달하는 대신 이번에는 서버를 콘텐츠에 "전달"하는 방법을 사용합니다. 실용적인 관점에서 볼 때 서버의 콜백 함수 onRequest()에서 얻은 응답 객체는 요청 라우팅을 통해 요청 핸들러로 전달됩니다. 그런 다음 핸들러는 해당 객체의 함수를 사용하여 요청에 응답할 수 있습니다.
이것이 원칙입니다. 이 솔루션을 단계별로 구현해 보겠습니다.
server.js로 시작:
function start(route, handler) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " 경로명 " 받았습니다.");
경로(핸들, 경로 이름, 응답);
}
http.createServer(onRequest).listen(8888);
console.log("서버가 시작되었습니다.");
}
exports.start = start;
router.js를 살펴보겠습니다.
exports.route = Route;
해당 요청 핸들러가 없으면 바로 "404" 오류를 반환합니다.
마지막으로 requestHandler.js를 다음 형식으로 수정합니다.
function start(response) {
console.log("요청 핸들러 'start'가 호출되었습니다.");
exec("ls -lah", function (error, stdout, stderr) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write (stdout);
response.end();
});
}
function upload(response) {
console.log("요청 핸들러 'upload'가 호출되었습니다.");
response.writeHead(200, {"Content-Type": "text/plain"} );
response.write("안녕하세요 업로드");
response.end();
}
exports.start = start;
exports.upload = upload;
시작 핸들러는 exec()의 익명 콜백 함수에서 요청 응답 작업을 수행하는 반면 업로드 핸들러는 여전히 "Hello World"라고만 응답하지만 이번에는 응답 개체를 사용합니다.
이제 애플리케이션(node index.js)을 다시 시작하면 모든 것이 잘 작동할 것입니다.
/start 핸들러에서 시간이 많이 걸리는 작업이 /upload 요청에 대한 즉각적인 응답을 차단하지 않는다는 것을 증명하려면 requestHandlers.js를 다음 형식으로 수정할 수 있습니다.
function start(response) {
console.log("요청 핸들러 'start'가 호출되었습니다.");
exec("find /",
{ timeout: 10000, maxBuffer: 20000*1024 },
함수(error, stdout, stderr) {
response.writeHead(200, {"Content- 유형: "text/plain"});
response.write(stdout);
response.end();
});
}
function upload(response) {
console.log("요청 핸들러 'upload'가 호출되었습니다.");
response.writeHead(200, {"Content-Type": "text/plain"} );
response.write("안녕하세요 업로드");
response.end();
}
exports.start = 시작;
exports.upload = 업로드;
더 유용한 시나리오
지금까지 우리는 잘 해왔지만 우리의 애플리케이션은 실용성이 없습니다.
서버, 요청 라우팅 및 요청 핸들러가 완료되었습니다. 이전 사용 사례에 따라 웹사이트에 상호 작용을 추가해 보겠습니다. 사용자가 파일을 선택하고 파일을 업로드한 다음 업로드된 파일을 브라우저에서 확인합니다. 간단하게 유지하기 위해 사용자가 이미지만 업로드하고 앱이 해당 이미지를 브라우저에 표시한다고 가정합니다.
자, 이제 차근차근 구현해 보겠습니다. 이전에 이미 많은 JavaScript 원리와 기술 내용을 소개했으므로 이번에는 속도를 조금 높여보겠습니다.
이 기능을 구현하려면 다음과 같은 두 단계가 있습니다. 먼저 POST 요청(파일이 아닌 업로드)을 처리하는 방법을 살펴보겠습니다. 그런 다음 파일 업로드를 위해 Node.js의 외부 모듈을 사용합니다. 이 구현에는 두 가지 이유가 있습니다.
첫째, Node.js에서 기본 POST 요청을 처리하는 것은 비교적 간단하지만 그 과정에서 많은 것을 배울 수 있습니다.
둘째, Node.js를 사용하여 파일 업로드(멀티파트 POST 요청)를 처리하는 것은 상대적으로 복잡하며 이 책의 범위를 벗어납니다. 그러나 외부 모듈을 사용하는 방법은 이 책의 범위에 속합니다.
POST 요청 처리
간단한 예를 생각해 보겠습니다. 사용자가 콘텐츠를 입력할 수 있는 텍스트 영역을 표시한 다음 POST 요청을 통해 서버에 제출합니다. 마지막으로 서버는 요청을 수신하고 핸들러를 통해 입력 내용을 브라우저에 표시합니다.
/start 요청 핸들러는 텍스트 영역이 있는 양식을 생성하는 데 사용되므로 requestHandlers.js를 다음 형식으로 수정합니다.
function start(response) {
console.log("요청 핸들러 'start'가 호출되었습니다.");
var body = ''
'
response.write(body);
response.end();
}
console.log("요청 핸들러 'upload'가 호출되었습니다.");
response.writeHead(200, {"Content-Type": "text/plain"} );
response.write("안녕하세요 업로드");
response.end();
}
exports.upload = upload;
자, 이제 우리의 신청서가 매우 완성되어 Webby Awards를 수상할 수도 있습니다. 하하. (번역자 주: 웨비 어워드는 국제디지털예술과학아카데미(International Academy of Digital Arts and Sciences)가 후원하는 세계 최고의 웹사이트를 선정하는 상입니다. 자세한 내용은 상세 설명을 참조하세요.) http://localhost를 방문하시면 보실 수 있습니다: 8888/start in your browser 간단한 양식입니다. 서버를 다시 시작하는 것을 잊지 마세요!
나머지 공간에서는 더 흥미로운 문제에 대해 논의하겠습니다. 사용자가 양식을 제출하면 /upload 요청 핸들러가 트리거되어 POST 요청을 처리합니다.
이제 우리는 초보자 중 전문가이므로 비동기 콜백을 사용하여 POST 요청 데이터를 비차단 방식으로 처리하는 것을 생각하는 것이 당연합니다.
여기에서는 비차단이 사용됩니다.