13. 시간을 다루는 마법 (비동기 처리)/13.4 더 쉽게! async & await

13.4.1 async/await - Promise를 더 쉽게 만드는 특별한 키워드

thejavascript4kids 2025. 7. 19. 04:53

📘 13.4.1 async/await - Promise를 더 쉽게 만드는 특별한 키워드

여러분과 함께 Promise의 길을 걸어오면서, 때로는 그 길이 조금 복잡하게 느껴지기도 했을 겁니다. 지금까지 Promise와 체이닝을 배우면서 콜백 지옥을 해결하는 여러 방법들을 익혔지만, Promise 체이닝도 때로는 우리 마음처럼 자연스럽지 않을 수 있지요.

오늘은 Promise를 훨씬 더 쉽고 자연스럽게 사용할 수 있게 해주는 async/await라는 아름다운 기능을 배워보겠습니다. 마치 복잡한 문장을 우리가 일상에서 쓰는 말로 바꿔주는 것처럼, 어려운 Promise 코드를 마치 평범한 대화처럼 만들어주는 도구입니다.

🧠 새로운 단어들과 친해지기

단어 쉬운 설명
async 함수가 비동기 함수라는 것을 나타내는 키워드예요
await Promise의 결과가 나올 때까지 "잠깐 기다려"라는 의미의 키워드예요
동기적 스타일 코드가 위에서 아래로 순서대로 실행되는 것처럼 보이는 작성 방식이에요
비동기 함수 async 키워드가 붙어서 비동기 작업을 할 수 있는 특별한 함수예요

✨ async/await의 핵심 개념

async/awaitPromise를 더 쉽게 사용할 수 있게 해주는 문법적 설탕입니다. 복잡한 Promise 체이닝을 마치 일반적인 동기 코드처럼 쓸 수 있게 만들어줘서, 코드를 읽고 이해하기가 훨씬 쉬워집니다.

async 키워드는 함수 앞에 붙여서 "이 함수는 비동기 작업을 하는 함수야"라고 선언합니다. async가 붙은 함수는 자동으로 Promise를 반환하게 됩니다.

await 키워드는 Promise 앞에 붙여서 "이 Promise가 완료될 때까지 기다려"라고 말합니다. await를 만나면 함수 실행이 잠깐 멈추고, Promise가 완료되면 그 결과값을 받아서 다음 줄로 넘어갑니다.

중요한 규칙: await는 반드시 async 함수 안에서만 사용할 수 있습니다!

따뜻한 비유: 친구와의 대화

복잡한 방식 (Promise 체이닝):
친구에게 부탁할 때 모든 상황을 미리 계획해야 합니다.

  • "숙제 다 했는지 확인해줘, 그리고 다 했으면 같이 놀자고 말해줘, 만약 안 했으면 도와주겠다고 해줘..."

간단한 방식 (async/await):
친구와 자연스럽게 대화하는 것처럼 순서대로 진행합니다.

  • "숙제 다 했어?" (기다림)
  • "응, 다 했어!" (답을 받음)
  • "그럼 같이 놀까?" (다음 질문)
  • "좋아!" (또 답을 받음)

async/await는 이런 자연스러운 대화 방식으로 코드를 쓸 수 있게 해줍니다.

🎯 async/await를 사용하는 이유

  1. 코드 읽기 쉬움: Promise 체이닝보다 훨씬 자연스럽고 이해하기 쉽습니다
  2. 학습 용이성: 프로그래밍을 처음 배우는 사람도 쉽게 이해할 수 있습니다
  3. 디버깅 쉬움: 코드가 순서대로 실행되는 것처럼 보여서 문제 찾기가 쉽습니다
  4. 오류 처리 간소화: 일반적인 try/catch 문을 사용할 수 있습니다

⚙️ 기본 사용법 살펴보기

async 함수 선언:

async function myFunction() {
    // 이 함수는 비동기 함수예요
    // 여기서 await를 사용할 수 있어요
}

await 사용:

