[ES6] async/await 완벽 가이드: 비동기를 동기처럼 다루는 방법

반응형

Promise, async, await란 무엇인가?

JavaScript의 비동기 프로그래밍을 이해하기 위해서는 세 가지 핵심 개념을 알아야 합니다.

개념 설명

Promise 비동기 작업의 결과를 나중에 받을 수 있게 하는 객체
async 함수를 비동기로 선언하며, 항상 Promise를 반환
await Promise의 결과가 준비될 때까지 기다림 (async 함수 내부에서만 사용 가능)

왜 async/await를 사용해야 할까?

비동기 코드의 흐름은 순차적이지 않기 때문에 처리 순서를 명확히 하기 위해 Promise와 async/await를 사용합니다.

예를 들어, 다음과 같은 순서로 작업해야 하는 경우:

// 서버에서 사용자 정보 가져오고, 가져온 정보로 다시 요청해야 함
getUserId() → getUserData(id) → renderUser(data)

콜백 지옥에서 async/await까지 단계별 진화

1단계: 콜백 지옥 방식 ❌

const getUserId = (callback) => {
    setTimeout(() => {
        callback(101);
    }, 1000);
};

const getUserData = (id, callback) => {
    setTimeout(() => {
        callback({ id, name: 'Alice' });
    }, 1000);
};

getUserId(id => {
    getUserData(id, data => {
        console.log('사용자 정보:', data);
    });
});

문제점: 중첩 구조로 가독성이 떨어지고, 에러 처리도 어렵습니다.

2단계: Promise 방식 ✅

const getUserId = () => {
    return new Promise(resolve => {
        setTimeout(() => resolve(101), 1000);
    });
};

const getUserData = (id) => {
    return new Promise(resolve => {
        setTimeout(() => resolve({ id, name: 'Alice' }), 1000);
    });
};

getUserId()
    .then(id => getUserData(id))
    .then(data => console.log('사용자 정보:', data))
    .catch(err => console.error('에러:', err));

개선점: then() 체인으로 순서를 보장하지만, 중첩이 계속 생기면 복잡해질 수 있습니다.

3단계: async/await 방식 ⭐ (최고의 선택)

const getUserId = () => {
    return new Promise(resolve => {
        setTimeout(() => resolve(101), 1000);
    });
};

const getUserData = (id) => {
    return new Promise(resolve => {
        setTimeout(() => resolve({ id, name: 'Alice' }), 1000);
    });
};

const fetchUser = async () => {
    try {
        const id = await getUserId();          // ⏳ 1초 기다림
        const data = await getUserData(id);    // ⏳ 또 1초 기다림
        console.log('사용자 정보:', data);     // ✅ 순차 실행
    } catch (e) {
        console.error('에러 발생:', e);
    }
};

fetchUser();

장점: 비동기 작업이지만 순차적으로 흐름을 제어할 수 있어 동기 코드처럼 읽힙니다.

반응형

병렬 처리와 Promise.all 활용법

비동기 작업을 동시에 실행하여 성능을 최적화할 수 있습니다.

const taskA = () => {
    return new Promise(resolve => setTimeout(() => resolve('A 완료'), 1000));
};

const taskB = () => {
    return new Promise(resolve => setTimeout(() => resolve('B 완료'), 1000));
};

// 병렬 처리 (권장)
const runParallel = async () => {
    const [resultA, resultB] = await Promise.all([taskA(), taskB()]);
    console.log(resultA, resultB); // 총 1초 소요
};

// 순차 처리 (비효율적)
const runSequential = async () => {
    const resultA = await taskA(); // 1초
    const resultB = await taskB(); // 추가 1초
    console.log(resultA, resultB); // 총 2초 소요
};

실무에서 자주 사용하는 패턴들

API 호출 패턴

const fetchUserProfile = async (userId) => {
    try {
        const response = await fetch(`/api/users/${userId}`);
        
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const userData = await response.json();
        return userData;
    } catch (error) {
        console.error('사용자 정보 가져오기 실패:', error);
        throw error;
    }
};

여러 API 병렬 호출 패턴

const loadDashboardData = async () => {
    try {
        const [userInfo, notifications, statistics] = await Promise.all([
            fetchUserProfile(userId),
            fetchNotifications(),
            fetchStatistics()
        ]);
        
        return {
            userInfo,
            notifications,
            statistics
        };
    } catch (error) {
        console.error('대시보드 데이터 로딩 실패:', error);
    }
};

조건부 비동기 처리 패턴

const processUserData = async (userId) => {
    const user = await fetchUserProfile(userId);
    
    if (user.isPremium) {
        const premiumData = await fetchPremiumFeatures(userId);
        return { ...user, premiumData };
    }
    
    return user;
};

핵심 원리 정리

개념 동작 방식

async function 항상 Promise 반환
await Promise의 완료를 기다리고 그 결과를 반환함
Promise .then()으로 다음 작업 연결 가능
await + async 비동기를 동기 흐름처럼 처리 가능
try/catch await 에러 처리 가능

핵심 공식

const main = async () => {
    const result = await asyncFunction(); // 여기서 기다림
    console.log(result);                  // 결과 사용
};

마무리

ES6+ async/await는 JavaScript 비동기 프로그래밍의 게임 체인저입니다. 복잡한 콜백 지옥에서 벗어나 직관적이고 읽기 쉬운 코드를 작성할 수 있게 해줍니다.

핵심 포인트:

  • 비동기 코드를 동기 코드처럼 작성 가능
  • try/catch로 간편한 에러 처리
  • Promise.all을 활용한 병렬 처리로 성능 최적화
  • 실무에서 API 호출, 데이터 처리에 필수적

#JavaScript #ES6 #AsyncAwait #Promise #비동기프로그래밍 #웹개발

반응형