Programming Language/Javascript

Javascript의 비동기 처리(콜백 함수, 프로미스, async/await)

devsean 2025. 7. 31. 20:13

비동기(Asynchronous)와 자바스크립트

비동기 처리란 특정 코드의 연산이 끝날 때까지 실행을 기다리지 않고 다음 코드를 먼저 실행하는 방법으로, Javascript에서는 주로 네트워크 요청(Ajax 요청), 파일 읽기/쓰기, 타이머 함수(setTimeout,setInterval), 이벤트 처리 등의 상황에서 사용된다. 만약 이러한 작업들이 동기식으로 처리된다면 다른 코드들의 실행이 블로킹되고 대기 시간이 길어지므로 웹 사이트의 성능과 사용자에게 부정적인 영향을 줄 수 있다. 비동기 처리를 통해 동시에 여러 가지 작업을 처리할 수 있고 기다리는 과정에서 다음 함수를 호출할 수 있다.

 

콜백 함수

비동기 처리를 위해 Javascript에서는 콜백 함수를 제공한다. 매개변수로 함수를 전달받아 내부에서 실행하는 함수이다. 필요한 시점에 매개변수로 받은 함수를 실행하여 자바스크립트의 비동기적인 작업을 처리하는 데 사용한다. 콜백 함수를 활용하면 비동기적인 처리를 순서대로 처리할 수 있도록(동기적으로) 바꿀 수 있다.

function timecheck(callback) {
    setTimeout(() => {
        console.log("1번 호출");
        callback();
    }, 3000);
}

function printview() {
    console.log("2번 호출");
}

timecheck(printview);

 

콜백 지옥

 

각 함수를 순차적으로 실행하기 위해 콜백 함수만 사용한다면, 소위 얘기하는 콜백 지옥에 빠질 수 있다. 피자를 주문하는 과정을 생각해보자. 토핑을 고르고, 주문을 하고, 주문을 받고, 피자를 먹는다. 각 단계를 콜백 함수를 이용해 순서가 어긋나지 않도록 처리했다. 코드를 읽기도 힘들고, 콜백을 계속해서 작성해줘야 해서 불편하다.

chooseToppings(function (toppings) {
  placeOrder(
    toppings,
    function (order) {
      collectOrder(
        order,
        function (pizza) {
          eatPizza(pizza);
        },
        failureCallback,
      );
    },
    failureCallback,
  );
}, failureCallback);

 

이러한 문제를 해결하기 위해, Promise를 사용할 수 있다.

 

프로미스(Promise)

Promise는 미래에 어떤 종류의 결과가 반환됨을 약속(promise)해주는 객체이다. 콜백 함수를 사용할 때보다 유연하고 편리하게 비동기 처리를 할 수 있다. Promise는 생성과 동시에 비동기 작업을 실행하고, 그 결과를 Promise 객체에 반환한다. 방금 확인한 예시에 Promise 방식을 적용하면, 다음과 같이 간결하게 작성할 수 있다.

// Promise + 화살표 함수
chooseToppings()
  .then((toppings) => placeOrder(toppings))
  .then((order) => collectOrder(order))
  .then((pizza) => eatPizza(pizza))
  .catch(failureCallback);

 

프로미스의 상태

프로미스는 다음 3가지 상태를 가질 수 있다.

 

대기(pending)

프로미스를 호출하면 프로미스 객체가 대기 상태가 되고, 프로미스에서 자체적으로 제공하는 resolve, reject라는 콜백 함수를 인자로 받아 상황에 맞게 작업 처리를 할 수 있다. 대기 상태 이후에 비동기 작업이 성공하면(이행) resolve(”성공!”)와 같이 결과(”성공!”)를 전달하고, 실패하면 reject(”실패!”)와 같이 오류 객체(Error(”실패”))를 전달한다.

let promise = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("성공!");
  } else {
    reject("실패!"); // Error 객체 반환, 아래와 같이 Error 던져도 동일하게 동작
    // throw new Error("실패");
  }
});

 

