Node.js
- Node.js, a program that allows you to apply your JavaScript skills outside of the browser. With it, you can build anything from small command line tools to HTTP servers that power dynamic websites.
- These chapters aim to teach you the main concepts that Node.js uses and to give you enough information to write useful programs for it.
Background
- One of the more difficult problems with writing systems that communicate over the network is managing input and output—that is, the reading and writing of data to and from the network and hard drive. Moving data around takes time, and scheduling it cleverly can make a big difference in how quickly a system responds to the user or to network requests.
네트워크를 통해 커뮤니케이션 하는 시스템을 만드는 것을 더 어렵게 하는 것 중 하나는 입력과 출력을 관리하는 것이다(네트워크/디스크로 부터/에 읽기/쓰기). 데이터를 움직이는 시간을 잘 스케쥴링하면 시스템이 사용자나 네트워크 리퀘스트에 훨씬 빠르게 응답할 수도 있다.
- In such programs, asynchronous programming is often helpful. It allows the program to send and receive data from and to multiple devices at the same time without complicated thread management and synchronization.
비동기 프로그래밍이 유용할 수 있다. (cf. 복잡한 스레드 관리와 동기화)
- Node was initially conceived for the purpose of making asynchronous programming easy and convenient. JavaScript lends itself well to a system like Node. It is one of the few programming languages that does not have a built-in way to do in- and output. Thus, JavaScript could be fit onto Node’s rather eccentric approach to in- and output without ending up with two inconsistent interfaces.
노드는 비동기 프로그래밍을 쉽고 편리하게 만들기 위해 고안되었다. 자바스크립트는 노드 같은 시스템에 아주 적합하다. (입/출력...)
The node command
- When Node.js is installed on a system, it provides a program called
node
, which is used to run JavaScript files. - The
console.log
method in Node does something similar to what it does in the browser. It prints out a piece of text. But in Node, the text will go to the process’s standard output stream, rather than to a browser’s JavaScript console. - If you run
node
without giving it a file, it provides you with a prompt at which you can type JavaScript code and immediately see the result. -
$ node > 1 + 1 2 > [-1, -2, -3].map(Math.abs) [1, 2, 3] > process.exit(0) $
- The
process
binding, just like theconsole
binding, is available globally in Node. It provides various ways to inspect and manipulate the current program. Theexit
method ends the process and can be given an exit status code, which tells the program that startednode
(in this case, the command line shell) whether the program completed successfully (code zero) or encountered an error (any other code).console
처럼 전역적으로 사용 가능한process
바인딩은 현재 프로그램을 점검하거나 조작하는 방법들을 제공한다. 그 중,exit
메소드는 프로세스를 끝내고 인자로 받은 종료 상태 코드를node
를 시작한 프로그램에게 전달한다. - To find the command line arguments given to your script, you can read
process.argv
, which is an array of strings. Note that it also includes the name of thenode
command and your script name, so the actual arguments start at index 2. If showargv.js contains the statementconsole.log(process.argv)
, you could run it like this: -
$ node showargv.js one --and two ["node", "/tmp/showargv.js", "one", "--and", "two"]
커맨드 라인 인자:
process.argv
로 문자열 배열에 접근할 수 있다. - All the standard JavaScript global bindings, such as
Array
,Math
, andJSON
, are also present in Node’s environment. Browser-related functionality, such asdocument
orprompt
, is not.노드에서도 자바스크립트의 표준 전역 바인딩은 모두 존재하지만, 브라우저와 관련된 기능들은 존재하지 않는다.
Modules
-
Beyond the bindings I mentioned, such as
console
andprocess
, Node puts few additional bindings in the global scope. If you want to access built-in functionality, you have to ask the module system for it.노드는 이 외에도 전역 스코프에서 여러 바인딩들을 제공한다. 만약 내장 기능들을 사용하고 싶다면 모듈 시스템에 요청해야 한다.
-
The CommonJS module system is built into Node and is used to load anything from built-in modules to downloaded packages to files that are part of your own program.
CommonJS 모듈 시스템을 사용하며, 이는 내장 모듈부터 다운로드된 패키지, 프로그램에 포함된 파일들을 불러오기 위해 사용된다.
-
When
require
is called, Node has to resolve the given string to an actual file that it can load. Pathnames that start with/
,./
, or../
are resolved relative to the current module’s path, where.
stands for the current directory,../
for one directory up, and/
for the root of the file system. So if you ask for"./graph"
from the file/tmp/robot/robot.js
, Node will try to load the file/tmp/robot/graph.js
./
: 파일 시스템의 루트 -
The
.js
extension may be omitted, and Node will add it if such a file exists. If the required path refers to a directory, Node will try to load the file namedindex.js
in that directory..js
확장자는 생략될 수 있는데, 이 경우에 해당 파일이 존재한다면 노드는 확장자를 알아서 붙일 것이다. 만약 경로가 디렉터리라면 노드는 해당 디렉터리 내의index.js
파일을 로드하려고 할 것이다. -
When a string that does not look like a relative or absolute path is given to
require
, it is assumed to refer to either a built-in module or a module installed in anode_modules
directory.require
에 절대경로, 상대경로가 아닌 문자열이 주어진다면, 이는 내장 모듈이나node_modules
에 설치된 모듈을 참조하는 것으로 여겨진다. -
For example,
require("fs")
will give you Node’s built-in file system module. Andrequire("robot")
might try to load the library found innode_modules/robot/
. A common way to install such libraries is by using NPM. -
Let’s set up a small project consisting of two files.
-
/* main.js */ const {reverse} = require("./reverse"); // Index 2 holds the first actual command line argument let argument = process.argv[2]; console.log(reverse(argument));
-
/* reverse.js */ exports.reverse = function(string) { return Array.from(string).reverse().join(""); };
-
Remember that adding properties to
exports
adds them to the interface of the module. Since Node.js treats files as CommonJS modules,main.js
can take the exportedreverse
function fromreverse.js
. -
$ node main.js JavaScript tpircSavaJ
Installing with NPM
- NPM is an online repository of JavaScript modules, many of which are specifically written for Node. When you install Node on your computer, you also get the
npm
command, which you can use to interact with this repository. - ... After running
npm install
, NPM will have created a directory callednode_modules
. Inside that directory will be anini
directory that contains the library. You can open it and look at the code. When we callrequire("ini")
, this library is loaded, and we can call its parse property toparse
a configuration file.node_modules
- By default NPM installs packages under the current directory, rather than in a central place. If you are used to other package managers, this may seem unusual, but it has advantages—it puts each application in full control of the packages it installs and makes it easier to manage versions and clean up when removing an application.
기본적으로 NPM은 어떤 중앙의 장소가 아니라 현재 디렉터리에 패키지들을 설치한다. 이는 각 애플리케이션을 그것이 설치한 패키지들의 관리하에 두고, 패키지의 버전 관리, 그리고 애플리케이션을 지울 때 정리하는 것을 더 쉽게 만들어 준다.
Package files
- In the
npm install
example, you could see a warning about the fact that thepackage.json
file did not exist. It is recommended to create such a file for each project, either manually or by runningnpm init
. It contains some information about the project, such as its name and version, and lists its dependencies.npm install
할 때,package.json
이 존재하지 않는다는 경고를 볼 수 있다. 각 프로젝트 마다 수동으로든npm init
명령어를 사용하든package.json
파일을 만드는 것이 추천된다. 이 파일은 프로젝트의 이름, 버전, 의존성과 같은 프로젝트에 대한 정보를 포함한다. - The robot simulation from Chapter 7, as modularized in the exercise in Chapter 10, might have a package.json file like this:
-
{ "author": "Marijn Haverbeke", "name": "eloquent-javascript-robot", "description": "Simulation of a package-delivery robot", "version": "1.0.0", "main": "run.js", "dependencies": { "dijkstrajs": "^1.0.1", "random-item": "^1.0.0" }, "license": "ISC" }
- When you run
npm install
without naming a package to install, NPM will install the dependencies listed inpackage.json
. When you install a specific package that is not already listed as a dependency, NPM will add it topackage.json
.패키지 이름 없이
npm install
을 사용하면package.json
의 의존성들이 설치될 것이다. 또한, 의존성에 포함되지 않는 어떤 패키지를 설치한다면, NPM은 이 패키지를package.json
에 추가할 것이다.
Versions
- A
package.json
file lists both the program’s own version and versions for its dependencies. Versions are a way to deal with the fact that packages evolve separately, and code written to work with a package as it existed at one point may not work with a later, modified version of the package. - NPM demands that its packages follow a schema called semantic versioning, which encodes some information about which versions are compatible (don’t break the old interface) in the version number. A semantic version consists of three numbers, separated by periods, such as
2.3.0
. Every time new functionality is added, the middle number has to be incremented. Every time compatibility is broken, so that existing code that uses the package might not work with the new version, the first number has to be incremented.semantic versioning
- A caret character (
^
) in front of the version number for a dependency inpackage.json
indicates that any version compatible with the given number may be installed. So, for example,"^2.3.0"
would mean that any version greater than or equal to 2.3.0 and less than 3.0.0 is allowed.caret character (
^
) - The
npm
command is also used to publish new packages or new versions of packages. If you runnpm publish
in a directory that has apackage.json
file, it will publish a package with the name and version listed in the JSON file to the registry.npm publish
- Another program,
yarn
, which can be installed from the NPM registry, fills the same role asnpm
using a somewhat different interface and installation strategy.
The file system module
- One of the most commonly used built-in modules in Node is the
fs
module, which stands for file system. It exports functions for working with files and directories. - For example, the function called
readFile
reads a file and then calls a callback with the file’s contents. -
let {readFile} = require("fs"); readFile("file.txt", "utf8", (error, text) => { if (error) throw error; console.log("The file contains:", text); });
- The second argument to
readFile
indicates the character encoding used to decode the file into a string. There are several ways in which text can be encoded to binary data, but most modern systems use UTF-8. So unless you have reasons to believe another encoding is used, pass"utf8"
when reading a text file.readFile
의 두 번째 인자는 파일을 문자열로 복호화할 때 사용되는 문자 인코딩을 나타낸다. 대부분의 모던 시스템들은 UTF-8을 사용하므로 다른 인코딩 방식이 사용되었다고 판단할 이유가 없다면"utf8"
을 사용해서 텍스트 파일을 읽어라. - If you do not pass an encoding, Node will assume you are interested in the binary data and will give you a
Buffer
object instead of a string. This is an array-like object that contains numbers representing the bytes (8-bit chunks of data) in the files.만약 인코딩 방식을 명시하지 않는다면
Buffer
객체를 얻을 것이다. 이 객체는 배열과 비슷한 객체로 바이트 단위를 나타내는 숫자를 포함한다. -
const {readFile} = require("fs"); readFile("file.txt", (error, buffer) => { if (error) throw error; console.log("The file contained", buffer.length, "bytes.", "The first byte is:", buffer[0]); });
- A similar function,
writeFile
, is used to write a file to disk. -
const {writeFile} = require("fs"); writeFile("graffiti.txt", "Node was here", err => { if (err) console.log(`Filed to write file ${err}`); else console.log("File written"); });
- Here it was not necessary to specify the encoding—
writeFile
will assume that when it is given a string to write, rather than aBuffer
object, it should write it out as text using its default character encoding, which is UTF-8.writeFile
은Buffer
객체가 아니라 문자열이 주어진다면 기본 문자 인코딩 방식인 UTF-8로 텍스트를 적는다. - The
fs
module contains many other useful functions:readdir
will return the files in a directory as an array of strings,stat
will retrieve information about a file,rename
will rename a file,unlink
will remove one, and so on.readdir
,stat
,rename
,unlink
(https://nodejs.org) - Most of these take a callback function as the last parameter, which they call either with an error (the first argument) or with a successful result (the second). As we saw in Chapter 11, there are downsides to this style of programming—the biggest one being that error handling becomes verbose and error-prone.
콜백 함수: 에레 처리가 장황하고 오류가 발생하기 쉽다.
- At the time of writing their integration into Node.js is still a work in progress. There is an object
promises
exported from thefs
package since version 10.1 that contains most of the same functions asfs
but uses promises rather than callback functions.promises
: 콜백 대신 프로미스를 사용하는 것 말고는fs
와 동일한 함수들을 대부분 제공한다. -
const {readFile} = require("fs").promises; readFile("file.txt", "utf8") .then(text => console.log("The file contains:", text));
- Sometimes you don’t need asynchronicity, and it just gets in the way. Many of the functions in
fs
also have a synchronous variant, which has the same name withSync
added to the end.때때로 비동기성은 오히려 방해가 될 때가 있다.
fs
의 대부분의 함수는 동기적인 버전을 갖는데, 이는 원래 이름에Sync
를 붙인 것이다. -
const {readFileSync} = require("fs"); console.log("The file contains:", readFileSync("file.txt", "utf8"));
- Do note that while such a synchronous operation is being performed, your program is stopped entirely.
The HTTP module
- Another central module is called
http
. It provides functionality for running HTTP servers and making HTTP requests.또 다른 주요한 모듈로
http
가 있다. 이 모듈은 HTTP 서버를 작동시키는 것과 HTTP 리퀘스트를 만드는 기능들을 제공한다. -
const {createServer} = require("http"); let server = createServer((request, response) => { response.writeHead(200, {"Content-Type": "text/html"}); response.write(` <h1>Hello!<h1> <p>You asked for <code>${request.url}</code></p>`); response.end(); }); server.listen(8000); console.log("Listening! (port 8000)");
- The function passed as argument to
createServer
is called every time a client connects to the server. Therequest
andresponse
bindings are objects representing the incoming and outgoing data. The first contains information about the request, such as itsurl
property, which tells us to what URL the request was made.createServer
가 인자로 받는 함수는 클라이언트가 서버에 연결할 때 매번 호출된다.request
와response
바인딩은 각각 들어오는 데이터와 나가는 데이터를 나타내는 객체이다. - To send something back, you call methods on the
response
object. The first,writeHead
, will write out the response headers. You give it the status code (200 for “OK” in this case) and an object that contains header values. The example sets theContent-Type
header to inform the client that we’ll be sending back an HTML document.무언가를 응답하고 싶다면
response
객체에 메소드를 호출하면 된다.writeHead
는 리스폰스 헤더를 쓰는 메소드로, 상태 코드와 헤더 값들을 가진 객체를 받는다. 예시는 클라이언트에게 HTML 문서를 보낸다는 것을 알리기 위해Content-Type
헤더를 설정했다. - Next, the actual response body (the document itself) is sent with
response.write
. You are allowed to call this method multiple times if you want to send the response piece by piece, for example to stream data to the client as it becomes available. Finally,response.end
signals the end of the response.response.write
를 사용하여 리스폰스 바디를 보낼 수 있다. 이는 여러 번 호출이 가능하다.response.end
를 호출하여 리스폰스가 끝났음을 알린다. - The call to
server.listen
causes the server to start waiting for connections on port 8000.server.listen
- When you run this script, the process just sits there and waits. When a script is listening for events—in this case, network connections—
node
will not automatically exit when it reaches the end of the script.스크립트가 이벤트를 듣고(listening) 있을때,
node
는 스크립트의 끝에 도달했어도 자동적으로 끝나지 않는다. - A real web server usually does more than the one in the example—it looks at the request’s method (the
method
property) to see what action the client is trying to perform and looks at the request’s URL to find out which resource this action is being performed on.실제 웹 서버는 예제의 서버보다 더 많은 일을 한다:
method
를 확인하여 어떤 액션이 수행되는 것인지 보거나, URL을 확인하여 어떤 리소스에 이 액션이 수행되는지를 확인하는 등 - To act as an HTTP client, we can use the
request
function in thehttp
module.HTTP 클라이언트의 역할을 하려면
http
모듈의request
함수를 사용할 수 있다. -
const {request} = require("http"); let requestStream = request({ hostname: "eloquentjavascript.net", path: "/20_node.html", method: "GET", headers: {Accept: "text/html"} }, response => { console.log("Server responded with status code", response.statusCode); }); requestStream.end();
- The first argument to
request
configures the request, telling Node what server to talk to, what path to request from that server, which method to use, and so on. The second argument is the function that should be called when a response comes in. It is given an object that allows us to inspect the response, for example to find out its status code.request
의 첫 번째 인자는 서버, 경로, 메소드 등을 노드에게 전달하며 리퀘스트를 설정한다. 두 번재 인자는 리스폰스가 들어왔을 때 호출될 함수를 명시한다. 이때, 리스폰스를 조사할 수 있는 객체가 인자로 주어진다. - Just like the
response
object we saw in the server, the object returned byrequest
allows us to stream data into the request with thewrite
method and finish the request with theend
method. The example does not usewrite
becauseGET
requests should not contain data in their request body.서버에서 봤던
response
객체처럼,request
에 의해 반환되는 객체는write
메소드를 사용하여 데이터를 리퀘스트에 더할 수 있고,end
메소르를 사용하여 리퀘스트를 끝낼 수 있다. (GET
리퀘스트는 바디를 포함하지 않으므로 예제에서는 사용되지 않았다.) - There’s a similar
request
function in thehttps
module that can be used to make requests tohttps:
URLs.https: URL
에 리퀘스트하기 위한https
모듈의request
함수도 이와 비슷하다. - Making requests with Node’s raw functionality is rather verbose. There are much more convenient wrapper packages available on NPM. For example,
node-fetch
provides the promise-basedfetch
interface that we know from the browser.노드의 기본 기능을 이용하여 리퀘스트를 만드는 건 꽤나 장황한 일이다. NPM에는 훨씬 더 편리한 래퍼 패키지들이 존재한다. 예를 들어, 브라우저에서 사용하는 프로미스 기반의
fetch
인터페이스를 제공하는node-fetch
패키지가 있다.
Streams
- We have seen two instances of writable streams in the HTTP examples—namely, the response object that the server could write to and the request object that was returned from
request
.스트림; 리스폰스 객체와 리퀘스트 객체
- Writable streams are a widely used concept in Node. Such objects have a
write
method that can be passed a string or aBuffer
object to write something to the stream. Theirend
method closes the stream and optionally takes a value to write to the stream before closing. Both of these methods can also be given a callback as an additional argument, which they will call when the writing or closing has finished.쓸 수 있는 스트림은 노드에서 널리 사용되는 개념이다. 그러한 객체들은
write
메소드를 통해 문자열이나Buffer
객체를 스트림에 쓸 수 있다.end
메소드는 스트림을 닫는데 닫기 전에 쓸 값을 선택적으로 받는다. 이러한 메소드들은 콜백 함수를 추가로 받을 수 있는데, 이 콜백들은 쓰기나 닫기가 완료되었을 때 호출된다. - It is possible to create a writable stream that points at a file with the
createWriteStream
function from thefs
module. Then you can use the write method on the resulting object to write the file one piece at a time, rather than in one shot as withwriteFile
.fs
모듈의createWriteStream
은 파일을 가리키는 스트림을 생성한다. (cf.writeFile
) - Readable streams are a little more involved. Both the
request
binding that was passed to the HTTP server’s callback and theresponse
binding passed to the HTTP client’s callback are readable streams. - Reading from a stream is done using event handlers, rather than methods.
스트림에서 읽는 것은 메소드가 아니라 이벤트 핸들러를 사용해서 가능하다.
- Objects that emit events in Node have a method called
on
that is similar to theaddEventListener
method in the browser. You give it an event name and then a function, and it will register that function to be called whenever the given event occurs.노드의
on
메소드는 브라우저의addEventListener
와 비슷하다. - Readable streams have
"data"
and"end"
events. The first is fired every time data comes in, and the second is called whenever the stream is at its end. This model is most suited for streaming data that can be immediately processed, even when the whole document isn’t available yet. A file can be read as a readable stream by using thecreateReadStream
function fromfs
.읽을 수 있는 스트림은
"data"
와"end"
이벤트를 갖는다."data"
는 데이터가 들어올 때마다 발생하고"end"
는 스트림이 끝날 때마다 호출된다. 이 모델은 즉각적으로 처리될 수 있는 스트리밍 데이터에 가장 적합하다. 파일은fs
의createReadStream
을 사용해서 스트림으로 읽힐 수 있다. -
const {createServer} = require("http"); createServer((request, response) => { response.writeHead(200, {"Content-Type": "text/plain"}); request.on("data", chuck => { response.write(chuck.toString().toUpperCase()); }); request.on("end", () => response.end()); }).listen(8000);
- The
chunk
value passed to the data handler will be a binaryBuffer
. We can convert this to a string by decoding it as UTF-8 encoded characters with itstoString
method.data 핸들러에 전달되는
chuck
는 이진Buffer
인데, 이는Buffer
의toString
메소드를 사용해서 UTF-8 문자들로 복호화된 문자열로 바꿀 수 있다. - The following piece of code, when run with the uppercasing server active, will send a request to that server and write out the response it gets:
-
const {request} = require("http"); request({ hostname: "localhost", port: 8000, method: "POST" }, response => { response.on("data", chuck => { process.stdout.write(chuck.toString()); }); }).end("Hello server");
- The example writes to
process.stdout
(the process’s standard output, which is a writable stream) instead of usingconsole.log
. We can’t useconsole.log
because it adds an extra newline character after each piece of text that it writes, which isn’t appropriate here since the response may come in as multiple chunks.쓸 수 있는 스트림인
process.stdout
A file server
-
Let’s combine our newfound knowledge about HTTP servers and working with the file system to create a bridge between the two: an HTTP server that allows remote access to a file system. Such a server has all kinds of uses—it allows web applications to store and share data, or it can give a group of people shared access to a bunch of files.
-
When we treat files as HTTP resources, the HTTP methods
GET
,PUT
, andDELETE
can be used to read, write, and delete the files, respectively. We will interpret the path in the request as the path of the file that the request refers to.파일을 HTTP 리소스로 다룰 때,
GET
,PUT
,DELETE
메소드는 각각 읽기, 쓰기, 삭제를 위해 사용될 수 있다. -
We probably don’t want to share our whole file system, so we’ll interpret these paths as starting in the server’s working directory, which is the directory in which it was started. If I ran the server from
/tmp/public/
, then a request for/file.txt
should refer to/tmp/public/file.txt
. -
We’ll build the program piece by piece, using an object called
methods
to store the functions that handle the various HTTP methods. Method handlers areasync
functions that get the request object as argument and return a promise that resolves to an object that describes the response. -
const {createServer} = require("http"); const methods = Object.create(null); createServer((request, response) => { let handler = methods[request.method] || notAllowed; handler(request) .catch(error => { if (error.status != null) return error; return {body: String(error), status: 500}; }) .then(({body, status = 200, type = "text/plain"}) => { response.writeHead(status, {"Content-Type": type}); if (body && body.pipe) body.pipe(response); // readable stream 끝에 도달하면 .end() 자동으로 호출 else response.end(body); }); }).listen(8000); async function notAllowed(request) { return { status: 405, body: `Method ${request.method} not allowed.` }; }
-
This starts a server that just returns 405 error responses, which is the code used to indicate that the server refuses to handle a given method.
405 에러; 해당 메소드를 거절한다.
-
When the value of
body
is a readable stream, it will have apipe
method that is used to forward all content from a readable stream to a writable stream. If not, it is assumed to be eithernull
(no body), a string, or a buffer, and it is passed directly to the response’s end method.body
가 읽을 수 있는 스트림일 때, 이는 읽을 수 있는 스트림으로부터 모든 내용을 쓸 수 있는 스트림으로 전달할 수 있는pipe
메소드를 가질 것이다. -
To figure out which file path corresponds to a request URL, the
urlPath
function uses Node’s built-inurl
module to parse the URL. It takes its pathname, which will be something like"/file.txt"
, decodes that to get rid of the%20
-style escape codes, and resolves it relative to the program’s working directory.url
내장 모듈, ... -
const {parse} = require("url"); const {resolve, sep} = require("path"); const baseDirectory = process.cwd(); function urlPath(url) { let {pathname} = parse(url); let path = resolve(decodeURIComponent(pathname).slice(1)); if (path != baseDirectory && !path.startsWith(baseDirectory + sep)) { throw {status: 403, body: "Forbidden"}; } return path; }
-
As soon as you set up a program to accept network requests, you have to start worrying about security. In this case, if we aren’t careful, it is likely that we’ll accidentally expose our whole file system to the network.
-
/../secret_file
-
To avoid such problems,
urlPath
uses theresolve
function from the path module, which resolves relative paths. ... Theprocess.cwd
function can be used to find this working directory. Thesep
binding from thepath
package is the system’s path separator. ... the HTTP status code indicating that access to the resource is forbidden.path
모듈의resolve
,sep
&process.cwd
, 403 forbidden -
We’ll set up the
GET
method to return a list of files when reading a directory and to return the file’s content when reading a regular file. -
One tricky question is what kind of
Content-Type
header we should set when returning a file’s content. Since these files could be anything, our server can’t simply return the same content type for all of them. NPM can help us again here. Themime
package (content type indicators liketext/plain
are also called MIME types) knows the correct type for a large number of file extensions.파일은 어떤 것이라도 될 수 있는데, 그렇다면
Content-type
를 어떻게 정해야하는가?: NPM의mime
패키지를 사용한다; 이는 많은 확장자들에 대해 정확한 MIME 타입을 알려준다. -
When a requested file does not exist, the correct HTTP status code to return is 404. We’ll use the
stat
function, which looks up information about a file, to find out both whether the file exists and whether it is a directory.404 Not found,
stat
은 파일의 정보를 찾아보는 함수인데, 이를 사용하여 파일이 존재하는지, 그리고 디렉터리인지를 확인한다. -
const {createReadStream} = require("fs"); const {stat, readdir} = require("fs").promises; const mime = require("mime"); methods.GET = async function(request) { let path = urlPath(request.url); let stats; try { stats = await stat(path); } catch (error) { if (error.code != "ENOENT") throw error; else return {status: 404, body: "File not found"}; } if (stats.isDirectory()) { return {body: (await readdir(path)).join("\n")}; } else { return {body: createReadStream(path), type: mime.getType(path)}; } };
-
When the file does not exist,
stat
will throw an error object with acode
property of"ENOENT"
. These somewhat obscure, Unix-inspired codes are how you recognize error types in Node.stat
은 파일이 존재하지 않을 때code
프로퍼티의 값이"ENOENT"
인 에러 객체를 던진다. -
The
stats
object returned bystat
tells us a number of things about a file, such as its size (size
property) and its modification date (mtime
property).stat
에 의해 반환된 객체는 파일에 대한 여러 정보를 담고 있다. (size
,mtime
,isDirectory
, ...) -
We use
readdir
to read the array of files in a directory and return it to the client. For normal files, we create a readable stream withcreateReadStream
and return that as the body, along with the content type that themime
package gives us for the file’s name.readdir
,createReadStream
,mime
-
const {rmdir, unlink} = require("fs").promises; methods.DELETE = async function(request) { let path = urlPath(request.url); let stats; try { stats = await stat(path); } catch (error) { if (error.code != "ENOENT") throw error; else return {status: 204}; } if (stats.isDirectory()) await rmdir(path); else await unlink(path); return {status: 204}; }
-
When an HTTP response does not contain any data, the status code 204 (“no content”) can be used to indicate this. Since the response to deletion doesn’t need to transmit any information beyond whether the operation succeeded, that is a sensible thing to return here.
HTTP 리스폰스가 어떠한 데이터도 포함하지 않을 때, 상태 코드 204가 사용된다.
-
You may be wondering why trying to delete a nonexistent file returns a success status code, rather than an error. When the file that is being deleted is not there, you could say that the request’s objective is already fulfilled. The HTTP standard encourages us to make requests idempotent, which means that making the same request multiple times produces the same result as making it once. In a way, if you try to delete something that’s already gone, the effect you were trying to do has been achieved—the thing is no longer there.
HTTP 표준은 리퀘스트가 멱등적이기를 권장한다.
-
const {createWriteStream} = require("fs"); function pipeStream(from, to) { return new Promise((resolve, reject) => { from.on("error", reject); to.on("error", reject); to.on("finish", resolve); from.pipe(to); }); } methods.PUT = async function(request) { let path = urlPath(request.url); await pipeStream(request, createWriteStream(path)); return {status: 204}; };
-
But since
pipe
isn’t written to return a promise, we have to write a wrapper,pipeStream
, that creates a promise around the outcome of callingpipe
. -
When something goes wrong when opening the file,
createWriteStream
will still return a stream, but that stream will fire an"error"
event. The output stream to the request may also fail, for example if the network goes down. So we wire up both streams’"error"
events to reject the promise. When pipe is done, it will close the output stream, which causes it to fire a"finish"
event. That’s the point where we can successfully resolve the promise (returning nothing)."error"
,"finish"
-
The command line tool
curl
, widely available on Unix-like systems (such as macOS and Linux), can be used to make HTTP requests. The following session briefly tests our server. The-X
option is used to set the request’s method, and-d
is used to include a request body.Unix-like systems의
curl
: HTTP 리퀘스트를 만드는데 사용될 수 있다.-X
옵션은 리퀘스트의 메소드를,-d
옵션은 리퀘스트의 바디를 설정하는데 사용할 수 있다.
Summary
- Node is a nice, small system that lets us run JavaScript in a nonbrowser context. It was originally designed for network tasks to play the role of a node in a network. But it lends itself to all kinds of scripting tasks, and if writing JavaScript is something you enjoy, automating tasks with Node works well.
- All input and output in Node is done asynchronously, unless you explicitly use a synchronous variant of a function, such as
readFileSync
. When calling such asynchronous functions, you provide callback functions, and Node will call them with an error value and (if available) a result when it is ready.노드의 모든 입출력은 비동기적으로 수행된다(동기적 버전을 사용하지 않는다면).
Exercises
'Programming > JavaScript' 카테고리의 다른 글
String primitive, String object, valueOf(), template literal ... (0) | 2020.07.23 |
---|---|
[EloquentJS] Ch19. Project: A Pixel Art Editor (0) | 2020.06.10 |
[EloquentJS] Ch18. HTTP and Forms (0) | 2020.05.21 |
[EloquentJS] Ch17. Drawing on Canvas (0) | 2020.05.21 |
[EloquentJS] Ch16. Project: A Platform Game (0) | 2020.05.21 |