개발하다 보면 계속 헷갈리는 동기/비동기/Promise를 실생활 예시로 쉽게 정리해보겠습니다!
// 동기 함수 - 즉석에서 바로 처리
function 편의점에서물건사기(돈) {
if (돈 >= 1000) {
return "🍫 초콜릿";
} else {
return "💸 돈이 부족해요";
}
}
const 결과 = 편의점에서물건사기(1500);
console.log(결과); // "🍫 초콜릿" - 즉시 나옴!
console.log("다음 일 진행"); // 바로 다음 줄 실행
특징:
// 비동기 함수 - 시간이 걸림
function 피자주문하기(메뉴) {
console.log("주문 접수!");
return new Promise((resolve, reject) => {
setTimeout(() => {
if (메뉴 === "페퍼로니") {
resolve("🍕 페퍼로니 피자 도착!");
} else {
reject("😭 재료가 떨어졌어요");
}
}, 3000); // 3초 후 결과
});
}
// ❌ 잘못된 사용법
const 피자 = 피자주문하기("페퍼로니");
console.log(피자); // Promise 객체만 나옴 (피자가 아님!)
// ✅ 올바른 사용법
피자주문하기("페퍼로니")
.then(피자 => console.log(피자)) // "🍕 페퍼로니 피자 도착!"
.catch(에러 => console.log(에러));
// 또는 async/await 사용
async function 피자먹기() {
try {
const 피자 = await 피자주문하기("페퍼로니");
console.log(피자); // "🍕 페퍼로니 피자 도착!"
} catch (에러) {
console.log(에러); // "😭 재료가 떨어졌어요"
}
}
특징:
then/catch 또는 async/await로 처리// Promise는 "약속표"같은 것
function 영화표예매(영화명) {
return new Promise((resolve, reject) => {
console.log("예매 진행 중...");
setTimeout(() => {
const 예매성공 = Math.random() > 0.3; // 70% 확률로 성공
if (예매성공) {
resolve(`🎬 ${영화명} 예매 완료!`);
} else {
reject(`😞 ${영화명} 매진되었습니다`);
}
}, 2000);
});
}
const 예매약속 = 영화표예매("아바타");
// 1. Pending (대기): 아직 결과 모름
console.log(예매약속); // Promise { <pending> }
// 2. Fulfilled (성공): resolve 호출됨
예매약속.then(결과 => {
console.log(결과); // "🎬 아바타 예매 완료!"
});
// 3. Rejected (실패): reject 호출됨
예매약속.catch(에러 => {
console.log(에러); // "😞 아바타 매진되었습니다"
});
영화표예매("어벤져스")
.then(예매결과 => {
console.log(예매결과);
return 팝콘사기(); // 다음 Promise 반환
})
.then(팝콘결과 => {
console.log(팝콘결과);
return 영화관가기();
})
.then(도착결과 => {
console.log("영화 관람 준비 완료!");
})
.catch(에러 => {
console.log("어딘가에서 문제 발생:", 에러);
});
// 동기식 (실제로는 불가능, 예시용)
function getUserSync(userId) {
// 만약 동기식이라면...
const user = fetch(`/api/users/${userId}`); // 즉시 완료
return user;
}
// 비동기식 (실제 방식)
function getUserAsync(userId) {
return fetch(`/api/users/${userId}`)
.then(response => response.json());
}
// 사용법 비교
// ❌ 동기식처럼 사용하면 안됨
const user = getUserAsync(123);
console.log(user.name); // Error! user는 Promise
// ✅ 비동기식 올바른 사용
getUserAsync(123)
.then(user => {
console.log(user.name); // 정상 동작
});
// ✅ async/await 사용
async function 사용자정보가져오기() {
const user = await getUserAsync(123);
console.log(user.name); // 정상 동작
}
// 순차 실행 (느림)
async function 순차실행() {
const user = await getUserAsync(1);
const posts = await getPostsAsync(1);
const comments = await getCommentsAsync(1);
return { user, posts, comments };
}
// 병렬 실행 (빠름)
async function 병렬실행() {
const [user, posts, comments] = await Promise.all([
getUserAsync(1),
getPostsAsync(1),
getCommentsAsync(1)
]);
return { user, posts, comments };
}
// mutate (동기): 즉시 반환, 결과를 기다리지 않음
const { mutate: createUser } = useCreateUser({
onSuccess: (data) => {
console.log("성공!", data);
},
onError: (error) => {
console.log("실패!", error);
}
});
function 버튼클릭() {
createUser({ name: "홍길동" }); // 즉시 완료
console.log("다음 코드 실행"); // 바로 실행됨
// API 결과는 onSuccess/onError에서 처리
}
// mutateAsync (비동기): Promise 반환, 결과를 기다림
const { mutateAsync: createUserAsync } = useCreateUser();
async function 버튼클릭() {
try {
const result = await createUserAsync({ name: "홍길동" });
console.log("성공!", result); // API 완료 후 실행
// 성공 후 처리 로직
} catch (error) {
console.log("실패!", error);
// 실패 후 처리 로직
}
}
// ❌ 문제가 있는 코드
function Modal({ onSubmit }) {
const handleSubmit = async () => {
await onSubmit(formData); // onSubmit이 동기함수면 즉시 완료
showSuccessToast(); // API 결과와 상관없이 성공 토스트
closeModal();
};
}
// ✅ 해결책 1: 모달에서는 검증만, 결과 처리는 React Query에서
function Modal({ onSubmit }) {
const handleSubmit = () => {
if (validateForm()) {
onSubmit(formData); // 그냥 호출하고 끝
closeModal();
}
};
}
// ✅ 해결책 2: mutateAsync 사용하여 실제로 기다리기
const { mutateAsync: submitData } = useSubmitData();
function Modal() {
const handleSubmit = async () => {
try {
await submitData(formData); // 실제 API 완료까지 기다림
showSuccessToast();
closeModal();
} catch (error) {
showErrorToast(error.message);
}
};
}
// ❌ 잘못된 방법
function 데이터가져오기() {
const data = fetch('/api/data'); // Promise 객체
return data.name; // Error! data는 Promise
}
// ✅ 올바른 방법
async function 데이터가져오기() {
const response = await fetch('/api/data');
const data = await response.json();
return data.name;
}
// ❌ 혼란스러운 코드
async function 혼란스러운함수() {
const result = await fetch('/api/data')
.then(res => res.json())
.then(data => data.name);
return result;
}
// ✅ 일관성 있는 코드 (async/await)
async function 깔끔한함수() {
const response = await fetch('/api/data');
const data = await response.json();
return data.name;
}
// ✅ 일관성 있는 코드 (.then())
function 깔끔한함수2() {
return fetch('/api/data')
.then(res => res.json())
.then(data => data.name);
}
// ❌ 에러 처리 없음
async function 위험한함수() {
const data = await fetch('/api/data'); // 실패하면 에러 발생
return data.json();
}
// ✅ 에러 처리 포함
async function 안전한함수() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('API 호출 실패');
}
const data = await response.json();
return data;
} catch (error) {
console.error('데이터 가져오기 실패:', error);
throw error; // 필요에 따라 다시 던지기
}
}
return 값return Promiseasync/await 또는 .then()이제 동기/비동기/Promise가 조금 더 명확해졌나요? 실무에서 막힐 때마다 이 가이드를 참고해보세요! 🚀