HEROPY
Tech
{{ scrollPercentage }}%

처음 시작하는 Node.js 개발 - 3 - Globals

nodejs javascript

타이머 함수를 사용하거나 현재 모듈의 디렉토리 이름을 알아내는 등 모든 모듈에서 사용할 수 있는 Node.js의 전역 객체들에 대해서 알아봅시다.

REPL

Node.js의 REPL(Read-Eval-Print-Loop) 기능은 Node.js 코드를 실험하고 JavaScript 코드를 디버깅하는 데 매우 유용합니다.

Read: 사용자의 입력을 읽고, JavaScript 데이터 구조로 입력을 파싱하고, 메모리에 저장합니다.
Eval: 데이터 구조를 평가합니다.
Print: 결과를 인쇄합니다.
Loop: 사용자가 ctrl+c를 두 번 누를 때까지 명령을 반복합니다.

REPL은 다음과 같이 Shell에서 node를 실행하면 시작할 수 있습니다.

$ node

간단한 연산이나 로그를 사용할 수 있습니다.

> 1 + ( 2 * 3 ) - 4
3
> console.log('hello REPL!');
hello REPL!

REPL은 여러 줄 표현(Multiline Expression)도 지원합니다.

> for (var i = 0; i < 4; i++) {
... console.log(i);
... }
0
1
2
3

REPL Commands

ctrl+c: 현재 명령을 종료합니다.
ctrl+c(2번): Node REPL을 종료합니다.
ctrl+d: Node REPL을 종료합니다.
Up/Down키: 명령 기록을 탐색하여 이전 명령을 수정할 수 있습니다.
tab키: 현재 작성된 명령으로 시작하는 지정된 명령이나 데이터를 표시합니다.
.help: 모든 REPL 커멘드 목록을 표시합니다.
.break: 여러 줄 표현을 종료합니다.
.clear: 여러 줄 표현을 종료합니다.
.save <filename>: 현재 Node REPL 세션을 파일로 저장합니다. 예를 들면 .save log.txt, .save session.js.
.load <filename>: 현재 Node REPL 세션에 파일을 불러옵니다.
.editor: 에디터 모드를 사용합니다.(ctrl+d: 에디터 모드 완료, ctrl+c: 에디터 모드 취소)

> .editor
// Entering editor mode (^D to finish, ^C to cancel)
function welcome(name) {
  return `Hello ${name}!`;
}

welcome('Node.js');

// ^D(ctrl+d) 완료
'Hello Node.js'

_(Underscore Variable)를 사용하면 마지막 결과의 값을 참조합니다.

> 1 + 1
2
> _ * 2
4
>

global

Node.js의 전역 정보를 가지는 객체를 반환합니다.

일반적으로 브라우저에서의 최상위 범위(Top-level scope)는 전역 범위(Global scope)입니다. 하지만 Node.js에서 최상위 범위는 전역 범위가 아닙니다. 모듈 내부의 내용은 해당 모듈에 대한 지역이 됩니다.

console.log(global);
console.log(window);  // window is not defined

Console

Node.js Console
웹 브라우저에서 제공하는 JavaScript 콘솔 메커니즘과 유사한 표준 출력(stdout)과 표준 오류(stderr)를 표시하는 디버깅 콘솔을 제공합니다.

console.log()

표준 출력(stdout)으로 디버깅 콘솔에 데이터를 표시합니다.

console.log('hello world!');

Placeholders

각 Placeholder 토큰은 해당 인수(Arguments) 값으로 대체됩니다.
다음과 같은 Placeholder 토큰들이 지원됩니다.

%s: 문자열
%d: 숫자(정수, 부동 소수점 값)
%i: 정수
%f: 부동 소수점 값
%j: JSON
%o: 일반 JavaScript 객체

console.log('My name is %s, I am %d.', 'HEROPY', 19);
My name is HEROPY, I am 19.

Timers

Node.js의 타이머는 특정 기간 후에 주어진 함수를 호출합니다.
타이머는 실행하기를 원하는 정확한 시간이 아니라 제공된 콜백이 일정 시간 후에 실행되어야 하는 기준 시간을 지정합니다.
타이머 콜백은 지정한 시간이 지난 후에 스케줄링 될 수 있는 가장 이른 시간에 실행됩니다.
하지만 운영체제 스케줄링이나 다른 콜백 실행 때문에 지연될 수 있습니다.

setInterval()

