2022.09.06 경일 메타버스 23주차 2일 수업내용. Node.js - 노드, 자바스크립트 ES2015+
p. 24 ~ 38
Node.js(이하 노드)는 Chrome V8 Javascript 엔진으로 빌드된 Javascript 런타임이다.
서버 :
네트워크를 통해 클라이언트에 정보나 서비스를 제공하는 컴퓨터 또는 프로그램
클라이언트 : 요청을 보내는 주체
즉, 서버는 클라이언트의 요청에 대해 응답한다.
런타임 (Runtime) :
특정 언어로 만든 프로그램들을 실행할 수 있는 환경
노드 - V8 엔진, libuv 라이브러리 사용
V8 엔진 : 구글 크롬 출시
libuv 라이브러리 : 노드의 특성 구현
V8 엔진, libuv 라이브러리 모두 C와 C++로 구현
노드의 특성
이벤트 기반
논 블로킹 I/O 모델
이벤트 기반 (Event-driven) :
이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식
이벤트 루프 (Event Loop) :
여러 이벤트가 발생했을 때 어떤 순서로 콜백 함수를 호출할지를 판단
컨텍스트 (Context) :
함수가 호출되었을 때 생성되는 환경
노드는 자바스크립트 코드의 맨 위부터 한 줄씩 실행
함수 호출 부분을 발견하면 함수를 호출 스택 (Call Stack)에 입력
스택이므로, 넣은 역순으로 실행 완료 (후입선출)
이벤트 루프 (Event Loop) :
이벤트 발생 시 호출할 콜백 함수들을 관리, 호출된 콜백 함수의 실행 순서를 결정.
노드가 종료될 때까지 이벤트 처리 작업을 반복 → 루프
백그라운드 (Background) :
타이머(ex. setTimeout 등)나 이벤트 리스너들이 대기하는 곳.
자바스크립트가 아닌 다른 언어로 작성된 프로그램.
여러 작업이 동시에 실행될 수 있다.
태스크 큐 (Task Queue) :
이벤트 발생 후, 백그라운드에서 태스크 큐로 타이머나 이벤트 리스너의 콜백 함수를 전송.
정해진 순서대로 콜백들이 나열되므로 콜백 큐라고도 한다.
보통 완료된 순서대로 나열.
I/O 작업은 동시에 처리될 수 있다.
I/O 작업 : 입출력 작업
파일 시스템 접근, 네트워크를 통한 요청 등
논 블로킹 :
이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행
블로킹 :
이전 작업이 끝나야만 다음 작업을 수행
작업들이 동시에 처리될 수 있는 작업이라는 전제 하에, 논 블로킹 방식이 블로킹 방식보다 빠르다.
노드는 I/O 작업을 백그라운드로 넘겨 동시에 처리한다.
→ 동시에 처리 가능한 작업들은 최대한 묶어야 시간이 절약된다.
의의 :
논 블로킹을 통해 실행 순서를 바꿔줌으로써 간단한 작업들이 대기하는 상황을 막을 수 있다.
주의 : 논 블로킹과 동시가 같은 의미가 아니다.
노드에서, 블로킹과 논 블로킹 / 동기와 비동기는 유사한 개념
동기 (Synchronous) :
동시에 일어난다. 요청과 그 결과가 동시에 일어난다.
설계가 간단하고 직관적이지만 결과가 주어질 때까지 대기해야 한다.
비동기 (Asynchronous) :
동시에 일어나지 않는다. 요청과 결과가 동시에 일어나지 않을 거라는 약속.
동기보다 복잡하지만 결과가 주어지는 동안 다른 작업을 할 수 있다. 자원 사용이 효율적.
싱글 스레드 : 스레드가 하나뿐
프로세스 :
운영체제에서 할당하는 작업의 단위.
프로세스 간에 메모리 등의 자원을 공유하지 않는다.
스레드 :
프로세스 내에서 실행되는 흐름의 단위.
프로세스는 스레드를 여러 개 생성해 여러 작업을 동시에 처리할 수 있다.
스레드들은 부모 프로세스의 자원을 공유한다.
같은 주소의 메모리에 접근 가능하므로 데이터를 공유할 수 있다.
엄밀히 말해 노드는 싱글 스레드가 아니다.
⇒ 직접 제어 가능한 스레드가 하나
I/O 작업을 처리할 때는 멀티 스레딩보다 멀티 프로세싱이 효율적
⇒ 노드는 멀티 프로세싱을 많이 한다.
멀티 스레드 방식은 프로그래밍이 어렵다 → 멀티 프로세싱 방식을 대신 사용
멀티 스레딩과 멀티 프로세싱 비교
멀티 스레딩 | 멀티 프로세싱 |
---|---|
하나의 프로세스 안에서 여러 개의 스레드 사용 | 여러 개의 프로세스 사용 |
CPU 작업이 많을 때 사용 | I/O 요청이 많을 때 사용 |
프로그래밍이 어려움 | 프로그래밍이 비교적 쉬움 |
스레드 풀 (Thread Pool)
특정 동작을 수행할 때 스스로 멀티 스레드를 사용
암호화, 파일 입출력, 압축
워커 스레드 (Worker Thread)
노드 12 버전에서 안정화된 기능
사용자가 직접 다수의 스레드를 제어 가능
CPU 작업(연산이 많은 작업)이 많은 경우 사용
p. 38 ~ 40
노드를 서버로 사용할 때의 장단점은 싱글 스레드, 논 블로킹 모델의 장단점과 크게 다르지 않다.
장점
개수는 많지만 크기는 작은 데이터를 실시간으로 주고받는 데 적합하다.
서버는 기본적으로 I/O 요청이 많이 발생하므로, I/O 처리를 잘하는 노드가 좋다.
논 블로킹 방식으로 I/O 작업을 처리하여 하나의 스레드가 많은 수의 I/O를 감당할 수 있다.
웹 서버가 내장되어 있어 입문자가 쉽게 접근할 수 있다.
자바스크립트를 사용하여 브라우저와 통일된 하나의 언어로 웹사이트를 개발할 수 있고, 이로써 개발 생산성을 획기적으로 높인다.
JSON이 자바스크립트 형식이라 쉽게 처리할 수 있다.
안전성, 보안성 측면에서 검증되었다.
단점
CPU를 많이 사용하는 작업을 위한 서버로 권장되지 않는다.
CPU 부하가 큰 작업에는 적합하지 않다.
다른 언어에 비해 속도가 많이 느리다.
싱글 스레드 방식이라 하나뿐인 스레드가 멈추지 않도록 잘 관리해야 한다.
노드의 장단점
장점 | 단점 |
---|---|
멀티 스레드 방식에 비해 적은 컴퓨터 자원 사용 | 기본적으로 싱글 스레드라 CPU 코어를 하나만 사용 |
I/O 작업이 많은 서버로 적합 | CPU 작업이 많은 서버로는 부적합 |
멀티 스레드 방식보다 쉬움 | 하나뿐인 스레드가 멈추지 않도록 관리가 필요 |
웹 서버가 내장되어 있음 | 서버 규모가 커졌을 때 서버 관리가 어려움 |
자바스크립트 사용 → 웹 브라우저와 통일 | 어중간한 성능 |
JSON 형식과 쉽게 호환됨 | |
검증된 안정성과 보안성 |
p. 41
노드는 웹, 모바일, 데스크톱 애플리케이션 개발에도 사용된다.
노드 기반 웹 프레임 워크
앵귤러 (Angular) :
구글 진영 프론트엔드 앱 개발
리액트 (React) :
페이스북 진영 프론트엔드 앱 개발
뷰 (Vue)
모바일 개발 도구
데스크톱 개발 도구
p. 67 ~ 68
var는 호이스팅 등의 문제로 코드 관리가 어려워 const와 let이 대체
var :
함수 스코프
const :
블록 스코프
상수, 초기화해야 하며 한 번만 값을 할당
let :
블록 스코프
변수
호이스팅 :
실제 선언된 위치와 상관없이 메모리 접근이 가능한 문제.
더 정확히는, 변수의 정의가 그 범위에 따라 선언과 할당으로 분리되어
변수의 선언을 항상 컨텍스트 내의 최상위로 끌어올리는 것을 의미한다.
예시 코드
x = 10;
console.log(x); // 10
if (true) {
var x = 3;
}
console.log(x); // 3
if (true) {
const y = 3;
}
//console.log(y); // Error
p. 68
백틱 (Tab키 위, ` )으로 감싸는 문자열
문자열 안에 변수를 넣을 수 있다.
다른 따옴표와 함께 사용 가능
예시 코드
const num1 = 1;
const num2 = 2;
const result = 3;
const string = `${num1} 더하기 ${num2}는 '${result}'`;
console.log(string); // 1 더하기 2는 '3'
p. 68 ~ 70
코딩의 편의를 위한 문법, 생략 위주
객체 리터럴 :
컨텐츠를 그대로 대입하는 방법으로 객체를 생성하는 방식
객체의 메소드에 함수를 연결할 때 콜론(:)과 function을 붙이지 않아도 된다.
sayNode: sayNode처럼 속성명과 변수명이 동일한 경우에는 한 번만 써도 된다.
객체의 속성명은 동적으로 생성할 수 있다.
예시 코드
// Old
var sayNode = function() {
console.log('Node');
};
var es = 'ES';
var oldObject = {
sayJS: function() {
console.log('JS');
},
sayNode: sayNode,
};
oldObject[es + 6] = 'Fantastic';
oldObject.sayNode(); // Node
oldObject.sayJS(); // JS
console.log(oldObject.ES6); // Fantastic
// New
const newObject = {
sayJS() {
console.log('JS');
},
sayNode,
[es + 6]: 'Fantastic',
};
newObject.sayNode(); // Node
newObject.sayJS(); // JS
console.log(newObject.ES6); // Fantastic
p. 70 ~ 71
람다식과 비슷하게 이해 가능할 것 같다.
=> 로 함수를 선언
함수 내부에 return문 이외에 없는 경우 축약 가능
매개 변수가 한 개면 소괄호로 묶어주지 않아도 된다.
예시 코드 1
function add1(x, y) {
return x + y;
}
const add2 = (x, y) => {
return x + y;
}
const add3 = (x, y) => x + y;
const add4 = (x, y) => (x + y);
function not1(x) {
return !x;
}
const not2 = x => !x;
기존 function과 다른 this 바인드 방식
⇒ 상위 스코프의 this를 그대로 물려받는다.
예시 코드 2
// function
var relationship1 = {
name: 'zero',
friends: ['nero', 'hero', 'xero'],
logFreinds: function () {
var that = this; // relationship1을 가리키는 this를 that에 저장
this.friends.forEach(function (friend) {
console.log(that.name, friend);
});
},
};
relationship1.logFreinds();
// =>
const relationship2 = {
name: 'zero',
friends: ['nero', 'hero', 'xero'],
logFriends() {
this.friends.forEach(friend => {
console.log(this.name, friend);
});
},
};
relationship2.logFriends();
p. 71 ~ 73
구조분해 할당을 사용하면 객체와 배열로부터 속성이나 요소를 쉽게 꺼낼 수 있다.
예시 코드 1
// 객체의 속성을 같은 이름의 변수에 대입
// Old
var candyMachine = {
status: {
name: 'node',
count: 5,
},
getCandy: function () {
this.status.count--;
return this.statucs.count;
},
};
var getCandy = candyMachine.getCandy;
var count = candyMachine.status.count;
// New
const candyMachine = {
status: {
name: 'node',
count: 5,
},
getCandy() {
this.status.count--;
return this.status.count;
},
};
const { getCandy, status: { count } } = candyMachine;
candyMachine 객체 안의 속성을 찾아서 변수와 매칭
다만 구조분해 할당을 사용하면 함수의 this가 달라질 수 있다.
예시 코드 2
// 배열에 대한 구조분해 할당
// 배열 array의 첫 번째, 두 번째 요소와 네 번째 요소를 변수에 대입
// Old
var array = ['nodejs', {}, 10, true];
var node = array[0];
var obj = array[1];
var bool = array[3];
// New
const array = ['nodejs', {}, 10, true];
const [node, obj, , bool] = array;
구조분해 할당 문법은 코드 줄 수를 상당히 줄이고 모듈 시스템에 유용
p. 73 ~ 75
자바스크립트는 프로토타입 기반으로 동작
프로토타입 :
디자인 패턴으로, 객체를 생성할 때 원본이 되는 객체를 복사해서 생성하는 패턴이다.
자바스크립트의 경우, 함수(생성자)를 이용해 원본을 복사하는 형식으로 객체를 생성한다.
프로토타입의 뼈대는 다음과 같다고 할 수 있다.
객체는 함수를 사용해서 만들어지고, 객체는 함수의 프로토타입 객체를 복제하여 생성된다.
모든 객체는 자신이 어떤 원본 객체를 복제하여 생성된 것인지에 대한 정보를 가지고 있다.
참고
예시 코드
// 프로토타입 상속 예제 코드
var Human = function(type) {
this.type = type || 'human';
};
Human.isHuman = function(human) {
return human instanceof Human;
}
Human.prototype.breathe = function() {
alert('h-a-a-a-m');
};
var Zero = function(type, firstName, lastName) {
Human.apply(this, arguments);
this.firstName = firstName;
this.lastName = lastName;
};
Zero.prototype = Object.create(Human.prototype);
Zero.prototype.constructor = Zero; // 상속하는 부분
Zero.prototype.sayName = function() {
alert(this.firstName + ' ' + this.lastName);
};
var oldZero = new Zero('human', 'Zero', 'Cho');
Human.isHuman(oldZero); // true
// Human 생성자 함수를 Zero 생성자 함수가 상속한다.
// Human.apply와 Object.create 부분이 상속받는 부분
// 클래스 기반 상속 예제 코드
class Human {
constructor(type = 'human'){
this.type = type;
}
static isHuman(human) {
return human instanceof Human;
}
breathe() {
alert('h-a-a-a-m');
}
}
class Zero extends Human {
constructor(type, firstName, lastName) {
super(type);
this.firstName = firstName;
this.lastName = lastName;
}
sayName() {
super.breathe();
alert(`${this.firstName} ${this.lastName}`);
}
}
const newZero = new Zero('human', 'Zero', 'Cho');
Human.isHuman(newZero); // true
// 전반적으로 class 안으로 그룹화되었다.
// extends 키워드로 쉽게 상속 가능