2019. 06. 02 수정


1. 콜백함수란?

JS에서 콜백 함수는 너무나 중요한 개념입니다.

Node.js 환경에서 프로그래밍 시 반드시 필요한 개념이기도 합니다.


웹 개발을 해보셨으면 jQuery를 한 번 쯤은 사용해보거나 보셨을 것이라 생각이 되는데요. ( 딱히 몰라도 됩니다. )

jQuery를 사용했다면 알게 모르게 콜백함수를 사용하고 있던것입니다.


콜백 함수란 어떤 이벤트가 발생한 후, 수행될 함수를 의미합니다.

JS에서 함수는 1급객체( 링크 )이므로, 인자 전달 시 함수를 전달할 수 있기 때문에 콜백함수가 가능합니다.

$(".submitBtn").click(function(){
alert("제출버튼을 클릭했습니다!");
})

위의 코드는 submitBtn 클래스를 가진 요소를 클릭했을 때, 콜백 함수가 실행되는 jQuery 코드입니다.

즉, 비동기 이벤트인 click에 대한 이벤트 리스너로 콜백 함수가 작성된 것이며, 그 내용은 alert()를 호출하고 있습니다.

click의 콜백 함수는 naming을 할 필요가 없는 익명함수인데요, 대부분의 콜백 함수는 이렇게 익명함수로 작성됩니다.


이제부터 콜백함수에 대해 자세히 알아보도록 하겠습니다.





2. 콜백함수는 클로저다.

비동기 프로그래밍을 하면, 어떤 함수가 실행될 때 특정 시점에 콜백함수가 수행될 것입니다.

콜백함수는 자신이 포함된 함수의 환경에 접근이 가능합니다.

클로저는 함수가 선언될 때의 환경을 기억하고 있으므로, 콜백함수는 클로저라고 할 수 있습니다.

( 클로저에 대한 자세한 내용은 여기를 참고해주세요. )


아래는 콜백함수가 클로저라는 것을 보여주는 예제입니다.

function callback(cb){
cb();
}

function add(x, y){
let sum = x + y;
callback( function(){
console.log(sum);
})
}

add(3,5);

add 함수 내부에 있는 callback 함수의 인자인 익명함수가 콜백함수 입니다.

add 함수는 3과 5를 인자로 받아 더한 값을 sum에 할당하고, sum변수를 콜백함수로 전달합니다.

콜백함수는 클로저이기 때문에 오류를 발생하지 않고 sum 변수를 참조할 수 있습니다.





3. 콜백함수 사용 시 this 객체에 유의하자.

콜백함수를 사용할 때는 this 객체에 유의해야 합니다.

( this 객체에 대한 자세한 설명은 여기를 참고해주세요. )


var obj = {
name: "victolee",
email: "empty",

setEmail : function(email){
this.email = email;
}
}

function callback(email, cb){
cb(email);
}

callback("example.com", obj.setEmail);
console.log(obj.email);
console.log(window.email);

obj 변수에 있는 setEmail 함수는 email을 인자로 받아 obj 객체의 email 프로퍼티 값을 바꾸는 함수입니다.

setEmail 함수에서는 obj 객체를 참조할 목적으로 this.email = email 코드가 있습니다.

위의 예제를 실행하면, 과연 email 프로퍼티 값이 바뀌었을까요?


callback() 함수를 실행할 때, 두 번째 인자(cb() 함수)에 obj.setEmail을 함수를 전달했습니다.

즉, example.com 이라는 값으로 obj.email 프로퍼티 값이 바뀌기를 기대하고 실행한 것이죠.

그러나 callback() 함수 내에서 콜백함수 setEmail 함수가 실행될 때, this는 전역 객체인 window가 됩니다.

따라서 첫 번째 출력 결과는 empty이며, 두 번째 출력 결과는 example.com가 됩니다.


이와 같은 문제를 해결하기 위해서는 call, apply 메서드를 사용하여 this객체를 명확히 하는 방법이 있습니다.

( call, apply 메서드에 대해서는 여기를 참고해주세요 ! )

var obj = {
name: "victolee",
email: "empty",

setEmail : function(email){
this.email = email;
}
}

function callback(email, cb){
cb.call(obj, email);
}

callback("example.com", obj.setEmail);
console.log(obj.email);
console.log(window.email);

callback() 함수에서 콜백 함수(cb)를 호출할 때, call() 함수를 사용하여 this 객체를 obj라고 명확히 전달했습니다.

따라서 이 예제에서는 첫 번째 출력 결과는 example.com이 되고, 두 번째 출력 결과는 undefined가 됩니다.


이와 같이 콜백함수를 작성할 때는 this에 주의하셔야 합니다.





4. 콜백지옥

콜백함수를 남용하면 콜백지옥에 빠질 수 있습니다.



이 사진은 if와 else를 남용하여 기이한 구조를 갖는 함수가 작성한 것입니다.

코드 읽기가 너무 힘들겠죠....?

콜백함수도 남용하면 가독성이 떨어지게 됩니다.


다음은 콜백함수를 남용한 예제입니다.

function add(x, callback){
let sum = x + x;
console.log(sum);
callback(sum);
}

add(3, function(result){
add(result, function(result2){
add(result2, function(result3){
add(result3, function(result4){
console.log("에너지 파")
})
})
})
})

위의 코드는 콜백함수가 단순히 add() 함수를 호출하기 때문에 코드가 복잡하지 않을지도(?) 모릅니다.

실제 비즈니스 로직을 짜다보면 이보다 훨씬 길겠죠.

코드가 얼마나 복잡해질지 한 번 상상하면... 답이 없는 코드가 될 것입니다.


콜백함수는 유용하지만 이처럼 잘못 사용할 경우 콜백지옥을 맛보게 되는데요.

이를 해결할 수 있는 방법으로 promise라는 것이 있습니다. ( 링크 )




이상으로 콜백함수에 대해 알아보았습니다.


[ 참고 ] 

http://beomy.tistory.com/10