자바스크립트의 동작원리

Dev_Oh·2023년 1월 14일
0

개요

자바스크립트엔진

자바 스크립트 엔진의 대표적인 예는 Google v8엔진 입니다.
v8은 Chrome과 Node.js에서 사용합니다.

엔진의 주요 두 구성요소는

  • Memory Heap : 메모리 할당이 일어나는곳
  • Call Stack : 코드 실행에 따라 호출 스택이 쌓이는 곳

런타임

거의 모든 자바스크립트 개발자들이 setTimeout 과 같은 브라우저 내장 API를 사용합니다. 하지만, 이 API를 자바스크립트 엔진에서 제공하지는 않습니다.

그럼 얘네들은 대체 어디서 오는 걸까요? 사실 현실은 좀 더 복잡합니다.

위 그림처럼, 자바스크립트 엔진 이외에도 자바스크립트에 관여하는 다른 요소드링 많습니다.
DOM, Ajax, setTimeout 과같이 브라우저에서 제공하는 API 들을 Web API라고 합니다. 그리고 아래쪽에 이벤트 루프와 콜백 큐도 있네요.

호출스택(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();

위 코드가 foo.js 에 있다고 하고 크롬에서 실행하면

호출 스택이 최대 크기가 되면 “스택 날려 버리기” 가 일어납니다. 이는 반복문 코드를 광범위하게 테스트하지 않고 실행했을 때 자주 발생합니다. 아래 코드를 보시면

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

엔진에서 이 코드를 실행할 때, foo()에 의해서 foo 함수가 호출됩니다. 그런데 여기서 foo 함수가 반복적으로 자신을 다시 호출하는 재귀 호출을 수행합니다. 그러면 매번 실행할 때마다 호출 스택에 foo() 가 쌓이게 됩니다. 아래와 같이 말이죠.

그러다가 특정 시점에 함수 호출 횟수가 호출 스택(Call Stack)의 최대 허용치를 넘게 되면 브라우저가 아래와 같은 에러를 발생시킵니다.

싱글 스레드 기반 코딩은 멀티 스레드 환경에서 제기되는 복잡한 문제나 시나리오를 고민하지 않아도 되기 때문에 상당히 쉽습니다. 예를 들면, 데드락 같은 게 있겠죠.

허나 싱글 스레드에서 코드를 실행하는 건 상당히 제약이 많습니다. 한 개의 호출 스택을 갖고 있는 자바스크립트의 실행이 느려지면 어떻게 될까요?

동시성(Concurrency) & 이벤트 루프(Event Loop)

호출 스택에 처리 시간이 어마어마하게 오래 걸리는 함수가 있으면 무슨 일이 발생할까요? 예를 들어, 브라우저에서 자바스크립트로 매우 복잡한 이미지 프로세싱 작업을 한다고 합시다.

이게 대체 어때서? 라고 의문이 생길지도 모르지만, 여기서 문제는 호출 스택에서 해당 함수가 실행되는 동안 브라우저는 아무 작업도 못하고 대기 상태가 된다는 겁니다. 이 말뜻은 브라우저는 페이지를 그리지도 못하고, 어느 코드도 실행을 못한다는 거죠. 말 그대로 그냥 가만히 있는 겁니다. 만약 매끄럽고 자연스러운 화면 UI를 가진 앱을 원한다면 위 경우는 문제가 됩니다.

문제는 이뿐만이 아닙니다. 브라우저가 호출 스택의 정말 많은 작업들을 처리하다 보면 화면이 아마 오랫동안 응답하지 않게 됩니다. 이 경우에 대부분의 브라우저가 아래와 같은 에러를 띄우면서 페이지를 종료할 건지 물어봅니다.

절대 이상적인 사용자 경험은 아니죠. 맞나요?

자 그렇다면 어떻게 페이지 렌더링 동작을 방해하지 않고 브라우저의 응답도 끊지 않으면서 연산량이 많은 코드를 실행할 수 있을까요? 정답은 바로 비동기 콜백 입니다.

참고사이트

https://joshua1988.github.io/web-development/translation/javascript/how-js-works-inside-engine/
profile
웹개발이 재밌다. 8년차 웹퍼블리싱

0개의 댓글