3. 콜백함수

Joy·2023년 11월 23일
0

📍 콜백함수

콜백 함수는 다른 코드의 인자로 넘겨 주는 함수입니다. 어떤 함수 X를 호출하면서 인자로 콜백 함수 Y를 넘겨 주었다고 하면, 함수 X는 특정 조건일 때 Y를 호출하게 됩니다. 이처럼 콜백 함수는 다른 코드에게 인자로 넘겨줌으로써 그 제어권을 함께 위임한 함수입니다. 콜백 함수를 위임받은 코드는 자체적인 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행합니다.

콜백 함수는 함수다

콜백 함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출됩니다.

var obj = {
	vals: [1, 2, 3],
        logValues: function(v, i) {
		console.log(this, v, i);
        }
};

// (1)
obj.logValues(1, 2); // { vals: 1, 2, 3], logValues: f} 1 2
// (2)
[4, 5, 6].forEach(obj.logValues); // Window { ... } 4 0

(1)번의 경우 logValues 메서드의 이름 앞에 점이 있으니 메서드로서 호출했습니다.

(2)번의 경우 obj를 this로 하는 메서드를 그대로 전달한 것이 아니라, obj.logValues가 가리키는 함수만 전달했습니다. 이 함수는 메서드로서 호출할 때가 아닌 한 obj와의 직접적인 연관이 없어집니다. 따라서 this는 다른 콜백 함수들과 같이 전역 객체를 바라보게 됩니다.

콜백 지옥과 비동기 제어

동기적인 코드는 현재 실행 중인 코드가 완료된 후에야 다음 코드를 실행하는 방식입니다.

반대로 비동기적인 코드는 현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어갑니다.

사용자의 요청에 의해 특정 시간이 경과되기 전까지 어떤 함수의 실행을 보류한다거나(setTimeout), 사용자의 직접적인 개입이 있을 때 비로소 어떤 함수를 실행하도록 대기한다거나(addEventListener), 웹브라우저 자체가 아닌 별도의 대상에 무언가를 요청하고 그에 대한 응답이 왔을 때 비로소 어떤 함수를 실행하도록 대기하는 등(XMLHttpRequest),

별도의 요청, 실행 대기, 보류 등과 관련된 코드는 비동기적인 코드입니다.

콜백 지옥은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상으로, 자바스크립트에서 흔히 발생하는 문제입니다.

자바스크립트 진영은 비동기적인 일련의 작업을 동기적으로, 혹은 동기적인 것처럼 보이게끔 처리해 주는 장치를 마련하고자 끊임없이 노력했습니다.

  1. Promise

new 연산과 함께 호출한 Promise 객체의 인자로 넘겨 주는 콜백 함수는 호출할 때 바로 실행되지만, 그 내부에 resolve 또는 reject 함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 then 또는 catch 구문으로 넘어가지 않습니다.

  1. Generator

*이 붙은 Generator 함수를 사용할 수 있습니다. Generator 함수를 실행하면 Iterator가 반환되는데, Iterator는 next라는 메서드를 가지고 있습니다. 이 next 메서드를 호출하면 Generator 함수 내부에서 가장 먼저 등장하는 yield에서 함수의 실행을 멈춥니다. 그러니까 비동기 작업이 완료되는 시점마다 next 메서드를 호출해 준다면 Generator 함수 내부의 소스가 위에서부터 아래로 순차적으로 진행될 것입니다.

  1. async / await

비동기 작업을 수행하고자 하는 함수 앞에 async를 표기하고 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 표기하는 것만으로도 뒤의 내용을 Promise로 자동 전환하고, 해당 내용이 resolve 된 이후에야 다음으로 진행합니다.

export const test1 = async() => {
	let value = await 1
	console.log(value) // 1
	value = await Promise.resolve(1)
	console.log(value) // 1
}

export async function test() {
	let value = await 'hello'
	console.log(value) // hello
	value = await Promise.resolve('hello')
	console.log(value) //hello
}

