티스토리 뷰
호출스택
노드는 자바스크립트 코드의 맨 위부터 한줄씩 실행하며 호출 스택에 순서대로 쌓아 놓고 맨 위에서부터 순서대로 하나씩 실행한다.
코드를 실행할때 기본적으로 호출스택에 anonymous함수가 맨 처음 쌓이는데 이 함수는 처음 실행 시의 **전역 콘텍스트(global context)**를 의미하고 **콘텍스트(context)**란 함수가 호출되었을때 생성되는 환경을 의미한다.
비동기 함수의 콜백함수를 실행하는데에 호출스택의 개념만으로는 설명이 안되고 이벤트 루프, 태스트 큐(task queue), **백그라운드라(backgound)**는 개념과 함께 설명한다.
- 이벤트 루프: 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복하는 과정을 말한다. 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정하는 역할을 한다.
- 백그라운드(backgound): setTimer 같은 타이머나 이벤트 리스너들이 대기하는 곳이다. 이 영역은 자바스크립트가아닌 c++같은 언어로 작성된 영역이다. 자바스크립트로 동작하지 않고 c++같은 언어로 동작하기에 여러 작업이 동시에 실행 될 수 있다.
- 태스크 큐(task queue): 이벤트 발생 후, 백그라운드에서 실행된 작업에 의해 나온 콜백함수들을 태스크 큐에 보낸다. 콜백함수들이 정해진 순서대로 들어있고 순서대로 실행된다.(보통 완료된 순서대로 들어있지만 Promise 같은 함수들은 먼저 실행된다.) 콜백 큐라도고 불린다.
var, const, let
var
함수 스코프를 가지는 변수 타입이다.
블록 스코프가 아니기 때문에 if, while, for문의 블록과 관계없이 변수 접근이 가능하다.
if(true){
var x = 3;
}
console.log(x); // 3
const
블록 스코프를 가지는 변수 타입이다.
블록 스코프는 if, while, for, function 등의 중괄호({ }) 밖에 존재하는 변수에 접근이 불가능하다
const를 사용해 함수 스코프대신 블록 스코프를 사용함으로서 호이스팅 같은 문제를 해결할 수 있다.
const 변수는 한번 선언하면 재할당이 불가능하다.
if(true){
const y = 3;
}
console.log(y); // Uncaught ReferenceError: y is not defined
let
블록 스코프를 가지는 변수타입이다.
const와 다르게 여러번 변수 재할당이 가능하다.
const a = 0;
a = 1; // Uncaught TypeError: Assignment to constant variable
let b = 0;
b = 1;
템플릿 문자열
기존 문자열과 달리 백틱(`)으로 감싸 문자열 안에 배열을 쉽게 넣을 수 있다.
// 템플릿 문자열 X
var won1 = 10000;
var string1 = "가격은: " + won + "원 입니다.";
console.log(string1); // 가격은 10000원 입니다.
//템플릿 문자열 O
const won2 = 10000;
const string2 = `가격은 ${won}원 입니다.`;
console.log(string2); // 가격은 10000원 입니다.
객체 리터럴
객체 리터럴에 편리한 기능들이 있다.
- 객체의 메서드에 함수를 연결할 때 콜론(:)과 function을 붙이지 않아도 된다.
// ES5
var oldObject = {
sayJS: function(){
console.log('JS');
},
};
//ES 2015
const newObject = {
sayJS(){
console.log('JS');
},
};
- 객체의 속성명과 할당하는 변수의 이름이 동일한경우 한번만 써도 된다.
{name: name, age:age} // ES5
{name, age} // ES2015
var sayNode = function(){
console.log('Node');
}
// ES5
var oldObject = {
sayNode: sayNode,
}
// ES2015
const newObject = {
sayNode,
};
- 객체의 속성명을 동적으로 생성할 수 있다.
// ES5
var es = ES;
var oldObject = {};
oldObject[es+6] = 'Fantastic';
// ES2015
const newObject = {
[es + 6]: 'Fantastic',
};
화살표 함수(Arrow Function)
화살표 함수에서는 function 선언 대신 ⇒ 기호로 함수를 선언하고 변수에 대입하여 나중에 재사용할 수 있다.
/* 기존 function */
function add1(x, y){
return x + y;
}
function not1(x){
return !x;
}
/* 화살표 함수 사용 */
const add2 = (x,y)=> {
return x + y;
}
const not2 = x => !x;
기존 function과 다른 점은 this 바인드 방식이다.
일반 함수는 호출 시점의 호출자에 따라 this가 동적으로 바인딩되는 반면, 화살표 함수는 선언된 상위 스코프의 this를 정적으로 캡처하여 사용한다.
구조 분해 할당(Destructuring Assignment)
구조 분해 할당을 사용하면 객체와 배열로부터 속성이나 요소를 쉽게 꺼낼수 있다.
const numbers = [1, 2, 3];
// 기존 방식
const first = numbers[0];
const second = numbers[1];
const third = numbers[2];
console.log(first, second, third); // 출력: 1 2 3
// 구조 분해 할당 사용
const [a, b, c] = numbers;
console.log(a, b, c); // 출력: 1 2 3
const user = {
id: 1,
info: {
name: '홍길동',
address: {
city: '서울',
zip: '12345'
}
}
};
const {
id,
info: {
name,
address: { city, zip }
}
} = user;
console.log(id, name, city, zip); // 출력: 1 홍길동 서울 12345
클래스
자바스크립트에서의 클래스는 프로토타입 기반 문법을 보기 좋게 클래스로 바꾼것이라고 생각하면 된다.
프로미스
프로미스는 비동기 작업의 완료(또는 실패)를 나타내는 객체다. 프로미스를 사용하면 콜백 지옥(callback hell)을 피하고, 비동기 코드를 더 직관적으로 작성할 수 있다.
- resolve(value): 프로미스를 이행 상태로 만들고, value를 결과로 전달한다.
- reject(reason): 프로미스를 거부 상태로 만들고, reason을 에러로 전달한다.
- .then()**:**프로미스가 이행되었을 때 실행할 콜백 함수를 지정한다.
- .catch(): 프로미스가 거부되었을 때 실행할 콜백 함수를 지정한다.
- .finally(): 프로미스가 이행되든 거부되든 상관없이 항상 실행되는 콜백 함수를 지정한다.
const myPromise = new Promise((resolve, reject) => {
// 비동기 작업 수행
const success = true; // 예시를 위한 변수
if (success) {
resolve('작업 성공!');
} else {
reject('작업 실패...');
}
});
myPromise
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
})
.finally(() => {
console.log('프로미스 처리 완료');
});
Promise.resolve는 즉시 resovle하는 프로미스를 만든다. Promise.reject도 마찬가지로 즉시 reject하는 프로미스를 만든다.
Promise.allSettled는 모든 프로미스가 이행되거나 거부될 때까지 기다리고, 각각의 결과를 배열로 반환한다.
const promise1 = Promise.resolve('성공 1');
const promise2 = Promise.reject('실패 2');
const promise3 = Promise.resolve('성공 3');
Promise.allSettled([promise1, promise2, promise3])
.then((results) => {
results.forEach((result) => {
if (result.status === 'fulfilled') {
console.log('이행:', result.value);
} else {
console.log('거부:', result.reason);
}
});
/*
출력:
이행: 성공 1
거부: 실패 2
이행: 성공 3
*/
});
async/await
async/await는 프로미스를 더 간편하게 사용할 수 있게 해주는 문법이다. 내부적으로 프로미스를 기반으로 동작하고 콜백 지옥을 해결한다.
// 비동기 작업 함수들
function doTask1(callback) {
setTimeout(() => {
console.log('Task 1 completed');
callback();
}, 1000);
}
function doTask2(callback) {
setTimeout(() => {
console.log('Task 2 completed');
callback();
}, 1000);
}
function doTask3(callback) {
setTimeout(() => {
console.log('Task 3 completed');
callback();
}, 1000);
}
// 콜백 지옥
doTask1(() => {
doTask2(() => {
doTask3(() => {
console.log('All tasks completed');
});
});
});
// async/await를 사용해 콜백 지옥 해결
async function executeTasks() {
try {
await doTask1();
await doTask2();
await doTask3();
console.log('All tasks completed');
} catch (error) {
console.error('Error:', error);
}
}
executeTasks();
Map/Set
Map
Map은 속성들간의 순서를 보장하고 반복문을 사용할수 있는 자바스크립트 자료구조이다
속성명으로 문자열이 아닌 값도 사용할 수 있고 size 메서드를 통해 속성의 수를 쉽게 알수 있다.
// 빈 Map 생성
const myMap = new Map();
// 키-값 쌍 추가
myMap.set('name', '홍길동');
myMap.set('age', 30);
myMap.set('isAdmin', true);
// 객체를 키로 사용
const objKey = { id: 1 };
myMap.set(objKey, '객체 키 값');
// 값 가져오기
console.log(myMap.get('name')); // 출력: 홍길동
console.log(myMap.get(objKey)); // 출력: 객체 키 값
// 키 존재 여부 확인
console.log(myMap.has('age')); // 출력: true
console.log(myMap.has('address')); // 출력: false
// Map의 크기 확인
console.log(myMap.size); // 출력: 4
// 키-값 쌍 삭제
myMap.delete('isAdmin');
console.log(myMap.has('isAdmin')); // 출력: false
// 모든 키-값 쌍 삭제
myMap.clear();
console.log(myMap.size); // 출력: 0
Set
Set은 중복을 허용하지 않는 자료구조다. 배열 자료구조를 사용하고 싶지만 중복을 허용하고 싶지 않을 때 배열 대신 사용하면 된다.
// 빈 Set 생성
const mySet = new Set();
// 값 추가
mySet.add(1);
mySet.add(5);
mySet.add('Hello');
mySet.add({ a: 1, b: 2 });
// 중복된 값 추가 시 무시됨
mySet.add(1);
mySet.add('Hello');
// Set의 내용 출력
console.log(mySet);
// 출력: Set(4) {1, 5, 'Hello', { a: 1, b: 2 }}
// 값 존재 여부 확인
console.log(mySet.has(5)); // 출력: true
console.log(mySet.has(3)); // 출력: false
// Set의 크기 확인
console.log(mySet.size); // 출력: 4
// 값 삭제
mySet.delete(5);
console.log(mySet.has(5)); // 출력: false
// 모든 값 삭제
mySet.clear();
console.log(mySet.size); // 출력: 0
널 병합/옵셔널 체이닝
널 병합 연산자
널 병합 연산자는 주로 || 연산자 대용으로 사용되며, falsy 값(0, ‘’, false, NaN, null, undefined) 중 null과 undefined만 따로 구분한다.
const userName = null ?? 'Guest';
console.log(userName); // 출력: 'Guest'
const score = 0 ?? 100;
console.log(score); // 출력: 0 (0은 null이나 undefined가 아니므로 기본값이 사용되지 않음)
const greeting = '' ?? 'Hello!';
console.log(greeting); // 출력: '' (빈 문자열은 null이나 undefined가 아니므로 기본값이 사용되지 않음)
const isActive = false ?? true;
console.log(isActive); // 출력: false (false는 null이나 undefined가 아니므로 기본값이 사용되지 않음)
옵셔널 체이닝
옵셔널 체이닝 연산자는 null이나 undefined의 속성을 조회하는 경우 에러가 발생하는 것을 막는다.
const user = {
name: '홍길동',
address: {
city: '서울',
zip: '12345'
}
};
// 기존 방식: 속성 존재 여부를 확인하며 접근
const city = user && user.address && user.address.city;
console.log(city); // 출력: '서울'
// 옵셔널 체이닝 사용
const cityOpt = user?.address?.city;
console.log(cityOpt); // 출력: '서울'
// 주소 정보가 없는 경우
const userWithoutAddress = { name: '김철수' };
const cityUndefined = userWithoutAddress?.address?.city;
console.log(cityUndefined); // 출력: undefined
// 함수 호출 시 옵셔널 체이닝
const userWithGreet = {
name: '이영희',
greet: function() {
console.log(`안녕하세요, ${this.name}님!`);
}
};
userWithGreet.greet?.(); // 출력: '안녕하세요, 이영희님!'
userWithoutAddress.greet?.(); // 아무 것도 출력되지 않음, 에러도 발생하지 않음
참고
'Node.js' 카테고리의 다른 글
[Node.js 교과서] 섹션 6 - 익스프레스 웹 서버 만들기 (0) | 2024.11.03 |
---|---|
[Node.js 교과서] 섹션 5 - 패키지 매니저 (1) | 2024.10.25 |
[Node.js 교과서] 섹션 4 - http 모듈로 서버 만들기 (1) | 2024.10.23 |
[Node.js 교과서] 섹션 3 - 노드의 기본 기능 익히기 (1) | 2024.10.19 |
[Node.js 교과서] 섹션 1 - 노드의 정의와 특성 (3) | 2024.10.11 |