지연 시간(ms)마다 콜백 함수를 반복 실행합니다.
clearInterval()에서 사용할 Timeout 객체를 반환합니다.
지연 시간이 2147483647보다 크거나 1보다 작으면 지연 시간은 1로 설정됩니다.

setInterval(callback, delay)
let sec = 0;
setInterval(() => {
  sec++;
  console.log(sec);
}, 1000);

clearInterval()

setInterval()로 생성된 Timeout 객체를 취소합니다.

let sec = 0;
const timer = setInterval(() => {
  sec++;
  console.log(sec);

  if (sec > 9) {
    clearInterval(timer);
    return;
  }
}, 1000);

setTimeout()

지연 시간(ms) 후에 일회성 콜백 함수를 실행합니다.
clearTimeout()에서 사용할 Timeout 객체를 반환합니다.
Node.js는 콜백이 발생하는 정확한 타이밍이나 순서를 보장하지 않습니다.
콜백 함수는 지정된 시간에 최대한 가깝게 호출됩니다.
지연 시간이 2147483647보다 크거나 1보다 작으면 지연 시간은 1로 설정됩니다.

clearTimeout()

setTimeout()로 생성된 Timeout 객체를 취소합니다.

setImmediate()

I/O 주기 내에서 콜백을 즉시 실행합니다.
clearImmediate()에서 사용할 Timeout 객체를 반환합니다.
setImmediate()를 여러 번 호출하면 콜백 함수가 생성 순서대로 실행을 대기하며, 얼마나 많은 타이머가 존재하냐에 상관없이 I/O 주기 내에서 스케줄 된 어떤 타이머보다 항상 먼저 실행됩니다.

const fs = require('fs');

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('timeout');
  }, 0);
  setImmediate(() => {
    console.log('immediate');
  });
  console.log('start');
  console.log('end');
});
start
end
immediate
timeout

clearImmediate()

setImmediate()로 생성된 Timeout 객체를 취소합니다.

Modules

__dirname

현재 모듈의 디렉토리 이름(경로)을 나타냅니다.

console.log(__dirname);
/Users/myDir/Project/

__filename

현재 모듈의 파일 이름(경로)을 나타냅니다.

console.log(__filename);
/Users/myDir/Project/app.js

module

각 모듈에서 module은 현재 모듈을 나타내는 객체에 대한 참조이며, 전역(Global)이 아니라 각 모듈의 지역(Local)입니다.

module.exports

모듈에서 선언한 내용은 모듈 안에서 참조해야 하는 유효범위(Scope)가 존재합니다.
만약 모듈 내용을 모듈 밖에서 참조하려면 module.exports를 사용해서 외부 참조가 가능하도록 만들 수 있습니다.

외부에서 특정 모듈을 참조하려면 require()가 필요합니다.

name.js:

const myName = 'HEROPY';
module.exports = myName;  // 내보내기

hello.js:

const name = require('./name');  // 가져오기 / `.js`는 생략합니다.
console.log(`Hello ${name}`);
Hello HEROPY

exports

exportsmodule.exports의 단축 형태(shortcut)입니다.
하지만 똑같진 않습니다. 다음 예제를 보겠습니다.

exports.js:

const array = [1, 2, 3];
exports = array;

module-exports.js:

const array = [1, 2, 3];
module.exports = array;

app.js:

const e = require('./exports');
const me = require('./module-exports');

console.log(`exports: ${e[1]}`);
console.log(`module.exports: ${me[1]}`);
exports: undefined
module.exports: 2

module.exports에서 .exportsmodule 객체의 프로퍼티(property)로서 값(일반 자료형)을 가질 수 있습니다.
하지만 exports는 하나의 객체(object) 데이터로 프로퍼티나 메소드를 가져야 합니다.
따라서 위 예제의 exports.js를 다음과 같이 수정해서 사용할 수 있습니다.

exports.js:

const array = [1, 2, 3];
exports.array = array;

app.js:

const e = require('./exports');

console.log(`exports: ${e.array[1]}`);

이러한 방식이 차이점이 불편하다면 module.exports를 사용할 것을 권장합니다.

require()

정의된 모듈을 require()를 통해 사용합니다.

msg.js:

// 모듈 정의
module.exports = 'Hello World!';

log.js:

// 모듈 사용
const msg = require('./msg');
console.log(msg);
Hello World!

Process

process 객체는 Node.js 프로세스에 대한 정보를 제공하고 제어합니다.

process.arch

