Node.js

자바스크립트의 비동기 처리에 대하여

안모 2023. 11. 15. 22:39

개요

자바스크립트에서 비동기 작업 처리 방식은 크게 3가지이다. 우선 Callback이 있다. 그리고 Promise가 있고, async/await가 있다.


문득 이 자바스크립트의 비동기 처리 방식들의 성격이 다르다는 생각이 들었다. 특히 Callback과 Promise, async/await가 더욱 그렇다. 한 번 간단하게 정리해보며 생각도 정리해 보자.

Callback

Callback은 함수에 매개변수로 넘겨준 함수다. 자바스크립트의 일급객체, 일급함수를 설명할 때 항상 등장한다. 어떤 함수 A의 매개변수로 B함수를 전달하여 호출한 A함수 내에서 매개변수로 전달된 B함수를 실행한다.

 

Callback함수 자체는 자바스크립트의 이벤트 등록이나 고차함수에 쓰이고 특성을 이용해 비동기 처리에도 사용한다.
비동기 처리에 초점을 맞추면, 비동기 함수를 처리한 결과값을 Callback함수를 호출하여 전달하는 형식이다.

 

문제는 Callback이 깊어질수록 코드가 밑도 끝도 없이 길고 알아보기도 힘들어지는 Callback Hell 현상이다. Callback함수의 매개변수로 Callback를 입력하고, 또 그 Callback함수에 다시......

 

게다가 Callback 방식은 동기 실행과 비동기 실행이 뒤섞여 있어도 한눈에 알아보기 힘들다.

Promise

이런 상황 중 ECMA2015(ES6)에서 비동기 코드를 잘 다루기 위한 표준으로 도입된 것이 Promise이다.
Promise는 비동기 작업의 미래 완료/실패와 그 결과값을 나타낸다

 

비동기 작업의 특징은 실제로 값을 얻을 때까지 얼마나 시간이 걸릴지 모른다는 것이다. 또 '동시에' 호출해도 '동시에' 결과값을 얻을 수 없다. 그래서 Promise는 미래에 비동기 작업의 결과값을 제공 하겠다는 약속을 우선 반환한다. Pending, Fulfilled, Reject 3가지 상태를 가지는데 각각 대기, 성공, 실패를 나타낸다.

 

  • Pending은 대기상태다. Promise를 선언하고 응답이 오기전에 접근하면 pending상태임을 알 수 있다.
  • Fulfilled는 비동기 작업이 성공적으로 완료된 상태이다.
  • Reject는 비동기 작업을 성공적으로 완료하지 못한 상태이다.

Fulfilled와 Reject는 결과에 따라 .then()이나 .catch()로 연결하여 반환값을 이용할 수 있다.

 

Promise는 보다 비동기 코드를 구조화하여 이용할 수 있게 해주었지만 여전히 콜백 방식을 필요로 하는 경우가 있다.
그래서 async/await가 도입된다.

async/await

async/await는 ECMA2017(ES8)에 도입되었다. 비동기 코드를 동기식 코드와 유사하게 다룰 수 있도록 하는 목적에서 등장했고, async/await 한 세트로 붙어다니며, async가 붙은 함수는 비동기 함수로 취급된다.

 

async 함수 안에 선언된 await는 async내부에 한하여 비동기 작업의 처리를 기다린 후 결과값을 받으면 await 이후 코드를 실행한다. 비동기 코드가 동기 코드처럼 동작하는 것이다. 이로써 비동기를 동기 코드처럼 다룰 수 있게 되었다.

 

이때, 전체 코드 진행을 멈추는게 아니라 async함수 자체는 비동기 함수로 취급되어 넘겨지고 다른 동기 함수부터 처리된다. async 함수는 비동기 작업을 처리하기 때문에 실제로 Promise를 반환한다.

 

또 async 안에서만 await을 선언할 수 있는 특성상 비동기 코드를 격리할 수 있게 되었고 비동기 코드임을 직관적으로 알 수 있게 되었다.

 

하지만 '동기 같은 비동기'는 장점이 있는 만큼 단점이 있다. await은 코드 진행을 막기 때문에 병목이 발생한다. 처리까지 오래 걸리는 함수를 비동기로 넘겨 빠르게 처리한다는 방식에서 과장 조금 보태면 시간이 오래 걸리는 비동기도 이제 기다리자는 방식이 되었다. 이 점을 생각하면 무작정 async/await을 쓰는 것은 올바른 접근법이 아니다.

 

따라서 async/await을 더 최신 스팩이라는 이유로 무작정 사용할 게 아니라 Promise와 async/await을 상호 보완하며 사용하는 것이 좋다.

결론?

비동기 처리가 목적인가 라는 관점에서 Callback과 Promise, async/await 그룹으로 나눠지는 것 같다. Promise, async/await 그룹은 비동기 처리를 위해 도입되었다.

 

자바스크립트 비동기 처리법의 근본은 무엇인가 라는 관점에서 보면 Promise와 async/await은 나누어 진다. Promise는 비동기 처리의 표준이고 async/await는 비동기 처리를 더 잘 하기위해 도입되었다.

 

이런 관점으로 봤을 때 Callback은 일급함수의 성질을 비동기 처리에 이용한 것이고 Promise는 비동기 처리 표준, async/await은 비동기 비동기의 동기식 처리로 받아들이는 것은 비약일까?

 

다 정리하고 난 후 떠올랐는데, 사실 Callback을 이용한 비동기 처리, Promise를 이용한 비동기 처리, async/await을 이용한 비동기 처리로 생각해야 하는데 정말 다른 것을 가져와서 왜 다를까 하고 고민한 것 같은 감이 있다.