
그동안 블로깅을 소홀히했다.
핑계라면 핑계지만, 새로 배우게된 stack, queue, BFS, DFS 등의 자료구조..
동기(synchronous) 비동기(asynchronous) 등의 개념들이 머릿속에서 복잡하게 얽혀서
어디서부터 어떻게 정리해야할지 엄두가 나지 않았다..

하지만, 계속해서 새로 배우는게 많은 시점에서 더이상 미룰수는 없는것..
하나하나 차근차근 정리해나가고자 한다. 그 첫번째로 이벤트 루프(Event-loop)에 관하여 정리하고자 한다.
자바스크립트의 비동기적(asynchronous) 처리를 배우면서
callback 함수, promise, async 등을 배웠다.
원래 이 3가지의 개념과 사용법을 정리해 총 3개의 포스팅을 하려고했다.
그런데 막상 정리하다보니 이 각각의 문법 자체보다도
왜 이런것들을 사용해야하는지?에 대한 배경이 중요하다는 걸 느꼈다.
그 근본 배경에는 바로 오늘 정리해볼 Event Loop가 있다.
이것은 또한 javascript 엔진의 동작방식에 대한 이해에도 도움이되기 때문에 매우 중요하다고 생각한다.
우선 javascript 엔진에 대한 이해가 필요하다.
javascript 엔진은 javascript 코드를, 즉 사람이 이해하는 언어로 작성된 것을
컴퓨터가 이해할수 있는 언어인 바이트 코드로 컴파일(변환) 해주는 프로그램 또는 인터프리터다.
종류가 다양한데 내가 자주 사용하는 chrome 브라우저와 node.js에서는
구글이 개발한 V8이라는 엔진을 사용한다.

이 엔진은 그림에서와 같이 크게 두 부분으로 구성된다.
바로 Memory Heap과, Call stack
이 두가지 개념은 이후 포스팅에서 더 자세하게 다루도록 하겠다.
다만, Call stack은 살짝 더 찍먹하고 넘어가야간다.
위에서 언급했듯 Call stack은 함수 실행 순서대로 아래에서 부터 쌓이고,
실행될 때는 위에서부터 순서대로 실행된다.
function first() {
second();
console.log('첫 번째');
}
function second() {
third();
console.log('두 번째');
}
function third() {
console.log('세 번째');
}
first();
third();
위 코드의 실행순서를 그림으로 간단하게 표현하면 아래와 같다.

stack 코드 및 사진 출처 : zerocho 님 블로그
이렇게 함수가 위에서부터 실행이 되면 stack에서 하나씩 지워진다.
참고로 stack의 맨 아래에 있는 main()은 전역 컨텍스트다.
(일단은 브라우저에서 스크립트를 로딩해서 실행하는 순간 자동으로 생기는 것이다라는 정도만 이해하자.)
javascript엔진은 이 call stack을 사용해서 코드를 처리한다.
위 그림처럼 이 call stack은 한번에 하나씩 추가되고 사라진다.
간단하게 말해서, 한 번에 하나의 작업밖에 못한다는 것이다.
흔히들 javascript는 싱글 스레드(single thread)라는 얘기를 많이 하는데,
이 의미는 Call stack이 하나라는 것이다.
동기 (synchronous) : 동기는 말그대로 동시에 발생한다는 뜻이다. 요청을 하면 시간이 얼마 걸리던지 요청한 자리에서 결과가 주어져야한다. 다르게 말해서, 한 작업이 끝날때까진 다른 작업을 할 수 없다는 뜻이다.
이를 blocking이라 한다.
비동기(asynchronous) : 반면에 비동기는 동시에 일어나지 않는다는 뜻이다. 요청한 그 자리에서 결과가 주어지지 않는다는 것이다.
이를 그림으로 보면 훨씬 직관적으로 이해가 간다.

아, 참고로 asynchronous의 정확한 발음은 '어싱크로노스'가 아닌 '에이싱크로노스'다..
(중요하진 않지만 내가 처음에 잘못 발음했던 기억이 나서..)

각설하고, javascript 엔진은 기본적으로 위에서 본 stack의 특징 때문에, 또 stack이 하나라는 점에서
동기적(synchronous)으로 작동한다. (한번에 하나의 작업만 순서대로 하기때문에)
그런데 javascript 엔진도 비동기적(asynchronous)인 처리를 할 수 있다.
이는 지금부터 살펴볼 이벤트 루프(Event-loop)와 관련이 있다.
오케이. 비동기 처리를 할 수 있다는 건 알겠는데,왜 굳이 비동기가 필요한데? 라고 생각할 수 있다.
우리가 자주 쓰는 chrome 브라우저를 상상해보자. 유튜브 사이트에 들어가서 영상을 하나 보려고 클릭했다.
이때, javascript엔진이 동기적으로 작동한다면..?
동영상이 완전히 다 로드될때까지 화면에는 검정색 플레이어 화면만 놓여있고,
나머지는 흰 공백으로 아무것도 렌더링되지 않을 것이다.(답답해 죽을것이다.)
따라서 브라우저에서 비동기적인 처리는 필수라고 볼 수 있다.
비동기적인 처리 때문에 우리는 유튜브에서 영상이 다 로드되지 않아도 렌더링된 나머지 화면을 볼 수 있다.
위에서 봤던 javascript 엔진 그림에 몇 가지 요소가 추가된 그림을 살펴보자.

