[코어 자바스크립트]4. 콜백함수

Donghun Seol·2022년 11월 24일
0

코어자바스크립트

목록 보기
4/7

콜백함수

콜백함수란?

콜백함수란 다른함수의 인자로 전달되는 함수다. 콜백함수를 넘겨 받은 함수는 콜백함수를 자신만의 로직에 따라 실행한다. 따라서 콜백을 인자로 전달하는 것은 콜백의 제어권을 개발자로부터 다른 함수로 넘겨주는 것이다.

제어권

호출시점

콜백을 받는 setInterval 함수를 통해 콜백함수의 동작을 파악 가능하다. 먼저 함수 시그니처를 살펴보자

var intervalID = setInterval(callback, delay [, param1, param2, ...]);

함수의 반환값은 clearInterval()의 인자로 활용되는 id다. 콜백과, 인터벌 딜레이를 필수인자로 받고 params를 선택인자로 받는데, params는 콜백에 들어가는 인자를 의미한다. setInterval 함수가 콜백으로 전달되는 함수의 호출 시점과, 호출맥락(인자값)을 결정하는 것이다. 다시 말하면, 콜백함수의 제어권을 setInterval함수에 위임한 것이다.

간단한 setInterval(), clearInterval()을 활용한 코드다.

var count = 0;
var cbFunc = function () {
  console.log(count);
  if (++count > 4) clearInterval(timer);
};

var timer = setInterval(cbFunc, 1000);

전달 인자

콜백함수를 인자로 받는 고차함수에 콜백을 적용하려면 고차함수의 규칙에 따라서 콜백을 작성하고, 설계해야한다. Array.prototype.map(cb [, thisArg])의 의 콜백함수 시그니쳐는 함수 스펙에서 미리 정해져 있고 다음과 같다. callback: function(currentValue, index, array) 따라서 map에 위임할 콜백은 map에서 정한 규격을 따라야만 정상적으로 작동된다.

콜백함수에서의 this

콜백함수도 '함수'이므로 함수의 실행컨텍스트가 형성될때 this 바인딩을 수행하는 js엔진의 기본적인 규칙을 따른다. 따라서 별도로 this를 지정하지 않으면 전역객체가 this에 바인딩 된다.(화살표 함수는 예외). 이러한 동작방식은 Array.prototype.myMap을 직접 구현해보면 이해가 쉽다.

Array.prototype.myMap = function (callback, thisArg) {
  var = mappedArr = [];
  for (var i = 0; i < this.length; i++) {
    var mappedVal = callback.call(thisArgs || window, this[i], i, this);
    mappedArr[i] = mappedVal;
  }
  return mappedArr;
};

주목할 것은 4행의 callback.call에서 인자로 전달된 this다. 여기서 this는 Array 인스턴스를 의미하는데, myMap이라는 함수는 해당 인스턴스의 메소드로 호출되었으므로, 실행컨텍스트 생성시 해당 인스턴스 자체가 this에 바인딩된 것이다. 게다가 4행에서는 콜백함수가 3개의 인자만을 받을 수 있도록 지정하기도 한다.

브라우저 api의 addEventListener는 내부적으로 call 메서드를 활용해서 this 바인딩을 해준다. 때문에 콜백함수의 this(이벤트를 일으킨 엘리먼트)가 의도에 맞게 잘 동작한다.

콜백함수는 "함수"다.

고차함수에 콜백함수로 객체의 메서드를 전달해 주어도 "함수"로 동작한다는 의미다.
메서드를 함수로 호출하면 this 바인딩이 새롭게 수행되므로(전역객체가 this에 바인딩 된다) 개발자가 의도하지 않게 동작할 수 있다. 이런 문제가 생긴다는 점은 잘 이해했다. 그럼 어떻게 해결할까?

콜백 함수 내부의 this에 다른 값 바인딩하기

Arrow Function에 .bind() 메서드 적용하기

화살표 함수에는 바인드 메서드로 바인딩 그냥 안된다. 화살표 함수의 스펙이 그렇게 되어 있다. 고민하다 찾아보니 답이 나와있었다. 출처

