내가 공부하고 있는 책에선 자바스크립트 ES2015+ 문법을 다루고 있는데, 사실 나는 자바스크립트를 아주 자세하게 공부한 적이 없고 그저 유튜브나 구글링을 통해 배우며 간단한 프로젝트를 진행하면서 배웠다.
그래서 책에서 알려주는 문법들을 보며 아.. 이게 이래서 이렇게 사용되는구나.. 라고 이해했다!
처음 유튜브로 자바스크립트 변수 선언하는 방법을 배울 때는 var로 선언을 했었다.
하지만 react와 express로 프로젝트를 진행하며 구글링을 많이 한 탓에 const와 let을 사용하게 되었는데, 정확하게 왜 var 대신 const와 let을 사용했고, 뭐가 다른지.. 이런건 전혀 모르고 그냥 다들 이렇게 쓰니까 나도 따라 썻었다..
const와 let - 블록 스코프를 가짐
var - 함수 스코프를 가짐
위 내용을 코드를 보며 알아보자.
if (true) {
var x = 3;
}
console.log(x); // 3
if(true) {
const y = 3;
}
console.log(y); // Uncaught ReferenceError: y is not defined
위 코드를 크롬 개발자 도구의 Console 탭에 적으면 주석과 같이 출력된다.
var은 함수 스코프를 가지므로 if문의 블록과 관계없이 접근할 수 있으나, const와 let은 블록 스코프를 가지므로 블록 밖에서는 변수에 접근할 수가 없다.
여기서 블록의 범위는 if, while, for, function 등에서 볼 수 있는 중괄호를 말한다.
그럼 이제 var와 const, let의 차이점을 알아봤으니까 const와 let의 차이점도 알아보자.
코드 먼저 보자.
const a = 0;
a = 1; // Uncaught TypeError: Assignment to constant variable
let b = 0;
b = 1; // 1
const c; // Uncaught SyntaxError: Missing initializer in const declaration
위 코드의 주석을 보면 바로 알아차릴 수 있다.
const는 한번 값을 할당하면 다른 값을 할당할 수 없으며, 초기화할 때 값을 할당하지 않으면 에러가 발생한다. 그래서 const로 선언한 변수는 상수라고 부르기도 한다.
반면에 let은 다른 값을 할당할 수 있는 것을 볼 수 있다.
개인적으로 이 문법을 처음 알고 굉장히 마음에 들어했다.
var string1 = num1 + ' 더하기 ' + num2 + '는 \'' + result + '\'';
보통 다들 문자열을 선언할 때 위 코드와 같이 작은 따옴표, 이스케이프, 더하기 기호 등을 사용하기 때문에 가독성이 좋지 않다.
const string1 = `${num3} 더하기 ${num4}는 ${result2}`;
그래서 ES2015 문법에선 위 코드와 같이 큰따옴표나 작은따옴표로 문자열을 감싸지 않고 백틱(`)으로 문자열을 감싸고 ${변수} 형식으로 변수를 더하기 기호 없이 문자열에 넣어서 더 가독성이 좋아졌다.
var es = 'ES';
var oldObject = {
sayJS: function() {
console.log('JS');
},
sayNode: sayNode,
};
oldObject[es + 6] = 'Fantastic';
const newObject = {
sayJS() {
console.log('JS');
},
sayNode,
[es + 6]: 'Fantastic',
};
위 코드에서 newObject는 oldObject를 새롭게 고친 코드이다.
sayJs 같은 객체의 메서드에 함수를 연결할 때 콜론(:)과 function을 붙이지 않아도 되고,
sayNode: sayNode처럼 속성명과 변수명이 동일한 경우 한 번만 써도 된다.
[es + 6]과 같이 속성명을 newObject 안에서 바로 사용할 수도 있다.
자바스크립트와 노드에서는 이벤트 리스너를 사용할 때 콜백 함수를 자주 사용하게 되는데, 이때부터 콜백 지옥에 빠지게 된다..
그래서 ES2015부터는 자바스크립트와 노드의 API들이 콜백 대신 프로미스기반으로 재구성되고, 콜백 지옥을 극복했다는 평가를 받고있다고 한다.
프로미스를 사용하려면 프로미스 객체를 먼저 생성해야 한다.
const condition = true;
const promise = new Promise((resolve, reject) => {
if (condition) {
resolve('성공');
} else {
reject('실패');
}
});
promise
.then((message) => {
console.log(message);
})
.catch((error) => {
console.error(error);
})
.finally(() => {
console.log('무조건');
});
위 코드를 보면 new Promise로 프로미스를 생성하고, 안에 resolve와 reject를 매개변수로 갖는 콜백 함수가 들어간다. 이렇게 생성된 promise 변수에 then과 catch 메서드를 붙일 수 있는데, 프로미스 내부에서 resolve가 호출되면 then이 실행되고, reject가 호출되면 catch가 실행된다. finally는 성공/실패와 상관없이 실행된다.
promise
.then((message) => {
return new Promise((resolve, reject) => {
resolve(message);
});
})
.then((message2) => {
console.log(message2);
return new Promise((resolve, reject) => {
resolve(message2);
});
})
.then((message3) => {
console.log(message3);
})
.catch((error) => {
console.error(error);
});
위 코드처럼 then이나 catch에 다시 다른 then이나 catch를 붙일 수 있다.
처음 웹 프로젝트를 진행하면서 react에서 axios를 사용하면서 then과 catch를 썼었는데, axios가 Promise API를 지원하는 Promise기반의 라이브러리라서 then과 catch를 쓸 수 있었다는 것을 이번에 책을 통해 공부하며 처음 깨닫게 되었다..;
프로미스를 이용하여 콜백 지옥을 해결할 수 있지만, 계속되는 then과 catch로 인해 코드가 지저분해진다. 그래서 ES2017에서 async/await 문법이 추가되었다.
프로미스 코드(프로미스가 내장된 코드라는 가정)
function findAndSaveUser(User) {
Users.findOne({})
.then((user) => {
user.name = 'zero';
return user.save();
})
.then((user) => {
return Users.findOne({gender: 'm'});
})
.then((user) => {
// 생략
})
.catch(err =>{
console.error(err);
});
}
async/await 코드
const findAndSaveUser = async (User) => {
try {
let user = await Users.findOne({});
user.name = 'zero';
user = await user.save();
user = await Users.findOne({ gender: 'm'});
//생략
} catch (error) {
console.error(error);
}
};
위 두번째 코드는 프로미스 코드를 화살표 함수와 async/await 코드로 바꾼 것인데 프로미스 코드에 비해 깨끗해졌다.
함수 선언부에 async를 붙이고, 프로미스 앞에 await를 붙였다.
단, async/await는 에러를 처리하는 부분이 없으므로 try/catch 등을 사용하여 에러를 처리해줘야 한다.
확실히 구글링과 유튜브를 통해 간단하게 공부했던 자바스크립트는 다른 언어들과 차이가 좀 있어서 머리아프고, 이해안되는 내용이 많았지만 책을 통해 다시 이해하고 자세하게 알 수 있어서 편안해졌다.
피드백 받습니다.