
JavaScript를 사용한 적이 없는 웹 개발자는 없을 것이다.
그만큼 JavaScript는 웹 개발을 하기 위해 필수적인 언어이고, 이젠 웹 개발을 넘어 백엔드, 네이티브, 심지어 임베디드 장치 등 다양한 분야에서 JavaScript를 사용한다.
JavaScript의 내부 작동 방식을 탐구해보고, 구성요소들이 어떻게 작동하는지 정리했다.
JavaScript에 크게 의존하는 프로젝트일 수록, 개발자는 더욱더 좋은 소프트웨어를 만들기 위해 내부 구조를 더욱더 깊이 이해하고, 언어가 제공하는 모든 것을 최대한 사용해야 한다고 생각한다.
실제로 매일 JavaScript를 사용하는 사람 중에서도, 내부에서 어떤 일이 일어나는지 모르는 사람들이 많다.
V8엔진은 익히 들어봤을 것이다. V8엔진은 현재 Chromium기반 인터넷 사용자의 약 75% 정도가 사용한다. (Chrome, Node.js에서 사용됨)

JavaScript의 엔진은 이렇게 두 가지의 구성요소로 구성된다.
브라우저엔 거의 모든 JavaScript 개발자가 사용한 API가 있다. ex) setTimeout
그러나 이런 API들은 엔진에서 제공하지 않는다.
좀 더 복잡한 형태로 제공되는데,

물론 JavaScript는 엔진으로 동작하지만, 실제로는 훨씬 더 많은 것들이 있다.
브라우저에서 제공하는 웹 API라는 것들을 가지고 있는데, DOM, AJAX, setTimeout등이 있다.
그리고, 이벤트 루프와, 콜백 큐가 있다.
JavaScript는 단일 스레드 프로그래밍 언어이다. 즉, 단일 호출 스택이 있다. 따라서 한 번에 한 가지 작업을 수행할 수 있다.
호출 스택은, 프로그램이 어느 위치에 있는지 기록하는 데이터 구조이다. 함수에 들어가면 함수를 스택 맨 위에 푸쉬한다. 함수를 리턴하면 스택 맨 위에서 팝한다. 스택의 역할은 그게 전부이다.
다음 코드를 보면,
function multiply(x, y) {
return x * y;
}
function printSquare(x) {
var s = multiply(x, x);
console.log(s);
}
printSquare(5);
엔진이 이 코드를 실행하기 시작하면, 호출 스택이 비어 있을 것이다. 그 후 단계는 다음과 같다.

호출 스택의 각 항목을 스택 프레임이라고 한다.
그리고 아래는 예외가 throw되었을 때 스택 추적이 구성되는 방식이다.
function foo() {
throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
foo();
}
function start() {
bar();
}
start();
Chrome에서 이 코드를 실행하면 다음과 같은 스택 추적이 생성된다.

"Blowing the stack"이라고 불리는 스택 날리기는 최대 호출 스택 크기에 도달했을 때 발생한다.
스택 날리기는 매우 흔하게 발생하며, 특히 코드를 매우 광범위하게 테스트하지 않거나 재귀를 사용하는 경우 더욱 자주 발생한다.
function foo() {
foo();
}
foo();
엔진이 이 코드를 실행하면, foo함수를 호출하는 것으로 시작한다. 하지만 이 함수는 재귀적이며, return 없이 자체 호출을 시작한다. 따라서 실행의 모든 단계에서 함수가 호출 스택에 계속해서 추가된다.

계속 추가되고 어느 시점에서 호출 스택의 함수 호출 수가 호출 스택의 크기를 초과하는 Stack Overflow가 발생하고 브라우저는 다음과 같은 오류를 발생시킨다.

단일 스레드에서 코드를 실행하는 것은 매우 쉽다. 다중 스레드에서 발생하는 복잡한 시나리오(ex: 교착상태)를 처리할 필요가 없기 때문이다.
하지만 단일 스레드로 실행되는 것은 꽤나 제약이 따른다. JavaScript가 단일 호출 스택(Call Stack)을 가지고 있기 때문에 작업이 느려지면 어떻게 될까?
처리에 엄청난 시간이 걸리는 함수 호출이 Call Stack에 있는 경우엔 어떡하지?
예를 들어, 브라우저에서 JavaScript로 복잡한 이미지 변환을 하고 싶다고 가정해보자,
이게 왜 문제인지 궁금할 수 있지만, 문제는 Call Stack에 실행할 함수가 있는 반면, 브라우저는 실제로 다른 일을 할 수 없다는 것이다.
즉, 브라우저가 렌더링 할 수 없고, 다른 코드를 실행할 수 없으며, 그냥 멈춘 것이다. 앱에서 멋지고 유동적인 UI를 원한다면 큰 문제가 될 것이다.
그게 유일한 문제는 아니다. 브라우저가 호출 스택에서 많은 작업을 처리하기 시작하면 꽤 오랫동안 응답하지 않을 수 있다. 브라우저가 호출 스택에서 많은 작업을 처리하기 시작하면 꽤 오랫동안 응답하지 않을 수 있다. 그리고 대부분의 브라우저는 오류를 발생시키고, 웹 페이지를 종류할지 묻는다.

그렇다면 브라우저가 죽지 않게 하고 무거운 코드를 어떻게 실행할 수 있을까?
해결책은, 비동기 콜백이다.
포스트의 시리즈를 나누기 위해
이에 대해선 다음 포스트로 설명하겠다.