콜백 함수는 다른 함수에 인수로 전달된 함수이며 외부 함수 내부에서 호출되어 일종의 루틴이나 작업을 완료합니다.
(모든 예시는 node.js 환경에서 nodemon으로 실행하였습니다.)
//With Function name
function introduce(name) {
console.log("My name is "+name)
}
function greeting(callback) {
console.log("Hello World!");
callback;
}
greeting(introduce("Roothyo"))
//Hello World!
//My name is Roothyo
// Without Function name
let numbers = [1,2,3,4,5]
numbers.forEach(function(x){
console.log(x*2);
})
// 2
// 4
// 6
// 8
// 10
위 예시들은 즉시 실행되는 동기식 콜백입니다. forEach 함수의 경우 함수 안에 익명의 함수를 넣어서 forEach문을 동작시키는 등의 코드를 자주 다루는 것을 볼 수 있습니다. 또한 함수를 콜백함수로 사용할 경우, 함수의 이름만 넘겨주는 것도 가능합니다. 위의 예제에서 함수를 인자로 사용할 때, 호출할 때, ()를 붙일 필요가 없는 것을 볼 수 있습니다.
콜백함수에서 this
this는 실행 객체와 연관된 특별한 객체이므로 Context Object라고 부를 수 있습니다.
어떤 객체든 Context의 값으로 this를 사용하는데, 콜백함수에서 사용할 때 문제가 됩니다.
let userData = {
signUp : '2020-10-06 15:00:00',
id : 'roothyo',
name: 'Not Set',
setName : function(first, last) {
this.name = first+' '+last;
}
}
function getUserName(first, last, callback) {
callback(first, last);
}
getUserName('Kang', "Hyogeun", userData.setName);
console.log(`1 : `, userData.name);
console.log(`2 : `, global.name);
// 1 : Not Set
// 2 : Kang Hyogeun
client 상에서는 this는 window 전역변수에 저장이 되지만, node 환경에서 this는 *global 전역변수에 저장됩니다.
(* 전역환경에서의 this는 module.exports고 함수 선언문 안의 this는 global 입니다. )
첫번째 나와야할 값으로 kang Hyogeun을 기대했지만, Not Set이 출력되는 것을 볼 수 있습니다. 따라서 this를 보호 할 수 있는 콜백함수가 필요합니다.
call()과 apply()를 사용하여 this를 보호할 수 있다. 이는 함수 바인딩과 관련된 내용으로 다음 글에 이야기하겠습니다.
- call() : 첫 번째 인자로 this 객체 사용, 나머지 인자들은 , 로 구분
- apply() : 첫 번째 인자로 this 객체 사용, 나머지 인자들은 배열 형태로 전달
콜백지옥(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!!');
})
})
})
-> 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!!');
})
})
})
-> Promise는 정상 수행 후 resolve, 실패 하면 reject가 실행됩니다. callback을 사용했던 것과 마찬가지로 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!!
-> Promise의 return을 사용하여 Promise Hell을 탈출할 수 있습니다. 프로미스를 사용하면 비동기 메소드에서 마치 동기 메소드처럼 값을 반환할 수 있습니다. 즉 resolve를 통해 전달 받은 값을 반환하며, 사용해야 합니다.
callback 함수의 대한 공부를 하면서 차근차근 알아보니 연쇄적으로 궁금해지는 것들이 많아지는 것이 사실이다.
물론 많은 글들이 정리가 잘 되어 있지만, 흐름을 직접 정리하는 것이 확실히 공부하는데 도움이 되는 것 같다.
다음 포스팅은 변수의 유효범위와 Closure에 대해 이야기하고, 이 후는 Promise에 대해 정리하고자 한다.
[출처 : https://developer.mozilla.org/en-US/docs/Glossary/Callback_function]
[참조 : https://www.zerocho.com/category/NodeJS/post/5b67e8607bbbd3001b43fd7b]
'Programming Language > JavaScript' 카테고리의 다른 글
JavaScript - 가비지 컬렉션(Garbage Collection) (0) | 2022.07.08 |
---|---|
JavaScript - 클로저(Closure) (0) | 2022.07.07 |
JavaScript - 변수(Variable) (0) | 2022.07.07 |
JavaScript - 객체(Object) (0) | 2022.07.05 |
JavaScript - 함수 (0) | 2022.07.04 |