fetchData()는 비동기 데이터 요청 함수 (setTimeout으로 시뮬레이션된 요청)document.querySelector("#request-btn3").addEventListener("click", () => {
console.log("메인 작업 시작");
"메인 작업 시작" 출력/*
fetchData(responseData => console.log(responseData.name));
fetchData(responseData => console.log(responseData.age));
*/
이건 순차 실행이 보장되지 않는 잘못된 방법이에요 ⚠️
이유:
fetchData()는 비동기 함수라서📜 실행 예시 (비정상적인 순서 가능)
메인 작업 시작
메인 작업 종료
20
홍길동
이처럼 순서가 섞일 수 있어요.
fetchData((responseData) => {
console.log(responseData.name); // ① 이름 출력
// 그다음 작업 - 나이 출력
fetchData((responseData) => {
console.log(responseData.age); // ② 나이 출력
});
});
fetchData()가 완료되면 → 이름 출력fetchData() 호출 → 나이 출력📜 실행 순서
메인 작업 시작
메인 작업 종료
홍길동
20
| 순서 | 동작 |
|---|---|
| 1 | "메인 작업 시작" 출력 |
| 2 | 첫 번째 fetchData() 실행 (비동기 예약) |
| 3 | "메인 작업 종료" 출력 (콜스택 비워짐) |
| 4 | 첫 번째 fetchData() 완료 → responseData.name 출력 |
| 5 | 두 번째 fetchData() 실행 (비동기 예약) |
| 6 | 두 번째 요청 완료 → responseData.age 출력 |
이 코드가 바로 콜백 지옥(callback hell) 구조예요 👇
fetchData((data1) => {
console.log(data1.name);
fetchData((data2) => {
console.log(data2.age);
fetchData((data3) => {
console.log(data3.city);
// ...
});
});
});
➡ 들여쓰기가 계속 깊어지고,
가독성이 떨어지며,
에러 처리나 디버깅이 매우 어려움 😵
이런 콜백 지옥을 피하기 위해 등장한 게 바로 👇
이 둘을 쓰면 순차적인 비동기 코드도
동기 코드처럼 깔끔하게 작성 가능해요.
| 구분 | 설명 |
|---|---|
| fetchData() | 비동기 데이터 요청 함수 |
| 콜백 함수 | 데이터 응답 후 실행될 함수 |
| 문제점 | 순서를 보장하려면 콜백 안에 콜백을 중첩해야 함 |
| 결과 | 코드 가독성 저하 → 콜백 지옥 |
| 해결책 | Promise, async/await 사용 |
fetchDataByPromise() 호출 이 함수는 Promise 객체를 반환해. -> Promise 객체를 반환한다는게 promise함수를 데려온다는거야?
좋은 질문이야 👍
여기서 말하는 “Promise 객체를 반환한다”는 건
“Promise 함수를 데려온다”는 뜻이 아니야 — 약간 다른 개념이야.
fetchDataByPromise() 함수 안을 보면 👇
function fetchDataByPromise() {
return new Promise((resolve) => {
const responseData = { name: "홍길동", age: 20 };
resolve(responseData);
});
}
여기서 new Promise(...)는 Promise 객체를 새로 “만드는” 코드야.
그래서 이 함수가 실행되면 👉
Promise 객체를 “리턴(반환)”하는 거야.
즉,
return new Promise(...)
는
"Promise 설계도를 이용해서 새 Promise 객체를 만들고,
그걸 밖으로 돌려준다(반환한다)"
는 뜻이야.
→ “Promise 객체를 밖으로 보내준다”는 뜻이야.
→ 즉, 이 함수를 호출하는 쪽에서 .then() 같은 걸 붙일 수 있게 되는 거야.
예를 들어 👇
fetchDataByPromise()
.then((data) => console.log(data));
여기서 .then()을 붙일 수 있는 이유는,
fetchDataByPromise()가 Promise 객체를 반환하기 때문이야.
(return이 없었다면 .then() 못 붙임)
| 표현 | 의미 |
|---|---|
| “Promise 객체를 반환한다” | 함수가 실행된 후, Promise 형태의 결과물을 돌려준다 |
| “Promise 함수를 데려온다” | ❌ 잘못된 표현 (Promise 생성자 함수 자체를 가져오는 게 아님) |
| 실제로 하는 일 | new Promise()로 Promise 객체를 새로 만들고, 그걸 return 함 |
원하면 fetchDataByPromise() 실행 시 실제 내부에서 일어나는 과정을 스택 + 마이크로태스크 큐 흐름으로 시각화해줄까?
(콜스택에 뭐 들어가고 언제 resolve가 실행되는지 단계별로)
①
fetchDataByPromise() 함수가 실행되면 무엇을 반환하지?
②
그 반환된 게 Promise 객체라면, 그 객체 안에서는 언제 resolve()가 실행되는 거야?
③
resolve(responseData)가 실행되면 .then() 메서드는 언제, 어떤 데이터를 받게 돼?
④
만약 fetchDataByPromise() 함수 안에서 return new Promise(...) 대신
그냥 console.log("hi")만 하고 끝낸다면,
밖에서 fetchDataByPromise().then(...) 이렇게 쓸 수 있을까?
답 👇
fetchDataByPromise()가 반환하는 것정답: Promise 객체
완벽하게 맞았어!
→ 즉, 이 함수는 “지금은 결과를 모르지만, 나중에 결과(성공/실패)를 줄게” 라고 약속하는 객체를 반환해.
resolve()는 언제 실행되는가정답: Promise 안의 비동기 작업(예: setTimeout, fetch 등)이 끝났을 때 호출돼.
그래서 “비동기 작업이 완료되면 그때 결과 데이터를 넘겨줘”라는 의미야.
→ 너가 쓴 “promise함수가 실행되고 나서”도 거의 맞아.
정확히는 “promise 안의 비동기 코드가 끝났을 때” 실행되는 거야.
resolve(responseData)가 실행되면정답: 그 순간
.then()에 연결된 콜백이 실행되고,responseData가 그 콜백의 인자로 전달돼.
즉,
resolve(responseData);
👇
.then((responseData) => { ... })
이 콜백이 그 데이터를 받게 되는 구조야.
너 말한 “끝날 때 데이터를 받게 됨” → 아주 정확해! 👍
.then()을 쓸 수 있을까?정답: ❌ 불가능해.
왜냐하면fetchDataByPromise()함수가 Promise 객체를 반환해야
그걸 기준으로.then()을 체이닝할 수 있기 때문이야.
즉,
return new Promise(...);
이 구문이 있어야
fetchDataByPromise().then(...)
이렇게 쓸 수 있어.
너가 말한 “then을 쓰려면 반드시 return을 써야돼” → 정확해! (100점)
단, 이유는 “then은 Promise 객체에서만 쓸 수 있기 때문”이야.