[Node.js] Node.js 학습 및 정리
1.1 Node.js 정의
-
Node.js 정의
Chrome V8 Javascript 엔진으로 빌드된 Javascript runtime이다.
-
Runtime 정의
컴퓨터 프로그램이 실행되고 있는 동안의 동작.
-
Runtime Environment 정의
컴퓨터가 실행되는 동안 프로세스나 프로그램을 위한 소프트웨어 서비스를 제공하는 가상 머신의 상태
즉, 노드는 가상머신이라고 봐도 된다. 실제로 Node 안에 가상머신이 들어 있다. 런타임은 자바스크립트를 웹브라우저 바깥에서 동작할 수 있게 해주는 프로그램이라고 생각해도 무방하다. 원래 자바스크립트는 웹브라우저 안에서 돌아가는 언어였고 노드의 등장으로 자바스크립트가 웹브라우저 바깥에서도 동작할 수 있게 되었다.
1.2 REPL과 헬로 노드
- REPL (Read Evaluate Print Loop)
1.3 호출 스택과 이벤트 루프
Node의 핵심 개념 3가지
🔘 Event 기반
🔘 Non blocking I/O Model
🔘 Single Thread
이벤트 루프
*참고 :
https://www.zerocho.com/category/JavaScript/post/597f34bbb428530018e8e6e2
http://asfirstalways.tistory.com/362
Stack
1function first(){
2 second();
3 console.log('첫 번째');
4}
5function second(){
6 third();
7 console.log('두 번째');
8}
9function third(){
10 console.log('세 번째');
11}
12first(); // 첫 번째, 두 번째, 세 번째
Task Queue
1function run(){
2 console.log('3초 후 실행');
3}
4console.log('시작');
5setTimeout(run, 3000);
6console.log('끝'); // 시작, 끝, 3초 후 실행
Task Queue는 Stack의 반대되는 개념.
Stack은 차곡차곡 쌓이면 위에서 부터 사라지게 된다(LIFO). 반대로 Queue는 먼저 들어온 순서대로 나간다(FIFO).
setTimeout(run, 3000)
부터 실행을 도식화 해보면 아래와 같다.
최종적으로 stack과 Queue가 모두 비워지면 프로그램이 종료된다.
🔘 언제 Task Queue에 들어가는가
- setTimeout
- setInterval
- setImmediate
- Promise resolve, reject
- async, await
- EventLister의 callback
💡 프로그램이 실행되다 보면 Queue가 여러개 있고 계속 쌓이게 되는데, 이때 Task Queue에서 정해진 순서대로 꺼내오는 것이 Event Loop의 역할이다. (Event Loop는 순서를 알고 있다.) 이것을 잘 사용하면 실행순서를 바꿔서 실행시킬 수 있다.
1.4 Event driven, Non blocking I/O Model, Single Thread
생각해보기
Node가 서버의 역할을 하려면 어떻게 해야 할까?
🔘서버
클라이언트가 요청을 보낸 것을 받아서 응답을 하는 것. 서버가 서버한테 요청을 보낼 수도 있다. 이 경우 서버가 클라이언트가 된다. 예를들어 웹 주소를 입력하는 것도 요청이다. 웹주소를 입력하면 서버를 찾아가서 서버에게서 html을 받아온다(즉, 응답으로 html을 받는 것). 인터넷 브라우저는 받아온 것을 파싱해서 화면으로 만들어 주는 것이다.
- 요청의 특징 : 언제 올지 모른다. 이때 대기할 때 사용하는 것이
EventListener
이다.
Event Driven
호출 stack과 task queue가 비어 있지만 EventListener에 등록해둔 것 덕분에 프로그램이 계속 동작한다. 이 동작 방식을 Event Driven이라고 한다.
Non Blocking I/O
Non Blocking
위에서 봤던 setTimeout(run, 3000)
에서 run
을 Task Queue로 보낸다. 이렇게 Task Queue로 보내고 실행 순서가 바뀌게 되는 동작을 Non blocking이라고 한다. (실행 순서의 문제)
I/O (Input Output)
- 파일시스템 I/O
- 네트워크 I/O
위 두가지는 자체적으로 Non Blocking으로 동작한다.
파일시스템의 경우는 더 나아가서 알아서 Multi thread를 돌린다. 즉, Non blocking에 팔까지 여러개 달아서 Multi thread로 작업하는 것이다.
Single Thread
사람으로 치면 팔의 갯수라고 보면 된다. Single Thread는 팔이 한개라서 한번에 한가지 일만 할 수 있다. Multi Thread는 팔이 여러개이기 때문에 동시에 작업이 가능하다.
자바스크립트는 Single Thread이다. 그래서 blocking이 일어나는데, 그것을 극복하기 위해 Non blocking으로 순서를 바꾸어서 효율적으로 처리할 수 있게 만드는 것이다.
Multi thread로 프로그래밍 하는 것이 어려운 측면이 있다. Node가 쓰는 Multi thread를 흉내내는 방식은 왼팔만 여러개를 달아놓은 것과 비슷해서 흉내는 냈지만 부족한 면이 있다.
정확하게 말하면 thread 보다 상위 개념으로 proccess가 있는데 Node가 Multi thread를 흉내내는 방식은 이 proccess를 여러개 만들어서 Multi proccessing을 하는 것이 Node가 single thread의 단점을 극복하는 방법중 하나이다.
2.1 const와 let
const에 객체가 할당된 경우 객체 내부 속성은 바꿀 수 있다. 즉, 참조에 대한 상수는 변경 가능.
1const val = {a: 1, b: 2, c: 3};
2val.a = 3;
3val.b = 1; // 객체 내부속성 변경 가능
4
5const h = [1, 2, 3, 4]
6h[0] = 4;
7h[3] = 2; // 객체 내부속성 변경 가능
2.2 템플릿 문자열(백틱, `)
백틱을 양 끝에 사용하면 하나의 문자열로 인식한다.
1const a = 'hello';
2const b = true;
3const c = 3;
4
5const d = a + ' ' + b + ' ' + c;
6const e = `${a} ${b} ${c}` // 위에 코드와 같은 의미
7
8const f = `${a}는 ${b}이고 ${c}은 몰라요.`
2.3. 객체 리터럴의 변화
객체 리터럴 : let val = {}
1var sayNode = function(){
2 console.log('Node')
3};
4var es = 'ES';
5
6// ES6 이전 문법
7var oldObject = {
8 sayJS: function(){
9 console.log('JS');
10 },
11 sayNode: sayNode, // key와 value 같음
12};
13
14oldObject[es + 6] = 'Fantastic'; // Fantastic
15oldObject.sayNode(); // Node
16
17
18
19// ES6 이후 문법
20let newObject = {
21 sayJS(){
22 console.log('JS');
23 },
24 sayNode, // key와 value가 같은 경우 한 번만 작성할 수 있게 바뀜
25 [es + 6]: 'Fantastic', // 동적 속성 할당을 리터럴 안에 표현 가능. {[변수]: 값} ***
26};
27
28newObject.sayJS(); // JS
29newObject.sayNode(); // Node
30console.log(newObject.ES6); // Fantastic
2.4 화살표 함수
함수 선언문
변수를 선언하고 함수를 대입.
1let add = function(x, y){
2 return x + y;
3}
4
5let add = (x, y) => x + y ;
함수표현식
함수에 이름 붙여서 선언하는 것.
1function add(x, y){ return x + y };
function() 이 살아남은 이유
function()과 화살표 함수의 가장 큰 차이 : this
function()과 화살표 함수의 this는 동작하는 방식이 다르다.
function() 사용한 경우 this 동작
1let relationship1 = { // let relationship = {} : 객체 리터럴 생성
2 name: 'zero',
3 friends: ['nero', 'hero', 'xero'],
4 logFriends: function() {
5 let that = this; // relationship1을 가리키는 this를 that에 저장
6 this.friends.forEach(function(friend){
7 console.log(that.name, friend); // 바깥의 this를 가져오기 위해 that에 대입해서 가져옴
8 });
9 },
10};
11
12relationship1.logFriends();
13/* zero nero
14 * zero hero
15 * zero xero
16 */
화살표 함수 사용한 경우 this 동작
-
** 부분 : 여기서 화살표함수
friend =>
를 쓰면 화살표 함수 내부의 thisthis.name
를 화살표 함수 밖의 thisthis.friends.forEach
와 같은 것으로 만들어준다.즉,
this.friends.forEach( ... => ... this)
화살표 함수 통해서 바깥의this
가 안의this
에 적용된다.
1let relationship2 = {
2 name: 'zero',
3 friends: ['nero', 'hero', 'xero'],
4 logFriends() {
5 this.friends.forEach(friend => { // **
6 console.log(this.name, friend); // this : 원래 window
7 });
8 },
9};
10
11relationship2.logFriends();
2.5 비구조화 할당 (destructuring)
객체에서 비구조화 할당
1let candyMachine = {
2 status: {
3 name: 'node',
4 count: 5,
5 },
6 getCandy(){
7 this.status.count--;
8 return this.status.count;
9 }
10}
11
12var status = candyMachine.status; // 변수와 속성의 이름이 같다
13let getCandy = candyMachine.getCandy;
var status = candyMachine.status;
처럼 변수와 속성의 이름이 같을 때 ES6에서는 간단하게 표현할 수 있게 새로운 문법을 제공한다. (🔑 활용도가 매우 높은 문법)
1let {status, getCandy} = candyMachine;
- Example
1const { Router } = require('express');
2// require express라는 객체에서 Router라는 속성값을 Router 변수에 대입
📌 비구조화 할당 시 this
가 의도와 다르게 동작하는 현상이 있을 수 있다. ⛔️⛔️⛔️
1candyMachine.getCandy(); // 4
2
3const { getCandy } = candyMachine;
4getCandy(); // undefined
candyMachine.getCandy();
에서 getCandy()
를 갖고 있는 객체 candyMachine.
이 앞에 붙어 있으면 getCandy()
가 호출이 될 때 this
를 앞에 붙어 있던 객체 candyMachine.
로 만든다.
마지막줄의 비구조화 할당 후 getCandy()
를 보면 앞에 객체가 안붙어 있다. 그래서 getCandy()
가 this
인 candyMachine
을 못찾는다. getCandy
를 호출해도 candyMachine
이 바뀌지 않고 undefined가 출력되는 것이다.
그러므로 반드시 앞에 객체를 붙여줘야 한다. 구조분해 할당으로 candyMachine
과 분리되면서 나타나는 현상이다.
배열에서 비구조화 할당
1const a = array[0];
2const b = array[1];
3
4const [a, b] = array;
5
6// 참고 : array[array.length - 1] 배열의 마지막 요소 가져오는 코드
- Example
1const array = ['node', {}, 10, true];
2const [node, obj, ...bool] = array;
3console.log(bool); // (2) [10, true]
4
5
6const m = (x, y) => console.log(x, y);
7m(5, 6); // 5 6
8m(5, 6, 7, 8, 9) // 5 6
9
10
11const n = (x, ...y) => console.log(x, y);
12n(5, 6, 7, 8, 9); // 5 [6, 7, 8, 9]
13 // ** argumnet는 배열처럼 보이지만 배열이 아님. 유사배열.
14
15const p = (...rest) => console.log(rest); // rest는 배열. 이 방식이 더 좋음.
16p(5, 6, 7, 8, 9); // [5, 6, 7, 8, 9]
2.6 rest 문법
rest 문법
1const n = (x, ...y) => console.log(x, y);
2n(5, 6, 7, 8, 9); // 5 [6, 7, 8, 9]
3 // ** argumnet는 배열처럼 보이지만 배열이 아님. 유사배열.
4
5const p = (...rest) => console.log(rest); // rest는 배열. 이 방식이 더 좋음.
6p(5, 6, 7, 8, 9); // [5, 6, 7, 8, 9]
참조
변수는 메모리에 위치하는데 이 경우 y는 x를 참조하고 있다. 즉, x가 저장된 메모리 값을 가리키고 있는 것이다.
1const x = {a: 1, b: 2};
2let y = x; // y가 x를 참조하고 있는 것
const는 가리키고 있는 위치를 바꿀 수 는 없지만 그 안에 있는 값은 바꿀 수 있는 것이다.
2.7 Callback과 Promise 비교
전형적인 콜백 코드
1Users.findOne('zero', (err, user) => {
2 if (err) {
3 return console.error(err);
4 }
5 console.log(user);
6})
7console.log('다 찾았니?');
예를들어, 데이터 베이스에 Users
라는 폴더가 있고 findOne
이 zero
라는 한 사람을 찾아오는 것이라고 한다면,
zero
를 찾았을 때 콜백이 실행되는 것이다. ( (err, user) => { ....
이하가 콜백)
콜백을 쓰는 이유는 코드가 non-blocking으로 작동하기 때문이다.
console.log(user);
보다 console.log('다 찾았니?');
가 먼저 뜨게 된다. 왜냐하면 Users.findOne('zero'
은 네트워크를 통해서 데이터 베이스에서 검색하므로 시간이 얼마나 걸릴 지 모르기 때문에 Non blocking 방식으로 먼저 요청만 보내놓고 그 다음 코드를 실행하기 때문이다.
다 찾은 경우 event loop로 콜백을 넣어줘서 실행되는 것이다. 찾는 과정에서 에러가 있다면 console.error(error);
가 실행되는 것이다.
단점은 순서가 매우 애매하다. 콜백이 연달아 있는 경우 실행순서가 어떻게 흘러가는지 파악하기 어려운 문제가 있다. (callback hell)
1Users.findOne('zero', (err, user) => {
2 if (err) {
3 return console.error(err);
4 }
5 console.log(user);
6 Users.update('zero', 'nero', (err, updatedUser)=> {
7 console.log(updateUser);
8 Users.remove('nero', (err, removeUser) => {
9 if (err) {
10 return console.error(err);
11 }
12 console.log(removeUser);
13 });
14 });
15});
16console.log('다 찾았니?');
Promise
위 코드에 Promise를 적용하면 아래와 같다.
1Users.findOne('zero')
2 .then((user) => {
3 console.log(user);
4 return Users.update('zero', 'nero');
5 })
6 .then((updatedUser) => {
7 console.log(updatedUser);
8 return Users.remove('nero');
9 })
10 .then((removedUser) => {
11 console.log(removeUser);
12 });
13 .catch((err) => { // 에러처리는 마지막에 한번
14 console.error(error);
15 });
16 console.log('다 찾았니?');
2.8 Promise 이해하기
Promise를 만드는 방법
함수의 맨 첫글자가 대문자이면 생성자라는 의미이다. 앞에 new
를 써서 생성가능하다는 것을 알려준다.
1> Promise
2 Promise() { [native code] }
Promise 생성 구조
Promise를 생성할 때 구조는 아래와 같다.
1new Promise((resolve, reject) => {
2 // resolve와 reject가 매개변수인 함수를 넣는다
3});
4
5plus
6 .then(() => { // resolve가 되면 then이 실행되고
7
8 })
9 .catch(() => { // reject가 되면 catch가 실행된다
10
11 })
Example
1const plust = new Promise((resolve, reject) => {
2 const a = 1;
3 const b = 2;
4 if (a + b > 2){
5 resolve(a + b); // resolve( 성공 메시지 )
6 } else {
7 reject(a + b); // reject( 실패 메시지 )
8 }
9});
10
11
12plus
13 .then((success) => { // resolve가 되면 then 실행
14 console.log(success);
15 })
16 .catch((fail) => { // reject가 되면 catch 실행
17 console.log(fail);
18 })
.then
, .catch
는 함수 내부적으로 이미 promise
를 return 하도록 정의되어 있기 때문에 사용할 수 있는 것이다. 즉, 내부적으로 promise 지원 되는 것들만 적용할 수 있다. 왠만한 라이브러리들은 모두 지원한다.
1const condition = true; // true면 resolve, false면 reject
2const promise = new Promise((resolve, reject) => {
3 if (condition) {
4 resolve('성공');
5 } else {
6 reject('실패');
7 }
8});
9
10promise
11 .then(message) => {
12 console.log(message); // 성공(resolve)한 경우 실행
13 })
14 .catch((error) => {
15 console.error(error); // 실패(reject)한 경우 실행
16 })
then이 여러개인 구조
then에 리턴값이 있으면 다음 then으로 넘어간다. Promise를 리턴하면 resolve나 reject되어 넘어간다.
1promise
2 .then(message) => {
3 return new Promise((resolve, reject) => {
4 resolve(message2);
5 });
6 })
7 .then(message2) => {
8 return new Promise((resolve, reject) => {
9 resolve(message3);
10 });
11 })
12 .then(message3) => {
13 return new Promise((resolve, reject) => {
14 resolve(message3);
15 })
16 })
17 .catch((error) => {
18 console.error(error);
19 });
2.9 Promise API
무조건 성공하거나 실패하는 Promise는 아래와 같이 줄일 수 있다.
1// 무조건 성공
2const successPromise = Promise.resolve('성공');
3 .then();
4
5// 무조건 실패
6const failurePromise = Promise.reject('실패');
7 .catch();
8
9// 아래 코드를 줄인 것
10const promise = new Promise((res, rej) => {
11 rej('실패');
12});
Promise의 장점
Promise.all
을 이용해서 한꺼번에 실행 가능하다. 다만, 하나라도 실패하면 모두 에러가 되는 단점이 있다.
1Promise.all([Users.findOne(), Users.remove(), Users.update()])
2 .then((results) => {})
3 .catch((error) => {})
정리 및 활용
Promise는 결과를 실행했는데 바깥에 표시를 안해준 것이라고 생각하면 된다.
1const promise = new Promise((res, rej) => {
2 res('성공');
3});
promise 변수는 이미 성공했다는 것을 이미 알고 있지만 바깥으로 꺼내지 않은 것이다. promise.then(() => {})
으로 꺼낼 때 바깥으로 드러난다. 이것의 장점은 바로 꺼내지 않았기 때문에 언제든지 필요할 때 쓸 수 있는 것이다. (비장의 카드 느낌?!) 결과를 갖고 있는 부분과 드러내는 부분이 분리된다는 것도 장점이다.
아래 콜백 코드의 문제점은 바로 결과를 사용하는 부분이 이어지게 된다. 중간에 다른 코드 삽입 불가.
1Users.findOne('zero', (err, user) => {
2 console.log(user);
3})
Promise로 바꾸면,
1const zero = Users.findOne('zero');
2
3// 다른 로직...
4
5zero.then((z) => {
6 console.log(z);
7});
2.10 async / await
콜백이 한번 콜백으로 넘어가면 모두 콜백 안에서 로직이 구성되어야 하는 것 처럼,
Promise
도 한번 .then
이나 .catch
로 넘어가면 모든 로직이 .then
, .catch
안에서 이루어져야 하는 문제점이 있다.
1Users.findOne('zero')
2 .then((user) => {
3 console.log(user);
4 return Users.update('zero', 'nero');
5 })
6 .then((updatedUser) => {
7 console.log(updatedUser);
8 return Users.remove('nero');
9 })
10 .then((removedUser) => {
11 console.log(removedUser);
12 });
13 .catch((err) => {
14 console.error(error);
15 });
16 console.log('다 찾았니?');
console.log('다 찾았니?');
는 첫 번째 .then
보다 먼저 실행된다. 즉, Promise로 가독성을 높였다고는 하지만 순서대로 실행되지 않는다는 것은 동일하다. 그래서 *
, yield
생성자가 만들어졌다가 ES6부터 async
/ await
가 공식적으로 등록 되었다. 순서대로 실행되는 것 처럼 보이게 만들 수 있다.
위 코드를 async
/ await
으로 바꾸면 아래와 같다.
1async func() => {
2 try{
3 const user = await Users.findOne('zero');
4 console.log(user);
5 const updatedUser = await Users.update('zero', 'nero');
6 console.log(updatedUser);
7 const removedUser = await Users.remove('nero');
8 console.log(removedUser);
9 console.log('다 찾았니?');
10 } catch (err) {
11 console.error(error); // 이 경우 에러가 나도 어느 부분인지 알 수 없다.
12 }
13}
14
15func();
await
쓸 때는 함수가 필요하다. 함수 이름 앞에 async
를 붙여주면 된다. 익명함수도 가능.
어느 부분에서 에러가 낫는지 알고 싶으면 try-catch를 개별적으로 해줘야 한다.
1async func() => {
2 try{
3 const user = await Users.findOne('zero');
4 console.log(user);
5 } catch (err) {
6 console.error(error);
7 }
8 try{
9 const updatedUser = await Users.update('zero', 'nero');
10 console.log(updatedUser);
11 const removedUser = await Users.remove('nero');
12 console.log(removedUser);
13 console.log('다 찾았니?');
14 } catch (err) {
15 console.error(error);
16 }
17}
18
19func();
3.1 Node Module System
Module System
모듈 시스템은 ES2015에서 나온 획기적인 변화이다. 자바스크립트는 예전부터 리소스 관리가 어려워서 말이 많았다. 리소스는 웹 페이지를 구성하는 자원들이다. 현재 웹에서는 해당 페이지에 필요한 모든 파일을 미리 불러와야 하고, 그 파일들이 사용하는 변수가 겹치지 않나 잘 살펴봐야 한다. 이런 부분을 해결한 것이 모듈 시스템이다.
모듈 만들기
아래 만든 JS파일을 다른 프로그램에서 쓰고 싶다면 어떻게 해야 할까?
1// var.js
2const odd = "홀수입니다.";
3const even = "짝수입니다.";
4
5console.log(odd);
6even = even + 'zero';
1// func.js
2console.log(odd);
3console.log(even);
HTML
프론트엔드 방식(HTML)이면 아래처럼 불러들여서 사용한다.
1<!-- example.html -->
2<script src="var.js"></script>
3<script src="func.js"></script>
Javascript
먼저 불러들여지는 파일에서 export가 가능하도록 module.exports
를 입력해준다.
1// var.js
2const odd = "홀수입니다.";
3const even = "짝수입니다.";
4
5// 모듈로 만들어질 준비가 되었다는 선언
6module.exports = { // ES6의 문법. 이전에는 module.exports={ odd: odd, even:even }으로 입력
7 odd,
8 even,
9};
위 코드의 모듈 선언과 동일한 의미로 아래와 같이 작성할 수 있다.
1// 위 코드와 동일한 의미로 아래와 같이 쓸 수도 있다.
2exports.odd = odd;
3exports.even = even;
module.exports === exports
.
exports는 객체 속성만 담을 수 있다. 그러므로 아래 코드에서 module.exports = checkOddEven;
는 위 코드처럼 exports.checkOddEven = checkOddEven
으로 바꿀 수 없다.
var.js
에 있는 변수 odd와 even을 require
를 통해서 불러온다. 변수로 불러들이게 되면 문제가 발생했을 때 역추적이 용이하다.
1// func.js
2const { odd, even } = require('./var.js'); // var.js 에 있는 변수 odd와 even을 불러온다.
3
4console.log(odd);
5console.log(even);
6
7function checkOddEven(num) {
8 if (num % 2) {
9 return odd;
10 }
11 return even;
12}
13
14module.exports = checkOddEven;
위 코드처럼 비구조화 할당을 하지 않게 되면 아래처럼 통째로 불러들인 후 매개변수를 통해서 읽어들어야 한다.
1const variable = require('./var.js');
2
3console.log(variable.odd);
4console.log(variable.even);
아래 코드는 var.js
변수와 fund.js
의 checkNumber를 모두 가져왔다.
1// index.js
2const { odd, even } = require('./var.js');
3const checkNumber = require('./func.js');
4
5function checkStringOddEven(str) {
6 if (str.length % 2) {
7 return odd;
8 }
9 return even;
10}
11
12console.log(checkNumber(10));
13console.log(checkStringOddEven('hello'));
3.2 global 객체
브라우저의 내장 객체
Node의 전역 객체는 global
이다. (window 아님. 최신 브라우저는 node와 통일하기 위해 global이 반영 됨.)
(global은 사용하지 않는 것이 가장 좋다. 프로그래밍에서 리스크가 매우 크다.)
1// globalA.js
2module.exports = () => global.message;
1// globalB.js
2const A = require('./globalA,js')
3global.message = '안녕하세요'
4
5console.log(A()); // 안녕하세요
3.3 console 객체
1> console
2console {debug: ƒ, error: ƒ, info: ƒ, log: ƒ, warn: ƒ, …}
3assert:ƒ assert()
4clear:ƒ clear()
5context:ƒ context()
6count:ƒ count()
7debug:ƒ debug()
8dir:ƒ dir()
9dirxml:ƒ dirxml()
10error:ƒ error()
11group:ƒ group()
12groupCollapsed:ƒ groupCollapsed()
13groupEnd:ƒ groupEnd()
14info:ƒ info()
15log:ƒ log()
16markTimeline:ƒ markTimeline()
17memory:(...)
18profile:ƒ profile()
19profileEnd:ƒ profileEnd()
20table:ƒ table()
21time:ƒ time()
22...
Example
console.time()
1console.time('전체시간');
2
3console.log('a');
4console.log('b');
5
6console.time('시간측정');
7for(let i = 0; i < 100000; i++){
8 continue;
9}
10console.timeEnd('시간측정');
11
12console.timeEnd('전체시간');
console.time('인자')
, console.timeEnd(인자)
에서 인자가 같아야 그 사이의 시간을 측정한다.
console.dir()
: 객체 전용 로그
1const obj = {
2 outside: {
3 inside: {
4 key: 'value',
5 },
6 },
7};
8
9
10console.dir(obj, {color: false, depth:2});
11console.dir(obj, {color: true, depth:1});
console.trace()
: 호출되는 경로를 추적해준다.
1function b(){
2 console.trace('에러 위치 추적');
3}
4function a(){
5 b();
6}
7a();
3.4 타이머 : setTimeout, setInterval, setImmediate
setTimeout, setInterval
1const timeout = setTimeout(() => {
2 console.log('1.5초 후 실행');
3}, 1500);
4
5const interval = setInverval(() => {
6 console.log('1초마다 실행');
7}, 1000);
8
9const timeout2 = setTimeout(() => {
10 console.log('실행되지 않습니다.');
11}, 3000);
12
13setTimeout(() => {
14 clearTimeout(timeout2);
15 clearInterval(interval);
16}, 2500);
1clearTimeout(timeout); // ex) 비밀번호 맞추면 타이머 해제
2clearInterval(interval);
setImmediate()
Node에는 setImmediate도 있다. 안에 들어 있는 함수를 이벤트루프로 보내줘서 비동기로 실행순서가 달라질 수 있어서 setImmediate를 사용한다. 즉, 바로 이벤트루프로 보내고 싶을 때 사용한다.
1cost im = setImmediate(() => console.log('즉시 실행'));
2clearImmediate(im);
3.5 ______filename, __dirname, process
______filename, __dirname
__filename
: 현재 파일경로__dirname
: 현재 파일이 들어 있는 경로
1console.log(__filename); // /Users/gouda/dev/JavaScript/exercise/test1.js
2console.log(__dirname); // /Users/gouda/dev/JavaScript/exercise
node는 브라우저 바깥에서 작동되므로 위 코드로 파일이 어디에서 실행되고 있는지 알 수 있다.
process
global.process
= process
스레드보다 좀더 큰 개념이 process.
하나의 프로그램이라고 생각해도 무방.
현재 노드가 실행하고 있는 JS 프로그램에 대한 정보들이 process에 담겨 있는 것이다.
1> proecess.version # node version
2> process.arch # bit
3> process.platform # mac, windows 등 platform 정보
4> process.pid # process id
5> process.uptime() # node 프로그램이 실행된지 얼마나 지났는지 알려줌
6> process.cwd() # node를 어디에서 실행했는지
7> process.execPath # node가 설치된 경로
8> process.cpuUsage() # 현재 CPU 사용량
9> process.exit() # process 종료
3.6 OS 모듈
OS 모듈은 내장 모듈. 운영체제와 관련된 모듈.
1require('os')
2const os = require('os');
3os.arch()
4os.type()
5os.uptime()
6os.hostname()
7os.release()
8os.homedir()
9os.tmpdir()
10os.freemem() // 추가로 사용 가능한 메모리
11os.totalmem() // 전체 사용 가능한 메모리
12os.cpus() // cpu 정보 알려줌
3.7 path 💡
매우 빈번하게 쓰이고 중요.
1const path = require('path');
2
3path.sep // 경로 구분자
4path.deilimiter // 환경변수 구분자
5path.dirname // 파일에서 사용가능
6path.extname // 확장자
7path.basename // 파일명
8path.parce // 구조 분해
9path.format // parsing 했던 것을 다시 합쳐줌
10path.normalize // 경로 잘못 친 부분 수정해서 입력해줌
11path.isAbsolute // 절대경로인지 상대경로인지 알려줌
12path.relative // 첫 번째 경로에서 두번째 경로로 가는 상대 경로를 알려줌
13path.join // 조각난 경로들을 하나로 합쳐줌
14path.resolve // join과 비교 해보기. resolve는 절대 경로 고려하고 합침
3.8 url 모듈 💡
URL 구조
위 쪽은 기존 방식의 주소 체계 (url.parse)
아래쪽은 WHATWGQ방식의 주소 체계(url.URL)
1const url = require('url'); // url 모듈을 불러옴
2
3const URL = url.URL;
4const myURL = new URL("https://www.youtube.com/watch?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23")
5
6console.log(myURL);
7
8// output
9URL {
10 href:
11 'https://www.youtube.com/watch?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23',
12 origin: 'https://www.youtube.com',
13 protocol: 'https:',
14 username: '',
15 password: '',
16 host: 'www.youtube.com',
17 hostname: 'www.youtube.com',
18 port: '',
19 pathname: '/watch',
20 search:
21 '?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23',
22 searchParams:
23 URLSearchParams {
24 'v' => 'm8C37nS8zh8',
25 'list' => 'PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw',
26 'index' => '23' },
27 hash: '' }
28
29
30console.log(url.format(myURL));
31
32// output
33https://www.youtube.com/watch?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23
1const parsedUrl = url.parse('https://www.youtube.com/watch?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23')
2
3console.log(parsedUrl)
4
5// output
6Url {
7 protocol: 'https:',
8 slashes: true,
9 auth: null,
10 host: 'www.youtube.com',
11 port: null,
12 hostname: 'www.youtube.com',
13 hash: null,
14 search:
15 '?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23',
16 query:
17 'v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23',
18 pathname: '/watch',
19 path:
20 '/watch?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23',
21 href:
22 'https://www.youtube.com/watch?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23' }
📌 기존 방식(url.parse)은 호스트가 없을 때도 쓸 수 있다. WHATWG방식(url.URL)은 search 처리가 편리하다.
1const url = require('url'); // url 모듈을 불러옴
2
3const URL = url.URL;
4const myURL = new URL("https://www.youtube.com/watch?v=m8C37nS8zh8&list=PLcqDmjxt30RsbFOspFG3EsxMwhFSnGFLw&index=23")
5
6console.log(myURL.searchParams);
7
8myURL.searchParams.getAll('category');
9myURL.searchParams.get('list');
10myURL.searachParams.has('page');
11
12myURL.searachParams.keys();
13myURL.searachParams.values();
14
15myURL.searachParams.append('filter', 'es3'); // &filter=es3 이 주소에 추가 됨
16myURL.searachParams.append('filter', 'es5'); // append는 기존에 있던 것에 추가
17myURL.searachParams.getAll('filter');
18
19myURL.searachParams.set('filter', 'es6'); // set은 기존 것을 지우고 수정
20myURL.searachParams.getAll('filter');
21
22myURL.serachParams.toString();
23myURL.search = myURL.searchParams.toString();