IT관련/Angular

[Angular] 왜 await을 썼는데 기다려주지 않을까? (Promise와 비동기의 비밀)

파란하늘999 2026. 3. 13. 10:23

Angular 개발을 하다 보면 async/await을 사용했는데도 데이터가 오기 전에 다음 코드가 실행되어 버리는 당황스러운 상황을 마주하곤 합니다.

 

왜 await이 우리가 원하는 대로 작동하지 않는지, 그 핵심 이유를 정리해 보겠습니다.


1. await은 오직 'Promise'만 기다립니다

가장 근본적인 원인입니다. 자바스크립트의 await 키워드는 Promise 객체가 resolve될 때까지 비동기 실행을 일시 정지시킵니다.

  • 문제 상황: Angular의 많은 비동기 처리(특히 HTTP 통신)는 Observable을 기반으로 합니다.
  • 결과: Observable 객체 앞에 await을 붙이면, 이 객체는 Promise가 아니기 때문에 자바스크립트 엔진은 기다릴 대상이 없다고 판단하고 즉시 다음 줄로 넘어가 버립니다.

2. Angular(RxJS)에서 흔히 발생하는 실수

❌ 잘못된 예시 (Observable에 직접 사용)

async getData() {
  // HttpClient는 Observable을 반환합니다.
  // await은 Observable을 기다리지 못하고 바로 다음 코드를 실행합니다.
  const data = await this.http.get('https://api.example.com/data'); 
  console.log(data); // 데이터가 아닌 Observable 객체가 출력될 수 있음
}

✅ 올바른 해결 방법: lastValueFrom 또는 firstValueFrom

RxJS 7 이상에서는 Observable을 Promise로 변환해 주는 함수를 제공합니다.

import { lastValueFrom } from 'rxjs';

async getData() {
  const observer$ = this.http.get('https://api.example.com/data');
  // Observable을 Promise로 변환해야 await이 작동합니다.
  const data = await lastValueFrom(observer$); 
  console.log(data); // 실제 데이터를 안전하게 출력
}

3. await이 무시되는 또 다른 이유: forEach vs for...of

배열 루프 안에서 비동기 처리를 할 때 자주 하는 실수입니다.

  • forEach: 내부 콜백 함수가 async라 하더라도, forEach 자체는 이 프로미스들이 완료될 때까지 기다려주지 않는 설계 구조를 가지고 있습니다.
  • 해결책: 반드시 for...of 문을 사용하거나 Promise.all()을 사용해야 합니다.
// ❌ 작동 안 함
items.forEach(async (item) => {
  await this.saveItem(item);
});

// ✅ 작동 함
for (const item of items) {
  await this.saveItem(item);
}

4. 핵심 요약: 이것만 기억하세요!

상황 원인 해결책
Observable 사용 시 await은 Promise만 인식함 lastValueFrom()으로 변환
배열 반복문 사용 시 forEach는 비동기를 기다리지 않음 for...of 또는 Promise.all 사용
함수 정의 시 async 키워드 누락 함수 앞에 반드시 async 추가

마치며

Angular는 RxJS의 강력한 비동기 스트림을 활용하지만, 때로는 단순한 async/await이 코드를 읽기 편하게 만들어 주기도 합니다.

 

**"await 뒤에는 반드시 Promise가 와야 한다"**는 점만 명확히 이해하면 비동기 버그를 획기적으로 줄일 수 있습니다!

반응형