본문 바로가기
차근차근

JavaScript 비동기 패턴 완전 정복|콜백부터 async/await, 병렬 처리까지 한 번에!

by 아빠고미 2025. 5. 20.
반응형

⏳ 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.allPromise.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심화 #프론트엔드실전

반응형