JavaScript 비동기를 공부하기 시작하면 Promise와 async/await를 아주 빨리 만나게 됩니다. 문법만 보면 생각보다 친숙해 보여서 금방 익힐 수 있을 것 같지만, 왜 이 도구들이 필요한지 이해하지 못하면 코드는 다시 금방 헷갈려집니다.
특히 async/await는 비동기 코드를 마치 순차 코드처럼 보이게 만들기 때문에 읽기는 쉬워지지만, 그렇다고 내부 동작까지 진짜 동기 코드로 바뀌는 것은 아닙니다.
이 글에서는 아래 내용을 정리합니다.
- Promise가 무엇인지
- async/await가 왜 필요한지
- callback과 어떻게 다른지
핵심은 Promise는 “나중에 완료될 값”을 표현하는 객체이고, async/await는 그 Promise 기반 비동기 코드를 사람이 읽기 쉽게 풀어 주는 문법이라는 점입니다.
Promise 는 무엇인가
Promise는 지금 당장 결과가 없지만, 나중에는 결과가 생길 작업을 표현하는 JavaScript 객체입니다.
예를 들어 네트워크 요청은 즉시 데이터를 돌려주지 않습니다. 이때 Promise는 작업이:
- 아직 기다리는 중인지
- 성공했는지
- 실패했는지
를 표현하고, 결과가 준비되면 그다음 처리 로직을 연결할 수 있게 해 줍니다.
즉 Promise는 “미래의 결과를 다루는 표준 인터페이스”라고 보면 이해하기 쉽습니다.
callback 만으로는 왜 불편했을까
예전에는 비동기 후속 처리를 callback으로 많이 작성했습니다.
fetchUser(userId, function (user) {
fetchOrders(user.id, function (orders) {
saveLog(orders, function () {
console.log('done');
});
});
});
작은 예제에서는 괜찮아 보이지만, 흐름이 길어질수록 들여쓰기가 깊어지고, 에러 처리가 흩어지고, 중간 로직을 재사용하기도 어려워집니다.
Promise는 이 문제를 완전히 없애지는 못해도, 적어도 후속 처리 흐름을 더 구조적으로 연결할 수 있게 만들어 줬습니다.
Promise 체인은 어떻게 읽으면 될까
가장 기본적인 Promise 사용 방식은 .then(), .catch(), 필요하다면 .finally()를 이어 붙이는 것입니다.
fetchUser(userId)
.then((user) => fetchOrders(user.id))
.then((orders) => saveLog(orders))
.catch((error) => console.error(error))
.finally(() => console.log('finished'));
이 방식은 중첩 callback보다 읽기 좋지만, 체인이 길어지면 여전히 눈으로 따라가기가 피곤해질 수 있습니다.
async/await 는 왜 등장했을까
async/await는 Promise 기반 비동기 로직을 위에서 아래로 읽히는 순차 코드처럼 보이게 만들어 줍니다.
async function run() {
try {
const user = await fetchUser(userId);
const orders = await fetchOrders(user.id);
await saveLog(orders);
} catch (error) {
console.error(error);
}
}
이 구조의 장점은 흐름을 따라가기가 쉽다는 점입니다. “먼저 사용자 정보를 가져오고, 그다음 주문을 가져오고, 마지막에 로그를 저장한다”라는 순서를 머릿속에서 더 자연스럽게 읽을 수 있습니다.
그래서 실무에서는 긴 비동기 로직을 설명하거나 유지보수할 때 async/await를 많이 씁니다.
async/await 가 코드를 동기로 바꾸는 걸까
그렇지는 않습니다. 이 부분이 아주 중요합니다.
await는 Promise 결과가 나올 때까지 그 async 함수 내부 흐름을 잠시 멈춘 것처럼 보이게 만들 뿐입니다. 내부 메커니즘은 여전히 Promise 기반 비동기 구조 위에서 동작합니다.
즉 async/await는 비동기 동작을 동기 코드처럼 “읽히게” 해 주는 문법이지, 비동기를 진짜 blocking 코드로 바꾸는 마법은 아닙니다.
Promise 와 async/await 중 무엇을 써야 할까
대부분의 일상적인 순차 흐름에서는 async/await가 읽기 쉽습니다. 하지만 Promise 자체의 도구들도 여전히 중요합니다.
예를 들어:
- 순차 단계가 긴 흐름:
async/await가 더 읽기 쉬운 경우가 많음 - 여러 비동기 작업을 함께 조합할 때:
Promise.all()같은 유틸리티가 자연스러움 - 간단한 후속 처리 연결:
.then()체인이 더 짧을 수도 있음
즉 둘은 경쟁 관계가 아니라 같은 기반 위의 서로 다른 표현 방식에 가깝습니다.
자주 하는 실수
1. await 를 많이 쓰면 성능이 좋아진다고 생각한다
await는 가독성과 제어 흐름을 위한 도구입니다. 자동 성능 최적화 도구는 아닙니다.
2. 독립적인 작업도 순서대로 await 한다
서로 의존하지 않는 작업이라면 병렬로 묶는 편이 더 나을 수 있습니다.
const [user, posts] = await Promise.all([
fetchUser(userId),
fetchPosts(userId),
]);
3. 에러 처리를 빼먹는다
Promise 체인이든 async/await든, 실패 경로를 설계하지 않으면 실제 서비스에서는 금방 문제가 됩니다.
읽는 연습을 어떻게 하면 좋을까
처음에는 문법을 외우기보다 같은 흐름을 세 가지 형태로 비교해 보는 것이 좋습니다.
- callback 버전
- Promise 체인 버전
- async/await 버전
이렇게 바꿔 보면 문법보다 흐름이 먼저 눈에 들어옵니다. 그리고 그다음에 Promise.all()처럼 독립 작업을 묶는 패턴까지 보면 훨씬 빠르게 감이 옵니다.
FAQ
Q. async 함수는 항상 Promise 를 반환하나요
그렇습니다. 내부에서 일반 값을 반환해도 Promise로 감싸져 반환됩니다.
Q. await 는 어디에서나 쓸 수 있나요
보통은 async 함수 안에서 사용합니다.
Q. Promise 를 알면 async/await 는 안 배워도 되나요
같이 보는 편이 좋습니다. async/await를 제대로 이해하려면 Promise 개념이 바탕에 있어야 합니다.
Read Next
- 비동기와 기다림 자체의 개념을 먼저 다지고 싶다면 동기 vs 비동기 가이드를 함께 읽어보세요.
- 호출한 쪽이 실제로 멈추는지 궁금하면 Blocking vs Non-Blocking 가이드가 자연스럽게 이어집니다.
- 런타임이 이런 작업을 어떻게 스케줄링하는지 보려면 Event Loop 가이드도 좋은 다음 글입니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis, RabbitMQ, Kafka 중 어디부터 볼까 Redis, RabbitMQ, Kafka가 함께 있는 시스템에서 지금 보이는 장애가 어느 계층에 더 가까운지, 첫 10분 안에 무엇을 확인하고 어떤 글로 들어가야 하는지 정리한 실전 허브 가이드입니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Astro 기술 블로그 SEO 체크리스트: 트래픽 기다리기 전에 먼저 고칠 것 Astro 기술 블로그를 위한 실전 SEO 체크리스트입니다. 배포 호스트 확인, robots.txt, sitemap, canonical, hreflang, 구조화 데이터, 페이지별 메타데이터, noindex 판단, 검증 명령까지 우선순위대로 정리합니다.
- 다국어 블로그 canonical과 hreflang 설정 가이드: 무엇을 확인하고 어디서 깨질까 다국어 블로그에서 canonical과 hreflang을 어떻게 설정해야 하는지 실전 기준으로 정리합니다. self-canonical, 상호 연결되는 hreflang 묶음, x-default, 카테고리 페이지, 최종 렌더 HTML 점검, 한 언어 버전이 다른 언어 버전을 눌러버리는 실수까지 다룹니다.
- OpenAI Codex CLI 설치 가이드: 설치, 인증, 첫 작업까지 OpenAI Codex CLI를 실전 기준으로 설치하는 방법을 정리했다. 설치, 로그인, 첫 실행, Windows 주의점, 첫 작업을 어떻게 시작하면 좋은지까지 다룬다.