Promise 객체를 통해 콜백 지옥에서 벗어나기

aksen5240·2024년 1월 7일
1

JavaScript-Web

목록 보기
6/7
post-thumbnail

📚 Promise 객체를 통해 콜백 지옥에서 벗어나기

개발자라면 누구나 한 번쯤은 겪는 '콜백 지옥'에서 벗어나고자 하는 욕구를 느낀 적이 있을 것이다. 비동기 처리를 진행하면서 '콜백 지옥'을 벗어나는 것은 중요하면서도, 때로는 그 늪에 빠져 흔히 말하는 '콜백 지옥'에 빠지는 경우가 종종 발생한다.

따라서, 이 글을 통해 그 해결책으로 등장한 'Promise 객체'에 대해 자세히 알아보고, 그것이 어떻게 JavaScript의 비동기 처리를 혁신적으로 변화시켰는지 탐구해보고자 한다. 특히, Promise 객체의 기본 개념부터 Promise Chaining, 그리고 then과 catch 메소드의 사용 방법까지 알아보며 JavaScript에서의 비동기 작업을 마스터하는 데 필요한 모든 것을 살펴보자.


Promise

📌 Promise 객체의 등장 배경

1. 콜백 지옥과 가독성 문제

자바스크립트에서 비동기 작업을 처리하기 위한 기존 방법은 콜백 함수를 사용하는 것이었다. setTimeout이나 addEventListener 같은 파라미터로 직접 콜백을 전달하는 함수들이 대표적인 예시다.

setTimeout(callback, milliseconds);
addEventListener(eventname, callback);

이런 방식의 문제점은 비동기 작업을 순차적으로 수행해야 할 때 콜백 내에 또 다른 콜백을 넣어야 하는, 소위 '콜백 지옥(callback hell)' 또는 '지옥의 피라미드(Pyramid of Doom)'이 발생한다는 것이다. '콜백 지옥'이 발생하면 자연스럽게 코드는 점점 복잡해지고 가독성이 떨어지는 문제점이 발생한다.

2. Promise 객체의 도입

이러한 문제를 해결하기 위해 ES6(ES2015)에서 Promise 객체가 도입되었다. Promise 객체는 비동기 작업의 상태(pending, fulfilled, rejected)를 관리하며, then, catch, finally와 같은 메소드를 제공하여 비동기 작업을 보다 세련되게 처리할 수 있게 한다.

📌 Promise 객체란?

그렇다면 promise 객체란 무엇일까? promise 객체는 어떤 작업에 관한 '상태 정보'를 갖고 있는 객체로, 비동기 작업의 상태와 그 결과를 관리하는 객체이다. 간단히 말해, 어떤 일이 완료되었는지 또는 실패했는지를 추적하는 방법을 제공한다.

이 때, Promise 객체는 세 가지 상태를 가진다.

  • Pending: 비동기 작업이 아직 완료되지 않은 초기 상태.
  • Fulfilled: 비동기 작업이 성공적으로 완료된 상태.
  • Rejected: 비동기 작업이 실패한 상태.

📌 Promise 객체의 상태 변화

  • Pending → Fulfilled: 비동기 작업이 성공적으로 완료되면, Promise는 fulfilled 상태가 된다. 이때, then 메소드를 통해 등록된 성공 콜백 함수가 호출된다.
  • Pending → Rejected: 비동기 작업이 실패하면, Promise는 rejected 상태가 된다. 이 경우, then의 두 번째 인자로 등록된 실패 콜백 함수나 catch 메소드를 통해 등록된 콜백 함수가 호출된다.

📌 Promise Chaining이란?

Promise Chaining은 여러 비동기 작업을 순차적으로 처리할 수 있는 방법이다. 한 작업의 결과가 다음 작업의 입력으로 사용되는 경우 특히 유용하다.

1. Chaining의 장점

  • 코드 가독성 향상: 여러 비동기 작업을 then 메소드를 통해 연결함으로써 코드의 가독성을 높일 수 있다.

  • 에러 핸들링 용이: 연쇄적인 작업 중 어디에서든 발생한 에러를 catch 메소드 한 곳에서 처리할 수 있다.

  • 순차적 로직 구현: 비동기 작업을 순차적으로 실행해야 할 때 각각의 then 메소드 안에서 다음 작업을 시작하게 함으로써 순차적인 로직을 쉽게 구현할 수 있다.

2. 예제

아래 코드에서는 여러 비동기 작업을 순차적으로 수행하고, 마지막에 최종 결과를 출력한다. 만약 어느 단계에서든 문제가 발생하면, catch 메소드에서 에러를 처리한다.

doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

then 메소드

자바스크립트의 Promise 객체는 비동기 작업을 다루는 데 있어 필수적인 요소이다. 특히 then 메소드는 Promise 객체를 사용할 때 핵심적인 부분이다.

📌 then 메소드의 기본 동작

먼저, then 메소드는 두 개의 콜백 함수를 인자로 받을 수 있다. 첫 번째 콜백은 Promise가 성공적으로 완료(fulfilled)되었을 때 실행되고, 두 번째 콜백은 실패(rejected)했을 때 실행된다.

예를 들어, fetch 함수를 사용하여 데이터를 가져오는 경우를 생각해보자. 아래 코드에서 fetch 함수는 Promise-A 객체를 반환하고, then 메소드는 Promise-B 객체를 반환한다. fetch의 작업이 성공하면 successCallback이, 실패하면 errorCallback이 실행된다.

