dev.syw

서버 요청처럼 시간이 걸리는 작업을 다루는 법.

비동기 — Promise 와 async/await

JavaScript는 한 번에 하나만 처리하지만, 네트워크 요청 같은 작업은 기다리지 않고 넘어갑니다. 이를 비동기라고 합니다. 이번 레슨에서는 콜백부터 Promise, async/await 까지 비동기를 다루는 도구를 정리합니다.

학습 목표

  • 콜백과 이벤트 루프의 개념을 이해한다
  • Promise 로 비동기 결과를 다룰 수 있다
  • async/await 로 비동기 코드를 동기처럼 작성할 수 있다
  • try-catch 로 비동기 에러를 처리할 수 있다
  • Promise.all·race·allSettled 를 구분해 쓸 수 있다

콜백과 이벤트 루프

가장 원초적인 비동기 처리는 콜백 함수입니다. 작업이 끝나면 호출될 함수를 미리 넘겨둡니다.

console.log('1');
setTimeout(() => console.log('2 (1초 뒤)'), 1000);
console.log('3');
// → 1
// → 3
// → 2 (1초 뒤)
JavaScript

JavaScript 는 이벤트 루프를 통해 시간이 걸리는 작업을 큐에 미뤄두고, 본 작업이 끝난 뒤 차례로 실행합니다. 그래서 32 보다 먼저 찍힙니다.

⚠️ 주의 — 콜백을 중첩하면 "콜백 지옥"에 빠지기 쉽습니다. 이를 해결하려고 나온 것이 Promise 입니다.

Promise

Promise 는 "미래에 완료될 작업의 결과"를 담는 객체입니다. 성공(then)과 실패(catch)를 나눠 처리합니다.

fetch('https://api.example.com/users')
  .then((res) => res.json())
  .then((data) => console.log(data))
  .catch((err) => console.error('실패:', err));
JavaScript

직접 만들 수도 있습니다.

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(500).then(() => console.log('0.5초 경과'));
JavaScript
상태의미
pending아직 진행 중
fulfilled성공 (resolve 호출됨)
rejected실패 (reject 호출됨)

async / await

.then 체인을 동기 코드처럼 읽기 쉽게 바꿔줍니다. async 함수 안에서 await 로 Promise 가 끝날 때까지 기다립니다.

async function loadUsers() {
  try {
    const res = await fetch('https://api.example.com/users');
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.error('실패:', err);
  }
}

loadUsers();
JavaScript

💡 TIPawait 는 반드시 async 함수 안에서만 쓸 수 있습니다(최상위 모듈 제외). async 함수는 항상 Promise 를 반환합니다.

try-catch 로 에러 처리

await 한 작업이 실패하면 예외가 발생하므로 try-catch 로 감쌉니다.

async function getProfile(id) {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (err) {
    console.error('프로필 로드 실패:', err.message);
    return null; // 폴백 값
  }
}
JavaScript

여러 요청을 동시에 — Promise.all

const [users, posts] = await Promise.all([
  fetch('/api/users').then((r) => r.json()),
  fetch('/api/posts').then((r) => r.json()),
]);
JavaScript

💡 TIP — 순서대로 await 하면 느립니다. 서로 독립적인 요청은 Promise.all병렬 처리하세요.

race 와 allSettled

// race: 가장 먼저 끝나는 하나만 (타임아웃 구현에 유용)
const result = await Promise.race([
  fetch('/api/data'),
  wait(3000).then(() => { throw new Error('타임아웃'); }),
]);

// allSettled: 모두 끝날 때까지 기다리고, 성공/실패를 각각 보고
const results = await Promise.allSettled([
  fetch('/api/a'),
  fetch('/api/b'),
]);
results.forEach((r) => {
  console.log(r.status); // → 'fulfilled' 또는 'rejected'
});
JavaScript
메서드동작
all모두 성공해야 성공, 하나라도 실패하면 즉시 실패
race가장 먼저 끝난 결과(성공/실패) 반환
allSettled모두 끝날 때까지 기다려 각각의 결과 보고
any가장 먼저 성공한 결과 반환

요약

  • 비동기 작업은 이벤트 루프 덕분에 본 흐름을 막지 않고 나중에 실행된다
  • Promise 는 pending → fulfilled/rejected 상태를 가진다
  • async/await 로 비동기 코드를 동기처럼 읽기 좋게 작성한다
  • 비동기 에러는 try-catch 로 처리한다
  • 독립 요청은 Promise.all 로 병렬화하고, 부분 실패 허용 시 allSettled 를 쓴다

연습문제

  1. wait(ms) 함수를 만들어 1초 기다린 뒤 "완료"를 출력하는 코드를 작성하세요.
  2. loadUsers.then 체인 버전으로도 작성해 두 방식을 비교하세요.
  3. 두 API 를 병렬로 호출해 결과를 합치는 코드를 Promise.all 로 작성하세요.

    힌트const [a, b] = await Promise.all([...])

  4. Promise.race 로 3초 타임아웃을 가진 요청 함수를 구현하세요.

    힌트 — 실제 요청과 "3초 뒤 reject 하는 Promise"를 경쟁시킵니다.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

JavaScript” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.