async function example() {
    let result = await somePromise();
    console.log(result); // Promise의 결과값
}

Promise 방식과 async/await 방식 비교:

// Promise 방식 (복잡함)
somePromise()
    .then(function(result) {
        console.log(result);
        return anotherPromise();
    })
    .then(function(result2) {
        console.log(result2);
    })
    .catch(function(error) {
        console.log("오류:", error);
    });

// async/await 방식 (간단함)
async function doSomething() {
    try {
        let result = await somePromise();
        console.log(result);

        let result2 = await anotherPromise();
        console.log(result2);
    } catch (error) {
        console.log("오류:", error);
    }
}

🧪 예제로 익혀보기

🔹 예제 1: 가장 기본적인 async/await 사용법

// 3초 후에 메시지를 반환하는 Promise
function waitAndReturn() {
    return new Promise(function(resolve) {
        console.log("⏱️ 3초를 기다리고 있어요...");
        setTimeout(function() {
            resolve("3초가 지났어요!");
        }, 3000);
    });
}

// async/await를 사용한 간단한 예제
async function simpleExample() {
    console.log("🎯 작업을 시작해요");

    // Promise가 완료될 때까지 기다려요
    let message = await waitAndReturn();

    // Promise가 완료된 후에 실행돼요
    console.log("📨 받은 메시지:", message);
    console.log("✅ 모든 작업이 완료되었어요");
}

// 함수 실행
simpleExample();

// 이 메시지는 바로 출력돼요
console.log("🔄 다른 작업들도 계속 진행할 수 있어요!");

🔹 예제 2: 여러 await를 순서대로 사용하기

// 각각 다른 시간이 걸리는 작업들
function cookRice() {
    return new Promise(function(resolve) {
        console.log("🍚 밥을 짓기 시작해요...");
        setTimeout(function() {
            resolve("밥이 완성되었어요!");
        }, 2000);
    });
}

function makeSoup() {
    return new Promise(function(resolve) {
        console.log("🍲 국을 끓이기 시작해요...");
        setTimeout(function() {
            resolve("국이 완성되었어요!");
        }, 1500);
    });
}

function prepareSideDish() {
    return new Promise(function(resolve) {
        console.log("🥬 반찬을 준비해요...");
        setTimeout(function() {
            resolve("반찬이 준비되었어요!");
        }, 1000);
    });
}

// async/await로 순서대로 요리하기
async function cookMeal() {
    console.log("🍽️ 식사 준비를 시작해요");

    // 첫 번째 작업: 밥 짓기 (2초 소요)
    let rice = await cookRice();
    console.log("✅", rice);

    // 두 번째 작업: 국 끓이기 (1.5초 소요)
    let soup = await makeSoup();
    console.log("✅", soup);

    // 세 번째 작업: 반찬 준비 (1초 소요)
    let sideDish = await prepareSideDish();
    console.log("✅", sideDish);

    console.log("🎉 모든 음식이 준비되었어요! 맛있게 드세요!");
}

// 요리 시작!
cookMeal();

🔹 예제 3: Promise 체이닝을 async/await로 변환하기

// 학생 정보를 가져오는 함수
function getStudentInfo(studentId) {
    return new Promise(function(resolve, reject) {
        console.log("📋 학생 정보를 조회해요...");
        setTimeout(function() {
            if (studentId > 0) {
                resolve({
                    id: studentId,
                    name: "김학생",
                    class: "5학년 3반"
                });
            } else {
                reject("잘못된 학생 ID예요");
            }
        }, 1000);
    });
}

// 학생의 성적을 가져오는 함수
function getStudentGrades(student) {
    return new Promise(function(resolve) {
        console.log("📊 " + student.name + "님의 성적을 조회해요...");
        setTimeout(function() {
            resolve({
                student: student,
                math: 95,
                korean: 88,
                english: 92
            });
        }, 1000);
    });
}

