📘 13.4.2 async/await에서 에러 처리하기 - 안전한 비동기 프로그래밍
여러분과 함께한 지난 시간, async/await의 기본 사용법을 배우면서 Promise를 훨씬 쉽게 사용할 수 있게 되었습니다. 하지만 실제 삶에서는 항상 일이 계획대로 되지 않을 수 있지요. 네트워크가 끊어지거나, 서버에서 문제가 생기거나, 예상치 못한 데이터가 들어올 수 있습니다.
오늘은 이런 예상치 못한 상황들을 차분하고 안전하게 처리하는 try/catch 문을 배워보겠습니다. 마치 우산을 준비해서 갑작스러운 비를 피하듯, 우리의 코드도 안전장치를 만들어줄 것입니다.
🧠 새로운 단어들과 친해지기
async/await에서 오류 처리와 관련된 중요한 단어들을 자연스럽게 살펴보겠습니다.
단어 | 쉬운 설명 |
---|---|
try/catch | 오류가 발생할 수 있는 코드를 안전하게 실행하고 처리하는 구문이에요. |
try 블록 | 오류가 발생할 가능성이 있는 코드를 넣는 영역이에요. |
catch 블록 | 오류가 실제로 발생했을 때 실행되는 처리 영역이에요. |
finally 블록 | 오류 발생 여부와 관계없이 항상 마지막에 실행되는 정리 영역이에요. |
try는 "시도하다", catch는 "잡다", finally는 "마지막에"라는 뜻의 영어 단어들입니다. 이름만 봐도 각각이 어떤 역할을 하는지 따뜻하게 다가옵니다.
✨ try/catch의 핵심 개념
try/catch는 자바스크립트에서 오류를 안전하게 처리하는 표준적인 방법입니다. async/await와 함께 사용하면 Promise의 .catch()
방법보다 훨씬 더 쉽고 읽기 좋은 오류 처리를 할 수 있습니다.
try 블록은 "이 코드를 조심스럽게 시도해봐"라는 의미입니다. 여기에 await를 포함한 오류가 발생할 수 있는 모든 코드를 넣습니다. 마치 안전한 실험실에서 새로운 실험을 하는 것과 같습니다.
catch 블록은 "문제가 생기면 이렇게 따뜻하게 처리해"라는 의미입니다. try 블록에서 어떤 오류든 발생하면 자동으로 catch 블록이 실행되어 오류를 안전하게 처리합니다. 마치 넘어진 아이를 일으켜 세우고 다독여주는 것과 같지요.
finally 블록은 선택사항이지만 매우 소중합니다. "어떤 결과든 상관없이 마지막에 이것은 꼭 해줘"라는 의미로, 리소스 정리나 로딩 상태 해제 같은 마무리 작업을 할 때 사용합니다.
가장 아름다운 점은 여러 개의 await를 하나의 try/catch로 처리할 수 있다는 것입니다. Promise 체이닝에서는 각 단계마다 catch를 달아야 했지만, try/catch는 모든 오류를 한 곳에서 차분하게 처리할 수 있습니다.
따뜻한 비유: 요리할 때의 안전 준비
try/catch를 더 쉽게 이해하기 위해 '요리할 때의 안전 준비' 이야기를 들려드리겠습니다.
새로운 요리에 도전할 때를 생각해보세요. 처음 해보는 요리라서 실수할 가능성이 있습니다.
try (시도): "새로운 파스타 요리를 만들어보자!" - 위험할 수 있지만 용기내어 시도해보는 단계입니다. 여기서는 면을 삶고, 소스를 만들고, 재료를 볶는 모든 과정을 포함합니다.
catch (오류 처리): "어? 소스가 좀 탔네!" - 문제가 생겼을 때의 따뜻한 대처 방법입니다. 타버린 소스 대신 간단한 토마토소스로 바꾸거나, 치즈를 뿌려서 맛을 보완하는 것처럼 문제를 슬기롭게 해결합니다.
finally (마무리): "어떤 결과든 상관없이 설거지하고 가스레인지 끄기" - 요리가 성공하든 실패하든 반드시 해야 하는 마무리 작업입니다.
이렇게 하면 요리가 실패해도 당황하지 않고 차분하게 대처할 수 있고, 항상 깔끔하게 마무리할 수 있습니다. try/catch도 이와 똑같은 마음으로 코드의 안전을 지켜줍니다.
🎯 try/catch를 사용하는 이유
async/await에서 try/catch를 사용하는 이유는 무엇일까요? 여러 소중한 이유들이 있습니다.
첫 번째는 코드의 안정성 향상입니다. 예상치 못한 오류가 발생해도 프로그램이 완전히 멈추지 않고 적절히 대처할 수 있습니다. 마치 따뜻한 담요가 추위로부터 우리를 보호해주는 것과 같습니다.
두 번째는 사용자 경험 개선입니다. 오류가 발생했을 때 사용자에게 친근하고 이해하기 쉬운 메시지를 보여줄 수 있습니다. "알 수 없는 오류가 발생했습니다" 대신 "인터넷 연결을 확인해주세요"처럼 구체적인 도움을 줄 수 있습니다.
세 번째는 Promise 체이닝보다 쉬운 오류 처리입니다. .catch().catch().catch()
같은 복잡한 체이닝 대신, 일반적인 프로그래밍 언어에서 사용하는 try/catch 패턴을 사용할 수 있어서 더 친숙하고 이해하기 쉽습니다.
네 번째는 통합 오류 관리입니다. 여러 개의 await 작업 중 어느 곳에서든 오류가 발생하면 하나의 catch 블록에서 모두 처리할 수 있어서, 오류 처리 로직이 중복되지 않고 관리하기 쉽습니다.
마지막으로 디버깅과 기록 남기기가 쉬워진다는 점입니다. 오류가 발생한 위치와 원인을 쉽게 파악할 수 있고, 적절한 기록을 남겨서 나중에 문제를 분석할 수 있습니다.
⚙️ 기본 사용법 살펴보기
try/catch의 기본 사용법은 매우 쉽고 이해하기 좋습니다.
기본 구조:
async function myFunction() {
try {
// 오류가 발생할 수 있는 코드들
let result = await somePromise();
let anotherResult = await anotherPromise();
console.log("모든 작업 성공!");
} catch (error) {
// 오류가 발생했을 때 실행되는 코드
console.log("오류가 발생했어요:", error);
} finally {
// 성공/실패와 관계없이 항상 실행되는 코드 (선택사항)
console.log("정리 작업을 해요");
}
}
Promise 방식과 비교:
// Promise 방식 (복잡함)
somePromise()
.then(function(result) {
return anotherPromise();
})
.then(function(result) {
console.log("성공");
})
.catch(function(error) {
console.log("오류:", error);
});
// async/await + try/catch 방식 (간단함)
async function handlePromises() {
try {
let result1 = await somePromise();
let result2 = await anotherPromise();
console.log("성공");
} catch (error) {
console.log("오류:", error);
}
}
중요한 규칙들:
- try 블록에는 오류가 발생할 수 있는 모든 await 코드를 넣습니다
- catch 블록은 try에서 발생한 모든 오류를 처리합니다
- finally는 선택사항이지만 정리 작업에 매우 유용합니다
- catch에서 오류를 처리한 후에는 적절한 값을 반환하는 것이 좋습니다
🧪 예제로 익혀보기
실제 예제를 통해 try/catch가 어떻게 작동하는지 단계별로 알아보겠습니다.
🔹 예제 1: 기본적인 try/catch 오류 처리
이번 예제에서는 가장 기본적인 try/catch 사용법을 익혀보겠습니다.
// 랜덤으로 성공하거나 실패하는 Promise
function luckyDraw() {
return new Promise(function(resolve, reject) {
console.log("🎲 행운의 뽑기를 돌리고 있어요...");
setTimeout(function() {
let isLucky = Math.random() > 0.4; // 60% 확률로 성공
if (isLucky) {
resolve("🎉 축하해요! 상품에 당첨되었어요!");
} else {
reject("😅 아쉽지만 꽝이에요. 다음 기회에 도전해보세요!");
}
}, 2000);
});
}
// try/catch로 안전하게 처리하기
async function playLuckyDraw() {
console.log("🎯 행운의 게임을 시작해요");
try {
// 위험할 수 있는 작업을 시도
let result = await luckyDraw();
// 성공했을 때의 처리
console.log("✅ 좋은 결과:", result);
console.log("🏆 상품 받기 절차를 진행해요!");
return "게임 성공";
} catch (error) {
// 실패했을 때의 처리
console.log("❌ 아쉬운 결과:", error);
console.log("💪 용기를 내어 다시 도전해보세요!");
return "게임 실패하지만 괜찮아요";
} finally {
// 성공/실패와 관계없이 항상 실행
console.log("🔚 게임이 완전히 끝났어요");
console.log("🧹 게임 기계를 정리해요");
}
}
// 게임 실행!
playLuckyDraw();
이 과정을 자세히 살펴보면, 먼저 try 블록에서 위험할 수 있는 luckyDraw()
함수를 await로 호출합니다. 성공하면 축하 메시지와 함께 후속 처리를 하고, 실패하면 catch 블록에서 위로와 격려 메시지를 보여줍니다. 마지막으로 finally 블록에서 성공/실패와 관계없이 게임 정리 작업을 수행합니다. 이런 구조 덕분에 어떤 결과가 나와도 안전하고 체계적으로 처리할 수 있습니다.
🔹 예제 2: 여러 단계 작업에서 통합 오류 처리
여러 단계의 비동기 작업을 하나의 try/catch로 안전하게 처리하는 방법을 알아보겠습니다.
// 각 단계별 작업 함수들 (각각 실패할 가능성이 있음)
function prepareToothbrush() {
return new Promise(function(resolve, reject) {
console.log("🪥 칫솔을 준비해요...");
setTimeout(function() {
let hasToothbrush = Math.random() > 0.2; // 80% 성공
if (hasToothbrush) {
resolve("칫솔 준비 완료!");
} else {
reject("칫솔을 찾을 수 없어요!");
}
}, 1000);
});
}
function applyToothpaste() {
return new Promise(function(resolve, reject) {
console.log("🧴 치약을 칫솔에 짜요...");
setTimeout(function() {
let hasToothpaste = Math.random() > 0.1; // 90% 성공
if (hasToothpaste) {
resolve("치약 준비 완료!");
} else {
reject("치약이 다 떨어졌어요!");
}
}, 1000);
});
}
function brushTeeth() {
return new Promise(function(resolve) {
console.log("🦷 양치질을 해요...");
setTimeout(function() {
resolve("양치질 완료! 치아가 깨끗해졌어요!");
}, 1500);
});
}
// 모든 양치 과정을 안전하게 처리
async function completeMorningRoutine() {
try {
console.log("🌅 아침 양치질 루틴을 시작해요");
// 1단계: 칫솔 준비 (실패 가능)
let step1 = await prepareToothbrush();
console.log("✅", step1);
// 2단계: 치약 준비 (실패 가능)
let step2 = await applyToothpaste();
console.log("✅", step2);
// 3단계: 양치질 실행 (안전함)
let step3 = await brushTeeth();
console.log("✅", step3);
console.log("🎉 완벽한 아침 준비 완료!");
return "건강한 하루 시작";
} catch (error) {
console.log("😓 아침 준비 중 문제 발생:", error);
console.log("💡 대안: 물로 입을 헹구거나 껌을 씹어보세요");
return "대안으로 해결";
} finally {
console.log("🧽 욕실을 정리하고 문을 닫아요");
console.log("🚿 다음 사람을 위해 욕실을 깔끔하게 정리했어요");
}
}
// 아침 루틴 실행
completeMorningRoutine();
이 예제에서는 세 단계의 작업 중 어느 곳에서든 오류가 발생할 수 있지만, 하나의 try/catch로 모든 오류를 처리합니다. 어느 단계에서 실패해도 catch에서 적절한 대안을 제시하고, finally에서는 정리 작업을 확실히 수행합니다. 이런 방식으로 복잡한 다단계 작업도 안전하게 관리할 수 있습니다.
🔹 예제 3: 실용적인 사용자 데이터 처리 오류 처리
실제 웹 개발에서 자주 사용하는 사용자 데이터 처리 과정에서의 오류 처리를 구현해보겠습니다.
// 사용자 정보를 가져오는 함수 (실패 가능성 있음)
function fetchUserProfile(userId) {
return new Promise(function(resolve, reject) {
console.log("👤 사용자 ID " + userId + "의 프로필을 조회해요...");
setTimeout(function() {
if (userId > 0 && userId <= 1000) {
resolve({
id: userId,
name: "김학생",
grade: "5학년",
class: "3반",
hobby: "독서"
});
} else {
reject("존재하지 않는 사용자 ID예요: " + userId);
}
}, 1500);
});
}
// 사용자의 성적을 가져오는 함수 (실패 가능성 있음)
function fetchUserGrades(userId) {
return new Promise(function(resolve, reject) {
console.log("📊 성적 정보를 조회해요...");
setTimeout(function() {
let networkOk = Math.random() > 0.3; // 70% 성공
if (networkOk) {
resolve({
math: 85,
korean: 92,
english: 78,
science: 88
});
} else {
reject("네트워크 오류: 성적 정보를 가져올 수 없어요");
}
}, 1000);
});
}
// 완전한 사용자 정보를 안전하게 가져오기
async function getCompleteUserInfo(userId) {
try {
console.log("🎯 " + userId + "번 학생의 전체 정보를 조회해요");
// 1단계: 기본 프로필 가져오기
let profile = await fetchUserProfile(userId);
console.log("✅ 프로필 조회 성공:", profile.name + " (" + profile.grade + " " + profile.class + ")");
// 2단계: 성적 정보 가져오기
let grades = await fetchUserGrades(userId);
console.log("✅ 성적 조회 성공");
// 3단계: 정보 통합하기
let completeInfo = {
student: profile,
grades: grades,
average: Math.round((grades.math + grades.korean + grades.english + grades.science) / 4)
};
console.log("🎉 완전한 정보 조회 완료!");
console.log("📈 평균 점수:", completeInfo.average + "점");
return completeInfo;
} catch (error) {
console.log("❌ 정보 조회 중 문제 발생:", error);
// 사용자에게 친근한 메시지 제공
if (error.includes("존재하지 않는")) {
console.log("💡 해결 방법: 올바른 학생 번호를 확인해주세요");
} else if (error.includes("네트워크")) {
console.log("💡 해결 방법: 인터넷 연결을 확인하고 잠시 후 다시 시도해주세요");
} else {
console.log("💡 해결 방법: 관리자에게 문의하거나 잠시 후 다시 시도해주세요");
}
return null;
} finally {
console.log("🔄 정보 조회 과정이 완료되었어요");
console.log("💾 조회 기록을 저장해요");
}
}
// 다양한 경우로 테스트해보기
getCompleteUserInfo(123); // 성공할 가능성 높음
getCompleteUserInfo(9999); // 존재하지 않는 사용자
이 예제는 실제 웹 애플리케이션에서 흔히 볼 수 있는 패턴입니다. 사용자 정보와 성적 정보를 순서대로 가져오는 과정에서 각각 다른 종류의 오류가 발생할 수 있습니다. catch 블록에서는 오류의 종류에 따라 사용자에게 적절한 해결 방법을 제시하고, finally에서는 기록 남기기 같은 마무리 작업을 수행합니다. 이렇게 하면 사용자가 문제 상황에서도 당황하지 않고 적절한 대처를 할 수 있습니다.
🔄 try/catch 실행 과정 정리하기
지금까지 배운 try/catch의 실행 과정을 단계별로 정리해보겠습니다.
첫 번째 단계는 try 블록 시작입니다. 오류가 발생할 가능성이 있는 모든 코드를 try 블록 안에 넣고 실행을 시작합니다. 여기서 여러 개의 await 문을 순서대로 실행할 수 있습니다.
두 번째는 await 실행과 모니터링입니다. 각 await 문이 실행되는 동안 자바스크립트 엔진이 오류 발생을 지속적으로 모니터링합니다. Promise가 reject되거나 다른 오류가 발생하면 즉시 감지합니다.
세 번째는 오류 발생 확인입니다. try 블록 내의 어느 지점에서든 오류가 발생하면 나머지 try 블록 코드는 건너뛰고 바로 catch 블록으로 이동합니다. 오류가 없으면 try 블록을 끝까지 실행합니다.
네 번째는 적절한 블록 실행입니다. 오류가 발생했으면 catch 블록이 실행되어 오류를 처리하고, 오류가 없었으면 catch 블록을 건너뛰고 다음 단계로 넘어갑니다.
마지막으로 finally 블록 실행입니다. 오류 발생 여부와 관계없이 finally 블록이 있으면 항상 실행되어 정리 작업을 수행합니다. 이것이 try/catch의 완전한 실행 사이클입니다.
🧠 자주 하는 실수와 주의할 점
try/catch를 사용할 때 자주 실수하는 부분들을 미리 알아두면 더 안전한 프로그래밍을 할 수 있습니다.
❌ 실수 1: try/catch 없이 await 사용하기
// 위험한 예시 - 오류 처리가 전혀 없어요
async function dangerousFunction() {
let result = await riskyPromise(); // 오류 발생 시 프로그램 중단!
console.log("결과:", result);
return result;
}
// 안전한 예시 - 적절한 오류 처리
async function safeFunction() {
try {
let result = await riskyPromise();
console.log("결과:", result);
return result;
} catch (error) {
console.log("문제가 발생했지만 안전하게 처리했어요:", error);
return "기본값"; // 적절한 기본값 제공
}
}
이런 실수가 발생하는 이유는 "우리 코드는 오류가 없을 거야"라고 생각하기 때문입니다. 하지만 네트워크 문제, 서버 오류, 잘못된 데이터 등은 언제든 발생할 수 있어서 항상 대비해야 합니다.
❌ 실수 2: catch에서 오류를 제대로 처리하지 않기
// 문제가 될 수 있는 예시 - 오류를 다시 던짐
async function problemFunction() {
try {
let result = await somePromise();
return result;
} catch (error) {
console.log("오류 발생:", error);
throw error; // 오류를 다시 던져서 문제 지속
}
}
// 더 나은 방법 - 오류를 처리하고 적절한 값 반환
async function betterFunction() {
try {
let result = await somePromise();
return result;
} catch (error) {
console.log("오류를 안전하게 처리해요:", error);
// 사용자에게 도움이 되는 메시지 제공
if (error.message.includes("network")) {
console.log("네트워크 연결을 확인해주세요");
}
return "기본값"; // 프로그램이 계속 작동할 수 있도록 기본값 제공
}
}
catch의 목적은 오류를 잡아서 적절히 처리하는 것입니다. 오류를 다시 던지면 결국 다른 곳에서 처리해야 하므로 문제가 해결되지 않습니다.
❌ 실수 3: finally에서 잘못된 작업하기
// 잘못된 예시 - finally에서 값을 반환
async function wrongFinally() {
try {
let result = await somePromise();
return result; // 이 값은 무시됨
} catch (error) {
return "오류 처리됨"; // 이 값도 무시됨
} finally {
return "finally 결과"; // 이것이 최종 반환값이 됨!
}
}
// 올바른 예시 - finally는 정리 작업만
async function correctFinally() {
let loadingShown = false;
try {
console.log("로딩 표시");
loadingShown = true;
let result = await somePromise();
return result;
} catch (error) {
console.log("오류 처리:", error);
return "기본값";
} finally {
// 정리 작업만 수행
if (loadingShown) {
console.log("로딩 숨기기");
}
console.log("리소스 정리 완료");
}
}
finally는 정리 작업을 위한 곳이므로 값을 반환하면 안 됩니다. finally에서 값을 반환하면 try나 catch의 반환값이 무시되어 예상치 못한 결과가 나올 수 있습니다.
✏️ 연습문제로 개념 다지기
연습문제를 시작하기 전에 잠시 마음을 가다듬어보겠습니다. try/catch라는 것이 단순히 기술적인 도구가 아니라, 우리가 일상에서 위험에 대비하는 마음가짐과 닮아 있다는 걸 느껴보셨나요. 예상치 못한 일에 당황하지 않고 차분히 대처하는 것. 그 마음으로 연습을 해보겠습니다.
Ex1) 오류가 발생할 수 있는 Promise를 try/catch로 안전하게 처리해보자
// 때로는 실패하는 Promise
function mayFailPromise() {
return new Promise(function(resolve, reject) {
let success = Math.random() > 0.4; // 60% 성공 확률
setTimeout(function() {
if (success) {
resolve("작업이 성공했어요!");
} else {
reject("작업이 실패했어요!");
}
}, 1000);
});
}
// try/catch로 안전하게 처리하는 함수
async function safeHandlePromise() {
try {
// 작업을 시도한다고 알려줘요
console.log("작업을 시도해요...");
// 오류가 발생할 수 있는 Promise를 기다려요
let result = await mayFailPromise();
// 성공했을 때의 처리
console.log("좋은 소식:", result);
return result;
} catch (error) {
// 실패했을 때의 처리
console.log("문제 해결:", error);
console.log("다른 방법을 시도해보겠어요");
return "대안 방법으로 해결";
} finally {
// 성공/실패와 관계없이 항상 실행
console.log("작업 과정이 완전히 끝났어요");
}
}
safeHandlePromise();
Ex2) 여러 단계의 작업을 하나의 try/catch로 처리하는 함수를 만들어보자
function step1() {
// 1단계 작업을 흉내내는 Promise
return Promise.resolve("1단계 완료");
}
function step2() {
// 2단계 작업을 흉내내는 Promise
return Promise.resolve("2단계 완료");
}
function step3() {
// 3단계 작업을 흉내내는 Promise
return Promise.resolve("3단계 완료");
}
async function runAllSteps() {
try {
// 모든 단계를 시작한다고 알려줘요
console.log("모든 단계를 시작해요");
// 1단계를 실행하고 결과를 기다려요
let result1 = await step1();
console.log("✅", result1);
// 2단계를 실행하고 결과를 기다려요
let result2 = await step2();
console.log("✅", result2);
// 3단계를 실행하고 결과를 기다려요
let result3 = await step3();
console.log("✅", result3);
console.log("🎉 모든 단계가 성공적으로 완료되었어요!");
return "전체 성공";
} catch (error) {
// 어느 단계에서든 오류가 발생하면 여기서 처리
console.log("❌ 어느 단계에서 문제 발생:", error);
console.log("💪 문제를 해결하고 다시 시도하겠어요");
return "문제 해결됨";
} finally {
// 성공/실패와 관계없이 마무리 작업
console.log("🔧 모든 단계의 처리가 완료되었어요");
}
}
runAllSteps();
🤔 심화 문제로 실력 확인하기
기본 연습을 마쳤다면, 조금 더 깊이 있는 문제들을 통해 try/catch에 대한 이해를 확인해보겠습니다.
Q1. try/catch/finally의 실행 순서와 조건을 설명해보세요.
정답: try 블록이 먼저 실행되고, 오류가 발생하면 즉시 catch 블록으로 이동하여 오류를 처리합니다. 오류가 발생하지 않으면 catch는 건너뜁니다. 마지막으로 오류 발생 여부와 관계없이 finally 블록이 항상 실행됩니다.
해설: 이는 try/catch의 핵심 동작 원리로, 오류가 발생한 순간 try의 나머지 코드는 실행되지 않고 바로 catch로 넘어갑니다. finally는 "정리 작업"을 위한 곳이므로 어떤 상황이든 반드시 실행됩니다.
Q2. async/await에서 try/catch를 사용하는 장점을 Promise의 .catch()와 비교해서 설명해보세요.
정답: 1) 여러 개의 await를 하나의 try/catch로 처리할 수 있어서 코드가 간결해지고, 2) 일반적인 프로그래밍 언어의 오류 처리 방식과 같아서 더 쉽고, 3) 오류 발생 지점을 파악하기 쉬워서 디버깅이 편합니다.
해설: Promise 체이닝에서는 각 단계마다 .catch()를 달거나 마지막에 하나만 달아야 하지만, try/catch는 모든 await 오류를 한 곳에서 처리할 수 있습니다. 또한 동기 코드와 비슷한 패턴이라서 학습하기 쉽습니다.
Q3. 다음 코드에서 finally 블록은 언제 실행되는지 설명해보세요.
async function testFinally() {
try {
let result = await somePromise();
return result;
} catch (error) {
console.log("오류:", error);
return "오류 처리됨";
} finally {
console.log("정리 작업");
}
}
정답: finally 블록은 somePromise()가 성공하든 실패하든 상관없이 항상 실행됩니다. 심지어 try나 catch에서 return 문이 실행되더라도 그 전에 finally가 먼저 실행됩니다.
해설: finally는 함수가 종료되기 직전에 반드시 실행되는 특별한 블록입니다. 이런 특성 때문에 리소스 정리, 로딩 상태 해제, 기록 남기기 같은 "반드시 해야 하는 마무리 작업"에 사용됩니다.
🔙 지난 시간 복습하기
13.4.1에서 배운 async/await 기본 사용법을 복습해볼까요?
복습 문제 1: async/await의 기본 구조를 설명하고 간단한 예제를 만들어보세요.
정답:
// async function 선언
async function simpleAsyncFunction() {
// await로 Promise 기다리기
let result = await Promise.resolve("안녕하세요!");
console.log(result);
return result;
}
// 함수 호출
simpleAsyncFunction();
해설: async 키워드로 함수를 선언하고, 함수 내부에서 await 키워드로 Promise의 완료를 기다립니다. await는 async 함수 안에서만 사용할 수 있고, Promise가 완료될 때까지 기다린 후 결과값을 반환합니다.
복습 문제 2: async/await가 Promise.then()보다 좋은 이유 2가지를 설명해보세요.
정답: 1) 코드가 동기적으로 보여서 읽기 쉽고 이해하기 쉽습니다. 2) 복잡한 .then() 체이닝 없이 순서대로 코드를 작성할 수 있어서 관리가 편합니다.
해설: Promise.then()을 사용하면 .then().then().then() 같은 체이닝이 복잡해지지만, async/await는 일반적인 함수처럼 위에서 아래로 순서대로 작성할 수 있습니다. 이런 특성 덕분에 코드를 읽고 수정하기가 훨씬 쉬워집니다.
지금까지 async/await에서 try/catch를 사용한 오류 처리에 대해 배웠습니다. 이제 여러분의 비동기 코드는 어떤 상황에서도 안전하게 작동할 수 있을 것입니다. 다음 시간에는 더 고급 기능인 Promise와 async/await의 비교에 대해 배워보겠습니다.
✅ 학습 완료 체크리스트
이번 시간에 배운 내용들을 모두 이해했는지 확인해보세요!
학습 내용 | 이해했나요? |
---|---|
try/catch의 기본 개념 | ✅ |
기본 사용법과 문법 | ✅ |
주요 특징과 차이점 | ✅ |
자주 하는 실수들 | ✅ |
실전 예제 이해 | ✅ |
🎯 추가 연습 문제들
조금 더 연습하고 싶은 분들을 위한 추가 문제들입니다!
추가 문제 1. 에러가 발생할 수 있는 Promise를 try/catch로 안전하게 처리해보세요.
// 답:
function mayFailPromise() {
return new Promise(function(resolve, reject) {
if (Math.random() > 0.5) {
resolve("성공!");
} else {
reject("실패!");
}
});
}
async function safeHandle() {
try {
let result = await mayFailPromise();
console.log("결과:", result);
} catch (error) {
console.log("에러 처리:", error);
} finally {
console.log("처리 완료");
}
}
safeHandle();
추가 문제 2. 여러 단계의 작업을 try/catch로 처리하는 함수를 만들어보세요.
// 답:
function task1() {
return Promise.resolve("1단계 완료");
}
function task2() {
return Promise.resolve("2단계 완료");
}
async function runMultipleTasks() {
try {
let result1 = await task1();
console.log(result1);
let result2 = await task2();
console.log(result2);
console.log("모든 작업 성공");
} catch (error) {
console.log("작업 중 에러:", error);
} finally {
console.log("작업 세션 종료");
}
}
runMultipleTasks();
추가 문제 3. 사용자 입력을 검증하고 에러를 처리하는 함수를 만들어보세요.
// 답:
function validateInput(input) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (input && input.length > 0) {
resolve("유효한 입력: " + input);
} else {
reject("입력값이 비어있습니다");
}
}, 500);
});
}
async function processInput(userInput) {
try {
let validInput = await validateInput(userInput);
console.log("검증 성공:", validInput);
return validInput;
} catch (error) {
console.log("검증 실패:", error);
return "기본값";
} finally {
console.log("입력 처리 완료");
}
}
processInput("테스트"); // 성공
processInput(""); // 실패
추가 문제 4. 다음 코드에서 문제점을 찾고 수정해보세요.
async function getData() {
let result = await riskyPromise();
console.log(result);
}
답: 에러 처리가 없어서 Promise가 실패할 경우 문제가 발생할 수 있습니다. 수정된 코드:
async function getData() {
try {
let result = await riskyPromise();
console.log(result);
} catch (error) {
console.log("에러:", error);
}
}
📂 마무리 정보
오늘 배운 13.4.2
내용이 여러분의 자바스크립트 지식에 깊이 스며들었나요? 다음 시간에는 더 재미있는 내용으로 만나겠습니다!
기억할 점: 오늘 배운 내용을 꼭 연습해보시고, 궁금한 점이 있으면 언제든 다시 돌아와서 읽어보세요.
무료 JavaScript 학습 플랫폼에서 단계별 학습과 실시간 코드 실행을 통해
더욱 효과적이고 재미있게 학습하실 수 있습니다.
'13. 시간을 다루는 마법 (비동기 처리) > 13.4 더 쉽게! async & await' 카테고리의 다른 글
13.4.3 Promise vs async/await - 두 가지 방법 비교해보기 (0) | 2025.07.19 |
---|---|
13.4.1 async/await - Promise를 더 쉽게 만드는 특별한 키워드 (0) | 2025.07.19 |