본문 바로가기
개발/개발지식

[인터뷰] 자바스크립트의 내부 동작 원리에 대해서 설명하라

by 핸디(Handy) 2021. 1. 30.

자바스크립트... 처음 입사하고 맞이한 이 친구,,, 일 년을 넘게 보고 있자니 이젠 익숙을 넘어 친근하기까지 한 언어가 되어버렸다.

하지만 이제껏 내부 구조를 살펴볼 생각보다는 var 부터 선언하기 급급했던 과거를 반성하며 이제서라도 내부 구조에 대해 알아보자.

한 줄 역사로, 자바 인척 하고 싶었던 스크립트 언어이다. 

책 모퉁이에서 한두번씩은 싱글 스레드, 이벤트 루프에 대해서 들어봤으리라 생각한다. 자바스크립트의 내부 동작을 설명하는 가장 핵심 키워드이기도 하다.

우리가 일반적으로 보는 브라우저는 2가지 엔진으로 구성되어 있다. 

자바스크립트를 해석하는 JavaScript 엔진 + 화면을 그리는 Rendering 엔진

이 중에 우리가 집중한 것을 자바스크립트를 해석하는 JavaScript 엔진, 그중에서도 구글 형님들이 만든 V8이다. 

올해 드디어 익스플로러가 나가리되고 맘 편히 개발할 수 있는 상황이 오고 있으니 더더욱 V8 기준으로 공부하는 게 좋을듯하다.

쨋든  V8 엔진은 크게 3가지 부분으로 이루어져 있다.

Call Stack + Event Queue + Heap

MDN 공식 사진인데, 아주 투박하다

다만 이것만 있으면 끝이냐 했을때. 노란색 박스를 Queue로 옮겨줄 놈이 필요한데, 이놈이 바로 Event Loop 다.

즉 V8 엔진은 

3개의 영역( Call Stack, Event Queue, Heap )과 1개의 액션(Event Loop)을 가지고 있다고 생각할 수 있다.

Call Stack

Call Stack은 이름 그대로 call(frame)의 스택이다. 아래 코드를 잠시만 살펴보자.

function foo(b) { 
	var a = 10; 
    return a + b + 11; 
} 

function bar(x) { 
	var y = 3; 
    return foo(x * y); 
} 

console.log(bar(7)); //returns 42

console.log 가 실행되면 bar함수가 호출되면서 Call Stack에 bar의 Stack frame이 형성되고 var y와 값들이 들어간다.
이후에 return를 진행하는데, 함수가 하나 더 있다.
그러면 일단 보이는 함수 foo 부터 처리한다. 다시 Call Stack에 foo의 Stack frame 이 형성되고, var a와 10이 들어간다.
그다음에 바로 return a+b+11; 이 나온다. return이 나왔으니 call stack에서 foo frame을 pop하여 날리고 그다음 call stack인 bar frame을 pop 한다.

이렇게 Call frame들을 Stack 구조로 push, pop 하여 Call Stack 이다.

Heap

객체(자바스크립트에서 모든 것은 객체이다)들은 힙 안에 할당됩니다. 힙은 구조화되지 않은 넓은 메모리 영역을 지칭한다.

Queue(event queue, task queue)

Queue는 JavaScript 런타임 시에 동작한다. 자바스크립트가 돌아가면서 처리해야 할 Event(Task)들이 순차적으로 쌓이는데 이게 Queue에 쌓인다. 왜 순차적이라고 했는지는 Queue의 자료구조를 보면 이해할 수 있다. 들어온 순서대로 처리하기 때문이다.

그럼 처리를 한다는 건 어떤 의미일까? 바로 Queue에 있는 Event를 Call stack으로 push 한다는 의미이다. Call stack에 들어갔다는 의미는 컴퓨터가 바쁘게 돌아가며 실제로 처리한다는 의미으니 대기 순에 있는 작업들을 실제 작업대로 던진 거라고 볼 수 있다. 다만 위에서도 말했다시피 싱글 스레드다. 작업대가 하나다. 다만 작업대가 하나라도 작업대가 넓다면 여러 개를 동시에 올려놓고 처리할 수도 있는데 

V8은 작업대도 하나인데 좁다. 그래서 하나씩 Event(Task)를 처리한다. 왜 하나씩 처리하는지는 Event Loop의 코드를 보면 알 수 있다.

while(queue.waitForMessage()){
  queue.processNextMessage();
}

실제 내부 코드인지는 모르겠으나 다음과 같은 코드로 설명한다,

while 문 안에서 processNextMessage(), 즉 다음(Next) 작업(Message)을 처리(process)한다. 여기서 의미하는 작업(message)란 하나의 Event(Task)이다.

MDN 문서에는 아래와 같이 표현한다.

Run to completion.

각 메시지는 다른 메시지가 처리되기 전에 완전히 처리됩니다. ( 하나씩 완전히 처리하겠단 의미 )

그럼 여기서 문제는 하나의 메시지가 엄청 시간을 잡아먹으면 그동안은 어떻게 됩니까?인데.

A) 답은 멈춥니다. 하나씩 처리하고 있는데 다음 작업을 할 수 없으니 멈출 수밖에요. 그래서 개발자가 해야 하는 일이 바로 큰 작업을 여러 개의 작은 작업으로 쪼개는 것, 작업 자체를 간소화시키는 것입니다. 

따라서 시간이 오래 걸리는 작업.
예를 들어 서버에서 데이터를 받아와 표시해주는 작업은 = 서버에 요청하는 작업 + 데이터 받아서 처리하는 작업 + 표시해주는 작업 등으로 쪼개집니다. 이때 + 로 연결된 사이사이에 다른 작업은 물론, 사용자의 클릭 이벤트 등 인터렉션 처리가 가능해집니다.

결국 우리가 브라우저는 보는 건 아주 작은 작업들끼리의 연결이라고 볼 수 있습니다.

이로써 한 페이지에 자바스크립트의 내부 동작에 대해 간단히 살펴보았습니다. 

<추가 Reference>

developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop

asfirstalways.tistory.com/362

댓글