// async/await로 깔끔하게 처리하기
async function getCompleteStudentReport(studentId) {
    try {
        console.log("🎯 학생 정보 조회를 시작해요");

        // 1단계: 학생 기본 정보 가져오기
        let student = await getStudentInfo(studentId);
        console.log("✅ 학생 정보:", student.name, student.class);

        // 2단계: 학생 성적 정보 가져오기
        let grades = await getStudentGrades(student);
        console.log("✅ 성적 조회 완료");

        // 3단계: 전체 리포트 만들기
        let report = {
            이름: grades.student.name,
            반: grades.student.class,
            수학: grades.math,
            국어: grades.korean,
            영어: grades.english,
            평균: Math.round((grades.math + grades.korean + grades.english) / 3)
        };

        console.log("📈 최종 리포트:", report);
        return report;

    } catch (error) {
        console.log("❌ 문제가 발생했어요:", error);
        return null;
    }
}

// 학생 리포트 조회 실행
getCompleteStudentReport(1);

🔄 async/await 실행 과정 정리하기

  1. async 함수 선언: function 앞에 async 키워드를 붙여서 비동기 함수 선언
  2. await로 대기: Promise 앞에 await를 붙이면 함수 실행이 일시적으로 멈춤
  3. 결과 받기: Promise가 완료되면 그 결과값이 await 표현식의 값이 됨
  4. 다음 작업 진행: 결과를 받은 후 다음 작업을 계속 진행
  5. 함수 완료: async 함수가 끝나면 자동으로 Promise를 반환

🧠 자주 하는 실수와 주의할 점

❌ 실수 1: async 없이 await 사용하려고 하기

// 잘못된 예시 - 문법 오류 발생!
function normalFunction() {
    let result = await somePromise(); // 오류!
    console.log(result);
}

// 올바른 예시
async function asyncFunction() {
    let result = await somePromise(); // 정상 작동
    console.log(result);
}

❌ 실수 2: await 없이 Promise를 직접 사용하기

// 잘못된 예시 - Promise 상자가 변수에 저장됨
async function badExample() {
    let result = somePromise(); // await를 빼먹었어요!
    console.log(result); // [object Promise]가 출력됨
}

// 올바른 예시 - Promise의 실제 결과값을 받음
async function goodExample() {
    let result = await somePromise(); // await 필수!
    console.log(result); // 실제 결과값이 출력됨
}

❌ 실수 3: async 함수의 결과를 바로 사용하려고 하기

// 잘못된 예시 - async 함수는 Promise를 반환함
async function getData() {
    return "중요한 데이터";
}

let data = getData(); // Promise 상자가 저장됨!
console.log(data); // [object Promise]

// 올바른 예시: await 사용
async function useData() {
    let data = await getData();
    console.log(data); // "중요한 데이터"
}

✏️ 연습문제로 개념 다지기

연습문제를 시작하기 전에 잠시 멈춰서 생각해보겠습니다. async/await라는 것이 우리에게 어떤 선물을 주었는지요. 복잡하던 Promise 체이닝이 마치 우리가 일상에서 자연스럽게 하는 대화처럼 바뀌었습니다. 이제 그 자연스러움을 직접 경험해보겠습니다.

Ex1) 2초 후에 "안녕하세요!"를 반환하는 async 함수

// Promise를 반환하는 함수
function sayHelloAfterDelay() {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve("안녕하세요!");
        }, 2000);
    });
}

// async/await를 사용한 함수
async function greetUser() {
    console.log("인사를 준비하고 있어요...");
    let greeting = await sayHelloAfterDelay();
    console.log("받은 인사:", greeting);
    return greeting;
}

// 함수 실행
greetUser();

Ex2) 두 개의 Promise를 순서대로 실행하는 async 함수

function firstTask() {
    return Promise.resolve("첫 번째 작업 완료!");
}

function secondTask() {
    return Promise.resolve("두 번째 작업 완료!");
}

async function runBothTasks() {
    console.log("작업들을 순서대로 실행해요...");

    let result1 = await firstTask();
    console.log("1단계:", result1);

    let result2 = await secondTask();
    console.log("2단계:", result2);

    console.log("모든 작업이 끝났어요!");
    return "전체 작업 완료";
}

