WEB/FE(HTML,CSS,JS | React)

[JavaScript] 동기와 비동기적 처리, 콜백 함수

tedium._.dev 2022. 12. 12. 03:45

자바스크립트를 이용해 개발을 할 때, 흔히 비동기와 동기적 처리에 대한 용어를 많이 접하고는 한다. 그리고 비동기적 처리에 항상 콜백 함수라는 용어도 같이 동반되고는 한다.

동기와 비동기적 처리 그리고 콜백 함수가 무엇인지, 그리고 어떻게 동기와 비동기적 처리를 구현할 수 있는지에 대해 간단히 정리해보았다.

 

동기와 비동기 방식

동기(synchronous) : 요청과 처리가 동시에 일어남
비동기(asynchronous) : 요청과 처리가 동시에 일어나지 않음

 

용어의 정의만 들어서는 무슨 말인지 잘 와닿지 않는다. 요청과 처리의 동시성이 어떤 의미를 가지는 걸까?

 

현실 상의 예시를 하나 들어보겠다.

당신은 카페에 갔다. 그리고 주문을 하려고 한다. 주문에는 두 가지 방식이 있다.

 

1) 주문을 하면 즉시 커피가 만들어지고, 당신은 그 자리에서 기다리다가 커피를 받아간다.

2) 주문을 하면 당신에게 진동벨이 주어지고, 당신은 자리에서 기다리는 대신 다른 일을 할 수 있다. 그러다 진동벨이 울리면, 다시 자리로 돌아와서 커피를 받아간다.

 

1)은 동기적 처리의 예시이다. 당신의 요청과, 그 요청에 대한 처리가 그 자리에서 일어난다.

방식 자체는 간단하지만, 앞에 당신보다 먼저 온 많은 사람들이 있을 수 있다. 그리고 그 시간 동안 당신은 그 자리에서 대기만 해야 한다. 시간 낭비가 발생할 수도 있는 것이다.

 

2)는 비동기적 처리의 예시이다. 당신의 요청과, 그 요청에 대한 처리가 그 자리에서 일어나지 않는다.

방식은 단순히 기다리기만 하면 되는 1)에 비해서는 조금 더 복잡하다. 그러나 당신이 커피를 받기까지 다른 일들을 행할 수 있다. 보다 시간을 효율적으로 사용할 수 있는 것이다.

 

실제 동기적 처리 방식비동기적 처리 방식도 이와 동일하다.

동기적 처리 방식은 설계가 매우 간단하며, 이해하기도 쉽다. 요청에 대한 처리가 순차적이기 때문에, 특정 작업이 수행 중일 경우 그 이후의 작업들은 그만큼 대기되는 시간이 발생한다. 이를 blocking이라고 표현한다.

비동기적 처리 방식은 설계가 동기보다 복잡하다. 요청에 대한 처리가 순차적이지 않기 때문에, 특정 작업이 수행 중이라 하더라도 추가적인 작업이 가능하다. 이를 non-blocking이라고 표현한다.

 

그렇다면 자바스크립트는?

자바스크립트는 동기적인 언어이다.

 

자바스크립트는 기본적으로 동기적인 방식으로 작동한다. 작성된 코드 블럭을 Hoisting하며, 이후 위에서부터 차례대로 실행한다.

 

여기서 Hoisting이란, 자바스크립트 상에서 선언된 함수나 변수들의 선언을 제일 상단으로 올리는 것이다.

Hoisting 덕분에 위와 같은 코드도 정상적으로 동작하게 된다.

 

기본적으로 동기적인 방식으로 동작하는 자바스크립트에서도 비동기적 방식의 동작이 필요할 때가 있다.

예를 들어 웹페이지에서 처음에 많은 양의 데이터베이스를 불러와야 할 때, 이를 동기적으로 처리할 경우 그 시간 동안 화면에는 아무 것도 나타나지 않을 것이다.

때문에 데이터베이스를 불러오는 방식을 비동기적으로 처리하여, 이를 불러오는 동안에도 다른 처리가 가능하게끔 해 주어야 한다.

그렇다면 어떻게 자바스크립트에서 비동기적인 처리를 수행할 수 있을까? 콜백 함수와 비동기 처리 함수를 통해 이를 가능하게 할 수 있다.

 

자바스크립트에서의 비동기적 처리, 그리고 CallBack 함수

CallBack 함수 : 다른 코드의 인자로 넘겨지는 함수

 

콜백 함수는, 쉽게 말해 다른 코드의 인자로 넘겨지는 함수이다. 다른 함수의 인자로 넘겨져서 특정 시점에 호출(Call Back)될 수 있다.

 

위의 경우, useCallBack 함수에서는 func라는 매개변수를 정의하고 내부에서 이 func를 바로 실행하고 있다.

그리고 선언된 useCallBack 함수를 실제로 사용할 때 인자로 함수를 넘겨받아 해당 함수를 실행하는데, 바로 이 때 useCallBack 함수의 인자로 넘겨준 함수들인 sayHi, sayHello, printCountry 함수가 바로 콜백 함수라고 볼 수 있다.

 

콜백 함수를 넘겨줄 때, 사전에 정의된 함수를 넘겨주기보단 그때그때 함수를 arrow function으로 간단하게 축약하여 넘겨주기도 한다.

 

arrow function으로 보다 간결한 표현이 가능함.

 

이 콜백 함수를, 비동기 함수에 넘겨주는 방식으로 비동기 처리를 할 수 있다.

대표적인 비동기 처리 함수에는, 넘겨받은 콜백 함수를 정해진 시간 이후에 실행하는 setTimeOut() 함수가 있다.

 

setTimeOut함수 예시

 

자바스크립트는 기본적으로 하나의 호출 스택(Call Stack)을 통해 요청을 호출 스택에 담은 후 순서대로 처리한다.

 

그런데 setTimeOut과 같은 비동기적 처리 함수를 사용할 경우 경우, 외부 공간인 Web API 그리고 Task Queue가 개입하게 되어 해당 요청은 호출 스택이 아닌 Web API 그리고 Task Queue에 보관되었다가 기존 호출 스택의 요청에 대한 처리가 끝난 후에야 다시 호출 스택으로 넘어가게 된다. 

 

쉽게 말해서, 동기적인 작동이 모두 종료된 이후에 비동기적인 작동이 일어난다고 볼 수 있다.

 

그렇기 때문에, 얼핏 보면 순서대로 실행되어야 할 것 같은 코드도 아래와 같은 결과를 가져오게 된다.

 

동기적 처리가 모두 이루어진 다음에야 비동기적 처리가 이루어짐.

이렇게 콜백 함수를 적절히 이용하면 필요한 상황에서 비동기적 처리를 행할 수 있다.

 

그러나 프로젝트의 규모가 점점 커질 경우 점점 중첩되는 콜백 함수들은 가독성이 떨어지고, 난해해 보인다. 중간에 잘못 설계가 되어 결과적으로 오류가 생긴다고 해도, 어디가 잘못되었는지를 찾는 것이 쉽지 않다.

이를 개발자들 사이에서 흔히 '콜백 지옥'이라고 표현하고는 한다.

으아악

이를 방지하기 위해 나온 대안이 Promise나 Async/Await을 이용한 방식이다. 아래의 포스팅에 그 방식에 대해 소개해 보았다.

 

(조만간 업뎃할게용)