JavaScript 비동기 패턴 완전 정복|콜백부터 async/await, 병렬 처리까지 한 번에!
⏳ JavaScript 비동기 패턴 완전 정복|콜백, 프로미스, async/await 그리고 병렬 처리 전략까지
안녕하세요, 퍼블리셔 노미입니다! 🙌
자바스크립트를 본격적으로 쓰다 보면 꼭 만나는 개념, 바로 비동기 처리예요. ⌛
처음엔 콜백(callback)으로 시작해서, Promise, async/await, 그리고 병렬 처리(Promise.all)까지 점점 발전한 비동기 흐름을 마스터하면, 진짜 “실전에서 API도 자유롭게 다루는 개발자”로 성장할 수 있어요. 🌱 오늘은 이 흐름을 아주 자세히! 아주 쉽게! 코드 예시 + 이모지로 설명해드릴게요!
🔄 자바스크립트는 왜 비동기 언어일까?
JS는 브라우저나 서버에서 논블로킹 방식으로 동작해요. 즉, 어떤 작업이 오래 걸려도 다른 작업을 멈추지 않고 계속 진행해요! 🏃♂️
예시
console.log('A');
setTimeout(() => {
console.log('B');
}, 1000);
console.log('C');
출력 결과는? 👇 A → C → B
왜냐하면 setTimeout은 비동기적으로 동작하기 때문이에요! 🔁
🔁 콜백 함수 (Callback)
비동기의 시작은 바로 콜백 함수였어요. 하지만 구조가 복잡해지면 콜백 지옥(callback hell)이 생기죠... 🕳️
📌 기본 예시
function fetchData(callback) {
setTimeout(() => {
callback('📦 데이터 도착');
}, 1000);
}
fetchData((data) => {
console.log(data); // 📦 데이터 도착
});
😱 콜백 헬 예시
login(user, (res1) => {
getProfile(res1, (res2) => {
getPosts(res2, (res3) => {
console.log(res3);
});
});
});
→ 괄호가 지옥처럼 깊어지고, 디버깅도 어려워져요! 😵💫
⚡ Promise란 무엇인가요?
Promise는 비동기 작업의 성공/실패 결과를 나중에 반환해주는 객체입니다. 콜백 헬을 해결하기 위해 등장했어요! 🎁
📌 Promise 기본 구조
const promise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
success ? resolve('🎉 성공!') : reject('❌ 실패!');
}, 1000);
});
promise
.then((result) => console.log(result))
.catch((err) => console.error(err));
- 🎯
resolve()
→ 성공 시 호출 - 🛑
reject()
→ 실패 시 호출 - 🔁
.then()
→ 결과 받기 - 🚨
.catch()
→ 에러 처리
→ 가독성도 좋아지고, 에러도 깔끔하게 다룰 수 있어요! ✅
🔗 Promise 체이닝
Promise는 계속 이어 붙이기도 가능해요! 이걸 체이닝(chaining)이라고 합니다. 🔗
📌 예시
function step1() {
return new Promise((res) => {
setTimeout(() => res('1️⃣ Step 1'), 500);
});
}
function step2(prev) {
return new Promise((res) => {
setTimeout(() => res(`${prev} → 2️⃣ Step 2`), 500);
});
}
step1()
.then(step2)
.then((final) => {
console.log('결과:', final);
});
→ 비동기 흐름을 순차적으로 깔끔하게 정리할 수 있어요! 🧼
✨ async / await의 등장
ES2017(ES8)부터는 더 간결하고 동기식처럼 보이는 async / await이 도입됐어요! 🌟 await
는 Promise가 끝날 때까지 기다렸다가 결과를 받아옵니다.
📌 기본 구조
async function getData() {
try {
const res = await fetch('https://api.example.com/data');
const json = await res.json();
console.log('📦 결과:', json);
} catch (e) {
console.error('❌ 에러 발생:', e);
}
}
getData();
- 🔍 async 함수 안에서만 await 사용 가능
- 🚨 try/catch로 에러 처리
→ 훨씬 직관적이고 깔끔하죠? 😌
🔁 병렬 처리 전략: Promise.all & Promise.race
여러 개의 비동기 작업을 동시에 실행하고 싶을 때는? 바로 Promise.all과 Promise.race를 사용하면 돼요! 🏃♀️🏃♂️
✅ Promise.all
모든 작업이 끝날 때까지 기다린 다음, 결과를 한 번에 받습니다.
const p1 = Promise.resolve('🍕 피자');
const p2 = Promise.resolve('🍔 버거');
const p3 = Promise.resolve('🥤 콜라');
Promise.all([p1, p2, p3])
.then((result) => {
console.log('주문 완료:', result);
// ["🍕 피자", "🍔 버거", "🥤 콜라"]
});
- 🧠 모든 Promise가 성공해야만
then
실행 - ⚠️ 하나라도 실패하면
catch
로 이동
🏁 Promise.race
가장 먼저 끝나는 Promise의 결과를 반환합니다.
const fast = new Promise(res => setTimeout(() => res('⚡ 빠름!'), 300));
const slow = new Promise(res => setTimeout(() => res('🐢 느림!'), 1000));
Promise.race([fast, slow]).then((winner) => {
console.log('결과:', winner); // ⚡ 빠름!
});
→ 속도 우선, 선착순 처리 로직에 딱! 🥇
💥 에러 핸들링 꿀팁 정리
1. try/catch 범위를 좁혀라!
try {
const res = await fetch(url);
const json = await res.json();
} catch (e) {
console.error('❌ 에러 발생:', e.message);
}
2. finally는 꼭 써라!
로딩 종료, 자원 정리 등은 항상 finally에!
try {
...
} catch (e) {
...
} finally {
hideSpinner(); // ✨ 로딩 종료
}
3. 사용자 정의 에러 throw
if (!user) {
throw new Error('🧍 유저 정보가 없습니다!');
}
→ 예상 가능한 에러는 미리 던지는 것도 전략! 🧠
🧪 실전 예제: API 호출 + 병렬 처리
async function fetchPosts() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
return await res.json();
}
async function fetchUsers() {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
return await res.json();
}
async function init() {
try {
const [posts, users] = await Promise.all([fetchPosts(), fetchUsers()]);
console.log('📘 게시물:', posts.length);
console.log('👥 유저 수:', users.length);
} catch (e) {
console.error('🔥 데이터 불러오기 실패:', e);
}
}
init();
→ 실전에서도 이렇게 Promise.all로 병렬 API 호출 많이 해요! 🛰️
📚 마무리 요약
✔️ 비동기 흐름 핵심 요약
- 🔁 콜백 함수: 시작은 여기지만, 지옥을 부를 수 있음
- ⚡ Promise: 체이닝과 예외 처리 가능
- ✨ async/await: 동기처럼 보이는 비동기 코드
- 🔁 Promise.all: 여러 작업 병렬 실행
- 🏁 Promise.race: 가장 빠른 하나만 받기
🔥 정리하면...
자바스크립트의 비동기는 무섭지 않아요! 패턴을 이해하고 흐름을 그릴 수 있다면, 실전에서 누구보다 유연하게 API와 이벤트를 다룰 수 있어요! 💪
다음 글에서는 🌈 비동기 상태 관리와 이벤트 루프 완전 정복 편으로 이어갈게요! 끝까지 읽어주셔서 감사합니다! 🙏
#JavaScript비동기 #콜백헬 #Promise #asyncawait #비동기API #병렬처리 #에러핸들링 #퍼블리셔노미 #JS심화 #프론트엔드실전