๐Ÿ’ป Programming

[ Node.js in Action ] 13์žฅ ์š”์•ฝ

/**
 *  ์›น์„œ๋ฒ„ ๊ทธ ์ด์ƒ์˜ ๊ฒƒ๋“ค
 * - Socket IO
 * - TCP/IP ๋„คํŠธ์›Œํ‚น
 * - OS์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•œ ํˆด
 * - ์ปค๋งจ๋“œ๋ผ์ธ ํˆด ๊ฐœ๋ฐœํ•˜๊ธฐ
 */


13.1. Socket.io


Socket IO ๋Š” WebSocket๊ณผ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉด์„œ๋„, broadcasting, volatile ๋ฉ”์‹œ์ง•์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

WebSocket์€ WebSocket ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์•„์ง ์™„์„ฑ๋œ ํ”„๋กœํ† ์ฝœ์ด ์•„๋‹ˆ๋ผ์„œ ๋ธŒ๋ผ์šฐ์ € ๋งˆ๋‹ค ์ง€์›์—ฌ๋ถ€์—๋„ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Socket IO๋ฅผ ์ด์šฉํ•  ๊ฒฝ์šฐ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, Socket IO๋Š” WebSocket์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฒฝ์šฐ์— ๊ฐ ๋ธŒ๋ผ์šฐ์ €๋ณ„๋กœ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.


๏‚ก A minimal Socket.IO application that pushes the serverโ€™s time to connected clients
๏‚ก A Socket.IO application that triggers page refreshes when CSS files are edited


13.1.1 ์ดˆ๊ฐ„๋‹จ Socket.IO ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋งŒ๋“ค๊ธฐ


<< ๊ฐ„๋‹จํ•œ Socket IO clock-server ํ”„๋กœ๊ทธ๋žจ >> ( ์‹œ๊ฐ„์ด ํ‘œ์‹œ๊ฐ€ ์•ˆ๋˜๋Š”๋ฐ ์ด์œ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์Œ ;;;; )


13.1.2 Socket.IO๋ฅผ ์ด์šฉํ•œ ํŽ˜์ด์ง€์™€ CSS reloading ํ•˜๊ธฐ


์›นํŽ˜์ด์ง€ ๋””์ž์ธ์„ ํ•  ๋•Œ ์ผ๋ฐ˜์ ์ธ ์ˆœ์„œ

1 Open the web page in multiple browsers.
2 Look for styling on the page that needs adjusting.
3 Make changes to one or more stylesheets.
4 Manually reload all the web browsers.
5 Go back to step 2.


<< ์œ„ 4๋ฒˆ ๊ณผ์ •์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ”„๋กœ๊ทธ๋žจ ์˜ˆ์ œ >> ( ์ด๋†ˆ๋„ ์ œ๋Œ€๋กœ ๋™์ž‘์„ ํ•˜์ง€ ์•Š์Œ...;;;)


13.1.3 Socket.IO์˜ ๋˜ ๋‹ค๋ฅธ ์‚ฌ์šฉ๋ฒ•


Socket.IO๋Š” ํ”„๋กœ๊ทธ๋ ˆ์Šค ๋ฐ”๋ฅผ ๊ณ„์† ์—…๋ฐ์ดํŠธํ•ด์„œ ์ˆ˜์น˜๊ฐ€ ์˜ฌ๋ผ๊ฐ€๋Š” ๊ฒƒ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ์— ์ ํ•ฉํ•จ




13.2 TCP/IP ๋„คํŠธ์›Œํ‚น


๋…ธ๋“œ๋Š” ๋„คํŠธ์›Œํ‚น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋งค์šฐ ์ ํ•ฉ. ๋„คํŠธ์›Œํ‚น์€ ๋งŽ์€ I/O๋ฅผ ์š”๊ตฌํ•˜๊ธฐ ๋•Œ๋ฌธ.


๏‚ก Working with buffers and binary data
๏‚ก Creating a TCP server
๏‚ก Creating a TCP client


13.2.1 Buffers์™€ Binary Data ๋‹ค๋ฃจ๊ธฐ


Buffer ๋ฐ์ดํƒ€ ํƒ€์ž…์€ ๋…ธ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” ํŠน๋ณ„ํ•œ ๋ฐ์ดํƒ€ ํƒ€์ž…์ด๋‹ค.

