밝을희 클태

콜백 지옥이란 그리고 해결법 본문

JavaScript

콜백 지옥이란 그리고 해결법

huipark 2024. 10. 30. 14:39

콜백 지옥?

주로 비동기 작업을 순차적으로 실행하기 위해 여러 콜백 함수가 중첩되는 경우 발생한다.
비동기 작업에서는 각 작업이 완료된 후에 다음 작업을 수행해야 할 때가 많다. 이때 콜백 함수를 계속 사용하다 보면 코드의 들여쓰기가 깊어지고 중첩되면서 가독성이 떨어지고, 유지보수가 어려워지는 문제가 생긴다. 이를 콜백 지옥이라고 부른다.

콜백 지옥 예시

아래 코드는 첫 번째 작업이 완료된 후 두 번째 작업이 실행되고, 세 번째 작업이 순차적으로 실행되도록 작성되었다. 하지만 작업이 순서대로 진행되도록 보장하기 위해 콜백 함수가 중첩되면서 코드의 가독성이 떨어지게 된다.

setTimeout(() => {
  console.log("첫 번째 작업 완료");
  setTimeout(() => {
    console.log("두 번째 작업 완료");
    setTimeout(() => {
      console.log("세 번째 작업 완료");
    }, 1000);
  }, 1000);
}, 1000);

Promise를 이용한 해결

Promise를 사용하면 코드 중첩을 피하고, 비동기 작업의 순서를 더 명확하게 표현할 수 있다. 아래 코드는 각 작업을 Promise로 감싸고 체이닝을 통해 순차적으로 실행한다.

const doWork = (onWork = () => {}, time) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            onWork();
            resolve();
        }, time);
    });
};

doWork(() => console.log("첫 번째 작업!"), 1000)
    .then(() => doWork(() => console.log("두 번째 작업!"), 1000))
    .then(() => doWork(() => console.log("세 번째 작업!"), 1000));

에러가 발생할 수 있는 상황

Promise 체이닝에서도 에러가 발생하면 복잡해질 수 있다. 에러를 처리하기 위해 catch()를 사용하면 코드가 다소 지저분해 보일 수 있다.

const doWork = (onWork = () => {}, time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      try {
        onWork();
        resolve();
      } catch (error) {
        reject(error);
      }
    }, time);
  });
};

doWork(() => console.log("첫 번째 작업!"), 1000)
  .then(() => doWork(() => console.log("두 번째 작업!"), 1000))
  .catch((error) => console.error("두 번째 작업 에러:", error))
  .then(() => doWork(() => console.log("세 번째 작업!"), 1000))
  .catch((error) => console.error("세 번째 작업 에러:", error));

async/await를 이용한 해결

async/await를 사용하면 코드가 더 직관적이 되고 가독성이 크게 개선된다. 비동기 작업이 마치 동기 작업처럼 순차적으로 실행된다. 또한 에러 처리는 try/catch로 깔끔하게 처리할 수 있다.

const doWork = (onWork = () => {}, time) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            try {
                onWork();
                resolve();
            } catch (error) {
                reject(error);
            }
        }, time);
    });
};

const run = async () => {
    try {
        await doWork(() => console.log("첫 번째 작업!"), 1000);
        await doWork(() => console.log("두 번째 작업!"), 1000);
        await doWork(() => console.log("세 번째 작업!"), 1000);
    } catch (error) {
        console.log(error);
    }
};
run();

결론

콜백 지옥은 비동기 작업을 순서대로 실행하기 위해 콜백 함수가 중첩될 때 발생한다. 이를 해결하기 위해 Promiseasync/await를 사용할 수 있다.

  • Promise는 비동기 작업의 순서를 체이닝을 통해 명확히 하고, 중첩을 피할 수 있게 해준다.
  • async/await은 비동기 코드를 동기 코드처럼 직관적으로 작성하게 해주며, 에러 처리를 간소화한다.

이제 콜백 지옥 문제는 Promiseasync/await을 사용해 보다 가독성 좋게 해결할 수 있다.