기존 그림에서 오른쪽에 Web APIs와 Callback Queue 그리고 Event Loop라는 것들이 추가됐다.
우선 확실히 해야할 것은 이 3가지는 javascript 엔진이 제공하는 것이 아니라는 점이다.
이는 브라우저(런타임)에서 제공하는 것이다.
function hello () {
console.log("안녕 나는 Taeroring")
};
setTimeout(hello, 5000);
// 5초 뒤 함수 hello 실행.
setTimeout() : 위 예제처럼 첫번째 인자로 함수를, 두번째 인자로 시간(밀리세컨드)를 전달하면 설정한 시간이 지난 뒤 전달받은 함수를 실행시킨다.드디어 이 포스팅의 주인공을 살펴보겠다..
그럼 과연 Call stack이며, Web APIs며, Callback Queue, Event-Loop는 어떻게 동작한다는 것인가..
지금부터 예제 코드를 그림으로 살펴볼 예정이다.(스압주의)
예제코드 및 그림 출처
console.log('Hi');
setTimeout(function cb1() {
console.log('cb1');
}, 5000);
console.log('Bye');
실행하기 전 초기 상태다. 모든 영역이 비어있다.

javascript가 먼저 읽는 위에서 부터 실행, 즉 console.log("hi")가 Call stack에 추가된다.

console.log("hi")가 실행된다.

실행이 됐으니 Call Stack에서 사라진다.

setTimeout( function cb1() {...} )이 Call Stack에 추가된다.

setTimeout( function cb1() {...} ) 이 실행된다! 동시에 setTimeout()은 Web API를 호출한다! 그리고 저 Web APIs 영역에는 timer가 생성된다. (째깍째깍)

setTimeout( function cb1() {...} ) 이 Web Api를 호출하면 실행이 된 것이다. 그렇기 때문에 Call Stack에서 사라진다.

그 다음 줄인 console.log('Bye')가 Call Stack에 추가된다.

console.log('Bye')가 실행된다.

console.log('Bye')가 실행됐으므로 Call Stack에서 제거된다.

5000ms = 5초가 지난 뒤 Web APIs에 있는 timer가 끝나면 안에 실행되기로 약속되었던
콜백함수(cb1)을 Callback Queue에 밀어넣는다.
(Callback과 Queue는 이후 포스팅에서 따로 다룰 예정이다. 지금은 단순히 저 영역이 있다는 것만 알아두자.)

이벤트 루프는 콜백함수 cb1을 Callback Queue에서 Call Stack으로 밀어넣는다.
(단, Call Stack이 완전히 비어있을 때만 밀어넣는다.)

콜백함수 cb1이 실행되고 그 함수 스코프 안에 있는 console.log('cb1')을 Call Stack에 추가한다.

console.log('cb1')이 실행된다.

console.log('cb1')가 Call Stack에서 제거된다.

마지막으로 콜백함수 cb1의 실행이 완료되었으므로 cb1 또한 Call Stack에서 제거된다.

console.log('Hi');
setTimeout(function cb1() {
console.log('cb1');
}, 5000);
console.log('Bye');
지금까지 우리가 봤던 예제코드다.
원래 javascript 엔진의 특성상
이런 동기적(synchronous)인 흐름으로 가야할 것만 같은데,
위에서부터 쭉 본 그림처럼 Web Apis때문에 5000ms를 기다리며 blocking되지 않고
비동기적(asynchronous)으로 처리할 수 있게된 것이다.
이는 브라우저라는 특수성 때문에 가능한 것이다.
하나만 마지막으로 짚고 넘어가자.
위 그림에 있는 Event-loop는 잠깐 과정 설명에서 언급했는데 역할은 단순하다.
항상 Call Stack과 Callback Queue를 감시하는 것이다.
감시하다가 Call Stack이 비었다면 그때 Callback Queue에서 대기하고 있던 이벤트를
Call Stack에 밀어넣어서 실행시키게 한다!
오늘 포스팅한 이벤트루프(Event-loop) 동작은 javascript에서 굉장히 중요하다.
또한, 이후에 포스팅하게 될 Callback, Promise, async를 이해하는데 도움이 많이 될 것이다.