Back Ground

NodeJS - ES2015+ 본문

Javascript/Node.js

NodeJS - ES2015+

Back 2019. 1. 18. 03:53




객체 리터럴



객체 리터럴에는 편리한 기능들이 추가되었다.

- ES5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
cs

oldObject 객체에 동적으로 속성을 추가하고 있다. 

앞의 코드를 다음과 같이 다시 쓸 수 있다.




- ES6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var sayNode = function(){
    console.log('Node');
}
var es = 'ES';
const newObject = {
    sayJS(){
        console.log('JS');
    },
    sayNode,
    [es + 6] : 'Fantastic',
};
 
newObject.sayNode(); // Node
newObject.sayJS();   //JS
console.log(newObject.ES6); //Fantastic
cs

oldObject와 newObject 를 비교해서 보면 된다.

sayJS 같은 객체의 메서드에 함수를 연결할 때 더는 : 과 function을 붙이지 않아도 된다.


sayNode: sayNode처럼 속성명과 변수명이 겹치는 경우에는 한 번만 쓸 수 있게 되었다.

스크립트에서 다음과 같은 경우가 많이 나오는데, 이때 코드의 중복을 피할 수 있어 편리하다.



1
2
name : name, age : age } //ES5
name, age }              //ES2015
cs


객체의 속성명을 동적으로 생성할 수 있다.

예전 문법에서는 ES6라는 속성명을 만들려면 객체 리터럴(oldObject) 바깥에서 [ es + 6 ]를 해야 했다.


객체 리터럴에 추가된 문법은 코딩 시 편의를 위해 만들어진 것이라는 느낌이 강하다.

익숙해지면 코드의 양을 많이 줄일 수 있다.









비구조화 할당


이름은 어색하지만 매우 유용한 기능이다.

객체와 배열로 부터 속성이나 요소를 쉽게 꺼낼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
var candyMachine = {
    statue: {
        name : 'node',
        count : 5,
    }
},
    getCandy: function(){
        this.status.count--;
        return this.status.count;
    }
};
var getCandy = candyMachine.getCandy;
var count = candyMachine.status.count;
cs


객체의 속성을 같은 이름의 변수에 대입하는 코드이다.

이를 다음과 같이 바꿀 수 있다.


1
2
3
4
5
6
7
8
9
10
11
const candyMachine = {
    statue: {
        name : 'node',
        count : 5,
    },
    getCandy(){
        this.status.count--;
        return this.status.count;
    }
};
const { getCandy, status : {count}  } = candyMachine;
cs


당황스럽겠지만, 위 문법은 유효한 문법이다. 

candyMachine 객체 안의 속성을 찾아서 변수와 매칭해준다.

const처럼 여러 단계 안의 속성도 찾을 수 있다. 

getCandy와 count 변수가 초기화 된 것




배열도 비구조화이다.

1
2
3
4
var array = ['nodejs' , {}, 10true];
var node = array[0];
var obj = array[1];
var bool = array[array.length -1];
cs


array란 배열의 첫 번째, 두번째 요소와 마지막 요소를 변수에 대입하는 코드이다.

다음과 같이 바꿀 수 있다.


1
2
const array = ['nodejs',{}, 10true];
const [node, obj, bool] = array;
cs

어색해 보이지만, 나름대로 규칙이 있다. 
node, obj와 bool의 위치를 보면 node는 배열의 
첫 번째 요소, obj는 두 번째 요소, bool은 마지막 요소라는 것을 알 수 있다.
obj와 bool사이의 요소들은 무시한다.

비구조화 할당 문법도 코드 줄 수를 상당히 줄여주므로 유용하다.








프로미스


자바스크립트와 노드에서는 주로 비동기 프로그래밍을 한다.
특히 이벤트 주도 방식 때문에 콜백 함수를 자주 사용한다.
ES2015부터는 자바스크립트와 노드의 API들이 콜백 대신 프로미스 기반으로 재구성된다.
그래서 악명 높은 콜백 헬(callback hell)을 극복했다는 평가를 받고 있다.

프로미스는 다음과 같은 규칙이 있다.
먼저 프로미스 객체를 생성해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const condition = true// true면 resolve, false면 reject
const promise = new Promise( (resolve, reject) => {
    if(condition){
        resolve('성공');
    }else{
        reject('실패');
    }
});
 
promise
    .then( (message) => {
        console.log(message); // 성공(resolve)한 경우 실행 
    })
    .catch( (error) => {
        console.error(error); // 실패(reject)한 경우 실행 
});
cs

new Promise로 프로미스를 생성할 수 있으며, 
안에 resolve와 reject를 매개변수로 갖는 콜백 함수를 넣어준다.
이렇게 만든 promise변수에 then과 catch메서드를 붙일 수 있다.
프로미스 내부에서 resolve가 호출되면 then이 실행되고, 
reject가 호출되면 catch가 실행된다.



resolve와 reject에 넣어준 인자는 각각 then과 catch의 매개변수에서 받을 수 있다.

즉, resolve('성공')가 호출되면 then의 message가 '성공'이 된다.

만약 reject('실패')가 호출되면 catch의 error가 '실패'가 되는 것이다.

condition 변수를 false로 바꿔보면 catch에서 에러가 로깅된다.


then이나 catch에서 다시 다른 then이나 catch를 붙일 수 있다.

