본문 바로가기
개발공부일지/JavaScript

JavaScript - Callback 함수와 동기/비동기

by Hynn1429 2022. 11. 11.
반응형

안녕하세요.

Hynn 입니다.

 

이번 포스팅에서는 이미 저의 JavaScript 포스팅을 보시면서 넘어오신분들이라면, 무의식적으로 이 Callback 의 대한 개념을 사용하고 계시리라 생각합니다.

이번 포스팅에서는 실제 함수의 작성보다는, JavaScript 의 개념적인 부분을 포스팅하도록 하겠습니다.

개념이 이해가 되면 당연히 이에 대한 실제 작성에도 이해에 도움이 될 것이라고 생각합니다.

 

==============

1. Callback 함수의 설명

2. 동기(Synchronous) , 비동기 (asynchronous) 

3. Background, TaskQ, EventLoop

==============

 

1. Callback 함수의 설명

 

먼저 Callback 함수라고 하는 단어적 의미를 이해하시면 조금 더 이해가 편리할 수 있습니다.

이전 포스팅에서 Hip, Call-Stack  의 내용이 포함된 Function 의 기본과, 재귀함수 이 두가지에 대해서 기억하고 계시다면, 조금 더 쉽게 이해가 가능합니다.

 

이 Callback 함수의 기본적인 개념은 함수가 함수를 호출하는 형태를 뜻하며, 앞으로 상당히 많이 쓰이게 될 것이고, 작성하면서도 무의식적으로 사용할 개념입니다.

이해하기 쉽게 예제를 하나 작성해보겠습니다.

function Hynn(count, cons, arr) {
    count < 3 ? cons() : arr();
  }
  
  function Linka() {
    console.log('안녕하세요');
    console.log('hynn.tistory.com');
  }
  
  function HynnName() {
    console.log('개인공간에 오신것을 환영합니다')
  }
  
  Hynn(2, Linka, HynnName);

위의 함수는 아래의 단계를 거쳐 수행합니다.

먼저 전체 작성을 익명함수에서 한번 검사하는 과정을 거칩니다.

그리고 이 코드를 줄 단위로 실행하기 위해 준비하겠습니다. 그러기 위해서는 Hip 으로 이동하겠지요?

 

여기서 함수 3개는 모두 Hip 으로 이동됩니다. 그리고 마지막줄의 Hynn 함수에 지정되있는 매개변수에 대입하는 값을 2, Linka, HynnName 을 대입하여 실행하도록 처리가 이루어집니다.

 

첫번째 Hynn 함수를 살펴보면, 2, Linka, HynnName 이라는 인자값을 대입하게 됩니다.

이 함수 내에서는 count, 즉 2라는 인자값이 3보다 작은지를 묻고 참일경우 Linka 함수, 거짓일 경우 HynnName 함수를 실행하도록 되어 있습니다. 여기서 사용된 ?는 삼항 연산자라고 하며, 이는 If 문을 축약한 것으로 이해하셔도 좋습니다.

 

여기서의 Callback 함수는 Hynn 이라는 함수가 조건에 따라, Linka 를 호출할지, HynnName 을 호출할지를 결정하게 되는 것이죠.

하지만 어떤 조건에서든 Hynn 이라는 함수는 결국에 함수를 호출합니다. 이를 Callback 함수라고 합니다. 

 

위의 내용을 If문으로 표현할 경우 아래와 같습니다.

삼항 연산자에 대해서는 별도의 포스팅으로 다룰 예정입니다.

  function Hynn(count, cons , arr){
    if (count < 3){
        cons()
    } else{
        arrr()
    }
  }

 

 

2. 동기(Synchronous) , 비동기 (asynchronous) 

 

위에서 설명했듯이, JavaScript 는 익명함수에서 Code 를 전체적으로 검사 후 , "한줄" 씩 실행한다는 작동방식입니다.

