나중에 실행할 수 있는 함수라고 하네요.. 나중에 실행한다는게 뭘까요?
콜백 함수(callback function)는 다른 함수의 인자로서 넘겨주는 실행 가능한 함수를 말합니다. 이 콜백함수는 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있습니다. 즉, 다른 함수의 인자로써 이용될 수도 있고, 어떤 이벤트에 의해 호출될 수도 있는 함수입니다.
쉽게말해 아래 조건을 가진 함수를 콜백 함수라고 합니다.
비동기의 개념에서 항상 언급되는 것이 Callback 함수이기 때문에 저는 처음에 비동기적으로만 이용하는 줄 알았습니다. 아래 예시와 같이요.
console.log('1');
setTimeout(()=>console.log('3'),1000);
console.log('2');
//실행
1
2
3
그런데 콜백함수는 아래의 조건만 만족하면 되는 것이지, "비동기적"으로 작동할 필요는 없었습니다. 사실 우리가 이용하는 많은 메소드가 동기적 콜백함수로 작동하고 있었습니다.
const a = [1,2,3,4,5,6];
const b = a.filter(num => num > 3); // b = [4,5,6];
이게 왜 콜백이지? 라고 저는 처음에 이해하지 못했습니다.
저와 같은 분들이 계실 것 같아 아래 코드를 준비해봤습니다.
const a = [1,2,3,4,5,6];
function filter(e) {
console.log(e);
if(i > 3){
return true;
}else {
return false;
}
}
const c = a.filter(filter); // c = [4,5,6];
// 위 함수를 익명함수로 바꾸기
function(e) {
return e > 3;
}
// arrow 함수 사용하기
const d = a.filter((e) => {return e > 3}); // d = [4,5,6];
// arrow 함수 줄이기
const e = a.filter(e => e > 3); // e = [4,5,6];
근데 모두들 알고있다시피 filter함수가 제일 마지막으로 실행되진 않습니다.
같은 예시로 sort를 볼까요?
const a = [1,6,5,4,2,3];
console.log(a); // [1, 6, 5, 4, 2, 3]
a.sort(function(a,b){
return a-b;
})
console.log(a); // [1, 2, 3, 4, 5, 6]
a.push(7);
console.log(a); // [1, 2, 3, 4, 5, 6, 7]
비동기로 작용했으면 [1, 6, 5, 4, 2, 3, 7]
이 먼저 나오고 제일 나중에 [1, 2, 3, 4, 5, 6, 7]
이 나왔어야 했겠죠?
콜백은 자신이 원한다고 비동기적이 되고 동기적이 되는것이 아닙니다. 어떠한 함수가 콜백을 인자로 사용하고 있느냐에 따라 콜백의 성질이 달라집니다.
// 동기적 콜백
function addSync(a, b, callback) {
const sum = a + b;
callback(sum);
}
console.log("Before addSync");
addSync(2, 3, (result) => {
console.log("Sum:", result);
});
console.log("After addSync");
// 결과값
Before addSync
Sum: 5
After addSync
// 비동기적 콜백
function addAsync(a, b, callback) {
setTimeout(() => {
const sum = a + b;
callback(sum);
}, 1000);
console.log("Before addAsync");
addAsync(2, 3, (result) => {
console.log("Sum:", result);
});
console.log("After addAsync");
// 결과값
Before addAsync
After addAsync
Sum: 5
이렇듯 콜백 자체는 동기적으로 작동합니다. 콜백 함수에 비동기 함수를 사용할 때 콜백함수를 비동기로 이용할 수 있습니다.
setTimeout()
: 설정한 시간 뒤에 설정한 동작 실행setTimeInterval()
: 설정한 시간 마다 설정한 동작 실행addEventListener
: 설정한 HTML 요소에 이벤트가 감지되면 설정한 동작 실행콜백지옥은 뭘까요?
비동기 함수의 콜백 내부에서 다음 작업을 호출하는 것이 콜백이었죠. 그런데 이러한 콜백을 이용한 비동기 처리가 쌓이면 가독성이 떨어지고, 어디서 에러가 발생했는지 찾기 힘들 수 있습니다. _특히 this
와 함께 한다? 이제는 죽음뿐...
function callbackHell() {
setTimeout(() => {
console.log('하나');
setTimeout(() => {
console.log('둘');
setTimeout(() => {
console.log('셋');
}, 0);
}, 0);
}, 0);
}
callbackHell()
// 결과값
하나
둘
셋
위 callbackHell() 함수에 하나부터 열까지 콘솔을 찍으려고 열번 들여쓰기를 한다고 생각해보세요. 가독성이 엄청나게 떨어지겠죠? 이것이 콜백지옥입니다.
주로 이벤트 처리나 서버 통신과 같은 비동기 작업을 제어하기 위해서 사용되는데 이러한 프로그래밍은 가독성이 떨어지고 추후 코드 유지보수에도 불편합니다.
이러한 문제를 해결하기 위해 Promise와 async/await가 등장했습니다. 요즘엔 콜백함수로 비동기 처리를 하는 일이 흔하진 않은 것 같습니다. (addEventListener 제외)
저는 콜백은 비동기처리만 생각하고 있었는데, 동기적 콜백함수는 항상 제 주위에서 제가 사용하고 있었습니다.
콜백함수는 다른 함수의 매개변수로 호출되어 사용되는 함수, 특정 이벤트에 의해 실행되는 함수로 동기, 비동기는 함수를 어떻게 구성하냐에 따라 나뉘어진다는 것을 글을 정리하면서 알 수 있었습니다.
이 글을 보시는 분들도 이 글을 읽고 콜백함수와 콜백지옥을 이해하는데 도움이 되셨으면 좋겠습니다.