티스토리 뷰


JS


콜백함수(Callback function)



자바스크립트에서 함수는 객체(object) 입니다. MDS(Mozilla Developer Site)에서는 밑에와 같이 설명하고 있습니다.


In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object.

What distinguishes them from other objects is that functions can be called. In brief, they are Function objects.


자바스크립트에서 함수는 프로퍼티와 메소드를 가질 수 있기 때문에 1급객체 입니다. 다른 객체와 구별되는 점은 함수를 호출 할 수 있다는 것입니다.



1. 콜백함수란?

콜백(Callback)이란 옵저버(Observer) 디자인 패턴에서 나온 개념으로 객체의 상태 변화(이벤트)가 발생하였을 경우에 이러한 사실을 함수를 통해 전달하게 되는데, 이를 콜백 함수라고 합니다.

콜백함수는 javascript 특히, ajax나 jQuery를 사용할 때, 알게 모르게 많이 사용하고 계셨을 것입니다.

$("#myButton").on("click", function () {
    // 콜백 함수
});

$.ajax({
    url : "URL",
    type: "GET",
    success: function () {
        // 콜백 함수
    },
    error: function () {
        // 콜백 함수
    },
    complete: function () {
        // 콜백 함수
    }
});

myButton의 on 메소드에 click 이벤트가 발생할 경우, 콜백 함수가 실행이 됩니다.

ajax는 ajax가 성공했을 경우 success의 콜백 함수가, 실패 했을 경우 error의 콜백 함수가, 완료 됬을 경우 complete 콜백 함수가 실행 됩니다.



2. 콜백함수의 예시

아래에 콜백함수 예시는 첫번째 인자 2의 제곱의 결과값을 콜백함수로 보내됩니다. 콜백함수에서는 넘겨 받은 결과값을 console.log로 출력을 하였습니다.

function square(x, callback) {
    setTimeout(callback, 100, x*x);
}

square(2, function(number) {
    console.log(number);
});



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

콜백함수는 클로저입니다. ([자바스크립트] 클로저(Closure) 참고)

function callbackFunction (callback) {
    callback();
}

function testFunction() {
    var text = "callback function is closure";
    callbackFunction(function () {
        console.log(text);
    });
}

testFunction();

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


콜백함수가 실행되는 2번째 줄의 시점에 text 변수는 존재하지 않습니다.

하지만 콜백함수는 클로저이기 때문에 함수가 만들어진 환경을 기억하게 됩니다.

6번째 줄에서 함수가 만들어질 때의 환경인 4번째 줄의 text 변수의 값을 기억하고 있어, 로그로 출력되게 된 것입니다.



4. 콜백함수 사용시 유의사항

비동기처리

동기 프로그래밍이란, 어떤 작업을 요청한 후 그 작업이 완료되기까지 기다렸다가 응답을 받아 처리하는 것을 말합니다.

function square(x) {
    return x*x;
}

var number = square(2);
console.log(number);

동기프로그래밍동기프로그래밍


반면 비동기 프로그래밍이란, 어떤 작업을 요청한 후 다른 작업을 수행하다가 이벤트가 발생하면 그에 대한 응답을 받아 처리하는 것을 말합니다.

function square(x, callback) {
    setTimeout(callback, 100, x*x);
}

var number = 0;
square(2, function(x) {
    number = x;
});
console.log(number);

비동기프로그래밍비동기프로그래밍


0.1초 후 callback 함수가 실행되는 코드입니다.

console.log로 0으로 초기화된 number 값을 먼저 출력 하고, 0.1초가 지난 뒤, 콜백함수 안에 number = x로 number 값이 저장이 됩니다.


비동기로 프로그래밍을 할 때, 실행 순서를 신경쓰며 코딩을 해야 합니다. 위와 같은 이유로, 비동기로 코드들 짤때, 콜백지옥에 빠지게 됩니다.

function square(x, callback) {
    setTimeout(callback, 100, x*x);
}

square(2, function(x) {
    square(x, function(x2) {
        square(x2, function(x3) {
            console.log(x3);
        });
    });
});

콜백지옥콜백지옥


위의 코드는 콜백지옥를 설명드리기 위해 square 함수를 3번 사용한 간단한 콜백지옥 예제입니다.

동기를 맞추기 위해, 여러개의 콜백함수를 중첩하여 사용하게 되는데, 이렇게 되면 오른쪽 화면을 뚫고 나갈듯한 코드를 만들게 됩니다.