즉 이 것은 1개의 작업을 1번씩 처리해애 하는 것입니다. 

이 작업은 CallStack 에서 처리한다고 할 수 있습니다. 이 개념을 Single Thread, 즉, 싱글 스레드(단일 스레드) 라고 지칭합니다.

 

일반적으로 컴퓨터를 좋아하는 사용자분들이라면 한번쯤은 들어보았을 개념이라고 생각합니다.

싱글스레드와 멀티스레드라는 두개의 용어가 있습니다.

이 용어를 이해하기 쉽게 그림으로 그리면 아래와 같습니다. 

싱글스레드, 멀티스레드

위의 그림에서 보면, 싱글 스레드는 하나의 작업을 하나씩 수행하지만, 멀티스레드는 동시에 여러개의 작업을 처리하는 것을 뜻합니다.

하지만, 그렇다고 해서 프로그래밍에서 반드시 "멀티 스레드"가 좋은 것만은 아닐 것입니다.

 

가령 게임을 좋아하는 사용자라면, 때때로 멀티스레드를 지원하지 않는 게임이 존재한다는 사실도 인지할 수 있고, 싱글스레드로 작동하는 게임이 더 빠르게 작동하는 결과를 보신 경우도 있을 것입니다.

이러한 차이로 꼭 싱글스레드이기 때문에 나쁜 것은 아닙니다.

 

하지만 JavaScript 에서는 싱글 스레드 방식으로 동작하고 있지만, 이 싱글 스레드 방식에 대해서 이해를 돕기 위해 하나의 예제를 작성해보겠습니다.

 

console.log("hi")
console.log("hello")
console.log("bye")

이미 따라오신분들은 어떻게 실행될 지 알고 계시리라 생각합니다.

각각 hi, hello, bye 가 한줄씩 출력되어 총 3줄의 명령이 출력되었을 것입니다.

이를 그림으로 표현하면 아래와 같습니다.

 

 

예약어를 기준으로 이미 모두 Hip이 아니라 Call-Stack 으로 이동되었을 것입니다. 이를 하나씩 처리하므로, 단계별로 나가는 형태로 3개의 줄로 이루어진 출력물이 나타나게 됩니다.

 

바로 이 방식을 "동기(Synchronous)" 라고 합니다. 즉 이 동기의 동작 방식은 이미 여러분들이 학습한 내용의 원리를 지칭한다고 생각하시면 편리합니다.

  1. JavaScript 의 코드가 실행되면, 순서대로 Hip, Call-Stack 에 실행할 함수가 쌓이게 됩니다.
  2. 쌓인 코드를 위에서부터 처리합니다. (아래가 아닌 위부터이므로 첫번째 줄부터 실행) 
  3. 실행된 함수는 Call-Stack 에서 더이상 처리할 것이 없으므로 종료합니다. 

하지만, 몇몇개의 함수나, 우리가 이전에 개념적으로 다루었던 DOM( Document Object Mode) 에서 사용되는 일부 Method 의 작동이나, 기능들은 비동기( asynchronous) 라는 개념으로 동작하는 것이 있습니다. 

이 비동기를 설명하기 위해서 반드시 설명해야 하는 개념이 추가로 존재하기 때문에, 이를 설명드려보도록 하겠습니다. 

 

3. Background, TaskQ, EventLoop

 

이름만 들어도 머리가 아플수도 있는 개념인가요?

사실 어렵다고만은 할 수 없는 개념입니다.

 

이를 위해 JavaScript 의 기능 하나를 예제를 가지고 왔습니다.

바로 setTimeOut 입니다. 이를 통해 예시를 작성해보겠습니다. 

console.log(1)
setTimeout(Xe,1000)
function Xe(){
    console.log(2)
}
console.log(3)

 

여기서 setTimeout 에 대한 개념을 간단하게 설명드리자면, 첫번째 매개변수에는 실행할 함수나 변수를, 두번째 매개변수에는 ms 단위로 언제 실행할지에 대해서 작성하는 것입니다.

 