fetch('https://jsonplaceholder.typicode.com/users')
  .then(successCallback, errorCallback);

📌 콜백에서 반환하는 값에 따른 then 메소드의 동작

1) Promise 객체를 반환하는 경우

콜백이 Promise 객체를 반환할 경우, then 메소드는 이 Promise 객체의 상태와 결과를 따른다. 예를 들면 여기서 response.json()은 Promise 객체를 반환한다. 따라서 then 메소드가 반환하는 Promise 객체는 response.json()의 결과를 따른다.

fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => response.json())
  .then((result) => { console.log(result) });

2) Promise 객체 이외의 값을 반환하는 경우

콜백이 일반 값(예: 숫자, 문자열, 객체 등)을 반환하면, then 메소드가 반환하는 Promise는 이 값을 작업 성공 결과로 가지며, fulfilled 상태가 된다.

📌 콜백에서 아무 값도 반환하지 않는 경우

콜백이 아무런 값도 반환하지 않으면, JavaScript는 이를 undefined로 간주한다. 이 경우 then 메소드가 반환하는 Promise는 fulfilled 상태가 되며, 그 값은 undefined가 된다.

📌 콜백 내부에서 에러가 발생하는 경우

콜백 실행 중 에러가 발생하면, then 메소드가 반환하는 Promise는 rejected 상태가 되며, 해당 에러 객체를 작업 실패 정보로 갖게 된다.

📌 then 메소드의 콜백이 실행되지 않는 경우

이 경우는 then 메소드가 반환하는 Promise 객체가 이전 Promise 객체와 동일한 상태와 결과를 가진다. 예를 들어, 인터넷 연결이 끊긴 상태에서 fetch를 호출하면, 첫 번째 then 메소드의 콜백은 실행되지 않고, 두 번째 then 메소드의 콜백이 실행된다.


Catch 메소드

앞서 Promise Chaining의 예제를 살펴볼 때, Catch메소드에 대해 잠시 언급하였었다. 그렇다면 이 Catch메소드는 어떤 역할을 할까? 이에 대해 조금 더 자세히 알아보자.

📌 then 메소드와 catch 메소드의 관계

catch 메소드는 사실 then 메소드의 특별한 형태라고 할 수 있다. then 메소드에는 두 개의 콜백 함수가 들어갈 수 있으며, 두 번째 콜백 함수가 바로 catch 메소드의 콜백과 동일하다. 즉, catch 메소드는 then 메소드에서 첫 번째 콜백(성공 콜백)을 undefined로 두고, 두 번째 콜백(실패 콜백)만을 사용하는 것과 같다.

📌 예제를 통한 이해

이 코드에서 fetch 함수는 Promise-A를 반환하고, 각각의 then 메소드는 Promise-B, C, D를 반환한다. fetch 함수의 작업이 실패하면, 첫 번째 then 메소드에 실패 콜백이 없기 때문에 Promise-B는 Promise-A와 동일한 rejected 상태와 실패 정보를 갖게 된다. 이 상태에서 catch 메소드(즉, 두 번째 then 메소드)의 실패 콜백이 실행되어 에러를 로그로 출력한다.

fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => response.text())
  .catch((error) => { console.log(error); })
  .then((result) => { console.log(result); });

📌 catch 메소드의 역할

catch 메소드는 Promise가 거부(rejected)되었을 때 실행될 콜백 함수를 정의한다. 즉, 어떤 이유로 Promise가 실패했을 때, 예외 처리를 할 수 있게 해주는 역할을 하게 되는 것이다.

fetch('https://example.com/data')
  .then(response => {
    // 정상적인 응답 처리
  })
  .catch(error => {
    // 에러 처리
    console.error('Fetch 실패:', error);
  });

위의 예시에서, 만약 fetch 요청이 실패하면 catch 메소드에 정의된 콜백 함수가 실행된다. 이는 네트워크 오류, 서버 오류 등 다양한 이유로 Promise가 거부될 때 유용하게 사용 가능하다.

만일 then 메소드만 사용할 경우, Promise의 거부 상태를 제대로 처리하지 못할 수 있다. 따라서, catch 메소드를 함께 사용함으로써 더 견고하고 안정적인 에러 처리를 할 수 있게 된다.


Outro

JavaScript에서의 비동기 처리는 이제 '콜백 지옥'의 굴레를 벗어나, Promise 객체를 통해 훨씬 더 직관적이고 관리하기 쉬운 형태로 진화하였다. 따라서, 오늘 소개한 Promise 객체의 개념, then과 catch 메소드, 그리고 Promise Chaining에 대한 이해는 현대 JavaScript 개발에서 필수적인 요소이다.

오늘의 학습을 통해 이전에 배웠던 비동기 처리에 대한 이해를 더욱 깊게 할 수 있었으며, 더욱 견고하고 안정적인 에러 처리를 하는 방법에 대해 학습할 수 있었다. 오늘의 학습내용을 바탕으로, 이전에 배웠던 fetch함수와 비동기 실행에 대해 다시 접목하여 학습함으로써 깊이 있는 학습을 이어나가자.

profile
Tags of MyStudy🌱

0개의 댓글