-/node

Node.js에서 Promise를 활용하는 방법 (프로미스를 지원하는 메서드 구현 방법)

벤권 2022. 2. 21. 21:10

개인적으로 노드를 이용해서 개발을 하면서 Promise에 대해서 직접 사용할 일이 없었다.

여기서 직접이라고 한 이유는 필자는 프로미스를 지원하는 모듈들을 이용하여 async/await로만 Promise를 간접적으로 다뤄왔다. 직접 Promise객체를 생성하여 이용한 적이 드물다는 이야기이다.

 

Promise에 대해서 좀 더 알아봐야겠다는 생각은 해왔지만 개발에 집중하여 자꾸 필요할 때만 구글링으로 이용하고 미루다가 최근에 정말 필요해져서 포스팅을 작성하려고한다.

 

 

 

왜 Promise객체를 직접 써야할까?

 

지금까지 개발해오면서 사용하던 fs모듈처럼 promise버전이 존재하면 Promise객체를 직접 사용해야 할 일이 없지만, 

child_process 모듈의 exec같은 경우 콜백이 있다. 즉 exec을 콜백지옥에서 벗어나서 Promise를 이용하여 async/await로 쓰기 위해서 Promisify를 해야한다. exec말고도 우리가 흔히 알고있는 setTimeout과 같은 것도 마찬가지이다.

 

exec은 callback방식이다. (exec의 동기버전인 execSync버전도 있다.)

exec(cmd, (error, stdout, stderr) => {
   if (error) {
    console.warn(error);
}

 

 

 

Node js에서 callback을 지원하는 함수를 Promise 와 함께 사용하는법

그럼 이제 Promise를 활용하여 콜백을 지원하는 함수를 Promise처럼 쓸 수 있게 바꿔보려고 한다.

본문 예제에서는 setTimeout으로 예를 들려고 한다.

 

function setTimeoutPromisify(delay) {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
           resolve("딜레이 만큼  시간 지났다!")
        }, delay);
    
    });
}

위와 같이 하면 setTimeOut을 프로미스처럼 사용할 수 있게 된다. 즉 setTimeOut함수를 async/await과 함께 사용하여 가독성을 높일 수 있다.

 

function setTimeoutPromisify(delay) {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
           resolve("딜레이 만큼  시간 지났다!")
        }, delay);
    
    });
}

(async () => {
   console.log(await setTimeoutPromisify(3000));
   console.log("3초후 이 문장이 출력!");
})();

위 코드를 실행시키면 async await을 활용하여 3초후 이 문장이 출력이라는 문장이 3초후에 출력이 되게 된다.

 

 

참고로 fs모듈의 readFile등등 많은 프로미스를 지원하는 비동기 함수 내부는 아래처럼 구현되어 있을 것이다.

function readFile('../somefile') {
  // new Promise() 추가
  return new Promise(function(resolve, reject) {
    실제파일읽는내부함수('../somefile', function(response) {
      // 데이터를 받으면 resolve() 호출
      resolve(response);
    });
  });
}

우리가 사용하는 readFile이라는 메서드 내부에 실제 파일 내부를 읽는 콜백을 지원하는 함수가 호출되게 되어 있을 것이고 그 함수의 콜백함수에서 resolve를 하게되면 우리가 그 파일을 이용할 수 있게 되는 것이다.

 

마치며..

사실 위에서 프로미스 객체를 직접 생성하여 callback패턴으로 Promise패턴으로 바꿨지만 이미 util모듈의 promisify를 통해 이를 대신 해주는 모듈이 있다.

const util = require('util');
const exec = util.promisify(require('child_process').exec);

async function lsExample() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.error('stderr:', stderr);
}
lsExample();

하지만 이를 쓰기 전에 직접 해보는거랑 안해보는거랑은 큰 차이가 있다고 생각한다.

 

Promise는 자바스크립트에서 동기적인 부분을 동기적으로 동작하게 바꿀 때 가독성이 있게 바꾸는 좋은 개념이다. 물론 위 예제처럼 콜백 체인이 복잡하지 않은 경우 그냥 사용하는게 낫겠지만 async/await에 익숙한 경우 혹은 체인이 복잡해서 콜백 지옥이 다가오는 경우는 Promise를 쓰자.