bind 메서드 활용

객체 메서드 자체에 정적으로 this를 지정해주는 방법이나, call을 활용하는 방법도 있지만, 가장 간단하고 가독성 좋은 방법은 ES5부터 나온 bind메서드를 활용하는 것이다.

바인딩한 형태로 함수를 호출하면 해당 함수를 특정 객체의 메서드로서 작동시킬 수 있다.

var obj1 = {
  name: 'obj',
  func: function () {
    console.log(this.name);
  }
};

setTimeout(obj1.func.bind(obj1), 1000);

var obj2 = {name: 'obj2'}
setTimeout(obj1.func.bind(obj2), 2000);

혼란스러운 개념이었는데, 천천히 코드를 작성하면서, 글을 되새기며 학습하니 일단은 이해된다. 😎

콜백지옥과 비동기제어

JS를 학습하면서 많이 접했던 개념이다. 이번 기회에 아는것과 모르는 내용을 확실히 파악하고 넘어가보자.
자바스크립트는 대부분 웹환경에서 활용되고, 모던웹에는 비동기적으로 동작하는 코드들이 많다. setTimout, fetch, axios, addEventListener, DB query, fs 등등 일상적으로 활용되는 모듈들은 대부분 비동기로 동작한다.

콜백지옥

콜백지옥을 가장 일차원적으로 해결하는 방법은 사실 모든 함수를 기명함수로 작성하는 것이다. 하지만 작성이 귀찮고, 명명은 더더욱 힘들다. 따라서 다른 해결책이 필요하다. ES6 에서는 Promise, Generator가 도입되었고, ES7 에서는 async/await이 도입되어 이러한 문제를 해결한다.

코드로 살펴보자.

promise와 클로저를 활용해서 짧게 작성된 코드.
아직 이런코드를 작성하기는 어렵겠는데... 계속 학습해보자.

var addCoffee = function (name) {
  return function(prevName) {
    return new Promise(function (resolve) {
      setTimeout(function () {
        var newName = prevName ? (prevName + ', ' + name) : name;
        console.log(prevName);
        console.log(newName);
        reslove(newName);
      }, 500);
    });
  };
};

addCoffee('에스프레소')()
  .then(addCoffee('아메리카노'))
  .then(addCoffee('카페모카'))
  .then(addCoffee('에스프레소'))
  1. addCoffee는 함수인데, name을 인자로 받아서 함수를 반환한다. 함수를 반환하는 함수이므로 addCoffee는 커리함수다.
  2. addCoffee의 반환값은 익명함수다. 해당 익명함수는 prevName을 입력값으로 받아서 프로미즈 객체를 반환한다. 프로미즈 체이닝에서 prevName은 어떻게 전달 되는건가?
  3. 체이닝에서 이전 프로미즈의 resolve된 값을 첫번째 인자로 받는다. 처음에는 addCoffee를 ()로 호출해줬고, .then()에서는 함수를 호출하지 않고 콜백으로 전달한 형태다.
  4. 콜백함수에 들어가는 인자는 고차함수에서 정해주므로 .then이라는 메서드 내부적으로 이전 프라미즈 객체의 resolve된 값을 콜백함수안에 넣어주는 것이다.
  5. 콜백함수는 결국 resolve된 프로미즈를 리턴하므로 닷덴 체이닝을 계속 적용할 수 있는 것🤔🤔 모르겠다.
  6. 반환되는 프로미즈는 0.5초 후에 resolve되고, newName이라는 값을 갖는다. newName은 promise체인의 결괏값이 된다. 모르겠다.🤔🤔

위 함수의 실행과정 설명할수 있도록 더 학습해보자.
혹은 다음장의 클로저를 공부하면 잘 알 수 있을 수도..

profile
I'm going from failure to failure without losing enthusiasm

3개의 댓글

comment-user-thumbnail
2022년 11월 24일

이야ㅋㅋㅋ 저도 자바스크립트 제대로 시작하려 했을 때 이 책 봤어요! 진짜 책 잘 써졌습니다.

1개의 답글