22/12/12 TIL - Node.js, 노드 문법
학습목표
학습목표
- Javascript
- 자바스크립트 문법에 익숙해질 수 있다.
- CommonJS와 ECMAScript의 문법 차이를 알고 이를 적절하게 선택하여 사용할 수 있다.
- apply 함수, bind 함수 및 call 함수가 언제 어떻게 사용되어야 하는지를 설명할 수 있다.
- 자바스크립트의 데이터 종류에 대해 이야기할 수 있고, 각각 메모리에 어떻게 저장되고 사용 되고, 수거 되는지를 이해 할 수 있다.
- 실행컨텍스트의 정의와, 실행컨텍스트를 이루는 요소 및 그 하위요소를 이해할 수 있다. 이를 통해 함수가 실행될 때 어떠한 과정이 일어나는지를 말할 수 있다.
- this가 어떤 환경에서는 무엇으로 채택되는지에 대한 일련의 과정을 설명할 수 있다.
- 콜백함수가 어떤 것인지, 어떤 역할을 통해 사용되는지를 이해할 수 있다. 콜백 함수의 필수 내용인 비동기 개념에 대해 이해할 수 있다.
- 모던자바스크립트 튜토리얼(주어진 링크)를 모두 학습하고 관련된 내용을 조원들에게 설명할 수 있다. 이후 프로젝트에서도 활용할 수 있다.
- Node.js



