Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

청개구리 개발자

[자바스크립트] callback 이란? 본문

JavaScript

[자바스크립트] callback 이란?

beConstant 2019. 11. 12. 01:19

이번 글은 나의 첫 포스팅이다. 어떠한 글을 나의 첫 포스팅으로 하면 좋을지 생각해보았다.
javascript로 백앤드를 공부하고 있는 나에겐 싱글스레드(single-Thread)로 작동한다고 알려져있는
javascript가 어떻게 비동기 작업을 통해 여러가지 Task를 동시에 처리하는지 포스팅하고 싶었다.

이에 앞서 우리는 callback이란 무엇인가에 대해서 알아보자!!


자바스크립트 callback

저 뿐만 아니라 대부분의 사람들이 Javscript의 Callback은 개념을 정리함에 있어서 어렵다고 생각한다.

하지만 callback은 javascript에서 매우 중요한 개념이므로 완벽히 이해를 하고 넘어가야 한다.

지금부터 callback 이란 무엇인가에 대해서 알아보도록 하자!!


callback이란 무엇인가?

callback이란 나중에 실행할 argument로 다른 함수에게 전달되는 함수를 의미한다.

(개발자들은 함수를 실행할 때 호출한다(call) 라는 표현을 씀으로 나중에(back) 호출(call)할 함수라는 의미에서 callback이라고 부른다.)

말로만 들었을 때는 이해하기 어려움으로 직접 코드를 보자 !!!

var func = function(callback){
    console.log('hello1');
    callback();
}

var callback = function(){
    console.log('hello2');
}

func(callback);
hello1
hello2

위의 코드처럼 func 함수가 먼저 호출되고 나중에 새롭게 정의한 callback 함수가 호출된다.

 

위 코드는 내가 직접 만든 코드이고 javascript에서 많이 쓰이는 하나의 예를 더 보자

// button 태그요소들을 Select한다
const button = document.querySelector('button')

// clicked 클래스를 요소에 추가하는 함수이다.
function clicked (e) {
  this.classList.add('clicked')
}

// click 함수를 콜백 함수로써 event listener에 등록한다.
button.addEventListener('click', clicked)

위 코드에서는 버튼에서 click 이벤트를 수신하도록 만들었습니다.

버튼에 클릭이 감지될 경우에 javascript는 clicked 함수를 실행해야 합니다.

이 경우에서 clicked는 callback 함수이고  addEventListener는 callback을 accept 하는 함수입니다.

 

밑의 예제는 어떻게 callback 함수 와 callback accepting 함수를 작성해야하는지에 대해서 나타내주는 예제입니다.

// callback 함수를 수용할 수 있는 callback Accepting 함수를 만들었다.
const callbackAcceptingFunction1 = function(fn){
    // 1,2,3 인수를 사용하여 fn함수를 호출한다.
    return fn(1, 2, 3)
}
const callbackAcceptingFunction2 = function(fn){
    // 1,2 인수를 사용하여 fn함수를 호출한다.
    return fn(1, 2)
}
// callback 함수를 정의했다.
const callback = function(arg1, arg2, arg3){
    return arg1 + arg2 + arg3
}

// callback accepting 함수에게 callback을 전달했다.
const result1 = callbackAcceptingFunction1(callback)
// 1, 2, 3의 인자를 가지고 callback 함수를 call 했다.
console.log(result1) //6

const result2 = callbackAcceptingFunction2(callback)
// 1, 2, undefined의 인자를 가지고 callback 함수를 call 했다.
console.log(result2) // NAN : 1 + 2 + undefined

이것이 callback의 구조(anatomy)입니다.

이제 여러분들은 addEventListner에 event argument를 포함하고 있음을 알고 있습니다.

button.addEventListener('click', function(event){
	event.prventDefault()
});

이것이 callback함수의 기초적인 idea 입니다.

여러분들은 콜백이란? 다른 함수(Callback Accepting function)에 함수(Callback)를 전달한다. 라는 사실만 기억하면 됩니다!!!

callback 함수를 callback Accepting에 인자로써 전달 할 수 있는 이유는 Javascript에서 함수는 1급 객체이자 1급 함수이기 때문이다.

더 자세한 내용은 구글링을 통해 알아보자!!


콜백을 왜 사용할까(Why use callbacks?)

콜백 함수는 2가지 방식으로 사용된다.

