chapter20. 노드

20.1 노드의 기초

브라우저 기반 자바스크립트는 브라우저에만 해당하는 API를 사용합니다.
노드에는 DOM이 없습니다. HTML이 없으니까요.
노드에만 해당하고 브라우저에는 존재하지 않는 API도 있습니다.
window와 document는 브라우저 환경에서 제공하는 API 입니다.
이 장에서는 노드에서 제공하는 API에 대해 설명합니다.

20.2 모듈

모듈은 패키지를 만들고 코드를 네임스페이스(namespace)로 구분하는 메커니즘이다.
네임스페이스는 이름 충돌을 방지하는 방법이다.

amanda.js
1
2
3
4
5
6
7
8
// ## 20.2 모듈
//아만다가 만든 calculate
function calculate(a, x, n) {
if (x === 1) return a * n;
return (a * (1 - Math.pow(x, n))) / (1 - x);
}
//내보내기
module.exports = calculate;
tyler.js
1
2
3
4
5
6
7
// ## 20.2 모듈
//타일러가 만든 calculate
function calculate(r) {
return (4 / 3) * Math.PI * Math.pow(r, 3);
}
//내보내기
module.exports = calculate;
app.js
1
2
3
4
5
6
//이들 모듈을 임포트함
const amanda_calculate = require("./amanda.js");
const tyler_calculate = require("./tyler.js");

console.log(amanda_calculate(1, 2, 5)); //31
console.log(tyler_calculate(2)); //33.510321638291124
  • module은 노드에서 모듈을 구현하기 위해 만든 특별한 객체이다.
  • export 프로퍼티에 무엇을 할당하든, 모듈은 그것을 내보낸다.
  • 모듈은 어떤 타입의 값이든 내보낼 수 있다.

20.3 코어 모듈, 파일 모듈, npm 모듈

모듈은 코어 모듈, 파일 모듈, npm 모듈 3가지로 나뉜다.
코어 모듈 : fs나 os처럼 노드 자체에서 제공 하는 모듈, 모두 예약어 이다.
파일 모듈 : module.exports 에 할당되는 파일을 만들고 그 파일을 불러옴
npm 모듈 : 특별한 디렉터리 node_modules에 저장되는 모듈 파일
require 함수를 사용하면 노드는 함수의 매개변수를 보고 어떤 타입인지 판단한다.

표 20-1 모듈 타입

  • 코어모듈
    process나 buffer 같은 일부 코어 모듈은 전역이고, 명시적인 require 문 없이도 사용할 수 있다.

표 20-2 코어 모듈

표 20-2 코어 모듈