runBothTasks();

🤔 심화 문제로 실력 확인하기

Q1. async와 await 키워드의 역할을 각각 설명해보세요.

정답: async는 함수가 비동기 함수라는 것을 선언하는 키워드로, 함수가 자동으로 Promise를 반환하게 만듭니다. await는 Promise의 결과를 기다리는 키워드로, Promise가 완료될 때까지 함수 실행을 일시 정지시킨 후 결과값을 반환합니다.

Q2. async/await의 장점을 Promise 체이닝과 비교해서 세 가지 말해보세요.

정답:

  1. 코드가 위에서 아래로 순서대로 읽혀서 이해하기 쉽습니다
  2. 복잡한 then/catch 체이닝 대신 일반적인 변수 할당과 try/catch를 사용할 수 있습니다
  3. 디버깅할 때 코드의 실행 순서를 따라가기가 훨씬 쉽습니다

Q3. 다음 두 코드가 같은 결과를 내는지 설명해보세요.

// 코드 A: Promise 체이닝
getData().then(result => console.log(result));

// 코드 B: async/await
async function printData() {
    let result = await getData();
    console.log(result);
}
printData();

정답: 두 코드는 같은 결과를 출력합니다. 둘 다 getData()의 결과를 기다린 후 console.log로 출력하지만, 코드 B가 더 읽기 쉽고 이해하기 쉬운 형태로 작성되어 있습니다.

🔙 지난 시간 복습하기 (13.3.3 - Promise 체이닝)

복습 문제 1: Promise 체이닝에서 가장 중요한 규칙은?

정답: then 메서드에서 다음 Promise나 값을 반드시 return해야 한다는 것입니다.

복습 문제 2: 다음 체이닝의 최종 결과는?

Promise.resolve(5)
    .then(num => num * 2)
    .then(num => num + 3)
    .then(result => console.log(result));

정답: 13 (5 × 2 + 3 = 13)

async/await는 Promise를 더 쉽고 자연스럽게 사용할 수 있게 해주는 현대 자바스크립트의 핵심 기능입니다. 다음 시간에는 async/await에서 오류를 처리하는 방법을 배워보겠습니다.

✅ 학습 완료 체크리스트

학습 내용 이해했나요?
async/await 기본 개념
async와 await의 역할
기본 사용법과 문법
Promise와의 차이점
자주 하는 실수들

🎯 추가 연습 문제들

여러분이 더 깊이 이해하고 싶어 하는 마음이 느껴집니다. 추가 연습문제들을 통해 그 마음에 응답하겠습니다.

추가 문제 1. 2초 후에 "완료!"를 반환하는 async 함수

function wait2Seconds() {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve("완료!");
        }, 2000);
    });
}

async function waitAndReturn() {
    console.log("기다리는 중...");
    let result = await wait2Seconds();
    console.log(result);
    return result;
}

waitAndReturn();

추가 문제 2. 다음 코드의 문제점을 찾고 수정해보세요

// 문제 코드
function myFunction() {
    let result = await somePromise();
    console.log(result);
}

// 수정된 코드
async function myFunction() {
    let result = await somePromise();
    console.log(result);
}

📂 마무리 정보

오늘 배운 async/await가 여러분의 자바스크립트 지식에 자연스럽게 스며들었나요? 다음 시간에는 async/await에서 오류를 처리하는 방법을 배워볼 예정입니다.


🚀 더 체계적인 JavaScript 학습을 원하신다면?
이 포스팅에서 다룬 내용을 실제로 실습해보세요!
무료 JavaScript 학습 플랫폼에서 단계별 학습과 실시간 코드 실행을 통해
더욱 효과적이고 재미있게 학습하실 수 있습니다.
📝 실시간 코드 실행 📊 학습 진도 관리 👥 체계적 커리큘럼
📚 171개 체계적 학습레슨 · 📋 855개 4지선다 연습문제 · 🆓 완전 무료 · ⚡ 즉시 시작