반응형
1. 도입: 비동기가 어렵게 느껴지는 이유
- 훅(Hook): JavaScript는 본질적으로 단일 스레드(Single-Threaded) 언어입니다. 하지만 네트워크 통신(API 호출)처럼 시간이 오래 걸리는 작업을 만나면, 메인 스레드가 멈추지 않도록 비동기적으로 처리해야 합니다.
- Callback Hell의 고통: 과거에는 콜백 함수(Callback Function)를 중첩하여 비동기 작업을 처리했습니다. 이 방식은 코드가 깊어지고 복잡해져서 콜백 헬(Callback Hell)이라는 악몽을 낳았습니다.
- 해결책 제시: Promise와 async/await는 이 콜백 헬을 종식시키고 비동기 코드를 동기 코드처럼 깔끔하게 작성할 수 있게 해주는 혁신적인 도구입니다.
2. Promise: 비동기 상태를 관리하는 약속
Promise는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체입니다.
2.1. Promise의 세 가지 상태
- Pending (대기): 작업이 아직 완료되지 않은 초기 상태.
- Fulfilled (이행/성공): 작업이 성공적으로 완료되었으며 결과를 사용할 수 있는 상태. (.then()으로 처리)
- Rejected (거부/실패): 작업이 실패했으며 에러 정보를 가진 상태. (.catch()로 처리)
2.2. 체이닝(Chaining)을 통한 흐름 제어
- .then().then(): 콜백 헬을 Promise 체인으로 평탄화하는 핵심 기법입니다. 각 .then()은 이전 Promise가 성공적으로 반환한 값을 받아 다음 비동기 작업을 순차적으로 실행할 수 있게 합니다.
- .catch()의 역할: 체인의 어느 단계에서든 발생한 모든 에러를 마지막 .catch() 블록에서 한 번에 처리할 수 있습니다.
3. async/await: 비동기를 동기 코드처럼
async/await은 ES8(ECMAScript 2017)에 도입된 문법으로, Promise를 훨씬 더 깔끔하게 사용할 수 있도록 도와줍니다.
3.1. 기본 원리
- async 함수: 함수 앞에 async 키워드를 붙이면, 그 함수는 항상 Promise를 반환하도록 만듭니다. 함수 내에서 return하는 값은 자동적으로 Promise.resolve()로 감싸집니다.
- await 키워드: async 함수 내부에서만 사용할 수 있으며, Promise가 Fulfilled 상태가 될 때까지 기다렸다가 그 결과 값을 반환합니다. 코드가 일시 정지되므로 비동기 작업이 동기적인 순서처럼 보이게 됩니다.
JavaScript
async function fetchData(url) {
try {
// await이 Promise가 해결될 때까지 코드를 일시 정지
const response = await fetch(url);
const data = await response.json();
return data; // 이 값은 Promise.resolve(data)로 반환됨
} catch (error) {
console.error('API 호출 중 에러 발생:', error);
}
}
3.2. 에러 처리의 핵심: try...catch
- async/await 환경에서는 .catch() 대신 동기 코드에서 사용하는 try...catch 블록을 사용하여 await에서 발생하는 에러를 깔끔하게 포착하고 처리할 수 있습니다.
4. 고급 비동기 관리: 병렬 처리와 동시성
비동기 작업이 반드시 순차적으로 실행될 필요는 없습니다. 여러 작업을 동시에 실행하여 성능을 극대화하는 방법을 다룹니다.
4.1. 순차적 실행 (기본)
JavaScript
// A -> B 순서대로 실행 (총 시간: A 시간 + B 시간)
const resultA = await taskA();
const resultB = await taskB();
4.2. 병렬 실행 (Promise.all())
- 목표: 서로 의존성이 없는 비동기 작업을 모두 동시에 실행하고, 모든 작업이 완료될 때까지 기다리는 가장 효율적인 방법입니다.
- 활용: 여러 개의 API를 한 번에 호출할 때 사용합니다.
- 주의: 작업 중 단 하나라도 실패(Rejected)하면 전체 Promise.all()은 즉시 실패(Rejected) 상태가 됩니다.
JavaScript
// A와 B를 동시에 실행 (총 시간: Max(A 시간, B 시간))
const [resultA, resultB] = await Promise.all([taskA(), taskB()]);
5. 마무리: 비동기 숙련이 개발 실력의 척도
- 요약: Promise는 비동기 코드의 상태를 관리하는 객체 모델을 제공하며, async/await는 그 Promise를 읽기 쉬운 문법으로 포장해 줍니다.
- 마지막 조언: 비동기 코드를 능숙하게 작성하고 에러 처리와 병렬화를 적절히 활용하는 것은 프론트엔드 개발자의 실력을 판단하는 중요한 기준입니다. 오늘 배운 내용을 바탕으로 동기적 사고방식으로 비동기 코드를 제어하는 마스터가 되시길 바랍니다!
이 주제는 비동기 코드 관리 능력을 향상시키는 데 매우 실질적인 도움이 될 것입니다. 이 외에 다른 궁금한 점이 있으신가요?
반응형