1. 동기적 함수

2. 비동기적 함수

 

1. 동기적 함수로써의 Callback (Callbacks in Synchronous functions)

동기적(Synchronous)

코드가 위에서 아래로 왼쪽에서 오른쪽으로 순차적으로 실행되면

다음 줄의 코드를 시작하기 전에 현재 줄의 코드가 완료될 때 까지 기다리는 방식을 의미한다.

 

이해하기 쉽게 하나의 예를 참고해서 보자

const addOne = function(n){
	console.log(n+1);
}
addOne(1);	// 2
addOne(2);	// 3
addOne(3);	// 4
addOne(4);	// 5

위의 예제를 보면, addOne(1)이 첫번째로 실행된다. addOne(1) 함수가 끝나면 addOne(2)가 실행되고,

addOne(2)함수가 끝나면 addOne(3)가 실행된다. 이 process는 code의 마지막 줄이 실행될 때 까지 진행된다.

 

동기적 콜백 함수는 코도의 일부를 다른 코드로 쉽게 교체하려는 경우에 사용된다.

 

동기적 콜백 함수의 예를 보자.

우리는 배열의 Array.filter 함수에 전해지는 callback 함수를 재사용해서 다양한 원소를 가지게끔 새로운 배열을 생성할 수 있다.

const numbers = [3, 4, 10, 20];

const getLessThanFive = function(num){
	return num<5;
}

const getMoreThanTen = function(num){
	return num>10;
}

// numbers의 filter함수에 getLessThanFive 함수를 전달해준다.
const LessThanFive = nubmersfilter(getLessThanFive);
console.log(lessThanFive);	// [ 3, 4 ]

// numbers의 filter함수에 getMoreThanTen 함수를 전달해준다.
const MoreThanTen = numbers.filter(getMoreThanTen)
console.log(MoreThanTen);	// [ 20 ]

위 예제는 우리가 왜 동기적 함수로써 콜백을 사용하는가에 대한 대표적인 예제이다.

 

다음으로 왜 우리가 비동기적인 함수로써 콜백을 사용하는지에 대해서 알아보자

2. 비동기적 함수로써의 Callback (Callbacks in Asynchronous Functions)

비동기적(Asynchronous)

Javascript가 완료 될 때까지 기다려야 하는 경우 대기하는 동안 제공된 나머지 작업을 실행하는 방식을 의미한다.

 

비동기적 함수의 하나의 예시는 setTimeout 함수이다. setTimeout 함수는 callback 함수를 주어진 시간 뒤에 실행한다.

const tenSecondLater = function(){
    console.log("ten seconds passed!");
}

setTimeout(tenSecondLater,10000);
console.log('start!')
Start! (즉시)
10 seconds passed! (10초 후)

위 코드에선 Javascript는 setTImeout를 실행한다. setTimeout 함수는 10초간 기다리고 '10 seconds passed!'라는 로그를 발생시킨다.

반면에, setTimeout의 callback함수를 10초간 기다리는 동안에, Javascript는 console.log("start!") 코드를 실행한다.

 

비동기 작업은 매우 복잡한데 왜 자바스크립트는 비동기 callback 함수를 사용하는 것일까? 

비동기 작업이 왜 중요한지 생각하려면 Javascript를 집에 있는 로봇 도우미라고 상상해보세요.

이 로봇 도우미는 멍청해서 한 번에 하나의 작업만 수행할 수 있습니다. (우리는 이것을 single-Thread라고 합니다.)

 

로봇 도우미에게 피자를 시키도록 말해 보세요. 로봇 도우미는 멍청해서 피자가게에 전화를 한 후 문앞에 앉아서 피자가
배달 올 때까지 기다릴 것입니다. 그 동안에 로봇도우미는 어떠한 작업도 하지 못합니다.

 

로봇 도우미는 피자가 배달될 때 까지 옷을 다림질 하지도 못하고 바닥을 청소하지도 못합니다. 피자가 도착할 때까지 20분을 기다려야
비로소 다른 일을 할 수 있습니다.  (우리는 이러한 행동들을 blocking 이라고 부른다.)

(당신이 무언가를 기다리는 동안에 다른 작업들은 blocked 되어진다.)

