자바스크립트는 어떻게 동작할까?
자바스크립트는 본래 HTML과 CSS로 만들어진 웹페이지를 동적으로 만들어주는 프로그래밍 언어였습니다. 이러한 자바스크립트가 점점 대중화되면서 Front-End뿐만 아니라 Back-End, APP에서도 사용되고 있습니다. 이런 유용한 언어의 동작원리에 대해서 알아보려 합니다. 그러기 위해서는 먼저 구조에 대해서 알아볼 필요가 있습니다.
자바스크립트 엔진
자바스크립트 엔진은 자바스크립트의 코드를 이해하고 실행을 도와주는 역할을 합니다. 대표적으로 Node.js와 크롬 브라우저에 내장되어 있는 V8 엔진이 있습니다. 이 외에도 Edge, Firefox 등 브라우저마다 각각의 엔진을 가지고 있습니다. 크롬 브라우저를 대표적으로 많이 설명합니다. 자바스크립트 엔진은 아래 이미지와 같이 메모리 힙과 콜 스택으로 이루어져 있습니다.
Memory Heap
메모리 힙은 프로그램에서 선언한 변수, 객체, 함수 등 모든 메모리 공간을 할당하는 곳입니다.
Call Stack
💡자바스크립트는 싱글 스레드( 단일 스레드, 하나의 스레드 )이기 때문에 한 번에 한 가지 일 밖에 처리할 수 없다. 이것을 Call Stack이 하나라고 표현한다.
호출 스택은 실행해야 할 코드의 실행 순서를 기록해 두고, 하나씩 순차적으로 처리해 주는 곳입니다.
호출 스택의 사이즈보다 많은 요청이 들어오거나 무한 루프에 빠지게 되면 에러가 발생하게 됩니다. 이때 발생하는 에러를 개발자 포럼 사이트의 이름으로 유명한 스택 오버플로우( Stack Overflow )라고 합니다. 아래와 같이 호출 스택 사이즈가 초과되었다는 오류를 보게 됩니다.
자바스크립트 런타임
메모리 힙과 호출 스택으로 이루어진 자바스크립트 엔진만으로는 웹이 동작하지 않습니다. 엔진 이외에 Web APIs, Callback Queue 그리고 Event Loop로 이루어진 자바스크립트 런타임 환경이 웹 동작을 완성시킵니다.
Web APIs
Web API는 브라우저 자체에서 제공하는 API이며 자바스크립트의 싱글 스레드 영향을 받지 않고 독립적으로 이벤트를 처리할 수 있습니다.
대표적으로 DOM Event, AJAX Request, setTimeout 등 비동기 이벤트를 처리합니다. 자바스크립트 엔진까지 동기적으로 작동시켰다면 Web APIs부터는 비동기적으로 작동시킬 수 있도록 지원하는 역할을 합니다.
Callback Queue
콜백 큐는 Web API에 있는 이벤트가 실행되고 나면 자바스크립트에서 실행할 콜백 함수를 보관하고 있는 곳입니다.
큐는 스택( LIFO, Last In First Out )의 후입선출로 처리하는 방식과 반대로 선입선출( FIFO, First In First Out )로 처리합니다. 하지만 ES6부터 Promise, Object.observe 등의 콜백 함수가 저장되는 MicroTask Queue가 도입되면서 아래의 우선순위가 생겼습니다.
🔍 Callback Queue 우선순위
MicroTask Queue ( Job Queue ) > Animation Frames > Task Queue ( Event Queue )
Micro Task Queue | Animation Frames | Task Queue |
Promise Async / Await process.nextTick Object.observe MutationObserver |
requestAnimationFrame | setTimeout() setInterval() setImmediate() |
예를 들어 TaskQueue에 대표적인 setTimeout 함수가 먼저 Queue에 담기더라도 이후에 담긴 MicroTask Queue의 Promise 함수가 있다면 이벤트 루프는 Promise 함수를 먼저 꺼내가게 됩니다.
setTimeout(() => console.log("Task Queue"), 0); // 시간을 0초로 세팅해도 순서는 같다 *
console.log("Call Stack");
Promise.resolve().then(() => console.log("MicroTask Queue"));
// Print
/*
첫 번째 출력: Call Stack
두 번째 출력: MicroTask Queue
세 번째 출력: Task Queue
*/
Event Loop
💡 호출 스택이 비어있는 경우, 이벤트 루프는 대기열에서 첫 번째 이벤트를 가져와 호출 스택으로 푸시(push)한다. 이러한 반복을 이벤트 루프에서 틱(Tick)이라고 한다.
이벤트 루프는 호출 스택과 콜백 큐를 주기적으로 감시하고 있다가 호출 스택이 비어있는 상태라면 콜백 큐에 있는 함수(콜백 함수, 이벤트 핸들러 등)를 꺼내서 호출 스택으로 옮겨주어 자바스크립트 코드가 실행될 수 있도록 돕는 역할을 합니다. 만약 호출 스택에 쌓여있는 함수가 있으면 기다립니다.
이벤트 루프( Event Loop )는
호출 스택( Call Stack )이 비어 있지 않다면
콜백 큐( Callback Queue )에 있는 함수를 옮기지 않는다.
정리
자바스크립트는 엔진에 의해 싱글 스레드 방식으로 동작하며 원래는 동기적으로 실행됩니다. 하지만 Web APIs, Callback Queue, Event Loop들의 의해서 비동기적, 멀티 스레드인 것처럼 동작하는 언어가 되었습니다 🙌
전체 흐름을 도식화하여 자세히 보고 싶다면 아래 이 링크에서 테스트해 보실 수 있습니다.
Reference
어쨌든 이벤트 루프는 무엇입니까? | Philip Roberts | JSConf EU
JS로 개발하는데 내부 동작을 모르면 곤란합니다 | 코드 실행 과정
'PROGRAMMING > Javascript & Typescript' 카테고리의 다른 글
[Javascript] 자바스크립트 물음표(?) 2개 / 느낌표(!) 2개 / 물결(~) 2개 연산자(feat.예시코드) (33) | 2023.03.07 |
---|---|
[Typescript] 타입스크립트, 정의 | 특징 | 비교 | 장단점 | 설치방법 | 문법 (feat.mac / window) (26) | 2023.03.03 |
[Javascript] filter / map / reduce 기능, 구조, 예제코드 (feat.ES6) (29) | 2023.02.25 |
[Javascript] ECMAScript, ES5와 ES6 비교 / 기능 / 사용법 / 예제코드 (35) | 2023.02.15 |
Babel, 바벨 | 정의 / 필요성 / 설치 / 설정 / 오류 (9) | 2023.01.27 |