처음 Node.js 개발을 하다가 갑자기 함수의 실행 순서가 이상했다. 난 당연히 내가 뭔가를 잘못 짰다는 생각만 하고 에러를 찾아봤다. 그렇지만 10분 뒤 내게는 이해할 수 없는 일이 일어났다. 잘못 짠게 하나도 없었다. 지금까지 계속 순차적인 함수 실행 방식만을 생각하고 코딩을 했기 때문이다. 지금까진 계속 안드로이드 자바 위주로 코딩을 하다보니 비동기는 생각을 하지 못했다. 우연하게 콘솔을 찍어보고 실행 순서가 이상하다는 걸 발견하고 Node.js는 비동기로 처리를 하는지 찾아봤다.
Syncronous 동기 - 요청을 보낸 후 해당 요청의 응답을 받아야 다음 동작을 실행하는 방식
Asynchronous 비동기 - 요청을 보낸 후 응답과 관계없이 다음 동작을 실행하는 방식
그러니깐 비동기는 요청을 보내면 서버에서 오래 걸리면 먼저 응답을 하고 그 후에 처리를 하고 있었다. DB에서 데이터를 받아오는 등의 작업을 해야한다면 우선적으로 그에 대한 응답을 해버리고 다른 작업들도 실행한다.
가끔 동기식 처리를 해야할 경우가 생긴다. 그에 대한 해결 방안도 있다.
Node.js 에서 많이 찾아봤다. 보통 웹에선 callback을 사용했을 것이다. 물론 난 자바스크립트를 모르기 때문에 callback의 개념을 아예 몰랐다. 나중에 callback을 썼는데 문장이 계속 길어지며 한 눈에 보기도 불편했다.
그래서 Promise를 적용했다.
function fooPromise(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("fooPromise");
if(data === 1000)
reject(new Error("err"));
else
resolve(data);
}, data);
})
}
솔직히 Promise가 callback보단 낫지만 그렇다고 가독성 또한 좋은건 아니다. 그리고 또 다른 문제들도 있었다. return 뒤에 생성자를 통해서 인스턴스화를 하였는데 결국은 return 과는 조금 다른 성질이다. 보통의 return은 함수 종료가 되서 아래의 내용이 있더라도 실행하지 않는데 resolve가 있더라도 아래 내용을 실행한다. 이 부분은 코드 개발을 하다보니 발견하였다. 난 return의 개념을 적용되는 거라고 생각했지만 그게 아니었다. 물론 비동기이기 때문일수도 있다고 생각한다. 위에 코드에서 IF문 안에 조건문을 통해서 else로 내려와 resolve에 오면 함수가 종료되지 않고 함수가 끝까지 실행된다. 이 부분에 대해서 내부 구조를 다시 살펴보고 수정하겠다.
물론 이 방법만 있는건 아니다 그보다 더 좋은 문법이 있긴하다. node.js 8 LTS 버전에서 정식 지원하는 문법인 async와 await이다. async와 await의 최고의 장점은 node.js의 장점인 비동기를 유지하면서 동기적인 모습의 코드 스타일을 적용할 수 있고, 실행시 코드를 비동기적으로 동작하면서 코드를 동기적으로 실행한다. 사용방법 또한 어렵지도 길지도 않다.
사용법은 function 앞에 async 붙여주고 그 함수 안에서 실행하려는 함수 앞에 await를 붙여 사용한다. 시스템에서 코드를 실행하다가 await 함수 실행을 만나면 바로 실행된다. await는 async가 붙은 함수 안에서만 작동한다.
async function foo(data) {
var result1 = await fooPromise(data);
var result2 = await fooPromise(result1);
var result3 = await fooPromise(result2);
}
물론 async와 await가 만능은 아니었다. async function은 결국엔 promise를 반환하게 된다. 그렇기 때문에 위에 Promise와 연결 된다. 그리고 다중 함수 구조에서는 async와 await가 제대로 작동하지 않는다. 그렇기 때문에 어느순간부터는 비동기화 되어 함수 순서가 바뀔 수 있다. 나도 정확한 이유를 파악하지 못했다. 그렇기 때문에 foo 함수 안에서 fooPromise를 호출하고 fooPromise 함수 안에서 다른 함수 A를 호출하게 된다면 async/await가 깨지는 걸 발견했다. 그래서 나는 아래의 코드처럼 다른 함수 A 안에서는 return new Promise를 사용하여 처리한다. 그렇게 되면 제대로 순서대로 작동하는 걸 발견했다.
async function foo(data) {
var result1 = await fooPromise(data);
var result2 = await fooPromise(result1);
var result3 = await fooPromise(result2);
}
async function fooPromise(data){
var result1 = await A();
}
async function A(){
return new Promise((resolve, reject)=>{
...
})
}
나처럼 자바나 C 계열을 자주 했다면 return을 자주하는 형식이 편했기 때문에 자주 화가 날 것이다...
그리고 이거는 아마 다른 모듈을 많이 사용하게 되면 발견하게 될 것인데...
단편적으로 지금 당장 생각나는건 MySQL 쿼리시 콜백에서 다른 함수를 호출해야할 경우... 아래처럼 사용하면 된다
conn.query(sql, async function(err, results, fields){
if(err){
console.log(err)
}
else
await A()
})
'IT > Node.js' 카테고리의 다른 글
[Node.js] 파일 업로드 - multer (0) | 2021.06.18 |
---|---|
[Node.js] exports / module.exports 다른 모듈에서 함수 및 변수 참조 (0) | 2021.06.17 |
[Node.js] 배열의 N번째부터 M번째까지 합치는 함수 (0) | 2021.05.25 |
[Node.js] MQTT Client (0) | 2021.05.21 |
[Node.js] CoAP Server Listener (0) | 2021.05.20 |