자바스크립트 엔진
자바스크립트는 Single Thread 기반 언어
자바스크립트는 Single Thread기반의 언어입니다. 쓰레드가 하나라는 말은 한번에 하나의 작업만 처리 가능하다는 뜻이죠. 하지만, 우리가 자바스크립트를 사용하는 환경인 Node는 여러 개의 HTTP요청을 처리하고, 브라우저는 이벤트 처리나 애니메이션 실행 등 다양한 일을 동시에 처리합니다.
이것은 마치 싱글 쓰레드가 아닌 것처럼 여러 작업을 동시에 처리합니다. 이것은 마치 멀티 쓰레드처럼 동작하지 않는가?라는 생각을 가질 수 있습니다.
Javascript가 이렇게 동작이 가능한 이유를 알아보겠습니다.
자바스크립트 엔진
JS엔진의 대표적인 예는 google에서 만든 V8엔진입니다. 우리는 이 v8엔진을 통해 웹에서 Javascript 코드를 실행시킬 수가 있습니다.이 엔진은 자바스크립트 코드를 파싱하고 기계어 수준으로 변환하여 컴퓨터에서 실행할 수 있도록 합니다.
엔진의 주요 두 구성요소는 Memory Heap과 Call Stack입니다. 이것에 대해 알아보고 각각은 무엇이고 Web APIs는 무엇이며 Queue그리고 Event Loop는 어떻게 동작하며 설명하겠습니다. 이것들을 모두 알게 되면 우리는 웹에서 자바스크립트가 어떻게 동작하는지 완전히 이해 할 수 있습니다.
Memory Heap
자바스크립트에서 메모리 관리는 자동으로 이루어집니다. 이를 위해 자바스크립트 엔진은 메모리 힙(Memory Heap)과 호출 스택(Call Stack)이라는 두 가지 메모리 공간을 사용합니다.
메모리 힙은 동적으로 할당되는 변수, 객체, 배열 등의 데이터를 저장하고 자바스크립트 엔진이 자동으로 관리합니다. 이때, 더 이상 사용하지 않는 데이터는 가비지 컬렉터(Garbage Collector)에 의해 자동으로 제거됩니다.
Call Stack
호출 스택(Call Stack)은 함수의 호출과 반환에 관한 정보를 저장하는 공간입니다. 호출 스택에는 현재 실행 중인 함수와 이 함수 안에서 호출된 다른 함수들의 정보가 저장됩니다. 함수가 호출되면 해당 함수의 정보가 호출 스택에 추가되고, 함수의 실행이 완료되면 해당 함수의 정보는 호출 스택에서 제거됩니다.
코드로 간단히 Call Stack 동작을 알아 보겠습니다.
function foo() {
console.log("Hello");
}
function bar() {
foo();
console.log("World");
}
bar();
- bar() 실행 컨텍스트가 Call Stack 추가
- bar()안에 있는 foo() 실행 컨텍스트가 Call Stack 추가
- foo() 함수가 실행이 끝나면 Stack에서 제거
- bar() 함수 실행이 끝나면 Stack에서 제거
위와 같은 과정을 거치며 JS함수들이 실행되어 집니다.
자바스크립트는 메모리 관리를 따로 해줄 필요없습니다. 자바스크립트 엔진이 메모리 사용량을 모니터링하고 필요하지 않은 데이터를 자동으로 제거함으로써 메모리를 효율적으로 관리합니다.
Web APIs
Web API는 주로 비동기적인 작업을 처리하거나 브라우저의 다양한 기능에 접근하기 위해 사용됩니다. 자바스크립트 코드에서 Web API를 호출하면, 해당 작업이 브라우저의 다른 스레드에서 비동기적으로 실행됩니다. 그리고 작업이 완료되면 결과나 이벤트가 자바스크립트 엔진(Event Queue)으로 다시 전달되어 처리됩니다.
Event Queue
Evnet Queue는 비동기 작업의 결과나 Web API 결과를 일시적으로 저장합니다. 위에서 말했다시피 JS는 단일 쓰레드여서 한번에 하나의 작업만 처리합니다. 하지만 비동기 함수들을 Web API에서 작업을 요청하면 브라우저의 쓰레드에서 비동기적으로 처리가 됩니다. 그리고 작업이 완료되거나 이벤트가 발생하면, 결과나 이벤트가 Event Queue에 저장됩니다.
또한 Event Queue는 하나로 이루어진 것이 아니라 3가지 Queue로 이루어져 있습니다.
Microtask Queue > Animation Frames > Task Queue
- Task Queue: setTimeout(), setInterval(), setImmediate()와 같은 task를 넘겨받는다.
- Microtask Queue: 이 Queueu는 Promise나 async/await, process.nextTick, Object.observe, MutationObserver과 같은 비동기 호출을 넘겨받는다.
- Animation Frames: requestAnimationFrame과 같이 브라우저 렌더링과 관련된 task를 넘겨받는 Queue이다.
정해진 우선순위에 따라 먼저 작업이 처리됩니다. ( 이벤트 루프로 넘어가는 작업 )
console.log('script start'); // A
setTimeout(function () { // B
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(function () { // C
console.log('promise1');
})
.then(function () { // D
console.log('promise2');
});
console.log('script end'); // E
* 실행결과 *
script start -> script end -> promise1 -> promise2 -> setTimeout
이 코드를 보다시피 먼저 호출된 setTimeout보다 Promise가 먼저 처리되는 것을 알수 있습니다.
Event Loop
마지막으로, 이벤트 루프입니다. 이벤트 루프는 Call Stack과 Event Queue를 주시하며, Call Stack이 비어있을 때 Event Queue에 있는 작업들을 하나씩 순서대로 Call Stack으로 이동시켜 실행합니다.
이로 인해 비동기 작업의 결과나 이벤트가 순차적으로 처리되며, 동시에 여러 작업을 처리하는 듯한 효과를 얻을 수 있습니다.
마무리
자바스크립트 엔진은 자바스크립트 코드를 실행하는 데 사용되며, 코드의 처리를 담당합니다.
자바스크립트는 단일 스레드로 동작하며, 비동기적인 작업을 처리하기 위해 Web API를 활용합니다. Web API는 웹 브라우저나 Node.js 환경에서 제공하는 인터페이스로, 비동기 작업을 처리합니다.
JS언어 자체는 단인 스레드이므로 엔진의 쓰레드를 사용하여 비동기 작업을 할 수 있습니다.
간단히 과정을 요약하면
- 코드가 실행되다 비동기 함수를 만나면, Web API에 요청을 보낸다. 해당 작업은 Web API에 의해 실행되고, Call Stack은 다른 작업을 지속적으로 처리한다.
- 비동기 작업이 완료되면 Event Queue에 추가됩니다.
- Event Loop는 콜 스택이 비는 것을 관찰하며 비어 있다면 Event Queue에 있는 첫 콜백 함수를 콜 스택으로 이동시킵니다.
- 마지막으로 해당 함수가 실행되고 함수가 실행되며 Stack에서 제거 됩니다.
이렇게 비동기 작업은 Web API에서 비동기적으로 실행되며, 완료된 결과나 콜백 함수는 이벤트 큐에 저장됩니다. 이러한 메커니즘을 통해 자바스크립트는 비동기 작업을 효율적으로 처리하고, 동시에 여러 작업을 순차적으로 처리하는 듯한 효과를 얻을 수 있습니다.