이행(fulfill)

비동기 작업이 성공적으로 완료되어 결과값을 가지고 있는 상태이다. 이 때 resolve 콜백이 실행되고, 프로미스 객체는 이행 상태가 된다. then() 함수를 이용하여 resolve의 결과값을 받을 수 있다.

한편, then에서는 인자를 2개 받을 수도 있다. 첫 번째 인자는 resolve 콜백 함수의 결과값을, reject 콜백 함수의 오류 객체를 받는다. 그래서 성공 상태일때는 resolve의 결과값을, 실패 상태일때는 reject의 결과값을 받도록 한다.

// .then의 인자로 2개의 함수를 받는 예시
// 일반적으로는 .then에서 성공했을 때의 처리를 하고 .catch로 실패했을 때의 처리를 한다.
promise.then(
  result => {
    console.log("성공 콜백:", result);
  },
  error => {
    console.log("실패 콜백:", error);
  }
);

 

실패(reject)

비동기 작업이 실패하여 오류나 예외가 발생한 상태이다. 프로미스의 콜백 함수로 전달받은 매개변수인 reject 콜백이 실행되고 프로미스 객체는 실패 상태가 된다. catch() 함수를 이용하여 결과값을 받을 수 있다.

// 위의 예제를 다음과 같이 작성할 수 있음(일반적으로 사용하는 방법)
promise.then(
  result => {
    console.log("성공 콜백:", result);
  })
.catch(error => {
    console.log("실패 콜백:", error);
  });

 

 

async와 await

프로미스로 가독성과 편리함을 높이기는 했지만, 이 또한 .then()을 계속해줘서 붙이기 때문에 프로미스 처리가 계속된다면 프로미스 지옥에 빠질 수도 있다. async/await 문법을 활용하여 더 편리하게 비동기 처리를 할 수 있다.

 

async/await 문법은 함수를 선언할 때 앞부분에 async 키워드를 작성하여 비동기 함수로 만든다. async가 앞에 붙은 함수는 프로미스를 반환한다. await는 async 함수 안에서만 동작하고 마치 동기식처럼 프로미스 처리가 끝날 때까지 기다린다.

// Promise를 작성하는 헬퍼 함수 작성
function helperPromise() {
	return new Promise((resolve, reject) => {
	  const success = true;
	  if (success) {
	    resolve("성공!");
	  } else {
	    reject("실패!");
	    // throw new Error("실패");
		  }
		});
}

// await는 단독으로 쓰일 수 없으므로, async로 감싼다.
async function useHelper() {
	result = await helperPromise();
	console.log("성공 콜백:", result);
}

useHelper();

 

async 예외 처리

async 함수 내부의 프로미스에서 예외가 되면(거부되면) throw로 에러를 반환한다. try/catch를 사용하면 프로미스가 거부될 때 해당 예외에 대한 추가 로직이나 오류 메세지를 출력할 수 있다.

async function useHelper() {
	try{
		result = await helperPromise();
		console.log("성공 콜백:", result);
	} catch (e) {
        console.log("실패 콜백:", error);
	}
}

 

 

참고 자료

 

🌐 자바스크립트의 핵심 '비동기' 완벽 이해 ❗

자바스크립트의 동기와 비동기 자바스크립트는 싱글 스레드 언어이기 때문에 한 번에 하나의 작업만 수행할 수 있다. 즉, 이전 작업이 완료되어야 다음 작업을 수행할 수 있게 된다. 우리가 프

inpa.tistory.com

 

 

Graceful asynchronous programming with Promises - Web 개발 학습하기 | MDN

Promises 는 이전 작업이 완료될 때 까지 다음 작업을 연기 시키거나, 작업실패를 대응할 수 있는 비교적 새로운 JavaScript 기능입니다. Promise는 비동기 작업 순서가 정확하게 작동되게 도움을 줍니

developer.mozilla.org