JavaScript

JavaScript 작동 원리

Hoon1994 2020. 2. 21. 16:53

JavaScript 작동 원리가 어떻게 될까요? 

 

매일 같이 JavaScript를 사용하고, 어느덧 코딩을 시작한지 1년이 다 되어가는데, 자바스크립트의 동작 원리를 스스로 잘 모르고 있었습니다.

Front-end에서 빠질 수 없는 언어, JavaScript의 작동 원리를 알아보았습니다.

 

JavaScript 는 싱글스레드 기반의 콜백 큐를 사용합니다. 따라서 한번에 한 작업만 실행할 수 있는 인터프리트 방식입니다.

 

자바스크립트 엔진 관련해서 사용되는 것들을 알아보겠습니다.

  • Stack
  • Queue
  • Heap
  • Event Loop

 

  • Stack

a(x, y) {
	return x + y;
}

b(x, x) {
	const sum = a(x, x);
    console.log(sum);
}

b(10,20);

위 코드를 실행했을 때 Call Stack은 어떤 단계로 진행이 될지 알아보겠습니다.

 

0. 빈 스택인 상태이다.

1. b(10,20)을 실행하면, Stack에 들어간다. 

2. b 함수 내에서 a(x, x)가 실행되어 Stack에 들어간다.

3. a(x, y)는 실행을 마치고 리턴값을 돌려주며 Stack에서 빠져나온다

4. 리턴이 된 sum을 콘솔에 띄우고, b(x, x)도 Stack에서 빠져나온다.

5. 빈 스택이 된다.

 

함수 호출 횟수가 Call Stack의 최대 한계치를 넘게되면 Maximum call stack size exceeded 오류가 뜹니다.

최대 한계치를 넘는 함수 호출의 예가 무엇이 있을까요? 엄청 간단한 재귀 함수를 작성해보겠습니다.

 

function sum() {
	sum();
};
sum();

 

이런! sum()을 호출 했는데, 호출한 sum 함수 내에서 또 sum() 을 부르고 있네요. 

Stack이 계속 쌓이고 쌓이다가, Stack의 한계치를 넘는 순간 Maximum call stack size exceeded 오류가 발생하게 됩니다.

 

 

  • Queue

Queue는 한쪽에서는 입력이 이루어지고, 한쪽에서는 출력이 이루어집니다. 선입선출? 과 비슷하다고 생각할 수 있겠네요.

자바스크립트 런타임 환경은 처리해야할 일들을 임시 저장하는 대기 큐가 존재합니다. 그리고 Call Stack이 비어졌을 때,

먼저 들어온 순서대로 수행이 됩니다.

 

setTimeout(() => {
	console.log('첫번째..일까요?')
}, 0);
console.log('두번째..일까요?');

 

그냥 생각해보면, setTimeout 에 delay를 0으로 설정했으니, setTimeout이 먼저 실행되고, 그 다음 '두번째..일까요?' 콘솔이 

실행될 것 같지 않으신가요? 저도 처음엔 그렇게 생각했습니다. 

 

하지만, 코드를 실행해보면 결과는 '두번째..일까요?' 가 먼저 실행되고, 그 다음 setTimeout이 실행됩니다. 무슨 이유일까요?

자바스크립트에서 비동기로 호출되는 함수는 Call Stack에 쌓이지 않고 바로 Queue에 들어가게 됩니다. 

쪼금 더 복잡한 코드를 작성해서 확실하게 이해해보겠습니다. 

 

function queue1() {
	console.log('1번');
	queue2();
};

function queue2() {
	setTimeout(() => {
    	console.log('2번');
    }, 0);
	queue3();
};

function queue3(){
	console.log('3번')
};

queue1() // 어떻게 될까요?? 

 

결과는, '1번' -> 3번' -> '2번' 차례로 콘솔에 출력됩니다.

 

먼저 '1번'이 콘솔에 출력됩니다. 그리고 queue2가 실행되고, setTimeout 은 바로 실행되지 않고 Event Queue로 들어가게 됩니다. 

그리고 queue3이 Call Stack으로 들어갔다가 '3'번이 출력되고 빠져나옵니다. 이어서 queue2와 queue1이 모두 Call Stack을 빠져나옵니다. 이 때, Stack이 비워졌는지, Queue에 처리해야 할 게 남았는지 확인하는 게 Event Loop다. Event Loop에 의해 하나의 Event가 dequeue 된 다음 Call Stack으로 들어와 실행됩니다.

 

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

 

MDN에서 Event Loop를 설명하기 위해 만든 위 코드를 보면 Event Loop가 어떻게 작동하는지 알 수 있습니다.

Queue에 메시지가 들어오길 계속 기다리다가 메시지가 들어오면 순서대로 처리하고, Queue가 비게 되면 다시 메시지를 기다립니다.

이것이 Event Loop의 역할이고, Queue는 Stack과 반대로 메시지가 들어온 순서대로 일을 수행하고 삭제하게 됩니다.

 

웹 브라우저에서는 이벤트 리스너를 이용해 언제든지 이벤트가 발생할 때 마다 메시지를 추가할 수 있습니다.

또한 CallBack 함수 같은 경우도 메시지로 등록되어 Queue에 들어가게 됩니다.

 

Queue는 등록된 순서대로 일을 처리하기 때문에 setTimeout으로 특정시간 딜레이를 준다고

하더라도 앞서 등록된 메시지들이 있다면 그 시간을 보장할 수 없습니다.

 

  • Heap

객체(인스턴스)는 heap에 할당됩니다. (구조화되지 않은 '더미'같은 메모리 영역을 대부분 'heap'이라 표현한다고 합니다.)