๊ณ ์ • ๊ธธ์ด์˜ raw binary data์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋ฉฐ, C์—์„œ์˜ malloc()์ด๋‚˜ C++, JAVA์—์„œ์˜ new ํ‚ค์›Œ๋“œ๋ผ๊ณ  ์ƒ๊ฐํ•ด๋„ ๋œ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ Stream ํด๋ž˜์Šค์— ์˜ํ•ด ๋ฐ์ดํƒ€ ์ด๋ฒคํŠธ ๋‚ด์— ๋ฐ˜ํ™˜๋œ๋‹ค.

Buffer๋Š” ์ˆซ์ž 0~ 255๊นŒ์ง€๋งŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” non-resizable ๋ฐฐ์—ด์ด๋‹ค.


TEXT DATA VS. BINARY DATA

1. -----------------------------------

var b = new Buffer("121234869");
console.log(b.length);
9
console.log(b);
<Buffer 31 32 31 32 33 34 38 36 39>


2. -----------------------------------

var b = new Buffer(4);
b.writeInt32LE(121234869, 0);
console.log(b.length);
4
console.log(b);
<Buffer b5 e5 39 07>

-----------------------------------


13.2.2 TCP ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ 


Socket ํด๋ž˜์Šค๋Š” client์™€ server ์–‘์ชฝ์—์„œ ๋ชจ๋‘ ์‚ฌ์šฉ๋จ

Itโ€™s a Stream subclass thatโ€™s both readable and writable (bidirectional).

That is, it emits data events when input data has been read from the socket,

and it has write() and end() functions for sending output data.


WRITING DATA


var net = require('net');
net.createServer(function (socket) {
socket.write('Hello World!\r\n');
socket.end();
}).listen(1337);
console.log('listening on port 1337');


$ telnet localhost 1337


READING DATA


socket.on('data', function (data) {
console.log('got "data"', data);
});


data ์ธ์ž๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Buffer์ธ์Šคํ„ด์Šค์ด๋‹ค.

setEncoding()์„ ์ด์šฉํ•ด์„œ decoded String์„ ์ธ์ž๋กœ ๋„˜๊ธธ ์ˆ˜๋„ ์žˆ๋‹ค.


socket.on('end', function () {
console.log('socket has ended');
});


end ์ด๋ฒคํŠธ๋ฅผ ๋ฆฌ์Šค๋‹ํ•จ์œผ๋กœ์จ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก์„ ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.


SAMPLE TEST  


var net = require('net');
var socket = net.connect({ host: process.argv[2], port: 22 });
socket.setEncoding('utf8');
socket.once('data', function (chunk) {
console.log('SSH server version: %j', chunk.trim());
socket.end();
});


$ node client.js github.com


SOCKET.PIPE() ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‘ ๊ฐœ์˜ ์ŠคํŠธ๋ฆผ ์—ฐ๊ฒฐํ•˜๊ธฐ


The pipe() ํ•จ์ˆ˜๋Š” readable ์ŠคํŠธ๋ฆผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์™€์„œ ์“ฐ๊ธฐ๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋ฆผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.


๋น„์ •์ƒ ์—ฐ๊ฒฐ๋Š๊น€ ํ•ธ๋“ค๋งํ•˜๊ธฐ

ํด๋ผ์ด์–ธํŠธ๊ฐ€ netcat์„ ์ผ์„ ๋•Œ Ctrl-D ๋Œ€์‹  Ctrl-C๋กœ ์ปค๋„ฅ์…˜์„ ์ข…๋ฃŒ์‹œํ‚ค๋ฉด ์„œ๋ฒ„์ชฝ ์†Œ์ผ“์ด ๊น”๋”ํ•˜๊ฒŒ ์ข…๋ฃŒ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋ฐ, ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•œ ๋’ท์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ close ์ด๋ฒคํŠธ๋ฅผ ๋ฆฌ์Šค๋‹ํ•˜๋ฉด ๋œ๋‹ค.


socket.on('close', function () {
console.log('client disconnected');
});


FINAL TEST APP


server


var net = require('net');
net.createServer(function (socket) {
console.log('socket connected!');
socket.on('data', function (data) {
console.log('"data" event', data);
});
socket.on('end', function () {
console.log('"end" event');
});
socket.on('close', function () {
console.log('"close" event');
});
socket.on('error', function (e) {
console.log('"error" event', e);
});
socket.pipe(socket);
}).listen(1337);


client


var net = require('net');
var host = process.argv[2];
var port = Number(process.argv[3]);
var socket = net.connect(port, host);
socket.on('connect', function () {
process.stdin.pipe(socket);
socket.pipe(process.stdout);
process.stdin.resume();
});
socket.on('end', function () {
process.stdin.pause();
});



13.3 Tools for interacting with the operating system


