[JS] callback 함수와 Promise의 차이

Kongveloper·2023년 8월 24일

비동기 프로그래밍

동기식 처리 모델(Synchronous processing model)

자바스크립트는 싱글 컨텍스트 스택을 가지고 있기 때문에 기본적으로 한 번에 한 가지 일만 수행할 수 있다. (동기 : 직렬적, 순차적 태스크 수행)

비동기식 처리 모델(Asynchronous processing model 또는 Non-Blocking processing model)

비동기 처리를 위해서 Web API와 같은 것을 사용할 수 있는데, 이때, 자바스크립트 엔진의 이벤트 루프가 콜스택과 태스크 큐(Task Queue)를 계속 주시하고 있다가 콜스택이 비어있을 때 태스크 큐에 있는 콜백 함수를 콜스택에 넣는 방식으로 비동기(병렬적) 처리가 가능해진다.

무겁고, 느린 작업의 경우, 다른 작업을 블로킹할 수 있기 때문에 비동기 처리를 통해 애플리케이션의 성능을 향상시키고, 사용자 인터페이스의 반응성을 유지하는 것에 신경써야 한다.
Promise와 Callback 함수 둘 다 비동기 처리를 위해서 사용하는 패턴이다.
콜백 함수는 비동기 작업이 완료되었을 때 호출되는 함수이며, Promise 객체는 비동기 작업이 성공적으로 완료되었는지 또는 실패했는지를 나타내는 객체이다.

📞 Callback 함수

특정 작업이 완료되었을 때 실행하도록 예약된 함수.

콜백 패턴은 콜백 헬로 인해 가독성이 나쁘고, 비동기 처리 중 발생한 에러 처리가 곤란하며, 여러 개의 비동기 처리를 한 번에 처리하는 데에도 한계를 보임.
➡️ ES6 프로미스 도입 : 콜백 지옥 보완하여 가독성이 높고, 비동기 처리 시점을 명확하게 표현 가능

콜백 헬 (Callback Hell)

Callback Hell : 비동기 함수의 처리 결과를 가지고 다른 비동기 함수를 호출해야 하는 경우, 함수의 호출이 중첩(nesting)이 되어 복잡도가 높아지는 현상

콜백 지옥

비동기 처리를 위해 콜백 패턴 사용 시, 처리 순서를 보장하기 위해 여러 개의 콜백 함수가 네스팅(중첩)되어 복잡도가 높아져 가독성에 저해되는 콜백 지옥이 발생한다.
비동기 함수의 처리 결과를 반환하는 경우, 순서가 보장되지 않기 때문에 그 반환 결과를 가지고 후속 처리를 할 수 없기 때문에 비동기 함수의 처리 결과에 대한 처리는 비동기 함수의 콜백 함수 내에서 처리해야 하기 때문에 콜백 지옥이 발생하게 된다.

에러 처리의 한계

🤙 Promise

프로미스는 Promise 생성자 함수를 통해 인스턴스화하며, Promise 생성자 함수는 비동기 작업을 수행할 콜백 함수를 인자로 전달받는데 이 콜백 함수는 resolve와 reject 함수를 인자로 전달받는다.
비동기 연산의 최종 완료 혹은 실패와 그 결과 값을 나타낸다.

  • IE 지원 안함
const promise = new Promise((resolve, reject) => {
  // 비동기 작업을 수행한다.

  if (/* 비동기 작업 수행 성공 */) {
    resolve('result');
  }
  else { /* 비동기 작업 수행 실패 */
    reject('failure reason');
  }
});
  • Promise는 비동기 처리가 성공(fulfilled)하였는지(resolve 함수가 호출된 상태) 또는 실패(rejected)하였는지(reject 함수가 호출된 상태) 등의 상태(state) 정보를 갖음.

프로미스의 후속 처리 메소드

Promise로 구현된 비동기 함수는 Promise 객체를 반환한다.
Promise로 구현된 비동기 함수를 호출하는 측(promise consumer)에서는 Promise 객체의 후속 처리 메소드(then, catch)를 통해 비동기 처리 결과 또는 에러 메시지를 전달받아 처리한다. Promise 객체의 상태에 따라 후속 처리 메소드를 체이닝 방식으로 호출한다.

프로미스 체이닝

프로미스는 후속 처리 메소드를 체이닝(chainning)하여 여러 개의 프로미스를 연결하여 사용하므로써 콜백 헬을 해결할 수 있다.

Promise와 Callback 비동기 처리 방식의 차이

콜백과 프로미스는 비동기 연산을 처리하는 두 가지 방법 중 대표적인 방식이다. 콜백은 간단한 비동기 작업에 적합하지만, 복잡한 연산이 필요할 때는 중첩 문제와 오류 처리의 복잡성으로 인해 프로미스가 더 효율적인 선택일 수 있다.

Callback : 함수의 매개변수로 전달되는 함수

  • 중첩 문제(콜백 지옥) : 여러 비동기 연산을 연속으로 처리하게 되면 콜백 내부에 또 다른 콜백이 중첩되어 코드의 가독성이 떨어지는 문제가 발생
    비동기 로직의 결과값을 처리하기 위해서는 callback안에서만 처리를 해야하고, 콜백 밖에서는 비동기에서 온 값을 알 수가 없다.

  • 오류 처리 : 오류 처리가 복잡할 수 있으며, 각 콜백마다 오류 처리 로직을 따로 구현해야 한다.
    callback pattern은 자유도가 높지만 template이 존재하지 않기 때문에 코드가 복잡해지고 에러 처리 같은 작업들이 어렵다.

Promise : 비동기 연산의 결과를 나타내는 객체로 연산 완료 후, 성공 또는 실패 상태가 됨.

비동기에에서 온 값이 promise 객체에 저장되기 때문에 코드 작성이 용이해진다.
Promise 클래스는 비동기 처리만을 위해 만들어졌기 때문에 클래스에 resolve나 reject 함수들이 잘 정의되어 있어 활용이 쉽다.

  • 체이닝: .then과 .catch 메서드를 사용해 비동기 연산의 결과를 처리하며, 이를 통해 코드의 가독성을 높이고 체인 중간에서 발생하는 오류를 쉽게 처리 가능

  • 오류 처리: .catch 메서드 하나로 연쇄적인 비동기 연산 중 발생하는 오류를 한 곳에서 처리할 수 있다.

  • 추가 기능: Promise.all(), Promise.race() 등의 유틸리티 메서드를 제공하여 복잡한 비동기 작업들을 더 간결하게 처리할 수 있다.

    ➡️ async-await :
    Promise는 아무래도 클래스이다 보니 함수형 컴포넌트가 대세가 된 지금은 함수에 바로 적용 가능한 async-await가 문법적으로 더 잘 맞음.
    async-await가 비동기 코드를 동기 형태로 만들어 가독성을 높여준다.

참고 자료

https://poiemaweb.com/es6-promise
https://velog.io/@southbig89/1%EC%9B%94-27%EC%9D%BC-%EC%88%98-promise-%EC%99%80-callback-http%EC%99%80-https-%EC%9D%98-%EC%B0%A8%EC%9D%B4

profile
개발자

0개의 댓글