[Javascript]

[Javascript 비동기 처리 뿌시기 (2/4)] 비동기 처리 방식 : 콜백 함수, 콜백 지옥 (callback 함수란?)

quokkalover 2022. 1. 25. 23:16

Javascript 비동기 처리 뿌시기 시리즈에서 필자가 추천하는 학습방법은 아래와 같다.

 

학습방법

step1) 비동기 처리의 기본 개념을 이해하기 위한 Core개념 익히기 (https://etloveguitar.tistory.com/84)

step2) 콜백함수와 콜백 지옥의 맛을 보기

step3) Promise로 살짝 콜백 지옥 해결하고 유연한 코드 짜기 (https://etloveguitar.tistory.com/86)

step4) Async & Await를 이해하고 더 우아하게 Promise활용하기 (https://etloveguitar.tistory.com/87)

 

물론 이미 알고 있는 개념이면 이 시리즈를 전부 보지 않아도 되지만, 최대한 쉽게, 그리고 예시와 함께 설명했으니 복습차원에서 보는것도 추천한다. 한 개념만 보면 대충 뭔지 알겠다가도, 개념들을 연속적으로 이해하지 못하면 결국 벽에 부딪힌다. 따라서, 지금 필요한 특정 개념보다는 전반적인 흐름을 가지고 공부하는 것을 추천한다.

 

본 글의 제목에서 알듯이 이번 글은 2탄이다. Javascript에서의 비동기 처리의 가장 기본적인 방식인 Callback 함수에 대해 간단하게 알아본다.

콜백 함수란 (Callback)?

  • 콜백 함수란 함수의 인자로 함수를 넣는 함수를 말한다. 이를 통해서 함수를 특정한 시점에 호출할 수 있다.
  • 콜백 함수를 활용하면 비동기 처리에 아주 요긴하게 쓸 수 있다.

 

 

콜백 함수가 없이 비동기로 함수를 실행했을 때 어떤 문제들이 생기는지 한번 보자.

 

비동기 처리 예시 1)  ajax통신 $.get()

function getData() {
    var tableData;
    $.get('https://domain.com/products/1', function(response) {
        tableData = response;
    });
    return tableData;
}

console.log(getData()); // undefined

위 코드에서 비동기 처리가 되는 부분은 $.get()이 ajax 통신을 하는 부분이다. https://domain.com 에다가 HTTP GET 요청을 날려 1번 상품(product) 정보를 요청하는 코드인데, 코드 상으로 봤을 때는 서버에서 받아온 데이터를 response인자에 담을 수 있을 것 같다.

 

하지만 getData()를 호출하게 되면 undefined가 출력된다. 이유는 $.get()이 비동기 함수이기 때문이다.

이렇게 비동기식으로 구현한 이유는 요청이 1개 일때는 모르지만 100개 1000개가 됐을 때 모든 요청을 동기적으로 처리하면 서버가 응답하는 시간까지 다 기다려야 하고, 따라서 내 서비스가 매우 느려질 수 있기 때문이다.

 

 

비동기 처리 예시 2) setTimeout()

setTimeout()는 코드를 바로 실행하지 않고 지정한 만큼 기다렸다가 로직을 실행한다.

 

setTimeout함수의 동작방식을 런타임 관점에서 설명하면 아래와 같다.

1) setTimeout() 함수가 실행되면

2) JavaScript 엔진은 웹 API를 통해 브라우저에게 setTimeout() 작업을 요청하면서 콜백 함수를 전달

3) 브라우저는 이러한 타이머 작업을 별도의 쓰레드에게 위임한다.

4) JavaScript 엔진의 스택에서는 setTimeout() 함수의 스택 프레임이 즉시 팝 된다.

5) 그리고 인자로 명시한 시간이 흐르고 나면 해당 타이머 작업을 처리하고 있던 쓰레드는 전달받았던 콜백 함수를 JavaScript 엔진의 태스크 큐에 집어넣게 된다.

 

예를 들어 아래 코드를 보자

function finishJob(num) {
  console.log(`${num}번 요원의 정보를 받아왔습니다.`);
}

setTimeout(finishJob, 2000, 1);
setTimeout(finishJob, 1500, 2);
setTimeout(finishJob, 1000, 3);
console.log("정보 요청을 모두 보냈습니다.");

위 코드를 보면 비동기에 대한 이해가 없는 사람은

1번 요원의 정보를 받아왔습니다.
2번 요원의 정보를 받아왔습니다.
3번 요원의 정보를 받아왔습니다.
정보 요청을 모두 보냈습니다.

을 기대하겠지만 실제 결과로는