가독성을 높이기 위해 콜백함수의 이름을 지정해 주는 것이 좋습니다.

function square(x, callback) {
    setTimeout(callback, 100, x*x);
}

square(2, firstCallback);

var firstCallback= function (number) {
    square(number, secondCallback);
}

var secondCallback = function (number) {
    square(number, thirdCallback);
}

var thirdCallback = function (number) {
    console.log(number);
}

콜백함수 이름 지정하기콜백함수 이름 지정하기


콜백함수에 이름을 지정하여 익명 함수를 인자로 넘기는 것이 아니라 기명 함수로 인자로 넘기게 되면 콜백지옥은 어느정도 해결할 수 있습니다.

콜백지옥의 다른 해결 방법은 promise 등이 있습니다.([자바스크립트] 비동기프로그래밍, ES6(ECMA Script 6) - Promise로 콜백지옥 해결하기 참고) 


this 객체

var callbackText = {
    text: "No text",
    setText: function (param) {
        this.text = param;
    }
}

function callbackSample(name, callback) {
    callback(name);
}

callbackSample("This is callback sample", callbackText.setText);

setTimeout(console.log(callbackText.text), 100);
setTimeout(console.log(window.text), 100);

thisthis


위에서 설명한 비동기 문제를 해결하기 위해 setTimeout api를 사용하여 로그를 출력하였습니다.

14번째 줄의 callbackText.text가 No text로 출력이 됩니다. This is callback sample이 아니라 No text로 출력이 되는 이유는 this에 있습니다.

4번째 줄의 this.text = param으로 2번째 줄의 text에 값을 저장하는 것을 예상하였지만, 콜백함수가 실행 된 후의 this는 window 객체가 됩니다.

9번째 줄에서 callback 함수는 일반함수로 실행이 됩니다. 일반함수에서 this는 window 객체가 되어([자바스크립트] this의 정체 참고), this.text로 저장되는 값은 결국은 window.text로 저장되게 됩니다.

그래서 15번 window.text가 This is callback sample이 출력되게 됩니다.


이런 문제를 해결하기 위해서 call, apply API를 사용하여 this를 지정해 주면 됩니다. ([자바스크립트] API - call, apply 함수 참고)

var callbackText = {
    text: "No text",
    setText: function (param) {
        this.text = param;
    }
}

function callbackSample(name, callback) {
    callback.call(callbackText , name);
}

callbackSample("This is callback sample", callbackText.setText);

setTimeout(console.log(callbackText.text), 100);
setTimeout(console.log(window.text), 100);

this 객체 유념하기this 객체 유념하기


결과로 확인 할 수 있듯이, callbackText.text에 원하는 값이 저장된 것을 확인 할 수 있습니다.



5. 비동기프로그래밍을 하는 이유

이렇게 신경써야 할 것들이 많은 비동기 프로그래밍을 사용하는 이유는 무엇일까요?

가장 큰이유는 속도 때문입니다. 동기 프로그래밍은 기능을 요청을 한 후, 요청이 끝날 때까지 무작정 대기를 해야 합니다. 하지만 비동기 프로그래밍은 기능을 요청을 한후, 다른 작업을 하고 있다가, 끝났다는 이벤트를 받고 난 후 이후의 처리를 하면 되기 때문에, 비동기 프로그래밍이 동기프로그래밍 보다 빠를 수 밖에 없습니다.

DB 호출이 잦은 경우, 비동기로 처리하게 되면, 동기로 처리된 것보다 빠른 성능을 경험 할 수 있습니다.



댓글
  • 프로필사진 JES 안녕하세요 잘 보고 갑니다!
    자세한 설명 감사드립니다. 많은 도움이 되었습니다.
    2016.12.26 17:39
  • 프로필사진 oWhen 좋은 글 감사합니다... setTimeOut의 세번째 인자는 무엇인가요??? O_O

    setTimeout(callbak, 100, x*x);
    2018.09.17 14:37
  • 프로필사진 버미노트 callback 함수의 인자값입니다.
    setTimeout(callbak, 100, x*x); 는
    100ms 후에 callback(x*x)가 호출 되는 것으로 이해하면 될 것 같습니다.

    https://developer.mozilla.org/ko/docs/Web/API/WindowTimers/setTimeout 가 도움이 될 것 같습니다.
    2018.09.17 14:51 신고
댓글쓰기 폼