본문 바로가기
프론트엔드/바닐라 자바스크립트

자바스크립트 콜백 Callback함수 알아보기

by juneMiller 2022. 3. 6.

CallBack 함수란? 

다른 함수에 전달되는 함수를 이야기한다.

결국, 나중에 실행된다? 

어떤 이벤트가 발생했거나 특정 시점에 도달하였을때, 

시스템에서 호출하는 함수를 말한다. 

즉, 콜백함수는 콜백함수라는 유니크한 문법적 특징을 가지고 있는것이 아니라, 

호출방식에 의한 구분이다. 

 

 

대표적인 콜백함수의 사용예로는 자바스크립트에서 이벤트 핸들러 처리이다. 

<button id="button1" onclick="button1_click();">버튼1</button>
<script>
function button1_click() {
	alert("버튼1을 누르셨습니다.");
}
</script>

 Html에 onclick에  button1_click 함수는 브라우저의 javascript API 에서 DOM 이벤트 핸들러에 전달 (등록)

되고, 해당 버튼에 클릭이벤트가 발생하였을테 이벤트 핸들러가 콜백함수를 호출한다. 

 

<script>
$( "#target" ).click(function() {
  alert( "Handler for .click() called." );
});
</script>

 

 

Callback을 활용한 비동기적 프로그래밍

Callback 함수를 사용하는 이유는, 자바스크립트에서 비동기적 프로그랭을 할수 있기 때문이다. 

이 콜백함수기법은 자바스크립트에서 가장 오래된 비동기적 메커니즘이라 한다.

 

비동기적 테그닉 :  소중한 싱글스레드의 멈춤을 방지한다. 즉 블로킹을 방지하여 싱글 스레드가 논블로킹

으로 동작하게 한다. 

 

비동기적 테크닉을 사용하는 경우 

1.사용자 이벤트 처리 

브라우저 화면에서 발생하는 사용자의 이벤트는 예측이 블가능하다. 

따라서 이런 화면이벤트를 관리담당하는 녀석에게 우리는 특정이벤트가 발생할 때 호출을 원하는 내용을 

Callback 함수에 전달하게 된다. 

 

 

2.네트워크 응답처리 

화면단에서 서버에게 요청을 보냈을때, 그 응답이 언제올지 알 수 없다. 

따라서 이런 서버에 대한 응답처리등도 비동기적으로 처리해야 한다. 

 

3.파일을 읽고 쓰는 등의 파일 시스템 작업

 

4.의도적으로 시간 지연을 사용하는 기능(알람 등) 

위와같이, 이벤트 등을 기다리는데 하나뿐인 소중한 스레드를 사용한다면, 또 서버의 응답을 기다리기 위해

하나뿐인 소중한 스레드를 사용한다면... 

사용자는 멈춰져있는 화면을 보게되는 것이다. 

위와 같이 스레드의 블로킹을 야기하는 작업은 필수적으로 비동기적 프로그래밍을 해야 한다. 

 

Callback 함수의 비동기적 사용의 예 

콜백 함수는 일반적으로 다른 함수에 넘기거나, 객체의 프로퍼티로 사용하낟. 

드물게는 배열에 넣어서 쓸때도 있다. 

항상 그런건 아니지만, 보통의 콜백함수는 익명함수로 사용한다. 

 

중요한 점은 콜백함수는 그냥 일반적인 함수이다. 

우리는 비동기적 프로그래밍을 하기 위해서는, 비동기적으로 콜백함수를 호출하는 함수에서 

비동기적으로 호출되기를 원하는 코드콜백함수에 담아서 전달해야 한다. 

 

<script>
function fn_fakeAsync(callback){
  calback();
}

console.log("------- fn_fakeAsync 호출 직전 -------");

fn_fakeAsync(function(){
  console.log("이게 비동기적으로 동작하길 바래");
});

console.log("------- fn_fakeAsync 호출 이후 -------");

</script>

위 코드의 결과는 ? 

 

------- fn_fakeAsync 호출 직전 -------
이게 비동기적으로 동작하길 바래
------- fn_fakeAsync 호출 이후 ------

위처럼 단순히 아무 함수에게나 콜백함수를 전달하여 호출하는것은 비동기적 콜백함수를 호출할수 없다.

 

다시 한번 강조하자면, 

비동기적으로 콜백함수를 호출하는 함수에서

비동기적으로 호출되기를 원하는 코드 콜백함수에 담아서 전달해야 한다. 

 

setTimeout 함수

setTimeout 은 콜백함수의 실행을 지정된 밀리초만큼 지연하는 내장함수이다.

<script>
function fn_newCallBack(){
  console.log("비동기적으로 호출되고 싶다.");
}

console.log("-------  호출 직전 -------");

setTimeout(fn_newCallBack, 3 * 1000); // 3초 뒤 콜백 호출

console.log("-------  호출 이후 -------");
</script>
-------  호출 직전 -------
-------  호출 이후 -------
비동기적으로 호출되고 싶다.

3초 뒤에 fn_newCallBack 함수가 호출되는 것을 확인할 수 있다. 

여기서 이게 무슨비동기적 프로그래밍인지 갸웃할수 있다. 

 