๏‚ก The global process objectโ€”Contains information about the current process, such
as the arguments given to it and the environment variables that are currently set
๏‚ก The fs moduleโ€”Contains the high-level ReadStream and WriteStream classes
that youโ€™re familiar with by now, but also houses low-level functions that weโ€™ll
look at
๏‚ก The child_process moduleโ€”Contains both low-level and high-level interfaces
for spawning child processes, as well as a special way to spawn node instances
with a two-way message-passing channel


13.3.1 The process global singleton


Every Node process has a single global process object that every module shares access
to. Useful information about the process and the context itโ€™s running in can be found
in this object.


the most interesting feature of the process object is that itโ€™s an EventEmitter instance, which emits very special events,
such as exit and uncaughtException.


USING PROCESS.ENV TO GET AND SET ENVIRONMENT VARIABLES


SPECIAL EVENTS EMITTED BY PROCESS


๏‚ก exit gets emitted right before the process exits.


process.on('exit', function (code) {
console.log('Exiting...');
});


๏‚ก uncaughtException gets emitted any time an unhandled error is thrown.


process.on('uncaughtException', function (err) {
console.error('got uncaught exception:', err.message);
process.exit(1);
});
throw new Error('an uncaught exception');


CATCHING SIGNALS SENT TO THE PROCESS 


three signals that Node handles by default


๏‚ก SIGINTโ€”Sent by your shell when you press Ctrl-C. Nodeโ€™s default behavior is to
kill the process, but this can be overridden with a single listener for SIGINT on
process.
๏‚ก SIGUSR1โ€”When this signal is received, Node will enter its built-in debugger.
๏‚ก SIGWINCHโ€”Sent by your shell when the terminal is resized. Node resets
process.stdout.rows and process.stdout.columns and emits a resize event
when this is received.


13.3.2 Using the filesystem module


MOVING A FILE 


fs.rename()

์›๊ฒฉ์œผ๋กœ ์ด๋™์‹œํ‚ค๋ ค๋ฉด stream์„ ์ด์šฉํ•ด์„œ ๋ณต์‚ฌํ•ด์•ผ ํ•จ. rename์€ ์›๊ฒฉ์ง€๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์„ ์ง€์›ํ•˜์ง€ ์•Š์Œ.


WATCHING A DIRECTORY OR FILE FOR CHANGES 


fs.watch() - ํ”Œ๋žซํผ์˜ ๋„ค์ดํ‹ฐ๋ธŒ ํŒŒ์ผ ๋ณ€๊ฒฝ ์•Œ๋ฆผ API๋ฅผ ์ด์šฉํ•จ. (ํ”Œ๋žซํผ ์ฐจ์ด๋กœ ์ธํ•ด unreliable ํ•จ)

fs.watchFile()


USING COMMUNITY MODULES: FSTREAM AND FILED 


fstream์„ ์ด์šฉํ•˜๋ฉด cp -rp srcDir destDir ๋ช…๋ น์–ด๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.


filed ์ธ์Šคํ„ด์Šค๋Š” req, res ๊ฐ์ฒด์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

http.createServer(function (req, res) {
req.pipe(filed('path/to/static/files')).pipe(res);
});

์œ„ ์ฝ”๋“œ๋Š” ํŒŒ์ผ์ด ์บ์‰ฌ๋˜์—ˆ์„ ๋•Œ 304 Not Modified ์˜ค๋ฅ˜๋ฅผ ๋ฑ‰์–ด๋‚ด๊ณ  ํŒŒ์ผ์„ ๋””์Šคํฌ์—์„œ ์—ด๊ฑฐ๋‚˜ ์ฝ๋Š” ํ–‰์œ„๋Š” ํ•˜์ง€ ์•Š๋Š”๋‹ค.



13.3.3 Spawning external processes


๏‚ก cp.exec()โ€”A high-level API for spawning commands and buffering the result
in a callback
๏‚ก cp.spawn()โ€”A low-level API for spawning single commands into a Child-
Process object
๏‚ก cp.fork()โ€”A special way to spawn additional Node processes with a built-in
IPC channel


์ž์‹ ํ”„๋กœ์„ธ์Šค์˜ ์žฅ๋‹จ์  (PROS AND CONS TO CHILD PROCESSES)

 

cons : child process ๋กœ ์‹คํ–‰๋˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ด๋ฏธ ํด๋ผ์ด์–ธํŠธ์— ์„ค์น˜๊ฐ€ ๋˜์–ด์žˆ์–ด์•ผ ํ•œ๋‹ค.

