[JavaScript] - 콜백 함수(Callback)란?

dev_woo·2024년 11월 30일

JS 개념 완벽정리

목록 보기
1/3
post-thumbnail

오늘은 JavaScript 콜백 함수에 대해 알아보겠습니다.

1.콜백 함수란?


콜백(Callback)는 매개변수로 함수 객체를 전달해서, 호출 함수 내에서 매개변수 함수를 실행하는 것을 말합니다.

아래 코드를 예로 들어보겠습니다.

function sayHelloWorld(result, callback) {
    console.log("Hello!");
    callback(result); // 매개변수로 받은 함수를 실행
}

function callback(result) {
    console.log(result);
}

sayHelloWorld("world!", callback);

결과
Hello!
world!

위 코드에서는 sayHelloWorld 함수가 callback 이라는 함수 객체를 매개변수로 받고, 내부에서 실행합니다. 결과적으로 "Hello!" 가 출력된 후, 전달된 result 값인 "world!"가 출력됩니다.

왜 콜백함수를 사용할까?

이렇게 단순히 결과를 출력하는 코드를 보면, "그냥 console.log(result)를 바로 호출하면 될 텐데, 왜 굳이 콜백 함수를 사용해야 할까?"라는 의문이 들 수 있습니다.

동작의 순서 제어

JavaScript 는 코드를 위에서 아래로 순차적으로 실행합니다. 하지만, 개발을 하다보면, 네트워크 요청 같은 시간이 오래 걸리는 작업들과 같이 특정 작업이 완료 된 후에 코드를 실행해야할 때가 있습니다.

아래 코드를 보시죠

// 데이터 가져오기 ( 비동기 작업 )

function fetchData(callback) {
  	setTimeout(() => {
      	const data = "서버에서 가져온 데이터"
        callback(data); // 데이터가 준비되면 콜백 함수 실행
    }, 2000); // 2초 후 데이터 준비
}

fetchData((result) => {
  	console.log(result); 
});

위 코드에서 fetchData 함수가 서버로 부터 데이터를 가져오는 작업을 수행합니다.
이 작업은 2초가 걸리며, 작업이 완료된 후 callback 함수가 실행되고, "서버에서 가져온 데이터"가 출력됩니다.

하지만 이런 작업을 단순히 함수 호출로 처리했으면 어떻게 됬을까요?
데이터가 준비 되기 전에 console.log 가 실행 될수도 있습니다.
하지만 콜백 함수를 사용하면, 작업이 완료된 후 특정 코드를 안전하게 실행 할 수 있습니다.

즉, 콜백 함수는 비동기 프로그래밍 같이, 순차적으로 작업이 실행되어야 할때, 순서를 보장하기 위해 사용합니다.

2. 콜백 함수는 어떻게 사용할까?


콜백함수 작성 방법

1. 익명 함수 사용

콜백 함수는 일반적으로 일회용으로 사용되는 경우가 많아, 코드의 간결성을 위해 이름 없는 익명 함수 를 많이 사용합니다.

sayHelloWorld("world!", function(result){
	console.log(result);
});

이렇게 익명 함수를 사용하는 이유는, 뜻하지 않은 개발자의 실수로 인한 함수 이름 충돌을 방지 하는 이유도 있다고 합니다.

2. 화살표 함수 콜백 사용

익명 함수 사용하는 것을 더 간결히 정의 하기 위해서 화살표 함수를 사용하는 경우도 있습니다.

sayHelloWorld("world!", (result) => {
	console.log(result);
});

3. 함수 참조

JavaScript 의 함수는 일급 객체의 특성을 가지기 때문에 매개변수에 일반적인 변수나 상수 값 뿐만 아니라 함수 자체를 객체로써 전달이 가능합나다. 따라서, 별도로 함수를 정의하고, 함수의 이름만 호출 함수의 인자에 전달 하는 방식으로 쓸 수 있습니다.

// 콜백 함수를 별도의 함수로 정의
function greet(name) {
  console.log("Hello, " + name);
}

function sayHello(callback) {
  const name = "Alice";
  callback(name); // 콜백 함수 호출
}

function sayHello2(callback) {
  const name = "Sam";
  callback(name); // 콜백 함수 호출
}

// 콜백 함수의 이름만 인자로 전달
sayHello(greet); // Hello, Alice
sayHello2(greet); // Hello, Sam

일급 객체 (first-class object)
다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체로, 다음과 같은 조건을 만족하는 객체를 일급객체 라고 한다.

 1. 무명의 리터럴로 생성 할 수 있다. (변수에 익명함수로 바로 선언 할 수 있다.)
 2. 변수나 자료구조(객체, 배열 등)에 저장 할 수 있다.
 3. 함수의 매개변수에 전달 할 수 있다.
 4. 함수의 반환값으로 사용할 수 있다.

또한 이러한 특징을 응용하면, 매개변수를 전달하는 콜백 함수의 종류를 바꿔줌으로써, 코드의 재사용성을 높일 수 있습니다.

function calculate(a, b, operation) {
    return operation(a, b);
}

function add(x, y) {
    return x + y;
}

function multiply(x, y) {
    return x * y;
}

console.log(calculate(5, 3, add)); // 8
console.log(calculate(5, 3, multiply)); // 15

콜백 함수 활용 사례

이벤트 리스너

우리가 버튼을 클릭하는 이벤트를 처리할 때, 자주 사용하는 방법 중 하나가 바로 addEventListener 메서드입니다. 이 메서드는 버튼을 클릭했을 때 실행할 함수를 미리 등록해두는 방식으로 동작합니다.

아래 코드를 보시죠

