클로저는 외부 함수의 변수를 계속 참조하기 때문에, 이러한 변수들은 가비지 컬렉션되지 않습니다. 이로 인해 메모리 사용량이 증가할 수 있습니다.
3.2 성능 고려사항
클로저를 과도하게 사용하면 애플리케이션의 성능에 영향을 줄 수 있습니다. 특히 루프 내에서 클로저를 생성할 때 주의가 필요합니다.
function createFunctions() {
var result = [];
for (var i = 0; i < 5; i++) {
result.push(function() { return i; });
}
return result;
}
var functions = createFunctions();
for (var i = 0; i < functions.length; i++) {
console.log(functions[i]()); // 모두 5를 출력
}
이 예시에서 의도와 달리 모든 함수가 5를 반환합니다. 이를 해결하려면 즉시 실행 함수(IIFE)나 let 키워드를 사용해야 합니다.
4. 클로저의 실제 활용
4.1 모듈 패턴
클로저를 사용하여 모듈 패턴을 구현할 수 있습니다. 이는 관련 기능을 그룹화하고 네임스페이스를 관리하는 데 유용합니다.
논블로킹 I/O : 함수가 실행되는 중에도 다른 작업을 동시에 진행할 수 있는 장점 (비동기 작업을 할때 효율적인 특성)
-> I/O란 입력(Input)/출력(Output)의 약자로, 컴퓨터 및 주변장치에 대하여
데이터를 전송하는 프로그램, 운영 혹은 장치를 일컫는 말
싱글 스레드 : 스레드 하나를 사용하는 것이며, 동시에 하나의 작업만을 처리할 수 있습니다.
-> 스레드는 프로그램이 동작할 때, CPU 또는 프로세서를 사용하는 단위
이벤트 루프 : JS가 가지고 있는 싱글 스레드의 약점을 극복하기 위해 효율적으로 작업을 처리할 수 있는 특성
CMD -> cd C:\Program Files\nodejs 에 들어간 후
node -v 와 node 를 입력해 노드 설치 확인가능
REPL이란?
입력받은 코드를 읽어(Read) 메모리에 저장하고, 평가(Evaluate) 된 값을 출력합니다.
특정 신호를 받기 전까지 위의 과정을 반복(Loop)합니다.
입력하는 코드의 결과를 즉시 확인할 수 있는 개발 환경
(예) CMD, 터미널 또는 F12 검사를 통한 console환경
동기(Sync) & 비동기(Async)
동기와 비동기에 대한 개념
일반적으로 "동기로 실행된다"라고 함은, 먼저 실행된 코드 결과가 나올때까지 대기 하는 것을 말합니다.
동기 : 놀이기구(전부 내릴때까지 못탐) / 비동기 : 맛집(한명씩 나오면 들어감)
블로킹 모델 & 논블로킹 모델
Blocking Model이란, 코드의 실행이 끝나기 전까지 실행 제어권을 다른곳에 넘기지 않아 다른 작업을 하지 못하고 대기
Non-Blocking Model이란, 코드의 실행이 끝나지 않아도 실행 제어권을 다른곳에 넘겨 다음 코드가 실행
결론적으로 자바스크립트는 각 명령들이 순서대로 실행될 수 있게 구현되어 있지만,
Non-Blocking Model에 의해 동기적 명령이 아닌 모든 함수는 비동기적으로 실행됩니다.
프로미스(Promise)
자바스크립트에서 비동기 처리를 동기로 처리할 수 있게 돕는 Built-in(미리 내부적으로 정의된)객체 유형입니다.
이 객체를 이용하면 여러분은 비동기 처리를 아주 손쉽게 할 수 있습니다.
자바스크립트는 비동기(논블로킹)이지만 Promise 함수를 통해 동기 처리도 가능하다! (예약)
Promise 생성자 인터페이스 executor에는 함수만 올 수 있으며 인자로 resolve, reject가 주입됩니다.
executor는 Promise의 실행 함수라고 불리고, Promise가 만들어질 때 자동으로 실행됩니다.
Promise가 연산을 언제 종료하는지 상관하지 않고, resolve, reject 중 하나를 무조건 호출해야합니다.
new Promise(executor);
// 예제
new Promise((resolve, reject) => {
// 명령문
});
생성자(Constructor)
Javascript에서는 원시 타입(String, Boolean 등) 을 제외한 대부분의 타입들이 객체(Object) 로 구성되어 있습니다.
일반적으로 객체(Object)를 생성하는 함수를 생성자(Constructor) 함수라고 부르게 되는데,
Promise 또한 객체로 구성되어 있기 때문에 생성자 함수를 이용해 Promise를 선언하게 됩니다.
function printFunc(data){
console.log(data);
}
// 생성자 함수
const obj = new Object();
const promise = new Promise(printFunc);
Promise의 상태
대기(Pending): 이행하거나 거부되지 않은 초기 상태
이행(Fulfilled): 연산이 성공적으로 완료됨
거부(Rejected): 연산이 실패함
Promise가 만들어 질 때 executor가 실행되며, executor에서 resolve 함수가 호출되기 전까지
firstPromise.then(...) 안에 있는 코드를 실행하지 않습니다.
이렇게 executor 가 실행되어 resovle된 프로미스를 Fulfilled Promise라고도 부릅니다.
Promise 안에서 resolve가 실행 된 경우 then메서드에 작성된 함수가 실행됩니다.
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.catch
Promise 안에서 에러가 throw 되거나 reject가 실행되면 catch 메서드에 작성한 함수가 실행됩니다.
// 비동기 + 일반 함수
async function 함수이름() {
// 명령문
}
// 비동기 + 익명 함수
async function() {
// 명령문
}
// 비동기 + 화살표 함수
async () => {
// 명령문
}
await 연산자 await 연산자를 사용하면 Promise가 fulfill 상태가 되거나 rejected될 때 까지 함수의 실행을 중단하고 기다릴 수 있습니다. Promise의 연산이 끝나면 함수에서 반환한 값을 얻을 수 있습니다. (대기한 후 결과값 반환) await 연산자는 async 함수 안에서만 사용할 수 있습니다
const result = await 값;
값에는 Promise가 아닌 다른 값도 들어갈 수 있습니다. 아래처럼!
Promise가 아니라면 기다리지 않고 해당 값 자체를 그대로 반환합니다.
async function 함수이름() {
const result = await 'Test!';
console.log(result);
}
함수이름();
// Print: 'Test!';
객체 리터럴
객체(Object)란?
Javascript의 데이터 타입은 크게 원시 타입과 객체 타입으로 분류됩니다.
원시 타입은 단 하나의 값만을 나타내고, 원시 타입의 값은 변경이 불가능 한 값입니다. (true, false)
객체 타입은 다양한 타입의 값을 하나의 단위로 구성한 복합적인 자료 구조이고, 객체 타입의 값을 변경 가능한 값입니다.
Javascript는 객체(Object) 기반의 프로그래밍 언어이고, Javascript를 구성하는 거의 모든 것은 객체로 구성되어 있습니다. 객체(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) 란?
프로퍼티를 참조하고 조작할 수 있는 동작(behavior)을 나타냅니다.
객체의 프로퍼티 값이 함수로 구성되어 있을 경우 메서드(Method)라고 부릅니다.
let objectLiteral = {
key: 'Value', // 프로퍼티
helloWorld: function () { // 메서드
return "Hello world";
}
};
console.log(objectLiteral.helloWorld()); // Hello world
Error handling
- 에러 핸들링(Error handling) 이란? 에러 핸들링은 에러를 관리하는 방법이고, 예상치 못한 상황에 대처하는 방식입니다. 에러는 예상할 수 있는 에러와 예상치 못한 에러로 구분할 수 있는데,
일반적인 어플리케이션을 설계할 때에는 예상치 못한 에러 상황이 더욱 많이 일어날 것으로 가정해야 합니다. 프로그래머가 작성한 코드에서 예상치 못한 에러가 일어날 가능성은 언제나 존재하고,
이러한 에러 상황을 대비해 언제든지 처리할 수 있어야 합니다.
- Try / Catch 서버에서 에러가 발생하지 않게 하기 위해서 저희는 예외 처리를 진행합니다. 예외 처리는 일반적으로 `try … catch` 문을 사용합니다. `users`에 들어있는 이름들을 `String.toUpperCase()`를 이용하여 대문자로 변경하려할 때
문자열(String)이 아닌 데이터가 들어온다면 에러가 발생합니다. 이렇게 예상치 못한 에러에 대처하기 위해선 `try … catch`문으로 코드 전체를 감쌀 수 있습니다. 에러가 발생하더라도 **프로그램이 멈추지 않고 에러를 기록**할 수 있습니다.
const users = ["Lee", "Kim", "Park", 2]; //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
- throw
위에서 에러를 핸들링하는 과정만 공부하였다면, 에러는 무조건 차단하고 발생시키면 안되는 걸까요?
아닙니다. 프로그래머의 입장에서 에러는 고의로 에러를 발생시키기도 해야합니다.
예를 들어서 은행 어플리케이션의 현금 인출 서비스를 만든다고 할 때,
계좌의 잔고가 요청받은 금액보다 적다면 현금 인출을 막고 예외를 발생시켜야겠죠? 이럴때 사용하는 것이 throw입니다.