우리가 위에서 학습한 "동기" 의개념으로 따진다면, Console.log(1) 이 먼저 Call-Stack 에서 출력되고, setTimeout 에서 1초 지연을 설정했으니, 1초뒤에 console.log(2) 가 실행되고, 그 뒤에 3이 실행되는 것으로 인지할 수 있겠습니다.

 

하지만 결과는 다릅니다.

출력 결과물은 1,3,2 가 순서대로 출력됩니다. 여기에 이제 비동기 개념이 발생합니다. 

JavaScript 의 언어가 동작하는데에서는 이전 포스팅에서 잠시 언급했듯이, 브라우저에 "엔진"이 존재합니다.

이 엔진에서는 내장객체도 포함하고 있는데, 대표적으로 DOM, Window 도 이에 포함된다고 할 수 있습니다.

 

JavaScript Engine from Sessionstack blog

 

우리가 알고 있는, JavaScript 의 Engine 은 두가지 요소로 구성되어 있습니다. 제가 Hip 이라고 표현했던,  MemeryHeap , 그리고 실제 출력이 되는 명령들을 순차적으로 한줄씩 처리하는 Call-Stack 이 존재합니다. 하지만 브라우저에서 제공하는 엔진은 여기에 몇가지 추가적 개념을 적용하여 비동기형태의 동작도 가능케 합니다.

 

JavaScript Engine in Google Chrome V8 Engine from Sessionstack Blog

 

기존의 Heap과, Call Stack 영역에 추가로 "Web APIs" 의 기능, 즉 DOM 과 같은 내장 객체등을 통해 비동기 동작 역시 가능케 함을 인지해야합니다.

 

위의 예시 코드를 가지고 설명드리자면, 이제 그림을 조금 더 활용해보도록 하겠습니다. 

 

그림의 순서대로 이해하기 최대한 쉽게 작성하면 위와 같습니다.

먼저 console.log 로, 각 1,2,3 이 존재하기때문에 Call Stack 에 순차적으로 처리를 위해 대기열에 올라가면, 1,2,3 순서대로 처리하는 것은 같습니다.

 

하지만 여기서 setTimeout 기능을 이용하면, 1초 뒤에 함수를 실행하는 명령을 받았고, 이는 DOM 에서 작동하는 기능, 즉 WebAPI 기능이므로, CallStack 은 백그라운드 영역으로 이 작업을 전달합니다.

그리고 3 의 작업을 처리하기때문에, 먼저 1,3 이 출력됩니다.

 

이후, 백그라운드에서 1초의 지연시간이 종료되면, 출력해야하는 숫자 2를 TaskQ 로 이동합니다.

이 TaskQ 에서 Call Stack 이 모든 작업을 처리한 것을 확인한 뒤, 이제 CallStack 으로 다시 이동하여, console 에 출력합니다.

 

이 백그라운드로 이동하여, 콘솔로 출력되기까지의 과정을 "Event Loop", 이벤트 루프라고 지칭합니다.

즉, 익명함수를 통해 모든 코드를 검사하고, Heap 과 CallStack 으로 나누고, 그를 콘솔에 처리하는것이 "동기" 라면, CallStack 에서 함수를 판단하여 백그라운드로 이동한 뒤 , TaskQ 로 이동하고, CallStack 이 모든 작업을 처리한 것을 확인 한 뒤, 다시 CallStack 으로 이동하는 일련의 과정을 Event Loop 라고 합니다.

 

이제 DOM 을 본격적으로 사용하게 될 것이고, 이후에 나오는 일부 개념들이, 이 비동기 과정을 거치므로,

이러한 기본 개념의 대한 이해가 반드시 필요합니다.

 

다음 포스팅에서는 setTimeout, setTimeinterval 에 대한 내용을 학습해보도록 하겠습니다.

 

감사합니다.

반응형

댓글