본문으로 바로가기

콜백함수

category 웹 개념/javascript 2022. 4. 20. 20:22

콜백함수(Callback Function) 란?

파라미터로 함수를 전달하는 함수

 

콜백함수(Callback Function)란 파라미터로 함수를 전달받아, 함수의 내부에서 실행하는 함수이다.

let number = [1, 2, 3, 4, 5];

number.forEach(x => {
    console.log(x * 2);
});

<output>
2
4
6
8
10

 

콜백함수는 이미 우리의 코드 속에서 자주 사용되고 있다.예를 들어, forEach 함수의 경우 함수 안에 익명의 함수를 넣어서 forEach 문을 동작시킨다.

 

콜백함수(Callback Function) 사용 원칙

익명의 함수 사용

let number = [1, 2, 3, 4, 5];

number.forEach(function(x) {
    console.log(x * 2);
});

위의 예제를 화살표 함수에서 일반 함수로 바꾼 예제이다.콜백함수는 이름이 없는 '익명의 함수'를 사용한다. 함수의 내부에서 실행되기 때문에 이름을 붙이지 않아도 된다.

 

함수의 이름(만) 넘기기

function whatYourName(name, callback) {
    console.log('name: ', name);
    callback();
}

function finishFunc() {
    console.log('finish function');
}

whatYourName('miniddo', finishFunc);

<output>
name: miniddo
finish function
[ TIP ]

JavaScript Data Type
- number, string, boolean, object(function, array, data, regexp), null, undefined

자바스크립트는 null과 undefined 타입을 제외하고 모든 것을 객체로 다룬다.

함수를 변수 or 다른 함수의 변수처럼 사용할 수 있다. 함수를 콜백함수로 사용할 경우, 함수의 이름만 넘겨주면 된다.

위의 예제에서, 함수를 인자로 사용할 때 callback, finishFunc 처럼 () 를 붙일 필요가 없다는 것이다.

 

 

전역변수, 지역변수 콜백함수의 파라미터로 전달 가능

  • 전역변수(Global Variable) : 함수 외부에서 선언된 변수
  • 지역변수(Local Variable) : 함수 내부에서 선언된 변수
let fruit = 'apple';	// Global Variable

function callbackFunc(callback) {
    let vegetable = 'tomato';	// Local Variable
    callback(vegetable);
}

function eat(vegetable) {
    console.log(`fruit: ${fruit} / vegetable: ${vegetable}`);
}

callbackFunc(eat);

<output>
fruit: apple / vegetable: tomato

 

 

콜백함수(Callback Function) 주의할 점

this를 사용한 콜백함수

let userData = {
    signUp: '2020-10-06 15:00:00',
    id: 'minidoo',
    name: 'Not Set',
    setName: function(firstName, lastName) {
        this.name = firstName + ' ' + lastName;
    }
}

function getUserName(firstName, lastName, callback) {
    callback(firstName, lastName);
}

getUserName('PARK', 'MINIDDO', userData.setName);

console.log('1: ', userData.name);
console.log('2: ', window.name);

<output>
1: Not Set
2: PARK MINIDDO

우리는 첫 번째 콘솔의 값이 PAKR MINIDDO 이기를 기대했지만, Not Set이 출력된다.

setName() 함수가 실행되기 전의 name 값이 나오는 것인데, 이는 getUserName() 이 전역 함수이기 때문이다.

 

즉, setName()에서 사용된 this 객체가 window라는 글로벌 객체를 가리킨다.

따라서 this를 보호할 수 있도록 콜백함수를 만들어야 한다.

해결 방안 : call()과 apply()를 사용하여 this를 보호할 수 있다.

  • call() : 첫 번째 인자로 this 객체 사용, 나머지 인자들은 , 로 구분
  • apply() : 첫 번째 인자로 this 객체 사용, 나머지 인자들은 배열 형태로 전달
// call

...

function getUserName(firstName, lastName, callback, obj) {
    callback.call(obj, firstName, lastName);	- (1)
}

getUserName('PARK', 'MINIDDO', userData.setName, userData);	- (2)

console.log(userData.name);

<output>
PARK MINIDDO

( 2 ) 에서 마지막 인자에 담긴 userData 는 ( 1 )에서 call 함수의 첫번째 인자로 전달된다.

 

즉, call() 에 의해서 userData에 this 객체가 매핑된다.

apply() 도 인자를 배열로 전달한다는 점만 다르고 동일하게 작동한다.

// apply

...

function getUserName(firstName, lastName, callback, obj) {
    callback.apply(obj, [firstName, lastName]);
}

getUserName('PARK', 'MINIDDO', userData.setName, userData);

console.log(userData.name);

<output>
PARK MINIDDO

 

콜백지옥 (Callback Hell)

비동기 호출이 자주 일어나는 프로그램의 경우 '콜백 지옥'이 발생한다.함수의 매개변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기 수준이 감당하기 힘들어질 정도로 깊어지는 현상이다.

function add(x, callback) {
    let sum = x + x;
    console.log(sum);
    callback(sum);
}

add(2, function(result) {
    add(result, function(result) {
        add(result, function(result) {
            console.log('finish!!');
        })
    })
})

<output>
4
8
16
finish!!

 

해결 방안 : Promise를 사용하여 콜백지옥을 탈출할 수 있다.

function add(x) {
    return new Promise((resolve, reject) => {
        let sum = x + x;
        console.log(sum);
        resolve(sum);
    })
}

add(2).then(result => {
    add(result).then(result => {
        add(result).then(result => {
            console.log('finish!!');
        })
    })
})

<output>
4
8
16
finish!!

 

Promise 는 정상 수행 후 resolve, 실패 후 reject 가 실행된다.

callback을 사용했던 것과 마찬가지로 resolve에 값을 담아 전달한다.

하지만, 이 패턴도 그리 좋은 방법은 아니다. 결국 콜백지옥처럼 들여쓰기 수준을 감당하기 힘들어진다.

해결 방안 : Promise의 return 사용하여 Promise Hell을 탈출할 수 있다.

프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다.

다만 최종 결과를 반환하지는 않고, 대신 프로미스를 반환해서 미래의 어떤 시점에 결과를 제공합니다.

MDN 에서 정의하고 있는 Promise에 대한 설명이다.프로미스는 비동기 호출 시, 마치 동기 호출 처럼 값을 반환할 수 있다는 문구에 집중해보자.

 

즉, resolve를 통해 전달 받은 값을 반환하여 사용해야 한다!

function add(x) {
    return new Promise((resolve, reject) => {
        let sum = x + x;
        console.log(sum);
        resolve(sum);
    })
}

add(2).then(result => {
    return add(result);
}).then(result => {
    return add(result);
}).then(result => {
    console.log('finish!!');
})

<output>
4
8
16
finish!!

 

 

출처:https://velog.io/@minidoo/%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%98Callback-Function

'웹 개념 > javascript' 카테고리의 다른 글

for await of  (0) 2022.04.24
try/catch  (0) 2022.04.24
super  (0) 2022.04.20
arguments 객체  (0) 2022.04.20
extends  (0) 2022.04.20