본문 바로가기
카테고리 없음

자바스크립트 Promise와 async/await 개념 정리

by ctrl-f 2025. 5. 4.

자바스크립트 비동기 처리

 

자바스크립트(JavaScript)에서 비동기 처리(asynchronous programming)는 사용자 경험(UX)과 시스템 안정성에 큰 영향을 미치는 핵심 개념입니다. 특히 네트워크 요청, 타이머, 파일 처리처럼 시간이 걸리는 작업을 효과적으로 다루기 위해, JS는 오랜 시간 동안 콜백(callback), Promise, async/await 등의 방식을 발전시켜 왔습니다. 이 중에서도 Promise는 콜백 헬을 해결하고 코드의 흐름을 구조화하는 데 중심적인 역할을 하며, 이후 async/await로 자연스럽게 이어졌습니다. 본 글에서는 Promise의 개념과 동작 방식, 콜백/async와의 비교, 실무 활용법과 면접 대응 전략까지 학습과 실전에 도움이 되도록 정리합니다.

Promise란 무엇이며 왜 필요한가?

Promise는 자바스크립트에서 비동기 작업의 완료 또는 실패를 표현하는 객체입니다. 쉽게 말해, "미래에 어떤 일이 완료될 것이라는 약속"을 의미하며, 그 결과값을 나중에 사용할 수 있도록 합니다.

1. 콜백 헬의 문제

Promise가 등장하기 전에는 콜백 기반 비동기 처리가 일반적이었습니다. 하지만 콜백을 중첩해 작성할 경우 다음과 같은 콜백 헬(callback hell) 문제가 발생합니다.

getData(function(result1) {
  processData(result1, function(result2) {
    saveData(result2, function(result3) {
      console.log('완료');
    });
  });
});

이처럼 깊게 중첩되면 코드 가독성과 유지보수가 떨어집니다. Promise는 이러한 구조를 평평하게(flatten) 만들고, 비동기 흐름을 체계적으로 처리할 수 있게 해줍니다.

2. Promise의 상태

  • pending: 아직 작업이 끝나지 않은 상태
  • fulfilled: 작업이 성공적으로 완료된 상태
  • rejected: 작업이 실패한 상태

Promise는 위 세 가지 상태 중 하나를 가집니다. 상태는 한 번 정해지면 바뀌지 않습니다(불변성). 이 특성 덕분에 예측 가능한 비동기 흐름 제어가 가능합니다.

3. 기본 문법

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('성공');
    } else {
      reject('실패');
    }
  }, 1000);
});

promise
  .then(result => console.log(result)) // 성공 시
  .catch(error => console.error(error)) // 실패 시
  .finally(() => console.log('작업 종료'));

.then()은 작업 성공 시 실행되고, .catch()는 실패 시, .finally()는 무조건 실행되는 후처리 구문입니다.

4. Promise 체이닝

여러 비동기 작업을 순차적으로 처리할 때는 다음과 같이 체이닝(Chaining) 방식으로 작성할 수 있습니다.

fetchUser()
  .then(user => fetchPosts(user.id))
  .then(posts => displayPosts(posts))
  .catch(error => console.error('에러 발생:', error));

각 then은 이전 작업의 결과를 다음 작업에 넘기며, 코드 흐름이 직관적이고 간결해집니다.

콜백과 async/await과의 비교

Promise는 자바스크립트 비동기 처리의 진화 과정에서 중간 단계입니다. 이 흐름은 아래와 같이 이해할 수 있습니다:

1. 콜백 방식

콜백은 함수의 인자로 다른 함수를 전달하여 비동기 완료 후 호출하는 방식입니다.

function getData(callback) {
  setTimeout(() => {
    callback('데이터');
  }, 1000);
}

단점: 중첩이 깊어지면 코드 유지보수 어려움 (callback hell)

2. Promise 방식

콜백을 분리된 then 체인으로 표현 가능해지고, 에러 처리도 체계화됩니다.

getDataPromise()
  .then(data => process(data))
  .catch(err => console.error(err));

3. async/await 방식

Promise 위에 구성된 문법으로, 동기 코드처럼 보이지만 실제로는 비동기 흐름입니다.

async function main() {
  try {
    const data = await getData();
    const result = await process(data);
    console.log(result);
  } catch (err) {
    console.error(err);
  }
}

비교 요약표

방식 가독성 에러 처리 중첩 구조 사용 시기
콜백 낮음 분산 처리 심함 구식 코드, 이벤트 중심
Promise 높음 .catch로 일괄 처리 적음 ES6 이후 일반적 사용
async/await 최고 try/catch로 구조화 없음 현대적 코드, 대부분 권장

요약: 콜백은 구식, Promise는 중간 단계, async/await은 가독성과 유지보수 측면에서 가장 우수합니다.

실무 활용법과 면접 질문 대응 전략

1. 실무 예시: API 호출

async function fetchUserProfile(userId) {
  try {
    const res = await fetch(`/api/users/${userId}`);
    if (!res.ok) throw new Error('API 호출 실패');
    const data = await res.json();
    return data;
  } catch (error) {
    console.error('에러 발생:', error);
    throw error;
  }
}

실제 업무에서는 Promise 체이닝보다는 async/await + try/catch 조합이 표준입니다. 특히 await가 직렬적으로 작동</strong하기 때문에, 동시에 여러 요청을 보낼 때는 Promise.all을 활용합니다.

2. 고급 활용: Promise.all

const [user, posts] = await Promise.all([
  fetchUser(),
  fetchPosts()
]);

위 코드는 두 작업을 병렬로 실행한 후 결과를 동시에 받습니다. 네트워크 효율성과 속도 개선에 유리합니다.

3. 면접 질문 예시

  • “Promise란 무엇인가요?”
  • “콜백 함수와 Promise의 차이는?”
  • “async/await이 어떻게 동작하나요?”
  • “Promise.all()과 Promise.race()의 차이는?”

4. 면접 답변 전략

“Promise는 비동기 결과를 다루는 객체이며, 상태를 관리하고 체이닝이 가능합니다. 콜백보다 가독성이 좋고, async/await과 함께 사용되면 동기 코드처럼 작성할 수 있어 유지보수성이 뛰어납니다. 실무에선 API 호출, 에러 처리, 병렬 실행 등 다양한 케이스에 사용하고 있습니다.”

팁: 실제 사용 경험을 곁들이면 훨씬 신뢰도 높은 답변이 됩니다.

결론

자바스크립트의 비동기 처리 핵심은 Promise입니다. 콜백 기반 코드의 복잡성을 줄이고, then/catch 체이닝으로 구조화된 흐름을 가능하게 만들었으며, async/await의 기반이기도 합니다.

실무에서는 API 호출, 데이터 처리, 파일 업로드 등 다양한 비동기 상황에서 Promise와 async/await이 필수처럼 사용되고 있으며, 면접에서도 그 개념, 비교, 활용 경험에 대해 자주 묻는 항목입니다. 따라서 Promise의 본질과 async/await의 관계를 깊이 이해하고, 실제 코드로 설명할 수 있도록 연습하는 것이 중요합니다.