Alexander Zlatkov의 'How JavaScript works: an overview of the engine, the runtime, and the call stack' 를 보고 번역, 정리한 글 입니다.

Overview

대부분의 사람들이 V8 Engine을 이미 들어봤을 것이다. 또한 자바스크립트가 싱글 스레드 기반의 프로그래밍 언어이며 callback queue를 사용한다는 것을 알고 있을 것이다. 이 글에서는, 이러한 개념들과 자바스크립트가 실제로 어떻게 작동하는지 설명한다. 이러한 개념들을 알게 됨으로서, 내장 API들을 더욱더 효율적으로 사용할 수 있게 될 것이다. 만약 당신이 자바스크립트에 익숙하지 않다면, 이 글이 다른 언어와 비교했을 때 "자바스크립트가 왜 그렇게 이상한지"에 대해 설명을 해줄 것이다. 그리고 만일 당신이 숙련된 자바스크립트 개발자라면, 바라건대, 이 글이 매일 사용하고 있는 자바스크립트에 대한 새로운 관점을 얻었으면 한다.

 

The JavaScript Engine

JavaScript Engine의 가장 유명한 예시는 구글의 V8 Engine이다. V8 Engine은 크롬과 Node.js와 같은 환경 내에서 사용된다. 아래는 어떻게 생겼는지 매우 간단하게 표현한 그림이다.

 

 

Engine을 구성하는 두 가지 메인 컴포넌트

Memory Heap 메모리 할당이 일어나는 곳이다.

Call Stack 코드가 실행되면서 스택이 쌓이는 곳

 

The Runtime

setTimeout과 같은 대부분의 자바스크립트 개발자이 사용하는 API들이 브라우저내에 내장되어 있다. 하지만, 이러한 API들은 Engine에 의해 제공되지 않는다. 그러면, 이러한 API들은 어디서 올까? 실상은 조금 더 복잡하다.

 

 

우리는 자바스크립트 Engine을 갖고 있다. 하지만 실제로는 더욱 많은 것이 있다. DOM, AJAX, setTimeout 등과 같이 브라우저에 내장되어 있고 우리가 Web API라고 부르는 것들이 그것이다. 그리고 Event loopcallback queue 또한 있다.

 

The Call Stack

자바스크립트는 싱글 스레드 기반의 프로그래밍 언어이다. 즉, 한 개의 호출 스택을 갖고 있다. 따라서, 자바스크립트는 한번에 하나의 행동만 할 수 있다. 호출 스택은 기본적으로 우리가 프로그램 내에서 어디에 있는지를 기록해주는 자료구조이다. 우리가 함수 안으로 들어가면, 스택 위로 함수가 쌓인다. 그리고 함수에서 리턴을 하면, 스택에서 뽑아져 나오게 된다. 아래 예시를 통해 살펴보자.

 

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);

 

자바스크립트 엔진이 해당 코드를 실행시킬 때, 호출 스택은 비어있을 것이다. 그 후에, 아래와 같은 절차를 거친다.

 

 

각 호출 스택의 엔트리를 Stack Frame이라고 부른다. 그리고 이것은 오류가 발생했을 때, Stack trace가 어떻게 구성되어 지는지 보여준다 ㅡ 이것은 기본적으로 오류가 발생했을 때 호출 스택의 상태이다. 아래 코드를 보자.

 

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();

 

이 코드를 크롬에서 실행시켰을 경우, 다음과 같은 Stack trace가 나타난다.

 

 

"Blowing the stack" ㅡ 이것은 호출 스택의 최대 크기에 도달했을 때 일어난다. 그리고 이것은 특히 당신이 반복문이나 재귀문을 테스팅하지 않고 실행시킬 때 꽤나 자주 일어난다. 아래 코드를 보자.

 

function foo() {
    foo();
}
foo();

 

자바스크립트 엔진은 이 코드를 실행시키면서 foo를 호출한다. 하지만 이 함수는 종료하는 어느 조건도 없으므로 계속해서 foo를 호출할 것이다. 따라서 함수를 호출할 때 마다 호출 스택에 함수가 추가되고 이러한 과정이 계속 반복되게 된다. 이러한 상황을 그림으로 보면 아래와 같다.

 

 

이렇듯 함수 호출 횟수가 호출 스택의 크기를 넘겨버리면, 브라우저에서 아래와 같은 에러를 띄울 것이다.

 

 

싱글 스레드 기반의 프로그래밍 언어는 멀티 스레드 환경에서 일어날 수 있는 문제들(deadlock 등)과 같은 복잡한 상황을 고려하지 않아도 되므로 코딩하기 비교적 쉬울 수 있다. 하지만 싱글 스레드는 여러가지 제한 사항이 있다. 호출 스택이 한 개인 자바스크립트의 실행 속도가 느려진다면,,?

 

Concurrency & the Event Loop

당신이 실행하는 데에 굉장히 많은 시간이 소요되는 함수를 호출 스택에서 호출하면 무슨 일이 일어날까? 예를 들어, 브라우저 내에서 자바스크립트를 통해 복잡한 이미지 변형을 한다고 상상해보자. 당신은 물을 것이다 ㅡ 왜 이런 문제가 생기지? 문제는 호출 스택에서 해당 함수를 호출할 때, 브라우저는 실제로 아무것도 안한다는 점이다 ㅡ 막혀있는 것이다. 즉, 브라우저는 렌더링을 할 수 없으며 아무런 코드도 실행시킬 수 없다. 만약 당신이 멋진 UI를 갖는 app을 원한다면 이것은 문제가 된다. 이 뿐만이 아니다. 브라우저가 호출 스택에서 많은 작업들을 처리하기 시작하면, 반응하기 까지 많은 시간이 소요되어 멈출 것 이다. 그리고 브라우저는 웹 페이지를 종료할 것인지 물으며 에러를 띄우게 된다.

 

 

자, 그렇다면 우리는 어떻게 멋진 UI를 유지하면서 브라우저가 항상 응답할 수 있게끔 긴 코드를 실행시킬 수 있을까? 정답은 asynchronous callback이다.

 

 

 


생강강

,