어떤 개념을 배울 때 이 개념이 무엇인지 왜 필요한지 어떻게 사용하는지 정리되지 않는다면 제대로 이해하기는 힘들다.
나에게는 callback 함수가 그러했다.
callback 함수가 무엇인지는 대충은 알았고 어떻게 사용하는지 또한 알고 있었지만
정확히 무엇인지 왜 필요한지는 알지 못 했다.
이번 기회에 pormise 를 공부하기 이전에 callback 함수도 확실히 정리하고 가려한다.
콜백함수
다른 함수에 인자로 전달되는 함수를 콜백 (callback) 함수 라고 한다.
좀 더 자세히 보면
다른 코드 (함수 또는 메서드) 에게 인자로 넘겨주면서 제어권도 함께 위임하고, callback 함수를 위임받은 코드는 내부 로직에 의해 callback 함수를 적절한 시점에 실행한다.
JS 에서 함수는 1급 객체 이므로, 인자 전달 시 함수를 전달할 수 있기 때문에 콜백 callback 함수가 가능하다.
※ 1급 객체
- 변수나 데이터 구조 안에 담을 수 있다.
- 파라미터로 전달 할 수 있다.
- 반환값으로 사용 할 수 있다.
- 런타임에 생성될 수 있다.
1급 객체 한 줄 요약 : 값 으로써 사용할 수 있다는 의미
코드
function fnA(callback){
console.log('A')
callback()
}
function testCallback(){
console.log('callback!!')
}
fnA(testCallback);
//결과값
// A
// callback!!
- 간단하게 함수를 인자로 던져버리는 형태이다
비동기 처리와 callback 함수
사실 이 부분을 정리하는데 많은 시간을 썼고 내용을 정리해야겠다 마음먹은 부분이었다.
만약 A 와 B 라는 작업을 순서대로 처리 해야 한다면 두 작업을 같이 처리할 때 즉 비동기 처리를 할 때 오류가 발생될 것이다.
function fnA(){
let result = '초기값';
setTimeout(()=>{result = 'fnA'},1000)
return result;
}
console.log(fnA())
// 결과
// 초기값
- 위 코드는 비동기 처리의 대표적인 setTimeout 메서드를 활용한 예시로,파라미터로 callback 함수와 지연시간을 받아서 지연시간만큼 뒤에 callback 함수를 실행하게 된다.
- 3번째 줄에서 result 값을 정의 바꾸기 전 다음 줄로 넘어가 result 의 값은 바뀌지 않은 채 '초기값' 으로 return 된다.
비동기 처리 오류를 callback 함수로 간단하게 해결 할 수 있다.
function fnB(callback){
let result = '';
setTimeout(()=>{result = 'fnB';callback(result)},1000)
}
function testCallback(text){
console.log(text);
}
fnB(testCallback)
// 결과
// fnB
- 코드가 단순해서 callback 함수의 필요성을 느끼기 힘들 테지만 우리는 원하는 타이밍에 전달 받은 함수로 값을 처리했다 그리고 자연스럽게 오류 또한 해결되었다.
- 이 부분이 위에 장황하게 써놓은 callback함수의 개념과 의의를 이해할 수 있는 부분이 아닐까 싶다.
callback 지옥
여러 비동기 작업을 연달아 처리하기 위해 callback 함수를 난사한다면 함수 안에 함수가 생기는 것이기에 한없이 들여쓰기가 되는 모습을 볼 수 있다.
이해를 돕기위해 사과 4개를 사고 끼니마다 하나씩 먹기 위해 3개를 씻고 점심 도시락에 하나 싸고 아침에 하나 먹기 위해 2개를 자르고 1개를 먹으며 출근하는 상황을 가정해 보았다.
function buyApple(appleCount,callback){
console.log(`사과를 ${appleCount} 개를 샀습니다.`)
callback(appleCount)
}
function wash(boughtApple,callback){
console.log(`${boughtApple - 1} 개를 씻었습니다.`)
callback(boughtApple - 1)
}
function cut(washedApple,callback){
console.log(`${washedApple - 1} 개를 잘랐습니다.`)
callback(washedApple - 1)
}
function eat(cutApple){
console.log(`${cutApple - 1} 개를 먹었습니다.`)
}
buyApple( 4 ,(boughtApple)=>{
wash(boughtApple,(washedApple)=>{
cut(washedApple,(cutApple)=>{
eat(cutApple)
})
})
})
// 결과 값
// 사과를 4 개를 샀습니다.
// 3 개를 씻었습니다.
// 2 개를 잘랐습니다.
// 1 개를 먹었습니다.
- 콜백함수가 많아질수록 코드의 가독성이 줄어들게 된다.
코딩패턴을 바꾸어서 콜백지옥을 탈출한 코드
function buyApple(appleCount){
console.log(`사과를 ${appleCount} 개를 샀습니다.`)
wash(appleCount)
}
function wash(boughtApple){
console.log(`${boughtApple - 1} 개를 씻었습니다.`)
cut(boughtApple - 1)
}
function cut(washedApple){
console.log(`${washedApple - 1} 개를 잘랐습니다.`)
eat(washedApple - 1)
}
function eat(cutApple){
console.log(`${cutApple - 1} 개를 먹었습니다.`)
}
buyApple(4)
// 결과 값
// 사과를 4 개를 샀습니다.
// 3 개를 씻었습니다.
// 2 개를 잘랐습니다.
// 1 개를 먹었습니다.
- 코드의 가독성은 좋아졌지만 만약 중간과정을 생략해야하는 상황이 온다면 코드를 다시 사용하기 불편할 것이다. (유연성 및 확장성이 떨어짐)
여기서 멈추지 않고 우리는 조금 더 효율적이고 새로운 방법을 배워 보려 한다.
-promise 도 함께 정리하려 했지만 글이 길어진 관계로 다음장에 >>
참고자료
https://coding-restaurant.tistory.com/210
https://solveaproblem.dev/javascript-callback-function/
https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/
'JS' 카테고리의 다른 글
JavaScript Set (0) | 2023.02.14 |
---|---|
유사 배열 객체와 반복 작업 (0) | 2022.08.26 |
웹팩과 번들러 (0) | 2022.04.12 |
promise 와 async & await (0) | 2022.03.02 |
전개연산자 (Spread Opertor) (0) | 2022.02.13 |