콜백 패턴


함수는 객체다. 즉 함수를 다른 함수에 인자로 전달할 수 있다. introduceBugs() 함수를 writeCode() 함수의 인자로 전달하면, 아마도 writeCode()는 어느 시점에 introduceBugs()를 실행(또는 호출)할 것이다. 이때 introduceBugs()를 콜백함수 또는 간단하게 콜백이라고 부른다.

function writeCode(callback){
// 어떤작업을 수행한다.
callback();
// ...
}

function introduceBugs(){
console.log('bug');
}

writeCode(introduceBugs); // bug


introduceBugs()가 writeCode()의 인자로 괄호없이 전달된 사실을 눈여겨 보자. 괄호를 붙히면 함수가 실행되는데 이 경우에는 함수의 참조만 전달하고 실행은 추후 적절한 시점에 writeCode()가 해줄 것이기 때문에 괄호를 덧붙이지 않았다.



콜백 예제


아래 함수는 findNodes()와 같은 형식으로 호출되며 DOM 트리를 탐색해 필요한 엘리먼트의 배열을 반환한다.

var findNodes = function(callback){
var i = 100000, // 긴루프
nodes = [], // 결과를 저장할 배열
found; // 노드 탐색 결과

// 콜백 함수를 호출할 수 있는지 확인한다.
if(typeof callback !== "function"){
callback = false;
}

while(i){
i -= 1;
// 이 부분에 복잡한 로직이 들어간다.

// 여기서 콜백을 실행
if(callback){
callback(found)
}
nodes.push(found);
}
return nodes;
};

var hide = function(node){
nodes[i].style.display = "node";
}

// 노드를 찾아서 바로 숨긴다.
findNodes(hide);


이미 존재하는 함수를 콜백으로 함수로 쓸 수도 있지만 findNodes() 함수를 호출할 때 익명 함수를 생성해서 쓸 수도 있다.


// 익명함수를 콜백으로 전달한다.
findNodes(function(node){
node.style.display = "block";
})



콜백과 유효범위


만약 콜백 메서드가 자신이 속해있는 객체를 참조하기 위해 this를 사용하면 예상치 않게 동작할 수도 있다.
myapp이라는 객체의 메서드인 paint()함수를 콜백으로 사용한다고 가정해보자.

var myapp = {};
myapp.color = 'green';
myapp.paint = function(node){
node.style.color = this.color;
};

// findNodes() 함수는 이런 식으로 동작한다.

var findNodes = function(callback){
// ...
if(typeof callback === "function"){
callback(found)
}
}


findNodes(myapp.paint)를 호출하면 this.color가 정의되지 않아 예상대로 동작하지 않는다. findNodes()가 전역 함수이기 때문에 객체 this는 전역 객체를 참조한다. findNodes()가 (dom.findNodes()처럼) dom 이라는 객체의 메서드라면 콜백 내부의 this는 예상과는 달리 myapp이 아닌 dom을 참조하게 된다.


이 문제를 해결 하기 위해서는 콜백함수와 함께 콜백이 있는 객체를 전달하면 된다.


findNodes(myapp.paint, myapp);



var findNodes = function(callback, callback_obj){
// ...
if(typeof callback === "function"){
callback.call(callback_obj, found)
}
}


다음과 같이 바꿀수도 있다. 메서드와 객체를 전달할 때 메서드를 문자열로 전달하게 되면 객체를 두번 반복하지 않아도 된다.


findNodes("paint", myapp);

var findNodes = function(callback, callback_obj){
// ...
if(typeof callback === "string"){
callback = callback_obj[callback];
}

// ...
if(typeof callback === "function"){
callback.call(callback_obj, found)
}
}



비동기 이벤트 핸들러


페이지의 엘리먼트에 이벤트 리스너를 붙이는것도 콜백이 발생하는 것이므로 document 이벤트 리스너에 콜백을 전달 할 수 있다.

document.addEventListener("onload", console.log('resdy'), false);



타임아웃


setTimeout()과 setInterval()도 콜백 함수를 받아서 실행 한다.

var thePlotThickens = function(){
console.log('500ms later...');
};
setTimeout(thePlotThickens, 500);


+ Recent posts