안녕하세요, 김현지입니다.
오늘은 콜백 함수에 대해 알아보겠습니다.
공부 중인 프로젝트 코드에 async/await
이 빈번하게 사용되어서 공부하고 있었는데요,
책과 구글을 찾아보다 보니 콜백 함수부터 promise
, async/await
까지 숙지해야 할 것 같아
먼저 콜백 함수부터 공부하겠습니다.
다른 언어를 사용하다가 자바스크립트를 공부하면, 초반에는 동기와 비동기가 굉장히 헷갈립니다.
비동기 방식은 어떠한 작업이 끝날 때까지 기다리지 않고, 다른 일을 먼저 수행하는 것입니다.
동기와 비동기가 헷갈릴 때면 저는 요리할 때를 생각합니다.
요리할 때, 전자레인지에 음식을 해동하고 찌개를 끓인다고 가정합시다.
전자레인지에 음식을 넣고 해동이 끝날 때까지 기다린 후에 찌개를 끓이는 것이 동기 방식이고,
전자레인지에서 음식이 해동되는 동안, 찌개를 끓이는 것이 비동기 방식이라고 생각하면 편합니다.
음식점에서도 주문을 계속 받으면서 음식이 완성되면 서빙을 나가죠.(비동기)
주문을 하나씩 받지 않습니다.(동기)
자바스크립트는 이런 비동기 방식을 지원합니다.
이것은 자바스크립트가 싱글 스레드인 것과 연관이 있는데요.
싱글 스레드인데 동기 방식만을 지원한다면, 앞에서 실행된 코드가 모두 실행된 후에 다음 코드가 실행되므로 유저에게 좋지 못한 경험을 제공하게 됩니다.
위키피디아에 따르면 콜백은 다른 코드의 인수로서 넘겨주는 실행 가능한 코드 라고 합니다.
const callback = () => {
console.log('Hi');
}
setTimeout(callback, 1000);
여기에서 제가 callback
이라고 명명한 함수가 바로 콜백 함수입니다.
callback
은 setTimeout
에 인수로 넘겨졌고, setTimeout
함수에 따라서 1초 후에 실행이 가능하게 됩니다.
이런 콜백 함수가 실제로는 어떻게 쓰이는지 아래 예시를 들어봤습니다.
ajax
는 jQuery
로 웹 서비스를 개발할 때 사용하는 통신입니다.
쉽게 말하면 api 통신이라고 볼 수 있습니다.
이러한 api 통신이 비동기적으로 일어나는 경우를 살펴보겠습니다.
function getData() {
var listData;
$.get('https://khyunjiee.com/lists/1', function(res) {
listData = res;
});
return listData;
}
console.log(getData()); // undefined
$.get(...)
부분은 ajax 통신을 하는 부분입니다.
getData()
는 해당 URL에 원하는 데이터를 요청하는 함수입니다.
이렇게 서버에서 받아온 데이터는 res
에 담겨서 listData
에 저장해 return 됩니다.
언뜻 보기에는 위의 코드는 정상적으로 동작할 것 같이 보입니다.
하지만 console.log(getData());
부분을 실행하면 undefined
가 출력됩니다.
WHY?
서버에서 데이터를 받아오는 것을 기다리지 않고, 코드가 모두 실행되기 때문입니다.
서버로 데이터를 요청하자마자 바로 끝난 것이죠.
만약 비동기 방식으로 api 통신을 구현하면, 사용자 경험이 엉망이 될 것입니다.
즉, api 통신은 동기적으로 실행되어야 할 것입니다.
그러면 위의 코드를 콜백 함수를 사용해서 다시 작성해보겠습니다.
function getData(callback) {
$.get('https://khyunjiee.com/lists/1', function(res) {
callback(res);
});
}
getData(function(listData) {
console.log(listData);
});
getData
함수 정의 코드를 보면 서버에서 받은 res
를 callback
함수에 넘겨줍니다.
그러면 getData
함수를 실행하는 아래 코드에서 인자로 넘어가는 callback
부분에 res
를 넘겨주어 listData
에 res
값이 저장되는 것입니다.
setTimeout
함수는 자바스크립트의 대표적인 내장 함수이자 비동기 함수입니다.
첫 번째 인자로 콜백 함수를 입력받고, 두 번째 인자로는 기다리는 시간을 입력받습니다.
인자로 넘겨진 시간 동안 기다린 후에 콜백 함수를 실행하는 함수입니다.
console.log('1');
setTimeout(() => {
console.log('khyunjiee');
}, 5000);
console.log('2');
간단한 코드를 적어봤습니다.
만약, setTimeout
함수가 동기 방식으로 작동한다면 실행 순서는 아래와 같을 것입니다.
- 1 출력
- (5초 후) khyunjiee 출력
- 2 출력
하지만 setTimeout
함수는 비동기 방식으로 작동하기 때문에 실질적인 실행 순서는 아래와 같습니다.
- 1 출력
- 2 출력
- (1 출력부터 5초 후) khyunjiee 출력
이것은 타이머에 0초를 할당해도 2번째 실행 결과와 동일합니다.
자바스크립트는 싱글스레드이기 때문에, 이벤트 루프가 콜백 함수의 순위를 뒤쪽으로 미룹니다.
콜백 함수의 타이머가 종료된 후 이벤트 루프에 의해서 콜백 함수가 실행되기 때문에 0초를 할당해도 결과는 동일합니다.
콜백 함수는 전통적으로 많이 사용되어왔고, 효과적인 비동기 처리를 할 수 있습니다.
하지만 콜백 함수가 무지막지하게 길어진다면, 가독성이 너무 떨어지는 콜백 지옥에 빠지는 문제점이 있습니다.
$.get('url', function(res) {
encoding(res, function(id) {
auth(id, function(result) {
display(result, function(username) {
console.log(username);
});
});
});
});
위의 코드처럼 비동기 처리를 위해 콜백 함수를 연속해서 사용하는 코드가 빈번하게 발생하면,
가독성이 크게 떨어지고 추후 유지보수가 굉장히 어려워집니다.
콜백 지옥을 해결하는 방법은 Promise
나 async/await
의 사용이 있습니다.
코딩 패턴으로만 콜백 지옥을 해결할 수도 있지만, 코드가 너무 조각조각 분리되어 그 또한 좋은 방법으로 볼 수 없습니다.
Promise
와 async/await
은 다음 포스팅에서 다루겠습니다 :)