모듈에 대한 상세한 API 문서(link: https://nodejs.org/api/) 를 참고

  • npm 모듈
    npm 모듈은 특수한 이름 표기법을 사용하는 파일 모듈이다.
    node_modules 디렉터리에 직접적으로 작업하지말고, 모듈 파일 관련 작업은 npm에서 하도록 해야한다.

20.4 함수 모듈을 통한 모듈 커스터마이징

모듈은 대부분 객체를 내보내지만, 가끔 함수 하나만 내보내기도 한다.
이럴 경우 그 모듈의 함수를 즉시 호출하기 위해 사용한다.
그 함수가 아닌, 함수가 반환하는 것을 쓰게 한 것이다.

1
2
const debug = require("debug")("main"); //모듈이 반환하는 함수를 즉시 호출 할 수 있다.
debug("starting"); //디버그가 활성화되어 있으면 main starting + 0ms라는 로그를 남긴다.
debug 모듈이 반환한 것을 즉시 호출했으므로 debug 모듈이 함수를 반환한다는 것을 알 수 있고,
반환값인 함수 역시 함수를 반환하며 최종적으로 반환된 함수는 첫 번째 함수에 넘긴 문자열을 기억한다.
  • 노드는 노드 앱을 실행할 때 어떤 모듈이든 단 한번만 임포트한다.

20.5 파일시스템 접근

자바스크립트는 노드가 만들어지기 전까지는 파일시스템에 접근 할 수 없었다.

  • 파일을 생성하고 쓸 때는 fs.writeFile()을 사용합니다.
writeFile
1
2
3
4
5
6
7
//파일 생성하고 쓰기
const fs = require("fs");
fs.writeFile("hello.txt", "hello from node!", function(err) {
if (err) {
return console.log("error");
}
});
write.js 파일을 저장한 디렉터리에 쓰기 권한이 있고, 
hello.txt 파일이 생성되어 글자가 써진다.
해당 애플리케이션은 자신이 실행된 현재 디렉터리를 __dirname 변수로 보관한다.
이 변수를 사용해서 write.js 파일을 고쳐 쓸 수 있다.
__dirname
1
2
3
4
5
6
7
8
9
10
11
12
const fs = require("fs");

fs.writeFile(__dirname + "/hello.txt", "hello from node!", function(err) {
if (err) return console.log("error writing to file");
});

//path.join
fs.writeFile(path.join(__dirname, "/hello.txt"), "hello from node!", function(
err
) {
if (err) return console.log("error writing to file");
});
이제 write.js가 있는 디렉터리에 hello.txt를 만든다.
__dirname은 특정 운영체제에 따라 호환되지 않을 수 있다.
이럴 경우 노드의 path.join을 사용하면 모든 운영체제에서 호환 가능하다. 
  • 파일을 읽을때는 fs.readFile()을 사용합니다.
readFile
1
2
3
4
5
6
7
const fs = require("fs");
var path = require("path");
fs.readFile(path.join(__dirname, "hello.txt"), function(err, data) {
if (err) return console.error("error");
console.log("read file contents");
console.log(data);
});
fs.readFile()는 가공되지 않은 바이너리 데이터인 버퍼를 반환한다.
기본 문자열 인코딩은 UTF-8이다.
인코딩을 지정하면 원하는 결과를 얻을 수 있다.
UTF-8
1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require("fs");
var path = require("path");
fs.readFile(path.join(__dirname, "hello.txt"), { encoding: "utf8" }, function(
err,
data
) {
if (err) return console.error("error");
console.log("read file contents");
console.log(data);
});
/*
결과 :
*/

파일 관련 함수에는 모두 동기적으로 작업하는 짝이 있으며,
이들 이름은 Sync로 끝난다.

1
2
3
4
5
6
7
8
9
//파일 쓰기
fs.writeFileSync(path.join(__dirname, "hello.txt"), "hi");

//
try {
fs.readFileSync(path.join(__dirname, "hello.txt"), "hi~");
} catch (err) {
console.err("error");
}
  • 디렉터리에 어떤 파일이 있는지 알아보려면 fs.readdir을 사용합니다.
readdir
1
2
3
4
5
6
const fs = require("fs");
fs.readdir(__dirname, function(err, files) {
if (err) return console.err("err");
console.log(`${__dirname}:`);
console.log(files.map(f => "\t" + f).join("\n"));
});
  • fs 모듈에는 이외에도
  • 파일을 지우는 fs.unlink();
  • 파일을 옮기거나 이름을 바꾸는 fs.rename(),
  • 파일과 디렉터리의 정보를 얻는 fs.stat(),
    등이 있으며 자세한 정보는 link(https://nodejs.org/api/fs.html) 을 참고

20.6 process

실행 중인 노드 프로그램은 모두 process 변수에 접근 가능
이 변수는 해당 프로그램에 관한 정보를 담고 있으며 실행 자체를 컨트롤 할 수 있다.
예를 들어, 애플리케이션이 치명적 에러를 만나서 실행하지 않는 편이 좋으면 (fatal error) process.exit를 호출해 즉시 실행을 멈춘다.
숫자형 종류 코드를 쓰면 프로그램이 성공적으로 종료됐는지, 에러가 있었는지 외부 스크립트에서 알 수 있다.
보통 에러없이 프로그램을 끝냈다면 종료코드 0을 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require("fs");
fs.readdir("data", function(err, files) {
if (err) {
console.error("fatal error");
process.exit(1);
}
const txtFiles = files.filter(f => /\.txt$/i.test(f));
if (txtFiles.length === 0) {
console.log("no files to process");
process.exit(0);
}
//.txt 파일 처리
});

process 객체를 통해 프로그램에 전달된 명령줄 매개변수 배열에 접근 할 수도 있다.
명령줄 매개변수는 process.argv() 배열에 저장된다.

process.env()를 통해 환경 변수에 접근 할 수 있다.
환경 변수는 시스템 변수이며 주로 명령줄 프로그램에서 사용한다.
export VAR_NAME=value 명령으로 환경변수를 설정할 수 있다.
환경변수는 보통 전부 대문자로 표현한다.

1
2
3
const debug = process.env.DEBUG === "1" ? console.log : function() {};

debug("visible only if environment variable DEBUG is set");
이 예제에서 debug 함수는 환경 변수 DEBUG가 1이면 console.log의 별칭이되고, 그렇지 않으면 null 함수가 된다. null 함수를 만들지 않으면 debug 가 정의되지 않는 경우가 생기고, 에러가 발생한다.

process.cmd 에서는 현재 작업 디렉터리가 저장되며,
process.chdir로 현재 작업 디렉터리로 바꿀 수 있습니다.

1
2
3
4
5
6
7
8
9
//프로그램에서 현재 작업 디렉터리를 호출한다음, 프로그램이 저장된 디렉터리를 현재 작업 디렉터리로 바꾸는 경우
console.log(`current directory ${process.cwd()}`);
process.chdir(__dirname);
console.log(`new directory ${process.cwd()}`);
/*
current directory C:\Users\moong\Desktop\소뭉이\komos_node\1day\sunday01
new directory C:\Users\moong\Desktop\소뭉이\komos_node\1day\sunday01

*/

20.7 운영체제

os 모듈은 프로그램을 실행하는 컴퓨터의 운영체제에 관한 정보를 제공합니다.

os모듈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const os = require("os");
console.log("hostname:" + os.hostname());
console.log("os type:" + os.type());
console.log("os platform:" + os.platform());
console.log("os release:" + os.release());
console.log("cpu uptime:" + (os.uptime() / 60 / 60 / 24).toFixed(1));
console.log("cpu architecture:" + os.arch());
console.log("cpu 갯수:" + os.cpus().length);
console.log("총 메모리:" + (os.totalmem() / 1e6).toFixed(1));
console.log("여유 메모리:" + (os.freemem() / 1e6).toFixed(1));
/*
hostname:DESKTOP-EN75874
os type:Windows_NT
os platform:win32
os release:10.0.17763
cpu uptime:7.0
cpu architecture:x64
cpu 갯수:8
총 메모리:8458.6
여유 메모리:3421.3
Program exited with code 0
*/

20.8 자식 프로세스

child_process 모듈은 애플리케이션에서 다른 프로그램을 실행할 때 사용.
주요함수는 exec, execFile,fork 입니다.
exec와 execFile은 운영체제에서 지원하는 실행파일은 무엇이든 실행할 수 있습니다.
exec는 명령줄에서 실행할 수 있는 것은 다 실행할 수 있다.
execFile은 셸을 통하지 않고 실행파일을 직접 실행하므로 효율적이지만 더 주의해야 할 점이 있다.
fork는 다른 노드스크립트를 실행할 때 사용한다

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
const exec = require("child_process").exec;
exec("dir", function(err, stdout, stderr) {
if (err) return console.err('error execiting "dir"');
stdout = stdout.toString(); //Buffer을 문자열로 바꿈
console.log(stdout);
stderr = stderr.toString();
if (stderr !== "") {
console.error("error:");
console.error(stderr);
}
});

/*

호출결과
Command: node "C:/Users/moong/Desktop/소뭉이/komos_node/1day/sunday01/linecount.js"
Volume in drive C has no label.
Volume Serial Number is F8DB-AEAD
Directory of C:\Users\moong\Desktop\소뭉이\komos_node\1day\sunday01
2019-08-26 오후 11:27
.
2019-08-26 오후 11:27
..
2019-08-26 오후 10:24 132 hello.js
2019-08-26 오후 10:49 3 hello.txt
2019-08-30 오후 08:51 368 linecount.js
2019-08-26 오후 09:46
node_modules
2019-08-26 오후 09:46 19,130 package-lock.json
2019-08-26 오후 09:46 278 package.json
2019-08-26 오후 10:49 389 read.js
6 File(s) 20,300 bytes
3 Dir(s) 139,629,719,552 bytes free
Program exited with code 0
*/

20.9 스트림

스트림은 스트림 형태의 데이터를 다루는 객체이다.
스트림은 흐름같은 느낌이고, 흐름은 비동기적으로 이루워질것이라 짐작됨
스트림에서는 읽기(read) 스트림, 쓰기(write) 스트림, 이중(duplex) 스트림이 있다.

쓰기 스트림
1
2
3
4
5
6
7
8
9
10
11
12
const fs = require("fs");
const ws = fs.createWriteStream("stream.txt", { encoding: "utf8" });
ws.write("line1\n");
ws.write("line2\n");
ws.end();
/*
결과
stream.txt 생성
line1
line2
라는 텍스트가 써짐
*/

end()를 호출해서 쓰기 스트림(ws)를 종료하기 전까진 write 메서드를 통해 스트림에 쓸 수 있다.
end를 호출한 다음 write를 호출하면 에러가 발생한다.

마찬가지로 읽기 스트림을 만들어서 들어오는 데이터를 읽을 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const fs = require("fs");
const ws = fs.createWriteStream("stream.txt", { encoding: "utf8" });
ws.write("line1\n");
ws.write("line2\n");
ws.end();

const rs = fs.createReadStream("stream.txt", { encoding: "utf8" });
rs.on("data", function(data) {
//줄바꿈 문자 이스케이프
console.log(">> data :" + data.replace("\n", "\\n"));
});

rs.on("end", function(data) {
console.log(">> end");
});
/*
호출결과
>> data :line1\n
>> data :line2\n
>> end
*/

데이터가 스트림을 ‘흐른다’는 표현을 보면 읽기 스트림에서 데이터를 읽는 즉시 쓰기 스트림에서 쓸 수 있다. 이런 작업을 파이프(pipe)라고 한다.
예를 들어, 읽기 스트림과 쓰기 스트림을 파이프로 연결하면 파일 콘텐츠를 복사하는 효과가 있다.

1
2
3
4
5
6
const fs = require("fs");
const rs = fs.createReadStream("stream.txt");
const ws = fs.createWriteStream("stream_copy.txt");
rs.pipe(ws);

//stream.txt에 있는 내용을 읽어서 stream_copy.txt를 생성하고 그 내용을 복사함
위 예제는 따로 인코딩을 명시할 필요없이 rs는 그냥 stream.txt의 데이터를 ws 파이프에 연결하고, ws는 그 데이터를 그대로 steam_copy.txt에 기록할 뿐이다.

20.10 웹서버

노드는 원래 웹서버를 만드는 것이 목적이였다.
http 모듈(보안 연결을 제공하는 https 모듈 역시)에는 기본적인 웹 서버를 만드는 createServer 메서드가 있다.들어오는 요청을 처리할 콜백 함수만 만들면 된다.
서버를 시작할 떈 listen 메서드를 호출하면 포트를 지정한다.

웹서버
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const http = require("http");
const server = http.createServer(function(req, res) {
console.log(`${req.method} ${req.url}`);
res.end("hello world");
});

const port = 8080;
server.listen(port, function() {
console.log(`server started on port ${port}`);
//server started on port 8080
});

/*
node cmd 에 찍히는 화면
GET /
GET /favicon.ico
*/
노드를 실행하면, 브라우저 http://localhost:8080/ 에 hello world가 보인다.
터미널을 보면 모든 요청이 기록되어 있다.
요청은 메서드(여기서는 GET)과 URL 경로로 구성된다. 브라우저에서 그 URL을 방문할 때마다 요청 두 개가 기록되는 것을 보고 의이할 수 있다.
대부분의 브라우저는 요청을 보낼 때 url 막대 또는 탭에 표시할 아이콘인 파비콘을 요청한다.

노드 웹 서버서의 핵심은 들어오는 요청에 모두 응답하는 콜백함수 이다.
이 함수는 매개변수로 IncomingMessage객체(req)와 **ServerRequest객체(res)를 받는다.
IncomingMessage 객체에는 요청받은 url, 보낸 헤더, 바디에 들어있던 데이터 등 http 요청에 관한 모든 정보가 들어있다.
ServerResponse 객체에는 클라이언트(보통 브라우저)에 보낼 응답을 컨트롤하는 프로퍼티와 메서드가 들어있다.
ServerResponse 객체는 쓰기 스트림 인터페이스이며, 이를 통해 데이터 클라이언트에 보낸다.

1
2
3
4
5
6
7
8
9
10
const server = http.createServer(function(req, res) {
if (req.method === "GET" && req.url === "favicon.ico") {
const fs = require("fs");
fs.createReadStream("favicon.ico");
fs.pipe(res); //end 대신 사용할 수 있다.
} else {
console.log(`${req.method} ${req.url}`);
res.end("hello world");
}
});

20.11 요약

fs,Buffer, process, stream 등 거의 모든 애플리케이션에서 사용하는 API는 이 장에서 소개했지만, 그외에 알아야할 API는 공식문서를 참고
link(https://nodejs.org/en/docs/)

Comentarios

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×