// anync 함수를 일반 함수처럼 사용한 예
// 두 함수가 마치 동시에 실행된 것처럼 보인다.
test1()
test2() // 실행 결과 1 \ hello \ 1 \ hello

// async함수를 Promise 객체로 사용한 예
test1()
	.then(() => test2()) // 실행 결과 1 \ 1 \ hello \ hello

1 콜백 함수란?
콜백함수

다른 코드의 인자로 넘겨주는 함수
콜백 함수를 넘겨받은 코드는 이 콜백 함수를 필요에 따라 적절한 시점에 실행함
다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수
콜백 함수를 위임받은 코드는 자체적인 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행함
callback

'되돌아 호출해달라'는 명령
어떤 함수 X를 호출하면서 '특정 조건일 때 함수 Y를 실행해서 나에게 알려달라'는 요청을 함께 보냄
함수 X 입장에서는 해당 조건이 갖춰졌는지 여부를 스스로 판단하고 Y를 직접 호출
→ 그럼 Y가 콜백 함수인 건가?
2 제어권
2-1 호출 시점
더보기
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가짐

2-2 인자
map 메서드

첫 번째 인자: callback 함수를 받음
생략 가능한 두 번째 인자: 콜백 함수 내부에서 this로 인식할 대상 특정이 가능
map의 callback 함수 인자

첫 번째 인자: 배열의 요소 중 현재값
두 번째 인자: 현재값의 인덱스
세 번째 인자: map 메서드의 대상이 되는 배열 자체
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수를 호출할 때 인자에 어떤 값들을 어떤 순서로 넘길 것인지에 대한 제어권을 가짐

2-3 this
이해가 잘 안됨 ㅜ

3 콜백 함수는 함수다
콜백 함수는 함수다. 콜백 함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출된다.

** 메서드 : 객체의 속성값으로 담겨진 함수

어떤 함수의 인자에 객체의 메서드를 전달하더라도 이는 결국 메서드가 아닌 함수일 뿐이다.

접기
var obj = {
vals: [1, 2, 3],
logValues: function(v, i){
console.log(this, v, i);
}
};
obj.logValues(1, 2); // {vals: [1, 2, 3], logValues: f} 1 2
[4, 5, 6].forEach(obj.logValues); // Window {...} 4 0
// Window {...} 5 1
// Window {...} 6 2
logValues 메서드를 forEach 함수의 콜백 함수로서 전달함.

obj를 this로 하는 메서드를 그대로 전달한 것이 아니라, obj.logValues가 가리키는 함수만 전달한 것이다.

--> 메서드로서 호출할 때가 아닌 한 obj와 직접적인 연관이 없어진다.

4 콜백 함수 내부의 this에 다른 값 바인딩하기
객체의 메서드를 콜백 함수로 전달하면 해당 객체를 this로 바라볼 수 없게 된다.

그럼에도 콜백 함수 내부에서 this가 객체를 바라보게 하고 싶은 경우, 전통적으로 this를 다른 변수에 담아 콜백 함수로 활용할 함수에서는 this 대신 그 변수를 사용하게 하고, 이를 클로저로 만드는 방식이 많이 쓰임

위 방식에는 아쉬움이 있기 때문에, ES5의 bind 메서드를 이용하면 된다.

5 콜백 지옥과 비동기 제어
콜백 지옥: 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상

동기: 현재 실행 중인 코드가 완료된 후에야 다음 코드를 실행하는 방식

비동기: 현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어감

사용자의 요청에 의해 특정 시간이 경과되기 전까지 어떤 함수의 실행을 보류한다거나(setTimeout), 사용자의 직접적인 개입이 있을 때 비로소 어떤 함수를 실행하도록 대기한다거나(addEventListener), 웹브라우저 자체가 아닌 별도의 대상에 무언가를 요청하고 그에 대한 응답이 왔을 때 비로소 어떤 함수를 실행하도록 대기하는 등(XMLHttpRequest), 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 비동기적인 코드이다.

현대 자바스크립트는 웹 복잡도가 높아진 만큼 비동기적인 코드의 비중이 예전보다 훨씬 높아진 상황으로, 콜백 지옥에 빠지기도 훨씬 쉬워졌다.

