Javascript - Promise를 사용해서 Callback hell을 벗어나자

Promise를 한 마디로 정의한다면 비동기 작업을 나타내는 객체입니다. 조금 더 풀어서 설명하면 비동기 작업을 쉽게 사용할 수 있도록 적의 된 객체입니다. Promise는 ES6(ES2015)에 표준으로 등록된 사양이고, 모던한 Javascript에서는 가진 역할과 책임이 더 중요해졌습니다. 그럼 이번 글에서는 콜백 지옥(Callback hell)을 벗어나는 방법을 통해 Promise가 가진 장점을 알아보도록 하겠습니다.

Promise를 사용하는 방법

먼저 콜백 지옥을 벗어나는 방법을 알아보기 전에 Promise의 사용 방식을 알아보려고 합니다. Promise의 사용 방식은 생각보다 중요한데요. 그 이유는 바로 Promise는 비동기에 Callback을 전달하지 않고 첨부하기 때문입니다. 말로는 이해하기가 어려울 수 있으니 코드를 통해 Promise의 사용 방식을 설명해보도록 하겠습니다.

function successCallback(result) {
  console.log("구독 성공: " + result);
}

function failureCallback(error) {
  console.log("구독 실패: " + error);
}

subscriptionYoutubeChannelAsync(subscription, successCallback, failureCallback);

 일반적으로 사용하는 비동기 처리 방식의 예제 코드입니다. 코드의 내용은 유튜브의 구독 기능을 예시로 성공, 실패 처리를 정의하고 해당 식을 실행되는 함수에 인자로 전달해 실행 시점을 실행되는 함수에게 위임했습니다. 즉 콜백 함수(Callback function)를 정의하고, 비동기로 실행되는 함수가 콜백 함수를 실행하는 형태가 되었습니다. 이 코드를 Promise를 통해 정의한다면 아래와 같습니다.

subscriptionYoutubeChannelAsync(subscription).then(successCallback, failureCallback);

 Promise를 사용하게 되면서 중간에 then()를 추가하게 되었습니다. 사실 그 부분 말고는 차이를 알 수 없지만 앞서 이야기한 것처럼 실행과 동시에 함수를 전달하지 않고 then()을 이용해서 콜백 함수(Callback function)를 첨부하는 방식으로 바뀌었다는 것을 알 수 있습니다. 사소한 차이라고 생각할 수 있지만 첨부하는 형태로 비동기를 처리할 수 있다는 점은 코드를 간결하고 읽기 쉽게 만들어 줄 뿐만 아니라 Chaning 형태로 비동기를 처리할 수 있도록 해줍니다. 콜백 지옥(Callback hell)을 벗어날 수 있는 것도 바로 이러한 방식을 채용했기 때문입니다.

 

 

콜백 지옥(Callback hell)을 벗어 날 수 있는 Promise

콜백 지옥은 어떤 기능의 실행 결과를 받아 또 다른 어떤 기능을 실행해야 하는 상황에 만들어지는 코드입니다. 주로 비동기적으로 처리해야하는 일이 2개 이상인 경우에 만들어집니다.

doSomething1(function(result1) {
  doSomething2(result1, function(result2) {
    doSomething3(result2, function(finalResult) {
      console.log('최종 실행 결과: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

 예제 코드에서는 3가지의 일들을 이전의 결과를 받아 순서대로 처리해야 하는 상황을 표현했습니다. 지금은 복잡하지 않다고 생각할 수 있지만 4개 아니면 그 이상의 일들을 순차적으로 처리해야 하는 상황이 온다면 보기도 어렵고, 이해하기 힘든 코드가 만들어집니다. 이럴 때 바로Promise가 가진 매력을 엿볼 수 있습니다.

doSomething1().then(function(reulst1) {
  return doSomething2(result1);
})
.then(function(result2) {
  return doSomething3(result2);
})
.then(function(finalResult) {
  console.log('최종 실행 결과: ' + finalResult);
})
.catch(failureCallback);

 Promise 객체를 사용한다면 then()을 이용해 기존의 중첩되는 구조를 피할 수 있습니다. 몇 가지의 일들이 추가된다고 해도 Promise 객체를 사용한다면 코드 줄 수가 추가될 뿐 중첩되지 않기 때문에 보다 쉽게 이해할 수 있는 코드를 만들 수 있습니다.

 

 예외를 처리하는 방법에 있어서도 Promise는 좋은 코드를 만들 수 있게 해 줍니다. 일반적인 비동기 처리 방식에서는 예외 상황을 위해 failureCallback를 3번 사용하고 있지만 Promise를 이용한 처리 방식은 failureCallback를 단 한 번만 사용하고 있습니다. 예외를 처리하는 방법은 실행하는 기능의 개수만큼 이 필요할 수 도 있다는 관점에서라면 안 좋아 보일 수 있지만 그 부분은 아래와 같이 catch()를 중간에 적절하게 섞어서 사용하면 문제가 없습니다.

new Promise((resolve, reject) => {
    console.log('시작');
    resolve();
})
.then(() => {
    throw new Error('문제 발생');
    console.log('실행1');
})
.catch(() => {
    console.log('문제 해결');
})
.then(() => {
    console.log('실행2');
});

끝맺음

모던한 자바스크립트 코드를 작성할 때 비동기를 잘 처리할 수 있는 방법은 Promise입니다. 그렇기 때문에 Promise는 비동기 처리가 필수적인 Fetch APIAxios에서도 활용되는 개념입니다. 처음 Promise를 접하게 되면 어려울 수 있지만, 이게 왜 필요한가 싶다가도 콜백 지옥을 만나거나 비동기적인 일들을 순차적으로 처리해야 하는 상황을 부딪히게 되면 Promise만큼 멋진 방법이 있다는 게 참 다행이라는 생각이 듭니다.

반응형

댓글

Designed by JB FACTORY