pros : ๋‹ค๋ฅธ ์–ธ์–ด๋กœ ์“ฐ์—ฌ์ง„ ์• ํ”Œ๋ฆฌ์ผ€์ž‡์…˜์„ ์ด์šฉํ•ด์„œ ์ข€ ๋” richํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.


cp.exec()๋กœ ๋ช…๋ น์–ด ๊ฒฐ๊ณผ ๋ฒ„ํผ๋งํ•˜๊ธฐ(BUFFERING COMMAND RESULTS USING CP.EXEC())


cp.exec(), is useful for when you want to invoke a command, and you only care about the final result. This API allows you to enter full sequences of commands, including multiple processes piped to one another.


SPAWNING COMMANDS WITH A STREAM INTERFACE USING CP.SPAWN()


cp.spawn() returns a ChildProcess object that you can interact with.


ex)

var child = cp.spawn('ls', [ '-l' ]);
// stdout is a regular Stream instance, which emits 'data',
// 'end', etc.
child.stdout.pipe(fs.createWriteStream('ls-result.txt'));
child.on('exit', function (code, signal) {
// emitted when the child process exits
});


DISTRIBUTING THE WORKLOAD USING CP.FORK()


Like cp.spawn(), cp.fork() returns a ChildProcess object.

The major difference is the API added by the IPC channel: the child process now has a child.send (message) function, and the script being invoked by fork() can listen for process.on('message') events.


ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด ๊ณ„์‚ฐ ์˜ˆ์ œ


13.4 ์ปค๋งจ๋“œ๋ผ์ธ ํˆด ๊ฐœ๋ฐœํ•˜๊ธฐ(Developing command-line tools )


๏‚ก Parsing command-line arguments
๏‚ก Working with stdin and stdout streams
๏‚ก Adding pretty colors to the output using ansi.js


13.4.1 Parsing command-line arguments


Node provides you with the process.argv property, which is an array of strings, which are the arguments that were used when Node was invoked.

The first entry of the array is the Node executable, and the second entry is the name of your script.


ex)

var args = process.argv.slice(2);
console.log(args);


$ node args.js
[]


$ node args.js hello world
[ 'hello', 'world' ]


$ node args.js "tobi is a ferret"
[ 'tobi is a ferret' ]


์–ด๋–ค ์˜ต์…˜์ด ๋“ค์–ด์™”๋Š๋ƒ์— ๋”ฐ๋ผ usageํ”„๋ฆฐํŠธํ•˜๋Š” ์˜ˆ์ œ


13.4.2 stdin, stdout ๊ฐ€์ง€๊ณ  ๋†€๊ธฐ (Working with stdin and stdout)


๏‚ก process.stdinโ€”A ReadStream to read input data from
๏‚ก process.stdoutโ€”A WriteStream to write output data to


WRITING OUTPUT DATA WITH PROCESS.STDOUT


console.log() ๋‚ด์—์„œ process.stdout.write()์ด ์‚ฌ์šฉ๋จ.


(์ดํด๋ฆฝ์Šค์—์„œ argument ์„ค์ •ํ•ด์ฃผ๊ณ  ์‹คํ–‰ํ•œ๋‹ค๊ณ  ๊ฐ€์ •)

var target = process.argv[2];
process.stdout.write('Entered value is : ' + target);


PROCESS.STDIN ๋กœ ์ž…๋ ฅ๊ฐ’ ์ฝ์–ด์˜ค๊ธฐ (READING INPUT DATA WITH PROCESS.STDIN )

 

Before you can read from stdin, you must call process.stdin.resume() to indicate that your script is interested in data from stdin.

After that, stdin acts like any other readable stream, emitting data events as data is received from the output of another process, or as the user enters keystrokes into the terminal window.


๋‚˜์ด ๊ฒ€์ฆํ•˜๋Š” ์˜ˆ์ œ


DIAGNOSTIC LOGGING WITH PROCESS.STDERR 


 process.stderr์„ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€๋ง๊ณ  console.error()๋ฅผ ์‚ฌ์šฉํ•˜์ž.


13.4.3 ์ถœ๋ ฅ์— ์ƒ‰ ์ž…ํžˆ๊ธฐ (Adding colored output)


ANSI ์ด์Šค์ผ€์ดํ”„ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ (CREATING AND WRITING ANSI ESCAPE CODES )


(cmd ์ฐฝ์—์„œ ansi.js ์‹คํ–‰)


console.log('\033[43m\033[35mhello\033[39m\033[49m');

var ansi = require('ansi');
var cursor = ansi(process.stdout);

cursor
.fg.green()
.write('This should be green.')
.fg.reset()
.write('\n');