콜백 지옥 해결 방법

익명의 콜백 함수들을 모두 기명 함수로 전환
ES6의 Promise, Generator
ES2017의 async/await
비동기 작업을 수행하고자 하는 함수 앞에 async를 표기하고, 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 표기
뒤 내용을 Promise로 자동 전환하고, 해당 내용이 resolve된 이후에야 다음으로 진행 → Promise의 then과 흡사

⭐️ 정리 ⭐️

  • 콜백 함수는 다른 코드에 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수이다.

  • 제어권을 넘겨받은 코드는 다음과 같은 제어권을 가진다.
    1) 콜백 함수를 호출하는 시점을 스스로 판단하여 실행
    2) 콜백 함수를 호출할 때 인자로 넘겨줄 값들 및 그 순서가 정해져 있다.
    이 순서를 따르지 않고 코드를 작성하면 엉뚱한 결과를 얻게 됨
    3) 콜백 함수의 this가 무엇을 바라보도록 할지 정하지 않은 경우에는 전역객체를 바라봄.
    사용자 임의로 this를 바꾸고 싶을 경우 bind 메서드를 활용하면 됨

  • 어떤 함수에 인자로 메서드를 전달하도라도 이는 결국 함수로서 실행된다.

  • 비동기 제어를 위해 콜백 함수를 사용하다 보면 콜백 지옥에 빠지기 쉽다.
    이를 해결하기 위해 Promise, Generator, async/await 등의 방법들을 활용한다.

콜백 함수와 비동기 제어

콜백 함수
콜백 함수는 다른 코드에 전달되어 나중에 호출되는 함수
이를 통해 제어권을 전달하며, 다른 코드에서 특정 조건이 충족되었을 때 콜백 함수를 실행

콜백 함수의 특징:
다른 코드에 인자로 전달되어 제어권을 위임하는 함수
호출 시점, 전달되는 인자, 그리고 내부의 this 값은 호출하는 코드에 의해 결정

콜백 함수는 함수이다:
객체의 메서드를 전달하더라도 해당 메서드는 함수로서 호출됩니다.

var obj = {
    vals: [1, 2, 3],
    logValues: function(v, i) {
        console.log(this, v, i);
    }
};

obj.logValues(1, 2);  // {vals: [1, 2, 3], logValues: f} 1 2
[4, 5, 6].forEach(obj.logValues);  // Window {...} 4 0 / Window {...} 5 1 / Window {...} 6 2

객체의 메서드를 콜백 함수로 전달할 때, 메서드 자체가 아닌 함수만 전달되어 this가 변경됩니다.

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

객체의 메서드를 콜백 함수로 전달할 때 this가 변경되어 문제가 발생할 수 있습니다.
이를 해결하기 위해 bind 메서드를 사용하거나, 클로저를 통해 this를 다른 변수에 담아 활용할 수 있습니다.

콜백 함수의 인자:
콜백 함수에 전달되는 인자는 호출하는 코드에 따라 결정됩니다.
예를 들어 forEach 메서드의 경우 첫 번째 인자로는 배열의 각 요소, 두 번째 인자로는 현재 요소의 인덱스, 세 번째 인자로는 배열 자체가 전달됩니다.

콜백 지옥과 비동기 제어
콜백 지옥은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 깊어져 가독성이 떨어지는 상황을 나타냅니다.
이는 주로 비동기 작업을 다룰 때 발생하며, 다음과 같은 방법으로 해결할 수 있습니다.

익명 함수를 기명 함수로 전환:
코드의 가독성을 높이기 위해 익명 함수를 기명 함수로 전환합니다.

Promise:
promise의 ㄴ인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행되지만 그 내부에 resolve, reject함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 다음 또는 오류 구문으로 넘어가지 않는다.
따라서 비동기 작업이 완료될 때 비로소 resolve Ehsms reject를 호출하는 방법으로 비동기 작업의 동기적 표현이 가능함