체크리스트
- 자바스크립트 문법 중, 주요 개념(데이터의 메모리 저장 구조, 실행컨텍스트, this, 콜백함수, 클로저)에 대해 다음 질문에 답할 수 있어요
- var a = 3;이 메모리에 저장되는 과정을 설명
- a = 7; 이 되었을 때, 3이 더 이상 사용되지 않을 때 무슨일이 일어나는지 설명
- Call stack의 개념과 Lexical Environment 및 그 하위개념에 대해 설명
- func()와 obj1.func()의 차이를 설명할 수 있고, 각 케이스에 this가 무엇을 가리키는지도 설명
- 콜백지옥에서 벗어나는 방법을 제시하고 설명
- Node.js 입문/숙련
- 컴파일 언어와 인터프리터 언어의 정의를 각각 설명
- 싱글스레드, 멀티스레드 언어의 정의를 각각 설명
- 동기/비동기, 블로킹/논블로킹에 대해 설명
- cpu intensive task 처리에 왜 애로사항이 있는지 설명
- 자주 사용하는 내장 모듈인 OS, Cluster, Path, File System, HTTP에 대해 설명
- 웹 서버의 클라이언트 인증 방식에 대해 설명
Node.js
- Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임
- Node.js는 이벤트 기반, 논 블로킹 I/O 모델을 사용해 가볍고 효율적
- Node.js 패키지 생태계인 npm은 세계에서 가장 큰 오픈 소스 라이브러리 생태계
- Javascript를 브라우저가 아닌 컴퓨터에서 브라우저 없이 실행하게 도와주는 환경
https://nodejs.org/ko/ (설치 홈페이지)
Node.js
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
nodejs.org
파이참(자기 개발 툴)의 터미널에서 npm install node 로 패키지 설치
동기(Sync) & 비동기(Async)
동기로 실행 => 먼저 실행된 코드의 결과가 나올때까지 대기한다 => 기다렸다가 한번에 출력된다.
비동기로 실행 => 실행된 순서와 관계 없이 결과가 나온다 => 먼저 완료된 코드가 먼저 실행된다.
Blocking Model & Non-Blocking Model
- Blocking Model이란, 코드의 실행이 끝나기 전까지 실행 제어권을 다른곳에 넘기지 않아 다른 작업을 하지 못하고 대기하는 것을 말합니다.
- Non-Blocking Model이란, 코드의 실행이 끝나지 않아도 실행 제어권을 다른곳에 넘겨 다음 코드가 실행될 수 있는것을 말합니다.
- 자바스크립트는 Async + Non-Blocking Model을 채용하여 현재 실행중인 코드의 실행이 끝나지 않아도 다음 코드를 호출합니다.
- 결론적으로 자바스크립트는 각 명령들이 순서대로 실행될 수 있게 구현되어 있지만,
Non-Blocking Model에 의해 동기적 명령이 아닌 모든 함수는 비동기적으로 실행됩니다.
예시 (setTimeout : 특정 시간 뒤에 함수를 실행시킴, 밀리초 단위)
function first() {
console.log('First');
}
setTimeout(first, 1000); // 1000ms(1초) 뒤에 first 함수를 실행해준다.
console.log('Middle');
console.log('Last');
// Print: Middle
// Last
// First
- 순서도
- first 함수선언
- -> setTimeout(first, 1000) : 1초 뒤에 fisrt 함수 실행
- -> 아래 있는 console.log('middle'),('last)출력
- -> console.log('first')출력
- Blocking Model 이라면 1초 뒤에 first를 출력한 뒤 middle, last 순서로 출력되었을 것이다.
Promise
자바스크립트에서 비동기 처리를 동기 처리로 할 수 있게 하는 Bulit-in(미리 내부적으로 정의된)객체
new Promise(executor);
// 예제
new Promise((resolve, reject) => {
// 명령문
});
- Promise 생성자 인터페이스 executor에는 함수만 올 수 있으며 인자로 resolve, reject가 주입됩니다.
- executor는 Promise의 실행 함수라고 불리고, Promise가 만들어질 때 자동으로 실행됩니다. Promise가 연산을 언제 종료하는지 상관하지 않고, resolve, reject 중 하나를 무조건 호출해야합니다.
생성자란? 객체를 생성하는 함수 = 생성자 함수,
Promise도 객체로 생성자 함수를 이용해 Promise를 선언한다.
Promise의 상태
- 대기(Pending): 이행하거나 거부되지 않은 초기 상태.
- 이행(Fulfilled): 연산이 성공적으로 완료됨.
- 거부(Rejected): 연산이 실패함.
executor가 실행되어 resolve된 promise를 Fulfilled Promise(풀필드 프로미스)라고 한다.
const timerPromise = new Promise((resolve, reject) => { // 이곳에 정의된 함수가 executor
setTimeout(() => {
console.log('First');
resolve();
}, 1000);
});
// 이 시점에서 timerPromise는 Fulfilled Promise라고 부를 수 있다.
timerPromise.then(() => {
console.log('Middle');
console.log('Last');
});
// Print: First
// Middle
// Last
Promise.then : resolve가 실행된 경우 then 메소드에 작성된 함수가 실행된다.
const resolvePromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('First');
resolve('Resolve!'); // resolve를 실행할 때, 안에 데이터를 넣어줄 수 있습니다.
}, 1000);
});
resolvePromise.then((data) => {
console.log('Middle');
console.log('Last');
console.log(data);
})
// Print: First -> 1초 뒤에 출력됩니다.
// Middle
// Last
// Resolve!
Promise.catch : 에러가 throw 되거나 reject가 실행되면 catch 메소드에 작성된 함수가 실행된다.
const errorPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('First');
reject('Error!!'); // 직접 reject를 실행하면 프로미스에서 에러가 발생한것으로 간주됩니다.
}, 1000);
});
errorPromise.then(() => {
console.log('Middle');
console.log('Last');
}).catch((error) => {
console.log('에러 발생!', error);
});
// Print: '에러 발생! Error!!'
비동기 함수( Async Function )
특징
1. 결과 값은 항상 Promise 객체로 resolve 된다.
2. 비동기 함수 안에서만 await 연산자를 사용할 수 있다.
3. 비동기 함수는 함수가 실행되기 전까지 Promise를 생성하지 않습니다.
// 비동기 + 일반 함수
async function 함수이름() {
// 명령문
}
// 비동기 + 익명 함수
async function() {
// 명령문
}
// 비동기 + 화살표 함수
async () => {
// 명령문
}
비동기 함수를 쓰는 이유
await 연산자를 비동기 함수 안에서만 사용할 수 있는데 이를 활용하면 문법이 훨씬 간결해진다.
await 연산자
await를 사용하면 프로미스가 fulfill 상태가 되거나 rejected될때까지 함수의 실행을 중단하고 기다릴 수 있다.
Promise의 연산이 끝나면 함수에서 반환한 값을 얻을 수 있습니다.
await 연산자는 async 함수 안에서만 사용할 수 있습니다.
const result = await 값;
"값" 에는 프로미스가 아닌 다른 값이 들어갈 수도 있다. 프로미스가 아니라면 기다리지 않고 값을 반환한다.
async function 함수이름() {
const result = await 'Test!';
console.log(result);
}
함수이름();
// Print: 'Test!';
객체 리터럴
객체(Object) 는 0개 이상의 프로퍼티로 구성된 집합이며, 하나의 프로퍼티는 Key와 Value로 구성되어 있습니다.
리터럴(literal)은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법입니다.
여기서 객체 리터럴은 객체를 생성하기 위한 표기법입니다.
객체 리터럴은 객체를 생성하기 위해 Class를 먼저 선언하고 new 연산자와 함께 생성자를 호출할 필요가 없이 일반적인 숫자, 문자열을 만드는것과 유사하게 객체를 생성할 수 있습니다.
객체 리터럴은 중괄호 내에 0개 이상의 프로퍼티를 정의해서 선언합니다.
let objectLiteral = {
key: 'Value',
helloWorld: function () {
return "Hello world";
}
};
프로퍼티(Property) : 객체의 상태를 나타내는 값(Data), 프로퍼티는 키(Key)와 값(Value)으로 구성
const human = {
// 프로퍼티 키: 'name', 프로퍼티 값: '이용우'
name: '이용우',
// 프로퍼티 키: 'human age', 프로퍼티 값: 28
'human age': 28
}
메소드(Method) : 프로퍼티를 참조하고 조작할 수 있는 동작, 객체의 프로퍼티 값이 함수로 구성되어 있는 경우
let objectLiteral = {
key: 'Value', // 프로퍼티
helloWorld: function () { // 메서드
return "Hello world";
}
};
console.log(objectLiteral.helloWorld()); // Hello world
객체 리터럴을 활용해 사칙연산을 하는 객체를 만들어주세요!
let calculator = {
add: function (a, b) {
let add = a + b;
return add;
},
sub: function (a, b) {
let sub = a - b;
return sub;
},
mul: function (a, b) {
let mul = a * b;
return mul;
},
div: function (a, b) {
let div = a / b;
return div;
}
}
console.log(calculator.add(2, 5)); // 7
console.log(calculator.sub(2, 5)); // -3
console.log(calculator.mul(2, 5)); // 10
console.log(calculator.div(2, 5)); // 0.4
Error handling (에러 핸들링)
에러를 관리하는 방법, 예상치 못한 상황에 대처하는 방식
try/catch, throw, finaly
Try / Catch (예외 처리)
users에 들어 있는 이름들을 String.toUpperCase()로 대문자로 변경하려 할 때 문자열이 아닌 데이터가 들어오면 에러가 발생하게 됩니다.
const users = ["Lee", "Kim", "Park", 2];
try {
for (const user of users) {
console.log(user.toUpperCase());
}
} catch (err) {
console.error(`Error: ${err.message}`);
}
// LEE
// KIM
// PARK
// Error: user.toUpperCase is not a function
이렇게 예상 못한 에러에 대처하기 위해 try - catch 문으로 코드를 감쌀 수 있습니다.
try 문에서 에러가 발생하더라도 프로그램이 멈추지 않고 catch 문이 작동됩니다.
Throw (강제로 오류 발생시키기)
throw를 호출하면 그 즉시 현재 실행되고 있는 함수의 실행이 멈춥니다. (braek)
function withdraw(amount, account) {
if (amount > account.balance)
throw new Error("잔고가 부족합니다.");
account.balance -= amount;
console.log(`현재 잔고가 ${account.balance}남았습니다.`); // 출력되지 않음
}
const account = { balance: 1000 };
withdraw(2000, account);
// Error: 잔고가 부족합니다.
Finally (필요없는 자원 삭제)
finally는 에러 발생 여부와 상관없이 언제든 실행됩니다.
function errorException(isThrow) {
try {
console.log('자원을 할당하였습니다.');
if (isThrow) throw new Error();
} catch (error) {
console.log('에러가 발생했습니다.');
} finally {
console.log('자원을 제거하였습니다.');
}
}
errorException(false);
// 자원을 할당하였습니다.
// 자원을 제거하였습니다.
errorException(true);
// 자원을 할당하였습니다.
// 에러가 발생했습니다.
// 자원을 제거하였습니다.
클래스(class)
현실과 비슷한 개념(객체)을 나타내기 위한 도구를 클래스(Class)라고 부릅니다.
클래스 : 정보들을 묶는 틀
인스턴스 : 동일한 클래스를 이용해 생성한 객체
클래스를 미리 정의해놓으면 필요할 때마다 해당 클래스로 동일한 틀을 가진 객체를 생성할 수 있다
class User {
}
const user = new User();
user.name = "이용우";
user.age = 28;
user.tech = "Node.js";
console.log(user.name); // 이용우
console.log(user.age); // 28
console.log(user.tech); // Node.js
클래스는 빵틀, 변수 user는 빵이라고 볼 수 있다
생성자
constructor() 로 정의한 메소드
class User {
constructor(name, age, tech) { // User 클래스의 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
}
const user = new User("이용우", 28, "Node.js"); // user 인스턴스 생성
console.log(user.name); // 이용우
console.log(user.age); // 28
console.log(user.tech); // Node.js
this와 프로퍼티(Property)
빵틀과 빵 => this라고 표시함으로써 빵틀이 아닌 빵 하나의 값만 바꾼다
class User {
constructor(name, age, tech) { // User 클래스의 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
}
const user = new User("이용우", "28", "Node.js"); // user 인스턴스 생성
console.log(user.name); // 이용우
console.log(user.age); // 28
console.log(user.tech); // Node.js
생성자의 바디에서 this 키워드를 사용합니다. 이 this는 클래스를 이용해 만들어 질 객체 자신을 의미하고
뒤에 붙는 name, age, tech는 클래스를 이용해서 만들어질 객체의 속성(Propety)입니다.
생성자를 이용해 name, age, tech 인자값을 입력 받아 클래스 내부변수에 저장합니다.
메소드(Method) : 객체에 묶여 있는 함수
class User {
constructor(name, age, tech) { // User 클래스의 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
getName() { return this.name; } // getName 메서드
getAge() { return this.age; }. // getAge 메서드
getTech() { return this.tech; } // getTech 메서드
}
const user = new User("이용우", "28", "Node.js"); // user 인스턴스 생성
console.log(user.getName()); // 이용우
console.log(user.getAge()); // 28
console.log(user.getTech()); // Node.js
상속
일반적으로 클래스의 인스턴스는 선언한 클래스의 기능을 모두 상속합니다.
상속을 이용해 부모 클래스와 자식 클래스로 나뉠 수 있는데요. 부모 클래스의 경우 메서드, 내부 변수와 같은 정보를 자식 클래스에게 할당해줄 수 있습니다.
class User { // User 부모 클래스
constructor(name, age, tech) { // 부모 클래스 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
getTech(){ return this.tech; } // 부모 클래스 getTech 메서드
}
class Employee extends User{ // Employee 자식 클래스
constructor(name, age, tech) { // 자식 클래스 생성자
super(name, age, tech);
}
}
const employee = new Employee("이용우", "28", "Node.js");
console.log(employee.name); // 이용우
console.log(employee.age); // 28
console.log(employee.getTech()); // 부모 클래스의 getTech 메서드 호출: Node.js
super 키워드
- 함수처럼 호출할 수도 있고, this와 같이 식별자처럼 참조할 수 있는 키워드입니다.
- super 키워드를 호출하면 부모 클래스의 생성자(constructor)를 호출합니다.
- super 키워드를 참조하면 부모 클래스의 메서드(Method)를 호출할 수 있습니다.
class unit {
constructor(name, hp) {
this.name = name;
this.hp = hp;
}
healing(heal) {
if (this.hp <= 0) return console.log('이미 사망한 유저입니다.');
this.hp += heal
console.log(this.hp)
if (this.hp >= 100) this.hp = 100;
}
damaged(damage) {
if (this.hp <= 0) return console.log('이미 사망한 유저입니다.');
this.hp -= damage
console.log(this.hp)
if (this.hp <= 0) {
this.hp = 0;
return console.log('You Died');
}
}
}
let user_1 = new unit('경보기', 100)
user_1.damaged(99)
user_1.healing(25)
user_1.damaged(20)
user_1.healing(150)
user_1.damaged(100)