const orderPizza = function(flavour){
	callPizzaShop(`I want a ${flavour} pizza`);
	wait20minsForpizzaToCome(); // 이 함수에선 어떠한 것도 일어날 수 없다.
	bringPizzaToYou();
}

orderPizza('hawaiian');

// 피자 주문을 완료하고 난 뒤 시작하는 2가지의 작업들
mopFloor();
ironClothes();

위 코드에서, blocking 작업은 매우 비효율적이다.

 

이제 멍청한 로봇 도우미를 브라우저의 문맥에 나둬보자.

너가 브라우저에서 버튼이 클릭될 때 버튼의 색깔을 바꾸도록 로봇 도우미에게 명령하도록 상상해보자.

 

로봇 도우미는 버튼을 클릭 할 때까지 오는 모든 명령을 무시하고 버튼을 열심히 쳐다 본다.
그 동안에 사용자는 다른 것을 선택할 수 없기 때문에 이를 해결하기 위해서 비동기적인 방법을 사용한 것이다.


이것이 Javascript에서 비동기 프로그래밍이 핵심인 이유입니다.

 

비동기 작업 동안에 무엇이 일어나는지에 대해서 알기 위해서 우리는 Event Loop에 대해서 알아 볼 필요가 있다.


이벤트 루프 (Event Loop)

이벤트 루프를 계획하기 위해 Javascript를 할 일 목록(todo-list) 다루는 집사라고 상상해보자.

이 목록에는 사용자가 지시한 모든 것이 포함되어 있습니다.

그러면 Javascript는 사용자가 지정한 순서대로 목록을 하나씩 살펴 봅니다.

 

사용자가 Javascript에게 아래와 같은 5개의 명령들을 주었다고 가정하자.

const addOne = function(n){
	return n+1;
}

addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
addOne(5) // 6

 

아래 그림은 Javascript의 할 일 목록(todo-list) 입니다.

Commands appear synchronously on Javascript's todo-list

Javascript는 할 일 목록(todo-list) 외에도 대기해야 할 항목을 추적하는 대기 목록(waiting-list)을 유지합니다.

만약 사용자가 Javascript 에게 pizza를 주문하라고 시킨다면, 자바스크립트는 pizza 가게에 전화할 것이고 
피자가 올때 까지 기다리라는 명령을 대기 목록(waiting-list)에 추가할 것이다.


그러는 동안에 할 일 목록(todo-list)에 이미 있는 명령들은 실행이 되어질 것이다.

const orderPizza (flavor, callback) {
  callPizzaShop(`I want a ${flavor} pizza`)

  // Note: 아래의 코드 3줄은 가상의 실제 javascript 코드가 아닌 가상의 코드이다.
  whenPizzaComesBack {
    callback()
  }
}

const layTheTable = function(){
  console.log('laying the table')
}
orderPizza('Hawaiian', layTheTable)
mopFloor()
ironClothes()

Javascript의 초기의 할 일 목록(todo-list)은 다음과 같다.

Order pizza, mop floor and iron clothes

Javascript는 orderPizza명령을 시행하는 동안에 pizza가 도착하기를 기다려야 한다는 것을 안다.

그래서 Javascript는 "Waiting for pizza to arrive" 명령을 나머지 작업을 처리하는 동안에 대기 목록(waiting-list)에 추가한다.

Javascript waits for pizza to arrive

피자가 도착했을 때, Javascript는 초인종으로부터 알림을 받고 다른 일들을 마치면 layTheTable함수를 실행 할 mental note를 만든다.

 

javascript는 mental note에 명령을 추가함으로써 layTheTable 명령을 실행할 필요가 있다는 것을 알게 된다.

일단 Javascript가 다른 작업들을 마치면, 콜백 함수인 layTheTable을 실행합니다.

다른 모든 작업들이 끝나면 Javascript는 위와 같이 Table을 배치합니다.

이러한 작업들을 이벤트 루프라고 합니다.

이벤트 루프에 대한 모든것을 이해하기위해 실제 키워드를 집사에 비유해 대체할 수 있습니다.

  • 할 일 목록(Todo-list) -> Call stack
  • 대기 목록(Waiting-list) -> Web apis
  • Mental note -> Evenet queue

Javascript's Event Loop

출처 : https://zellwk.com/blog/callbacks/

 

Zell Liew

Zell is a designer, developer and writer. He shares things he knows about web development on this blog.

zellwk.com

 

Comments