Promise는 비동기 작업을 조금 더 효과적으로 다룰 수 있도록 해줍니다.
콜백 지옥의 문제를 완화하고 코드를 더 간결하게 작성할 수 있습니다.

Generator:
Generator 함수를 활용하여 비동기 작업을 동기적으로 보이도록 만들 수 있습니다.

async/await:
async 함수를 사용하여 비동기 작업을 수행하고, await를 통해 작업의 완료를 기다릴 수 있습니다.
코드를 동기적으로 작성할 수 있는 장점이 있습니다.

정리
콜백 함수는 제어권을 다른 코드에게 전달하는 함수로, 호출 시점과 인자는 호출하는 코드에 의해 결정됩니다.
콜백 함수로 전달된 메서드는 함수로 호출되며, this 값이 변경될 수 있습니다.
bind 메서드나 클로저를 활용하여 콜백 함수 내부의 this에 다른 값이 바인딩될 수 있습니다.
콜백 지옥은 비동기 작업을 다룰 때 발생하는 들여쓰기 수준이 깊어져 가독성이 떨어지는 문제를 나타냅니다.
해결 방법으로는 익명 함수를 기명 함수로 전환하거나, Promise, Generator, async/await를 활용할 수 있습니다.

콜백 함수와 비동기 제어

콜백 함수란?
콜백 함수는 다른 코드에 전달되어, 나중에 호출되는 함수를 말합니다. 이를 통해 제어권을 전달하며, 특정 조건이 충족되었을 때 콜백 함수를 실행할 수 있습니다.

var obj = {
    vals: [1, 2, 3],
    logValues: function(v, i) {
        console.log(this, v, i);
    }
};

obj.logValues(1, 2);  // {vals: [1, 2, 3], logValues: f} 1 2
[4, 5, 6].forEach(obj.logValues);  // Window {...} 4 0 / Window {...} 5 1 / Window {...} 6 2

이런 식으로 객체의 메서드를 콜백 함수로 전달할 때, 메서드 자체가 아닌 함수만 전달되어 this가 변경될 수 있습니다.

콜백 함수 내부의 this에 다른 값 바인딩하기
그런데, 객체의 메서드를 콜백 함수로 전달할 때 this가 변경되어 문제가 발생할 수 있습니다. 이를 해결하기 위해서는 bind 메서드를 사용하거나, 클로저를 통해 this를 다른 변수에 담아 활용할 수 있습니다.

콜백 함수의 인자
콜백 함수에 전달되는 인자는 호출하는 코드에 따라 결정됩니다. map 메서드의 경우 첫 번째 인자로 배열의 각 요소, 두 번째 인자로 현재 요소의 인덱스, 세 번째 인자로 배열 자체가 전달됩니다.

var numbers = [1, 2, 3, 4, 5];

var squared = numbers.map(function(num, index, array) {
    console.log(num, index, array);
    return num * num;
});

// 콜백 함수에 전달되는 인자 출력 및 결과
// 1 0 [1, 2, 3, 4, 5]
// 2 1 [1, 2, 3, 4, 5]
// 3 2 [1, 2, 3, 4, 5]
// 4 3 [1, 2, 3, 4, 5]
// 5 4 [1, 2, 3, 4, 5]

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

콜백 지옥과 비동기 제어
이렇게 콜백 함수를 다뤄 나가다 보면 "콜백 지옥"이라 불리는 문제가 발생합니다. 이는 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 깊어져 가독성이 떨어지는 상황을 나타냅니다.

해결 방법으로는 익명 함수를 기명 함수로 전환하거나, Promise, Generator, async/await을 활용할 수 있습니다.

마무리
콜백 함수는 자바스크립트에서 강력한 도구 중 하나로, 비동기 작업을 다루는 데에도 중요한 역할을 합니다. 이해하고 적절히 활용한다면 높은 수준의 코드를 작성할 수 있을 것입니다.

이상으로 콜백 함수와 비동기 제어에 대한 더 자세한 설명이었습니다. 궁금한 점이나 추가로 알고 싶은 내용이 있으면 언제든지 물어봐주세요!

profile
🐣

0개의 댓글