'arm', 'ia32'과 같은 Node.js 프로세스가 현재 실행 중인 프로세서 아키텍처 식별자를 반환합니다.

'x64'

process.env

사용자 환경 정보(변수)를 가진 객체를 반환합니다.

const PORT = process.env.PORT || 3000;

process.env는 객체이기 때문에 값(환경 변수)을 설정할 수 있으며, 문자열로 변환되어 할당됩니다.

process.env.MY_VARIABLE = true;
console.log(process.env.MY_VARIABLE);
`true`

Windows 운영 체제에서는 대소문자를 구분하지 않으니 주의합니다.

process.env.MY_VARIABLE = true;
console.log(process.env.my_variable);
`true`

delete를 이용하여 환경 변수를 삭제할 수 있습니다.

delete process.env.MY_VARIABLE;
console.log(process.env.MY_VARIABLE);
undefined

process.exit()

Node.js가 프로세스를 종료하도록 지시합니다.
code0이 생략 가능한 기본값으로, 0은 ‘정상(성공) 종료’, 1은 ‘비정상(실패) 종료’를 의미합니다.

process.exit(1);

process.nextTick()

process.nextTick()은 콜백을 ‘다음 틱 대기열(Next Tick Queue)’에 추가합니다.

process.nextTick(callback)

틱(tick)은 ‘이벤트 루프 대기열(Event Loop Queue)’에서 이벤트를 꺼내어 그 이벤트를 실행하는 것입니다.

이것은 setTimeout(fn, 0), setImmediate()에 대한 간단한 별칭이 아닙니다.
객체를 생성한 후 I/O가 발생하기 전에 사용자에게 이벤트 핸들러를 할당할 수 있는 기회를 제공하기 위해 사용합니다.

이벤트 루프란?

process.platform

Node.js 프로세스가 실행중인 운영 체제 플랫폼 식별자를 반환합니다.
'darwin', 'freebsd', 'linux', 'sunos' 또는 'win32'

'darwin'

process.memoryUsage()

Node.js 프로세스의 메모리 사용량을 바이트 단위로 설명하는 객체를 반환합니다.

{
  rss: 11145216,
  heapTotal: 7184384,
  heapUsed: 4982952,
  external: 8644
}

heapTotal, heapUsed: V8의 메모리 사용량을 나타냅니다.
external: V8에서 관리하는 JavaScript 객체에 바인딩 된 C++ 객체의 메모리 사용량을 나타냅니다.
rss: rss(Resident Set Size)는 프로세스의 heap, code segment, stack을 포함하는 주 메모리 장치(총 할당 된 메모리의 하위 집합)에 차지하는 공간의 크기입니다.

heap은 객체, 문자열 및 클로저가 저장되는 곳입니다. 변수는 Stack에 저장되고, 실제 JavaScript 코드는 code segment에 있습니다.

process.uptime()

현재 Node.js 프로세스가 실행된 시간(sec)을 반환합니다.

console.log(
  process.uptime(),
  Math.floor(process.uptime())
)
130.072
130

process.versions

Node.js와 그 종속성의 버전을 나열하는 객체를 반환합니다.

{
  http_parser: '2.7.0',
  node: '8.9.4',
  v8: '6.1.534.50',
  uv: '1.15.0',
  zlib: '1.2.11',
  ares: '1.10.1-DEV',
  modules: '57',
  nghttp2: '1.25.0',
  openssl: '1.0.2n',
  icu: '59.1',
  unicode: '9.0',
  cldr: '31.0.1',
  tz: '2017b'
}

Buffers

Buffer 모듈은 바이너리 데이터(Binary data)의 스트림(Stream)을 처리하는 방법을 제공합니다.

Buffer(버퍼)란 I/O 수행의 속도 차이 극복을 위해 한 장소에서 다른 장소로 전송되는 데이터 묶음(Chunk)에 대한 임시 저장 공간 의미합니다.

Buffer.alloc()

원하는 길이(Size)의 안전한 새로운 Buffer를 생성합니다.

Buffer.alloc(size, fill, encoding)

기존 new Buffer()는 v6.x부터 ‘deprecated’ 되었습니다.

const buf = Buffer.alloc(4);  // 4byte
console.log(buf);
<Buffer 00 00 00 00>

Buffer.from()

String, Array 또는 Buffer로 채워진 새로운 버퍼를 생성합니다.

Buffer.from()

기존 new Buffer()는 v6.x부터 ‘deprecated’ 되었습니다.