정보 요청을 모두 보냈습니다.
3번 요원의 정보를 받아왔습니다.
2번 요원의 정보를 받아왔습니다.
1번 요원의 정보를 받아왔습니다.

위가 출력된다.

위처럼 동작했다고 생각하면 좀 이해가 편하다. (그림 출처 : https://elvanov.com/2597)

아까 위에서 런타임 관점의 동작방식에서 설명했듯이

  1. setTimeout 은 인자로 들어온 콜백 함수를 예약하기만 하고 바로 끝난다.
  2. setTimeout 에 의해 기다리는 동작은 본래의 코드 흐름과는 상관 없이 따로따로 독립적으로 돌아간다. (위 그림처럼 세 개 동시에 기다리는 모습이다.)
  3. 이렇게 따로따로 독립적으로 돌아가는 작업을 비동기 작업이라고 한다.

 

콜백 함수 : 비동기 처리 방식의 문제점 해결

  • 비동기 처리 방식의 문제는 결과값을 바로 받아볼 수 없다는 점이다. 그래서 이걸 해결하기 위해서 제일 처음 도입된 방법이 콜백 함수를 이용하는 것이다.
  • 콜백 함수란 앞서 말했듯 함수의 인자로 함수를 넣는 함수를 말한다. 이를 통해서 함수를 특정한 시점에 호출할 수 있다.
function callMeWhenYouArrive(callback) {
  setTimeout(() => {
    callback();
  }, 5000)
}

function doSomething() {
  console.log("I called you!");
}

waitFiveSec(doSomething);

위에서 처럼 콜백함수를 사용하면 5초 뒤에 doSomething()을 실행하도록 할 수 있다. 이러한 특성을 활용하면 아까 위의 ajax코드의 문제를 해결할 수 있다.

function getData(callback) {
    var tableData;
    $.get('https://domain.com/products/1', function(response) {
        callback(response)
    });
    return tableData;
}

console.log(getData(function(tableData) {
    console.log(tableData);
})); 

 

콜백 지옥

비동기 프로그래밍을 하다보면, 실행 순서를 신경쓰며 코딩을 해야 한다. 아래와 같이 콜백이 너무 많은 콜백 지옥에 빠질 수 있기 때문이다. 웹 서비스 개발을 하다보면 데이터를 받아와서 인코딩하고, 사용자 인증을 처리 하는 등 다양한 로직이 필요하게 되는데, 그 때마다 콜백을 계속 물리다보면 콜백 지옥이 생기게 된다.

step1(function (value1) {
    step2(function (value2) {
        step3(function (value3) {
            step4(function (value4) {
                step5(function (value5) {
                    step6(function (value6) {
                        // Do something with value6
                    });
                });
            });
        });
    });
});

이를 방지 하기 위해서 나오는 방법은 기명함수를 이용해 함수들을 분리하는 것이다.

step1(afterStep1);
const afterStep1 = (value1) => {
    step2(afterStep2);
}

const afterStep2 = (value2) => {
    step3(afterStep3);
}

const afterStep2(value2) => {
    step3(afterStep3);
}

const afterStep2(value2) => {
    step3(afterStep3);
}

.
.
.
.

 

위 방법 외에도 자바스크립트에는 다음의 두 가지 방법이 있다.

  • Promise
  • async & await

다음 글에서는 예상했겠지만 Promise에 대해 다룰 것이고 그 다음 장에는 async & await을 다루도록 하겠다.

망설이지말고 가보자!!

 

지금 당장 Promise  & async 만나러가기!!

https://etloveguitar.tistory.com/86

 

[Javascript 비동기 처리 뿌시기 (3/4)] 비동기 처리 방식 : 자바스크립트 Promise 완전 쉽게 이해하기 (

Javascript 비동기 처리 뿌시기 시리즈에서 필자가 추천하는 학습방법은 아래와 같다. 학습방법 step1) 비동기 처리의 기본 개념을 이해하기 위한 Core개념 익히기 (https://etloveguitar.tistory.com/84) step2)..

etloveguitar.tistory.com

https://etloveguitar.tistory.com/87

 

[Javascript 비동기 처리 뿌시기 (4/4)] 비동기 처리 끝판왕 async & await 이해하기

Javascript 비동기 처리 뿌시기 시리즈에서 필자가 추천하는 학습방법은 아래와 같다. 학습방법 step1) 비동기 처리의 기본 개념을 이해하기 위한 Core개념 익히기 (https://etloveguitar.tistory.com/84) step2)..

etloveguitar.tistory.com

 

 

 

 

참고자료

https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/

https://elvanov.com/2597

https://it-eldorado.tistory.com/86