const button = document.getElementById('myButton');
button.addEventListener('click', function () {
    console.log('버튼이 클릭되었습니다!');
});

위 코드에서 버튼 요소에 클릭 이벤트가 발생했을 때, 실행할 콜백 함수를 전달합니다.
즉, 이벤트 리스너는 해당 이벤트가 발생한 이후, 콜백 함수를 실행하는 구조입니다.

고차함수

JavaScript 에서 배열을 다루다보면 map(),forEach(),filter() 등의 메소드를 사용해 보신 경험이 있으실겁니다. 이 메소드들 역시 콜백 함수를 파라미터로 전달 받는 방식으로 동작합니다.

const numbers = [1, 2, 3, 4, 5];
const squared = numbers.map((num) => num * num); // 각 요소를 제곱
const evenNumbers = numbers.filter((num) => num % 2 === 0); // 짝수만 필터링

console.log(squared); // [1, 4, 9, 16, 25]
console.log(evenNumbers); // [2, 4]

이외에도, 네트워크 요청할때 사용되는 fetch() 메서드나, 타이머 함수인 setTimeout() 메서드 등과 같이 특정 이벤트가 발생한 후 이벤트를 처리하는 로직을 사용하는 메소드들에서 콜백 함수가 사용 되고 있습니다.

콜백 함수 사용시 주의할 점

먼저 일반 함수에서 this 는 전역객체를 가르킵니다. 호출의 대상이 따로 없기 때문이죠
하지만 메서드는 객체라는 호출 대상이 있으므로 this 는 해당 객체를 가르킵니다. 이를 암묵적 바인딩이라고 합니다.

this 를 사용한 콜백함수

아래의 코드를 보시죠

const obj = {
    name: 'Alice',
    sayName: function () {
      console.log(this.name);
    }
};

function getName(callback) {
  	callback();
}

getName(obj.sayName); // undifined

위 코드에서 getName 함수가 실행 됬을때, Alice 가 나오길 기대했지만, 실제로는 undifined 가 출력 되는 것을 볼 수 있습니다.

반면에 아래 코드를 보시면

const obj = {
    name: 'Alice',
    sayName: function () {
        console.log(this.name); // this는 obj를 가리킴
    }
};

obj.sayName(); // "Alice"

기대하던 Alice 가 호출 되는 것을 볼 수 있습니다.

왜 이런 차이가 생기는 걸까요?

그 이유는 this 는 함수를 호출한 대상(객체) 를 가르키는 지시자 이기 때문입니다.
obj.sayName 으로 함수가 참조되었을뿐 실제 실행은 getName 에서 실행됬으므로, this 가 가르킬 객체가 없기 때문에 전역객체가 호출 되는 것입니다.

콜백 함수에서 this 사용 방법

그래서 콜백 함수 내에서 this 를 사용하기 위해선 가르키는 대상이 누구인지 명확히 설정해줘야 합니다.
이를 명시적 바인딩이라고 합니다.

1. bind

bindthis 를 고정한 새로운 함수를 반환합니다. 이를 통해 thisobj 를 고정적으로 호출하게 됩니다.

const obj = {
    name: 'Alice',
    sayName: function () {
        console.log(this.name);
    }
};

function getName(callback) {
    callback();
}

// sayName의 this를 obj로 고정
getName(obj.sayName.bind(obj)); // "Alice"

2. call

call 은 첫번째 인자를 this 객체로 할당하고, 함수를 바로 실행합니다.
나머지 파라미터는 쉼표를 기준으로 전달됩니다.

const obj = {
    name: 'Alice',
    sayName: function () {
        console.log(this.name);
    }
};

function getName(callback) {
    callback();
}

// 즉시 실행 되므로, 익명 함수로 전달
getName(() => obj.sayName.call(obj)); // "Alice"

3. apply

apply 는 첫번째 인자를 this 객체로 할당하고, 함수를 바로 실행합니다.
나머지 파라미터는 배열로 전달 됩니다.

const obj = {
    name: 'Alice',
    sayName: function () {
        console.log(this.name);
    }
};

function getName(callback) {
    callback();
}

// 즉시 실행 되므로, 익명 함수로 전달
getName(()=> obj.sayName.apply(obj)); // "Alice"

4.화살표 함수

화살표 함수는 자신의 this 를 가지지 않고, 상위 컨텍스트의 this 를 그대로 사용합니다.
이를 활용해서 콜백 함수 내에서 this 를 유지 할 수 있습니다.

const obj = {
    name: 'Alice',
    sayName:() => {
        console.log(this.name);
    }
};

function getName(callback) {
    callback();
}

// sayName을 호출하며 this를 obj로 지정
getName(obj.sayName()); // "Alice"

마무리


이렇게 콜백 함수의 사용 이유와 방법을 알아보았습니다.
하지만 이렇게 콜백 함수를 활용하다 보면, 코드가 복잡해지고, 가독성이 떨어지는
콜백 지옥(Callback Hell) 에 빠질 수 있습니다.

다음 시간에는 Promise 가 어떻게 콜백 지옥 문제를 해결했는지를 알아보겠습니다.

참고자료

모던 자바스크립트 Deep Dive
https://f-lab.kr/insight/mastering-javascript-this
https://ko.wikipedia.org/wiki/%EC%9D%BC%EA%B8%89_%EA%B0%9D%EC%B2%B4
https://www.freecodecamp.org/korean/news/https-www-freecodecamp-org-news-javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/
https://developer.mozilla.org/ko/docs/Glossary/Callback_function
https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%BD%9C%EB%B0%B1-%ED%95%A8%EC%88%98
https://www.youtube.com/watch?v=-iZlNnTGotk

profile
꾸준히 한걸음씩

0개의 댓글