진짜 ! 쉽게 알아보는 자바스크립트 동작 원리
Javascript
를 처음 접하는 분들이 가장 어려워 하는 부분이 비동기 동작입니다. 그래서 오늘은 Javascript
가 어떻게 비동기 기반으로 작동하고, 원리는 무엇인지 간단하게 알아보도록 하겠습니다.
자바스크립트는 머리가 하나 ! (싱글 스레드)
자바스크립트는 싱글 쓰레드 언어입니다. 이 의미는 쉽게 풀어쓰면 한 번에 한 가지 일 밖에 처리할 수 없다는 것을 의미합니다. (어려운 용어로는 Call stack
이 하나라고 표현 합니다)
현실 세계에 비유해서 이야기 하면 멀티 태스킹 안되는 글쓴이와 같습니다.
글쓴이는 하나의 일을 할 때, 다른 일을 동시에 하지 못합니다. 예를 들어, 게임을 하면 대화를 듣지 못하고, 대화를 하면 게임에 집중 못하는 그런 부류의 사람인거죠.
즉, 현재 실행하고 있는 함수가 있는 경우에 다른 일을 할 수가 없고, 다른 일들이 블락되게 됩니다. 이렇게 되면 브라우저에서 오래 걸리는 작업이 실행될 경우, 웹 페이지의 UI는 멈춰버리고, 사용자는 어떠한 행동도 할 수 없게 됩니다.
예를 들어, 크롬에서 alert
창을 띄어 보면 alert
창을 닫기 전까지는 아무것도 할 수 없는 것과 마찬가지 입니다.
1분 이상이 시간이 소요되는 오래 걸리는 작업이 있다고 가정 해보겠습니다. 자바스크립트는 머리가 하나이기 때문에, 1분 동안 아무것도 안하고 기다리면 브라우저는 아무런 행동(화면 스크롤, 버튼 클릭…)도 하지 못하게 됩니다. 그렇다면 어떻게 해야할까요? 이 때 사용하는 것이 바로 비동기 콜백입니다.
비동기 콜백을 이해하기 위해서는 JS Engine
과 web API
그리고 이벤트 루프에 대한 이해가 먼저 필요합니다.
자바스크립트 코드를 실행 시켜 줘 (JS Engine)
자바스크립트 엔진은 Javascript
코드를 이해하고 실행을 도와주는 녀석입니다. 대표적인 JS engine
으로 V8엔진(Chrome
, Node.js
에서 사용)이 있으며, 이외에도 각 브라우저 별로 여러가지 엔진들이 존재합니다. 자바스크립트 엔진은 크게 Memory Heap
과 Call Stack
으로 이루어져 있습니다.
Memory Heap
데이터를 임시 저장하는 곳으로, 함수나 변수, 함수를 실행할 때 사용하는 값들을 저장합니다.
Call Stack
코드가 실행되면 코드의 내부의 실행 순서를 기록해 놓고, 하나씩 순차적으로 진행할 수 있도록 도와주는 곳입니다.
예를 들어 위의 함수를 실행하면 아래와 같은 Call Stack
이 차례대로 생기게 됩니다.
즉 실제로 코드를 실행했을 때, 다음에 실행되어야 할 코드를 순서대로 기록을 하며, 순차적으로 코드를 실행 할 수 있게 도와 줍니다.
Call Stack
이 호출되는 과정이 좀 더 궁금 하시다면 사이트(링크)에서 테스트 해보실 수 있습니다. 그런데 만약에 Call Stack
에 들어간 코드 중 일부가 시간이 오래 걸린다면 어떻게 될까요?
노랑색으로 칠해진 코드가 어떠한 이유로 시간이 오래걸리게 된다면 당연히 다음에 호출되어야 코드는 실행에 시간이 걸릴 수 밖에 없습니다. 이와 같은 상황이 서두에서 이야기했던 특정 코드가 오래 걸려 다른 코드를 실행하지 못하게 되어 block
이 된 상태입니다.
마냥 오래 걸리는 일을 지켜볼 순 없으니, 어떻게 해야 할까요?
이 때 효과적으로 event
를 관리하기 위해 필요한 것이 바로 web API
와 Callback Queue
, event loop
입니다.
능률적으로 일하기 (web API, Callback Queue, event loop)
라면을 끓일 때를 생각해보겠습니다.
라면의 조리 과정은 물을 올리고, 라면을 넣고, 파나 계란을 넣는 순서로 진행됩니다. 이 때, 우리가 물을 끓이면 끓을 때까지 물 앞에서 지켜보고 있다가 라면을 조리하는 분은 거의 없을 것입니다. 왜냐하면 물을 끓이면서 다른 일을 할 수 있는데 기다리는 것은 능률적이지 않기 때문이죠.
그래서 물을 올려놓은 다음 라면도 꺼내고, 파도 썰고, 계란도 꺼내어 조리를 합니다.
Javascript
의 세계도 마찬가지로 하나하나씩 순차적으로 진행하는 것이 아니라 효과적으로 시간을 분배하면서 이벤트를 관리합니다. 그럼 어떻게 진행되는지 하나하나씩 알아보도록 하겠습니다.
물이 끓고 나면 해야 하는 일 (Callback Function)
라면에 물이 다 끓으면 어떻게 하나요? 자연스럽게 우리는 팔팔 끓는 물에 라면을 넣거나, 스프를 넣습니다.
스프 먼저, 면 먼저처럼 기호가 다르듯, 하나의 일이 끝나면 사람마다 다음 해야 할 일이 다를 수 있습니다.
자바스크립트에서는 함수가 실행이 끝나면, 다음에 실행할 일을 정할 수 있는데 이 것을 Callback
이라고 부릅니다.
아래의 코드를 예로 들어 보겠습니다.
setTimeout
함수는 첫번째 인자로 callback function
을 받고, 두번째 인자로 기다릴 시간을 받는 함수입니다.
위 코드를 실행해보면 1초 후에 function(){ console.log('1초가 지나갔다')}
가 실행 됨을 알 수 있습니다.
즉, callback Function
은 함수가 정해놓은 일이 끝난 뒤, 후속으로 하는 일을 알려주는 함수입니다.
setTimeout
이외에도 Callback
함수는 AJAX
, Dom
을 관리하는 event
등에 사용되며, javascript
를 효과적으로 사용하기 위해선 반드시 알아야 하는 개념입니다.
오래 걸리는 일 따로 처리하기 (browser web APIs)
토이가 아침 식사를 준비한다고 생각해보겠습니다.
토스트기 없이 프라이팬에 빵을 구우면, 빵을 굽는 동안은 식탁을 셋팅 한다거나, 음료를 준비하는 등 다른 일을 할 수가 없습니다. 반면에 토스트기가 있다면 토스트기에 빵 굽는 일은 시키고, 저는 다른 일을 할 수 있습니다.
이를 브라우저에 대입 해보면, browser web API
가 브라우저에서 토스트기와 같이 오래 걸리는 작업을 대신해 주는 역할을 합니다. browser web API
는 브라우저 안에 C++ 구현된 쓰레드로 주로 DOM event
, AJAX request
, setTimeout
등 비동기 이벤트를 처리합니다. javascript
싱글 쓰레드의 영향을 받지 않고, 독립적으로 이벤트를 처리할 수 있습니다.
머리 속에 다음에 할 일 생각해 놓기 (Callback Queue)
아까 아침 식사를 준비하던 때로 돌아 가보겠습니다.
저는 지금 토스트기에 식빵을 넣어둔 상황이고, 빵이 다 구워지면 빵에 잼을 바를 생각입니다.
이처럼 사람은 어떤 일이 끝나면 다음에 할 일을 머리 속에 저장해 놓지만, 브라우저의 경우 Callback Queue
에 저장 해놓고 사용합니다.
즉, Callback Queue
는 browser web API
에 있는 event
가 실행되고 나면 javascript
에서 실행할 callback
을 저장하고 있는 저장소입니다.
예를 들어 위 코드를 실행하게 되면 1초가 지나면 Callback Queue
에 function(){console.log(‘1초가 지나갔다’)}
함수가 담기게 됩니다.
할 일을 정돈하기 (event loop)
아침 식사로 다시 돌아가 보겠습니다.
토스트기에 빵을 넣고, 저는 냉장고에서 우유와 과일을 꺼내 셋팅을 하고 있습니다. 우유를 따르던 중에 토스트에서 빵이 다 구어졌습니다. 저는 빵이 다 구어지면 잼을 바를 생각이었지만, 우유를 따르고 있기 때문에 우유를 마저 따르고 빵을 꺼내 잼을 바를 것입니다.
이처럼 사람은 능률적으로 일하기 위해 우유를 마저 따르고, 빵을 굽는 행동을 효과적으로 판단하고 행동합니다.
Javascript
내에서도 효과적으로 일을 처리하기 위한 비슷한 매커니즘이 있는데 이를 event loop
라고 부릅니다.
위 코드 실행했을 때, event loop
매커니즘이 어떻게 처리하는 알아보도록 하겠습니다.
처음 코드를 실행하면 console.log(‘시작’)
코드가 call stack
에 들어가게 되고 실행이 됩니다.
Call Stack
에 setTimeout
함수가 들어갑니다.
setTimeout
함수가 비동기 함수이기 때문에 Call Stack
에서 바로 실행되지 않고, web API
로 콜백 함수 timeout()
이 들어 갑니다.
다음 코드인 console.log('끝!');
을 실행합니다. 아직 1초가 지나지 않아 timeout()
함수는 여전히 web API에 있습니다.
1초가 지나 callback Queue
에 timeout()
함수가 들어 옵니다.
event loop
가 Call Stack
이 비어있는지 확인하고, timeout()
함수를 call Stack
으로 보냅니다.
timeout
함수 안에 있던 console.log(‘1초가 지났습니다’)
코드가 호출됩니다.
모든 함수가 호출되고 Call Stack
이 비워집니다.
결과적으로 event loop
는 Call Stack
비어있는지를 주기적으로 확인하여 Callback Queue
에서 Callback function
을 가져와 Call Stack
에서 Javascript
코드가 실행될 수 있도록 돕는 역할을 합니다. event loop
가 반복적으로 Call Stack
이 비어있는지 확인 하는 것을 tick
이라고 합니다.
Wrap-Up
이로서 javascript
환경에서 어떻게 비동기 이벤트들이 처리되는지 알아 보았습니다. 좀 더 자세하게 event loop
에 대해서 알고 싶다면 event loop
에 관한 전설적인 영상(링크)를 확인 해보시길 바랍니다.
참고 자료
https://engineering.huiseoul.com/자바스크립트는-어떻게-작동하는가-엔진-런타임-콜스택-개관-ea47917c8442