async/await

2025. 3. 30. 22:45Web Development/JavaScript

async/await

ES2017에서 도입된 async/await는 Promise를 기반으로 하지만, 더 간결하고 동기식 코드와 유사한 방식으로 비동기 코드를 작성할 수 있게 해줍니다.

async/await 기본 문법

// async 함수 선언
async function fetchUserData() {
  try {
    // await는 Promise가 이행될 때까지 기다림
    const response = await fetch('https://api.example.com/users');
    
    if (!response.ok) {
      throw new Error(`HTTP 오류! 상태: ${response.status}`);
    }
    
    const userData = await response.json();
    console.log('사용자 데이터:', userData);
    return userData;
  } catch (error) {
    console.error('데이터 가져오기 오류:', error);
    throw error; // 오류 다시 던지기
  }
}

// async 함수 호출 (결과는 Promise)
fetchUserData()
  .then(data => {
    console.log('처리된 데이터:', data);
  })
  .catch(error => {
    console.error('최종 오류 처리:', error);
  });

// 화살표 함수로도 가능
const fetchUsers = async () => {
  const response = await fetch('https://api.example.com/users');
  return response.json();
};

Promise 체인을 async/await로 변환

Promise 체인을 async/await를 사용하여 더 읽기 쉽고 직관적인 코드로 변환할 수 있습니다.

// Promise 체인 버전
function getCommentsByUser(userId) {
  return fetchData(`https://api.example.com/users/${userId}`)
    .then(user => {
      return fetchData(`https://api.example.com/users/${user.id}/posts`);
    })
    .then(posts => {
      return fetchData(`https://api.example.com/posts/${posts[0].id}/comments`);
    });
}

// async/await 버전
async function getCommentsByUser(userId) {
  try {
    const user = await fetchData(`https://api.example.com/users/${userId}`);
    const posts = await fetchData(`https://api.example.com/users/${user.id}/posts`);
    const comments = await fetchData(`https://api.example.com/posts/${posts[0].id}/comments`);
    return comments;
  } catch (error) {
    console.error('댓글 가져오기 오류:', error);
    throw error;
  }
}

비동기 작업의 병렬 처리

여러 비동기 작업을 병렬로 처리하여 성능을 향상시킬 수 있습니다.

// 순차 처리 (더 느림)
async function sequentialFetch() {
  console.time('순차 처리');
  
  const user = await fetchData('https://api.example.com/user');
  const posts = await fetchData('https://api.example.com/posts');
  const comments = await fetchData('https://api.example.com/comments');
  
  console.timeEnd('순차 처리');
  return { user, posts, comments };
}

// 병렬 처리 (더 빠름)
async function parallelFetch() {
  console.time('병렬 처리');
  
  // 모든 요청을 동시에 시작
  const userPromise = fetchData('https://api.example.com/user');
  const postsPromise = fetchData('https://api.example.com/posts');
  const commentsPromise = fetchData('https://api.example.com/comments');
  
  // 결과를 기다림
  const user = await userPromise;
  const posts = await postsPromise;
  const comments = await commentsPromise;
  
  console.timeEnd('병렬 처리');
  return { user, posts, comments };
}

// Promise.all을 사용한 병렬 처리 (더 간결)
async function parallelFetchWithPromiseAll() {
  console.time('Promise.all 병렬 처리');
  
  const [user, posts, comments] = await Promise.all([
    fetchData('https://api.example.com/user'),
    fetchData('https://api.example.com/posts'),
    fetchData('https://api.example.com/comments')
  ]);
  
  console.timeEnd('Promise.all 병렬 처리');
  return { user, posts, comments };
}

async/await 에러 처리

try/catch 블록을 사용하여 비동기 코드의 오류를 동기 코드와 유사한 방식으로 처리할 수 있습니다.

// 개별 try/catch
async function fetchAndProcessData() {
  try {
    const data = await fetchData('https://api.example.com/data');
    console.log('데이터:', data);
    
    try {
      const processedData = await processData(data);
      console.log('처리된 데이터:', processedData);
      return processedData;
    } catch (processingError) {
      console.error('데이터 처리 오류:', processingError);
      // 기본 처리 결과 반환
      return { processed: false, data };
    }
  } catch (fetchError) {
    console.error('데이터 가져오기 오류:', fetchError);
    // 기본 데이터 반환
    return { error: true, message: fetchError.message };
  }
}

// 대역 try/catch와 오류 종류 구분
async function handleUserData(userId) {
  try {
    const user = await fetchUser(userId);
    const posts = await fetchUserPosts(userId);
    
    return {
      user,
      posts,
      status: 'success'
    };
  } catch (error) {
    // 오류 종류에 따른 처리
    if (error.name === 'NetworkError') {
      console.error('네트워크 오류:', error);
      // 재시도 로직
    } else if (error.name === 'ValidationError') {
      console.error('유효성 검사 오류:', error);
      // 입력 정정 요청
    } else {
      console.error('알 수 없는 오류:', error);
    }
    
    return {
      error: true,
      message: error.message,
      status: 'error'
    };
  }
}

async/await의 한계와 주의사항

async/await을 사용할 때 알아두어야 할 몇 가지 주의사항이 있습니다.

// 1. 최상위 레벨에서 await 사용 불가 (일부 최신 환경 제외)
// await fetch('https://api.example.com/data'); // 오류 발생

// 즉시 실행 비동기 함수로 해결
(async () => {
  const data = await fetch('https://api.example.com/data');
  console.log(data);
})();

// 2. forEach와 await 사용 시 주의
async function processItems(items) {
  // 잘못된 사용: forEach는 await를 적절히 처리하지 않음
  items.forEach(async (item) => {
    const result = await processItem(item); // 병렬로 실행됨
    console.log(result);
  });
  console.log('모든 항목 처리 완료'); // 실제로는 처리가 완료되기 전에 실행됨
}

// 올바른 사용: for...of 또는 Promise.all
async function processItemsCorrectly(items) {
  // 순차 처리
  for (const item of items) {
    const result = await processItem(item);
    console.log(result);
  }
  
  console.log('모든 항목 순차 처리 완료');
  
  // 병렬 처리
  const results = await Promise.all(items.map(item => processItem(item)));
  console.log(results);
  console.log('모든 항목 병렬 처리 완료');
}

'Web Development > JavaScript' 카테고리의 다른 글

모듈 시스템  (0) 2025.03.30
실전 비동기 프로그래밍 패턴  (0) 2025.03.30
Promise  (0) 2025.03.30
콜백 함수  (0) 2025.03.30
비동기 프로그래밍  (0) 2025.03.30