이전 then의 return값을 다음 then의 매개변수로 넘긴다.

프로미스를 return한 경우 프로미스가 수행된 후 다음 then이나 catch가 호출된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
promise
    .then( (message) => {
        return new Promise( (resolve, reject) => {
            resolve(message2);
        });
    })
    .then( (message2) => {
        console.log(message2);
        return new Promise( (resolve, reject) => {
            resolve(message2);
        });
    })
    .then( (message3) ) =>{
        console.log(message3);
    })
    .catch( (error) => {
        console.error(error);
    })
});
 
cs


처음 then에서 message를 resolve하면 다음 then에서 받을 수 있다.

여기서 다시 message2를 resolve했으므로 다음 then에서 message3를 받는다.


이것을 활용해서 콜백을 프로미스로 바꿀 수 있다. 다음은 콜백을 쓰는 콜백을 쓰는 패턴 중 하나이다.

이를 프로미스로 바꿔보겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function findAndSaveUser(Users){
    Users.findOne( {}, ( err, user ) => { //첫 번째 콜백
        if(err){
            return console.error(err);
        }
        user.name = 'zero';
        user.save( (err) => { // 두 번째 콜백
            if(err){
                return console.error(err);
            }
        }
        Users.findOne( {gender : 'm'}, (err, user) => { // 세 번째 콜백
            //생략
        });
    });
  });    
}
cs


콜백 함수가 세 번 중첩되어 있다. 

콜백 함수가 나올 때마다 코드의 깊이가 깊어진다.

각 콜백 함수마다 에러도 따로 처리해줘야 한다. 

이 코드를 다음과 같이 바꿀 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function findAndSaveUser(Users){
    Users.findOne( {} ) 
        .then( (user) => {
            user.name = 'zero';
            return user.save();
        }) 
        .then( (user) => {
            return Users.findOne( {gender : 'm' });
        })
        .then( (user) => {
            // 생략
        })
        .catch( err => {
            console.error(err);
        });
}
cs


코드의 깊이가 더 이상 깊어지지 않다.

then메소드들은 순차적으로 실행 된다. 

콜백에서 매번 따로 처리해야 했던 에러도 마지막 catch에서 한번에 처리할 수 있다.

하지만 모든 콜백함수를 위와 같이 바꿀 수 있는 것은 아닙니다. 

메서드가 프로미스 방식을 지원해야 한다. 

예제의 코드는 findOne과 save메서드가 내부적으로 프로미스 객체를 가지고 있어서 가능한 것이다.



1
2
3
4
5
6
7
8
9
const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
Promise.all([promise1, promise2])
    .then( ( result) => {
        console.log(result); // ['성공1', '성공2']
    })
    .catch( (error) => {
        console.error(error);
    });
cs


Promise.resolve는 즉시 resolve하는 프로미스를 만드는 방법이다.

비슷한 것으로 즉시 reject하는 Promise.reject도 있다.


프로미스가 여러 개 있을 때 Promise.all에 넣으면 모두 resolve될 때까지 기다렸다가 then으로 넘어간다.

result 매개변수에 각각의 프로미스 결괏값이 배열로 들어 있다.

Promise 중 하나라도 reject가 되면 catch로 넘어간다.





async/await


노드 7.6 버전부터 지원되는 기능있다. 

자바스크립트 스펙은 ES2017이다. 최신 기능이면서 정말 혁신적인 기능이다.

특히 노드처럼 비동기 프로그래밍을 해야 할 때 도움이 많이 된다.


프로미스가 콜백 지옥을 해결했다지만, 여전히 코드가 장황한다. 

async/await 문법은 프로미스를 사용한 코드를 한 번 더 깔끔하게 줄여준다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function findAndSaveUser(Users){
    Users.findOne({})
        .then( (user) => {
        user.name = 'zero';
        return user.save();
    })
    .then( (user) => {
        return Users.findOne( {gender: 'm'});
    })
    .then( (user) => {
        // 생략
    })
    .catch( err => {
        console.error(err);
    });
}
cs


콜백과 다르게 코드의 깊이가 깊진 않지만, 

코드 길이는 여전히 깁니다. async/await 문법을 사용하면 다음과 같이 바꿀 수 있다.

async function이라는 것이 추가 되었다.


1
2
3
4
5
6
7
async function findAndSaveUser(Users){
    let user = await Users.findOne( {} );
    user.name = 'zero';
    user = await user.save();
    user = await Users.findOne( {gender: 'm'} );
    // 
}
cs


놀라운 정도로 코드가 짧아졌다.

함수 선언부를 일반 함수 대신 async function으로 교체한 후, 

프로미스 앞에 awit을 붙여주었다.

이제 함수는 해당 프로미스가 resolve될 때까지 기다린 뒤 다음 로직으로 넘어간다.

예를 들면 awit User.findOne({})이 resolve될 때 까지 기다린 뒤, user 변수를 초기화 하는 것이다.


'Javascript > Node.js' 카테고리의 다른 글

Node - Node 기능  (0) 2019.02.15
Callback Hell 이란 [ 해결방법 ]  (2) 2019.01.23
NodeJS - RestFul [ES5]  (0) 2018.11.11
NodeJS - Router 사용해 요청 라우팅하기 [ES5]  (0) 2018.10.07
NodeJS - Static [ES5]  (0) 2018.10.07
Comments