hello world라는 문자열(String)로 새로운 버퍼를 생성합니다.
문자열 생성시 두번째 인수로는 인코딩을 설정할 수 있으며, 생략할 수 있고 기본값은 'utf8'입니다.

var buf1 = Buffer.from('hello world', 'utf8');
console.log(buf1);
<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>

Buffer.concat()

Buffer.concat([buffer1, buffer2], totalLength)

Buffer 객체들을 하나의 Buffer 객체로 결합(이어 붙이기)합니다.

var buf1 = Buffer.from('abc');  // 3
var buf2 = Buffer.from('def');  // 3
var buf3 = Buffer.concat([buf1, buf2], 6);

console.log(
  buf3,
  buf3.toString()
)
<Buffer 61 62 63 64 65 66>
'abcdef'

totalLength를 지정하지 않으면 자동으로 계산됩니다. 그러나 totalLength를 계산하기 위해 추가 루프가 실행되므로 값을 알고 있다면 제공하는 것이 더 빠릅니다.
버퍼의 결합 길이가 totalLength를 넘으면 결과는 잘립니다.

var buf1 = Buffer.from('abc');  // 3
var buf2 = Buffer.from('def');  // 3
var buf3 = Buffer.concat([buf1, buf2], 4);

console.log(
  buf3,
  buf3.toString()
)
<Buffer 61 62 63 64>
'abcd'

Buffer.isBuffer()

Buffer 객체인지 검사합니다.

Buffer.isBuffer(obj)
var fs = require('fs')

fs.readFile('./abc.txt', function (err, data) {
  console.log(
    Buffer.isBuffer(data)
  );
});
true

buf.copy()

Buffer 객체를 복사하여 대상 Buffer 객체에 붙여넣습니다.

buf.copy(targetBuf, targetStart, sourceStart, sourceEnd)

target: 복사된 Buffer를 붙여넣을 대상 Buffer를 지정합니다.
targetStart: 복사된 Buffer의 시작 위치를 지정합니다. 기본값은 0입니다.
sourceStart: 복사할 Buffer의 시작 위치를 지정합니다. 기본값은 0입니다.
sourceEnd: 복사할 Buffer 객체의 끝 위치를 지정합니다. 기본값은 buf.length입니다.

var buf1 = Buffer.from('abcdef');
var buf2 = Buffer.from('ABCDEF');
buf1.copy(buf2, 1, 0, 2);
// buf2: 붙여넣을 대상
// 1: 대상의 어느 위치부터 붙일까요?
// 0: buf1의 어디서부터 복사할까요?
// 2: buf1의 어디까지 복사할까요?

console.log(
  buf2,
  buf2.toString()
);
<Buffer 41 61 62 44 45 46>
'AabDEF'

buf.length

할당된 메모리를 바이트 단위로 반환합니다.

var buf1 = Buffer.from('abc');
var buf2 = Buffer.from('가나다');

console.log(
  buf1.length,  // 3byte
  buf2.length  // 9byte
);
3
9

한글은 ‘3byte’, 띄어쓰기는 ‘1byte’입니다.

buf.toString()

Buffer 객체를 문자 인코딩에 따라 문자열로 디코딩합니다.

buf.toString(encoding, start, end)

encoding: 디코드할 문자 인코딩을 설정합니다. 기본값은 'utf8'입니다.
start: 디코드할 시작 위치를 지정합니다. 기본값은 0입니다.
end: 디코드할 끝 위치를 지정합니다. 기본값은 buf.length입니다.

abc.txt:

abcdefghijklmnopqrstuvwxyz

app.js:

var fs = require('fs')

fs.readFile('./abc.txt', function (err, data) {
  console.log(
    data,
    data.toString()
  );
});
<Buffer 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a>
abcdefghijklmnopqrstuvwxyz

buf.write()

지정된 문자열을 Buffer 객체에 작성합니다.

buf.write(string, offset, length, encoding)
var data = '가나다라';
var buf = Buffer.alloc(12);
buf.write(data);

console.log(
  buf,
  buf.toString()
);
<Buffer ea b0 80 eb 82 98 eb 8b a4 eb 9d bc>
'가나다라'

참고 자료(References)

https://nodejs.org/dist/latest-v8.x/docs/api/
https://vimeo.com/96425312
http://voidcanvas.com/setimmediate-vs-nexttick-vs-settimeout/
https://developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop
https://www.tutorialspoint.com/nodejs/nodejs_repl_terminal.htm