동기적 프로그래밍에서 만약 3초뒤에 fn_newCallBack을 호출시키려면, 3초를 자바스크립트의 하나뿐인 

메인 스레드가 카운팅을 하고 있어야 한다. 

즉, 프로그래밍이 멈추는 것이다. 

setTimeout 함수는 메인스레드가 할일을 자바스크립트 API에게 위임시킴으로써 메인쓰레드의 

멈춤(블로킹)을 피하게 해준다. 

 

 

 

다음 예제를 보자!

<script>
function fn_newCallBack(){
  console.log("비동기적으로 호출되고 싶다.");
}

console.log("-------  호출 직전 -------");

setTimeout(fn_newCallBack, 0); // 즉시 실행되지 않을까?

console.log("-------  호출 이후 -------");
</script>

------- 호출 직전 -------
------- 호출 이후 -------
비동기적으로 호출되고 싶다.

 

위 코드를 보면 setTimeout 함수에 지연시간을 0으로 설정하였지만 

3초 세팅과 마찬가지로 마지막에 호출되었다. 

 

setTimeout에 지연시간을 0으로 해도 내장으로 지연시간이 있는 것일까? 

 

다음 예제를 보자

<script>
function fn_newCallBack(){
  console.log("비동기적으로 호출되고 싶다.");
}

console.log("-------  호출 직전 -------");

setTimeout(fn_newCallBack, 0); // 콜백이 언제 동작할지 예측해보자.

console.log("-------  호출 이후 -------");

//무한로프로 콘솔을 찍어낸다.
while(true){
  console.log("한국 일교차 너무해...");
}
</script>

위 코드를 실행하면 while 문의 콘솔이 무한이 찍히고 fn_newCallBack 함수는 절대 호출되지 않는다. 

 

setTimeout 함수에 전달된 콜백함수는 javascript 실행환경의 어느 바구에 담겨진다. 

그러면 javascript API 가 setTimeout 의 지연시간을 보고 카운팅을 대신 해준다. 

 

음.. 그런데 지연시간이 0 이네? 

javasciprt API 는 이 콜백함수를 바구니에서 꺼내서, 

TaskQueue 라는 곳에 적재한다. 

 

이 TaskQueue 는 현재 동작중인 메인스레드가 일을 다 끝내면, 다음으로 해야할 일들이 있는 

Queue 이다. 

 

즉 위 코드는 while 무한 루프에 빠져, 자기일을 끝내지 못하고 있기 때문에 

TaskQueue 에 적대된 콜백함수가 호출 되지 못하는 것이다. 

 

여기서 중요한 점은 setTimeout 과 같은 비동기적으로 콜백을 호출 시켜주는 함수는,

전달된 콜백 함수를 현재 실행중인 싱글스레드에서 뽑아내어 특정 장소에 보관하고 특정 조건을 

만족시킬때 호출이 가능하게 한다는 점이다. 

 

setTimeout은 시간을 카운팅하는 기능 외에, 콜백함수를 비동기영역으로 넘기는 역할을 하는 것이다. 

<script>
setTimeout(function(){
  //나라의 말이 중국과 달라
  //문자와 서로 맞지 아니해서
  //이러한 까닭으로 어리석은 백성이 말하고자 할 바가 있어도
  //마침내 제 뜻을 능히 펴지 못하는 사람이 많으니라
  //내 이를 위하여 가엾게 여겨
  //새로 스물여떯 자를 만드노니
  //사람마다 하여금 쉬이 익혀 날로 쓰며 편안케 하고자 할 다름이니라
}
, 1000 * 5);
</script>

setTimeout에 콜백으로 익명함수를 주로 사용하는데 익명함수가 길어질 경우 두번째 인자를 식별하기 어려울수 있다.

따라서, 위처럼 두번째 식별인자는 마지막 행에 분리하는 것이 사용팁니다. 

 

setInterval 함수와 clearInterval 함수

setInterval 도 setTimeout 과 같이 콜백을 비동기적으로 호출 가능하게 해주는 함수이다.

차이점은 지정된 시간을 기준으로 반복적으로 콜백을 호출해 준다는 것이다. 

 

<script>
let i = 0;
const intervalId = setInterval(function(){
  
  if(i === 3) return clearInterval(interbalId);

  console.log(`${i++}: 인터벌로 호출 되는 콜백입니다.`);
  
}
, 5 * 1000);
</script>

 

위 코드에서 console.log 내부에 문자열을 감싼 것은 싱글쿼터가 아니라

백틱(키보드 esc 아래에 있음)이다.
Es2015 부터 백틱을 사용하여 위 처럼 템플릿 문자열을 사용 할 수 있다.

 

setInterval의 기능은 단순하다 중요한 것은 고유의 intervalId를 리턴하고

이것을 clearInterval 함수에 넘겨 해당 setInterval을 중지시킬수 있다.


특시 Node등 서버단에서 setInterval를 사용할 경우 반드시 clearInterval로 인터벌을

정지시키지 않을경우 서버 메모리 누수가 발생할 수도 있으니 주의해야 한다.

 

지금까지 setTimeout, setInterval, clearInterval등은 모두 전역 객체(브라우저에서는 Window, 노드에서는 global)에 정의 되어 있으므로 어디서나 사용할 수 있다.