Studying/JavaScript & Frameworks

[Node.js 떠먹여 주는 남자] Promises vs. async/await in Node.js (1)

국장 지킴이 앨런 2022. 4. 3. 18:03
반응형

안녕하세요!

 

오늘은 JS 혹은 Node.js 개발자 분들이시라면 한번쯤은 꼭 써보셨을 거라고 생각 되는 Promise 와 async/await 에 관하여 알아보려고 합니다. 우리가 Promise 혹은 async/await 을 쓰는 이유는, 코드가 동시에 처리되지 않고, 위의 결과값을 이용하여 그 다음 코드를 실행시키게 하기 위함입니다. JavaScript 와 Node.js 는 비동기식 코드를 처리하기 위하여 callbacks, Promises, 그리고 async/await 을 사용합니다. 그렇다면, 도대체 왜 Promise 가 이미 존재함에도 async/await 이 개발된 것일까요? 지금부터 한번 알아보도록 하겠습니다.

 

Promise

Node.js 에서 Promise 는 위에서 언급되었듯이, 어떠한 일이 발생할 것이라는 약속 혹은 보증이라고 할 수 있습니다. Promise 는 비동기식 이벤트가 실행되었는지를 추적하는 데 사용되며, 이벤트가 발생한 후 어떤 일이 발생하는지 확인합니다. Promise 에는 3가지의 상태가 존재합니다.

  • Pending: 초기 상태이며 이벤트가 발생하기 전의 상태입니다.
  • Resolved: 요청 처리가 성공적으로 끝난 후의 상태입니다.
  • Rejected: 만약 요청 처리 도중 에러가 발생했을 시, Promise 는 실패하고 Rejected 상태가 됩니다.

그럼 코드 예제를 통해 어떤 식으로 코드를 작성하는지 보도록 하겠습니다.

function promiseFunction(isResolved) {
  return new Promise((resolve, reject) => {
    if (isResolved) resolve('Success!');
    else reject('Failure');
  });
}

promiseFunction(true)
  .then((result) => {
    console.log(result); // "Success!"
  }).catch((error) => {
    console.error(error);
  });

위의 코드에서 볼 수 있듯, Promise 에서 resolve 안에 리턴 된 값을 then 문에서 매개변수 형태로 받아서 사용할 수 있습니다. 하지만 만약 Promise 안에서 코드 실행 도중 에러가 발생했다면 reject 되어 catch 문으로 가게 됩니다. 여기에 추가로 finally 문을 추가하여 Promise 가 resolve 나 reject 상태인지에 관계 없이 코드 블록을 한 번 실행 시키는 것도 가능합니다.

async/await

async/await 또한 Promise 와 같이 코드를 비동기식으로 처리하기 위한 기술입니다. 그렇다면, 왜 async/await 이 개발 되었을까요? 먼저 코드 예제를 확인한 후에 알아보도록 하겠습니다.

function promiseFunction(isResolved) {
  return new Promise((resolve, reject) => {
    if (isResolved) resolve('Success!');
    else reject('Failure');
  });
}

async function callPromiseFunction() {
  try {
    const result = await promiseFunction(true);
    console.log(result); // "Success!"
  } catch (e) {
    console.error(e);
  }
}

callPromiseFunction();

Promise 를 사용했을 때보다 코드가 훨씬 간결해진 것을 확인할 수 있습니다. 이로 인하여 가독성이 증가하게 됩니다. 또한 콜백을 사용하지 않기 때문에 콜백 헬에 대한 걱정할 필요도 없으며, 동기식 코드처럼 보이기 때문에 관리/이해하기도 쉽습니다. 하지만 async/await 은 Promise 처럼 catch 문을 지원하지 않기 때문에 try...catch 블록을 사용하여 에러 핸들링을 해야 합니다.

Why async/await?

제가 이 주제에 대하여 깊이 고민해 보지 않았을 때부터 저는 이미 async/await 을 즐겨 썼었습니다. 제가 작성했던 코드를 보면 Promise 를 사용 한 경우가 거의 손에 꼽을 정도인데요, 그냥 더 깔끔하고 간편해 보였기 때문입니다. 그렇다면 async/await 을 사용함으로 인해 얻을 수 있는 장점들을 살펴보면서 개발자의 의도를 추측해 보는 시간을 가져보고자 합니다.

 

코드의 간결함

바로 위에서 언급했듯이, 저는 async/await 을 즐겨 썼는데 그 이유는 단순히 더 깔끔해 보였기 때문입니다. 완독을 하지 않았지만 처음 회사에 입사했을 때 Clean Code 라는 책을 잠깐 봤었는데요, 거기에 코드는 간결하고 직관적으로 작성해야 하며, 짧게 작성해야 한다고 보았던 것 같습니다 (오래 전이라 확실하진 않습니다). 그것 때문에 왠만하면 한 펑션이나 메소드 안에 코드를 길게 작성하지 않으려고 노력하는 편이고 길어지면 다른 함수를 정의하는 방식으로 코드를 작성해 왔습니다. Promise 와 비교했을 때 async/await 을 사용하는 것이 더 간결해 보인다고 생각하는 사람이 저 뿐만은 아니겠죠?

 

Nested Promise 의 불필요성

만약 하나의 Promise 가 요청 처리의 끝이 아니게 될 경우, nested Promises 가 필요하게 될 것입니다. 이 경우에 Promise 안에 Promise, 또 그 안에 Promise... 의 형태가 되기 때문에 코드 길이가 길어지고 따라서 가독성도 줄어들어 코드를 이해하기 어려워질 것입니다.

 

이렇게 Promise 와 async/await 의 차이점에 대하여 간략하게 알아보았습니다. 다양한 의견이 공존할 수 있기에 무엇이 맞다 틀리다 정의 내릴 수는 없으니 취향에 맞게 정확하게 사용만 한다면 모두가 해피할 것 같네요 ^.^

 

처음 이 주제에 관하여 블로그를 작성해야겠다 마음 먹었을 때는 정말 야심차게 잘 작성할 수 있을 것 같았는데 막상 마무리 지어보니 뭔가 부실한 것 같다는 생각을 지울 수가 없습니다.. 이렇게 완전 마무리 짓기에는 좀 찝찝하니 다음에 실제 코드 예제를 통해서 2편을 준비해 보도록 하겠습니다.

반응형