본문 바로가기
내맘대로 개념정리

콜백 함수(Callback Function)

by 강물둘기 2022. 12. 31.

1. 콜백 함수란?

콜백 함수는 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 말한다. 함수안에서 실행되는 또다른 함수라고 할 수 있다. JS에서 함수는 일급객체이기 때문에 다른 함수의 매개변수로 등록할 수 있다.

function repeat(n, f) {
  for (var i = 0; i < n; i++) {
    f(i); // i를 전달하면서 f를 호출
  }
}
// callback으로 등록될 함수
const logAll = function (i) {
  console.log(i);
};

repeat(5, logAll); // 0 1 2 3 4

 

2. 콜백 함수의 사용

⓵ 내장 매서드

const nums = [1, 2, 3, 4, 5, 6];

const result = nums.filter(num => num > 3);
// 조건을 만족하는 요소를 추출해 새로운 배열 리턴

console.log(result);	// [4, 5, 6]

const initialValue = 0;
const sum = nums.reduce(
  (accumulator, currentValue) => accumulator + currentValue, initialValue
  );
// 배열 내 모든 수를 더하는 콜백함수를 사용, accumulator를 return 한다. 

console.log(sum);

 

⓶ 고차 함수

고차 함수란 외부에서 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 말한다.

const nums = [1, 2, 3, 4, 5, 6];

// filter 매서드를 직접 만들어보자
function myfilter(array, callback){
	let result = [];
    for(let i=0; i<array.length; i++){
    	let current = array[i];
        if(callback(current)){
        	result.push(current)
         }
     }
     return result;
}

const newArr = myfilter(nums, num => num > 3);
console.log(newArr);	// [4,5,6]

 

⓷ 이벤트 처리

// 콜백으로 등록될 함수
function onClick1() {
  alert(' clicked!');
}
// addEventListener의 두번째 인자로 콜백 함수를 등록한다.
document.getElementById('clickMe').addEventListener('click', onClick1);

 

⓸ 비동기 처리

비동기 함수를 사용하는 경우 예상과는 다르게 결과가 나오는 경우가 있다.

function findUser(id) {
  let user;
  setTimeout(function () {
    console.log("wait 1 sec.");
    user = `${id}`;
  }, 1000);
  return user;
}

const user = findUser('kim');
console.log("user :", user);
// 실행 결과  	user : undefined 
//		wait 1 sec.

콜백 함수를 이용하면 이러한 비동기 처리를 제대로 할 수 있다.

function findUserAndCallBack(id, cb) {
  setTimeout(function () {
    console.log("wait 1 sec.");
    const user = `${id}`;
    cb(user);
  }, 1000);
}

findUserAndCallBack("kim", function (user) {
  console.log("user:", user);
});
// 실행 결과 	wait 1 sec.
//		user:kim

 

3. 콜백 함수의 장점과 단점

 장점

 ⓵ 함수 외부에서 주입하기 때문에 자유로운 교체가 가능하다. (코드의 가변성이 높아진다.)

 ⓶ 함수를 굳이 정의하지 않고 익명 함수로도 전달 가능하다.(메모리 측면에서 유리)

 ⓷ 비동기(Asynchronous) 처리 방식의 문제점을 해결할 수 있다.

 

단점

⓵ 콜백 함수를 너무 남용하면 코드의 가독성이 떨어진다.(콜백 지옥)

⓶ 에러 처리가 어렵다.

 

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

⓵ 인자로 넘길때 소괄호 넣으면 안된다.

⓶  this를 보호할 수 있도록 콜백함수를 만들어야 한다. (call, apply 등을 통해 보호)

const user = {
  name: null,
  setName : function(name){
    this.name = name
  }
}

// apply 사용 예제 	apply() : 첫 번째 인자로 this 객체 사용, 나머지 인자들은 배열 형태로 전달
function setUserName(name,callback,thisObj){
  callback.apply(thisObj,[name])
}

setUserName('IU',user.setName,user)
console.log(user.name) //'IU'

//call 사용 예제 		call() : 첫 번째 인자로 this 객체 사용, 나머지 인자들은 , 로 구분
function setUserName(name,callback,thisObj){
  callback.call(thisObj,name)
}

setUserName('IU',user.setName,user)
console.log(user.name) //'IU'

 

⓷ 콜백 지옥에 빠지지 않도록 해야한다.( 주로 비동기 처리에서 발생한다.)

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function errorFunction() {
  console.log('재고가 없습니다.');
}

function getChicken(callback, errorFunction) {
  if (callback) {
    setTimeout(() => {
      console.log('동묘시장 -> chicken');
      callback();
    }, randomTime());
  } else {
    errorFunction();
  }
}

function getEgg(callback, errorFunction) {
  if (callback) {
    setTimeout(() => {
      console.log(`동묘시장 -> chicken -> egg`);
      callback();
    }, randomTime());
  } else {
    errorFunction();
  }
}

function getMeal(callback, errorFunction) {
  if (callback) {
    setTimeout(() => {
      console.log(`동묘시장 -> chicken -> egg -> fried egg`);
      callback();
    }, randomTime());
  } else {
    errorFunction();
  }
}

getChicken(() => {
  getEgg(() => {
    getMeal(() => {}, errorFunction);
  }, errorFunction);
}, errorFunction);

 

ES6에서 추가된 promise, ES2017에서 추가된 async/await을 이용하면 콜백 지옥을 예방할 수 있다.

function randomTime() {
  return Math.floor(Math.random() * 10) * 1000;
}

function errorFunction() {
  console.log('재고가 없습니다.');
}

function getChicken() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('동묘시장 -> chicken');
      resolve(getEgg);
    }, randomTime());
  });
}

function getEgg() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('동묘시장 -> chicken -> egg');
      resolve(getMeal);
    }, randomTime());
  });
}

function getMeal() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('동묘시장 -> chicken -> egg -> fried egg');
    }, randomTime());
  });
}

getChicken()
  .then((data) => data())
  .then((data) => data())
  .then((data) => console.log(data));

 

 

Reference

- https://kong-dev.tistory.com/120

- https://im-designloper.tistory.com/26

- https://www.daleseo.com/js-async-callback/

- https://www.youtube.com/watch?v=TAyLeIj1hMc&t=6s

- 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://yoo11052.tistory.com/153

댓글