
콜백 함수는 다른 함수에게 실행 권한을 맡기는 함수입니다.
쉽게 말해, "이 함수를 나중에 적절한 타이밍에 실행해줘!"라고 다른 함수에게 부탁하는 것과 같습니다.
// 콜백 함수의 기본 개념
function 주문받기(메뉴, 완료시실행할함수) {
console.log(`${메뉴} 준비 중...`);
setTimeout(() => {
console.log(`${메뉴} 완성!`);
완료시실행할함수(); // 콜백 함수 실행
}, 2000);
}
주문받기('아메리카노', function() {
console.log('감사합니다!');
});
// setInterval 예제
let count = 0;
function 숫자세기() {
console.log(count);
if(++count > 4) {
clearInterval(timer);
}
}
// setInterval이 언제 실행할지 결정
const timer = setInterval(숫자세기, 300);
// 출력: 0, 1, 2, 3, 4 (0.3초 간격)
핵심: setInterval이 숫자세기 함수를 언제 실행할지 완전히 통제합니다.
// map 메서드 예제
const 원본배열 = [10, 20, 30];
const 결과1 = 원본배열.map(function(값, 인덱스, 전체배열) {
console.log(`값: ${값}, 인덱스: ${인덱스}`);
return 값 + 5;
});
console.log(결과1); // [15, 25, 35]
// 매개변수 순서를 바꾸면?
const 결과2 = 원본배열.map(function(인덱스라고생각한것, 값이라고생각한것) {
return 값이라고생각한것 + 5; // 실제로는 인덱스 + 5
});
console.log(결과2); // [5, 6, 7] (인덱스 0,1,2 + 5)
주의사항: 매개변수 이름은 중요하지 않습니다. 순서가 중요해요!
// this 바인딩 예제
// 전역 객체를 가리키는 경우
setTimeout(function() {
console.log(this); // Window 객체 (브라우저 환경)
}, 100);
// HTML 엘리먼트를 가리키는 경우
document.getElementById('버튼').addEventListener('click', function() {
console.log(this); // 클릭된 버튼 엘리먼트
});
const 객체 = {
데이터: [1, 2, 3],
출력메서드: function(값, 인덱스) {
console.log('this:', this, '값:', 값, '인덱스:', 인덱스);
}
};
// 메서드로 호출 - this는 객체를 가리킴
객체.출력메서드(1, 2);
// this: {데이터: Array(3), 출력메서드: ƒ} 값: 1 인덱스: 2
// 콜백으로 전달 - this는 전역 객체를 가리킴
[4, 5, 6].forEach(객체.출력메서드);
// this: Window 값: 4 인덱스: 0
// this: Window 값: 5 인덱스: 1
// this: Window 값: 6 인덱스: 2
교훈: 콜백으로 전달되는 순간, 원래 객체와의 연결이 끊어집니다.
setTimeout(function(name) {
let coffeeList = name;
console.log(coffeeList);
setTimeout(function(name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(function(name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(function(name) {
coffeeList += ', ' + name;
console.log(coffeeList);
}, 500, '카페라떼');
}, 500, '카페모카');
}, 500, '아메리카노');
}, 500, '에스프레소');
이 코드의 문제점:
let coffeeList = '';
function 에스프레소추가(name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(아메리카노추가, 500, '아메리카노');
}
function 아메리카노추가(name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(모카추가, 500, '카페모카');
}
function 모카추가(name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(라떼추가, 500, '카페라떼');
}
function 라떼추가(name) {
coffeeList += ', ' + name;
console.log(coffeeList);
}
setTimeout(에스프레소추가, 500, '에스프레소');
이 코드는 어떻게 동작하나요?
1. setTimeout(에스프레소추가, 500, '에스프레소') - 0.5초 후 에스프레소추가 함수 실행
2. 에스프레소추가 실행 → coffeeList에 '에스프레소' 저장 → 출력 → 다음 함수 예약
3. 0.5초 후 아메리카노추가 실행 → ', 아메리카노' 추가 → 출력 → 다음 함수 예약
4. 이런 식으로 각 함수가 다음 함수를 호출하는 체인 형성
5. 총 2초(0.5×4) 후에 모든 커피가 순서대로 추가됨
function 커피추가(name) {
return function(prevName) {
return new Promise(resolve => {
setTimeout(() => {
const newName = prevName ? `${prevName}, ${name}` : name;
console.log(newName);
resolve(newName);
}, 500);
});
};
}
커피추가('에스프레소')()
.then(커피추가('아메리카노'))
.then(커피추가('카페모카'))
.then(커피추가('카페라떼'));
이 코드는 어떻게 동작하나요?
1. 커피추가('에스프레소')()를 호출 → 첫 번째 Promise 생성
2. 0.5초 후 '에스프레소' 출력하고 resolve('에스프레소') 호출
3. .then(커피추가('아메리카노')) 실행 → 새로운 함수 반환
4. 이전 결과('에스프레소')를 받아서 '에스프레소, 아메리카노' 생성
5. 이런 식으로 .then() 체이닝이 계속 이어져서 순차적으로 실행
핵심 포인트: 커피추가는 고차함수(함수를 반환하는 함수)이고, 각 단계에서 Promise를 반환해서 체이닝이 가능합니다.
// 🔄 함수 실행을 멈췄다 재개할 수 있는 특별한 함수
function 커피준비(prevName, name) {
setTimeout(() => {
const newName = prevName ? `${prevName}, ${name}` : name;
console.log(newName);
커피메이커.next(newName); // 다음 단계로 진행
}, 500);
}
function* 커피제조과정() {
const 에스프레소 = yield 커피준비('', '에스프레소');
const 아메리카노 = yield 커피준비(에스프레소, '아메리카노');
const 모카 = yield 커피준비(아메리카노, '카페모카');
const 라떼 = yield 커피준비(모카, '카페라떼');
return '주문 완료!';
}
const 커피메이커 = 커피제조과정();
커피메이커.next(); // 제조 과정 시작
이 코드는 어떻게 동작하나요?
1. 커피제조과정()을 호출하여 Generator 객체(커피메이커) 생성
2. 커피메이커.next() 호출하면 첫 번째 yield까지 실행 후 일시정지
3. 커피준비 함수가 0.5초 후 실행되어 커피메이커.next(newName) 호출
4. Generator가 정지된 지점부터 재개되어 다음 yield까지 실행
5. 이 과정이 반복되면서 각 단계가 순차적으로 실행됨
핵심 포인트: yield로 함수를 멈추고, next()로 다시 시작하는 특별한 함수입니다.
// 🎉 가장 읽기 쉬운 방식
function 커피만들기(name) {
return new Promise(resolve => {
setTimeout(() => resolve(name), 500);
});
}
async function 커피주문() {
let 주문목록 = '';
const 항목추가 = async (name) => {
const 커피 = await 커피만들기(name);
주문목록 += (주문목록 ? ', ' : '') + 커피;
console.log(주문목록);
};
await 항목추가('에스프레소');
await 항목추가('아메리카노');
await 항목추가('카페모카');
await 항목추가('카페라떼');
}
커피주문();
이 코드는 어떻게 동작하나요?
1. 커피주문() 함수 호출 (async 함수이므로 자동으로 Promise 반환)
2. await 항목추가('에스프레소') 실행
3. 항목추가 내부에서 await 커피만들기('에스프레소') 실행
4. 커피만들기가 0.5초 후 '에스프레소'를 resolve
5. await가 Promise 완료까지 기다린 후 다음 줄 실행
6. 주문목록에 '에스프레소' 추가하고 console.log로 출력
7. 이런 식으로 각 await에서 기다렸다가 순차적으로 실행
핵심 포인트: 비동기 코드를 동기 코드처럼 위에서 아래로 읽을 수 있게 만드는 마법 같은 문법입니다!
콜백 함수란? 다른 함수에게 "나중에 실행해줘"라고 맡기는 함수
콜백 함수의 특징:
콜백 지옥 해결 방법:
1. 함수를 분리해서 가독성 향상
2. Promise로 체인 구조 만들기
3. Generator로 실행 흐름 제어하기
4. async/await로 동기 코드처럼 작성하기 (추천!)
실무 팁: 콜백 함수를 사용할 때는 항상 "누가 언제 이 함수를 호출하는가?"를 생각해보세요. 그러면 예상치 못한 동작을 피할 수 있습니다!