상세 컨텐츠

본문 제목

자바스크립트 개발자라면 알아야하는 핵심 33개 이론 | #1. Call Stack

자바스크립트/Javascript

by amanda.hyon 2021. 10. 11. 00:31

본문

https://github.com/leonardomso/33-js-concepts

 

GitHub - leonardomso/33-js-concepts: 📜 33 JavaScript concepts every developer should know.

📜 33 JavaScript concepts every developer should know. - GitHub - leonardomso/33-js-concepts: 📜 33 JavaScript concepts every developer should know.

github.com

 

아래 그림은 Javasript Concurrency Model(V8 엔진)으로써 sing call stack, heap, queue로 구성되어 있습니다. 

 

1. Heap

객체들은 대부분은 메모리의 구조화되지 않은 영역과 같은 heap에 위치하는데, 
변수나 객체들에 대한 메모리 할당이 이곳에서 일어 납니다.

 

2. Queue

자바스크립트 런타임은 처리할 message 목록, 실행 관련 콜백 함수가 포함된 message queue를 포함합니다.
스택이 충분한 용량이 있을 때, 메시지를 큐에서 꺼내서 관련 함수를 호출하여 진행시키고(이렇게 초기의 스택 프레임을 만든다), 스택이 다시 비어지면 메시지 처리가 종료됩니다.
기본 용어로, 이런 메시지들이 외부 비동기 이벤트들에 대한 응답, 콜백 함수가 제공된 경우에 대해 큐입니다.(마우스 클릭됨, HTTP request의 응답 수신)
예를 들어, 사용자가 버튼을 클릭하고 콜백 함수가 없다면 어떤 message도 enqueue되지 않았을 것입니다.

 

3. Call Stack

Call Stack은 여러 함수들(functions)을 호출하는 스크립트에서 해당 위치를 추적하는 인터프리터 (웹 브라우저의 자바스크립트 인터프리터같은)를 위한 메커니즘입니다. 현재 어떤 함수가 동작하고 있는지, 그 함수 내에서 어떤 함수가 동작하는 지, 다음에 어떤 함수가 호출되어야하는 지 등을 제어합니다.

 

즉 자바스크립트가 실행해야 하는 함수는 스택(stack)에 올라가게 됩니다.

자바스크립트에서 선언된 함수들은 스택 위에 push로 올려 놓고 차례로 함수를 다 실행하면 pop으로 제거됩니다.

그렇게 콜스택의 함수들이 다 실행되어  다 제거되면 프로그램은 종료되게 되죠.

스택구조

예를 들어...

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

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

console.log(bar(6));

해당 코드를 실행하면...

콜스택은 아래 gif영상처럼 실행될 때마다 Call Stack에 하나씩 쌓이게 되고, 콜스택에 쌓인 console.log와 bar, foo 함수가 차례로 실행 결과를 리턴함과 동시에 pop되어 Call Stack에서 사라지게 됩니다. 

 

 

그리고 만약 아래 그림처럼 foo 함수에서 강제로 예외를 발생시키면 stack trace를 콘솔에서 볼 수 있습니다.
그림처럼 에러는 현재 콜스택의 상태를 나타내주고 함수안에서 스택과 마찬가지로 위에서 아래의 이동이 실패한 것을 보여주고 있습니다.

 

따라서 Call Stack을 통해 함수의 실행순서를 확인하고 추적할 수 있습니다.

 

 

물론 함수를 반복해서 무한 호출하게 되면 제한된 스텍사이즈로 인해 Max Stack Error Reached로 throw 에러를 확인할 수 있을 것 입니다.

 

 

일반적인 이벤트 루프

자바스크립트 환경에서 코드를 실행했을때 Call Stack, Event Loop, 그리고 Web APIs가 어떤 식으로 동작하는지 살펴보고자 합니다.

 

아래 코드를 실행하면..

setTimeout(() => { 
  console.log('hi')
}, 1000)

 

코드가 실행되면..

해당 사실을 Call Stack에 (여기서는 <global>) 푸시하게 됩니다.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          |              | |               |
    console.log('hi') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |

그리고나서 코드의 첫번째 라인을 실행시키기 위해 setTimeout 함수가 Call Stack에 두번째 항목으로 들어(푸시)갑니다.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
> setTimeout(() => {  | <global>          |              | |               |
    console.log('hi') | setTimeout        |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |

setTimeout을 실행하면 JS가 아닌 코드가 호출됩니다. setTimeout은 브라우저가 우리에게 제공하는 웹 API의 일부이기 때문입니다.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
> setTimeout(() => {  | <global>          |              | | timeout, 1000 |
    console.log('hi') | setTimeout        |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |

setTimeout의 실행이 완료되면..요청된 1000ms의 시간 동안 대기할 Web API로 작업을 오프로드 합니다.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          |              | | timeout, 1000 |
    console.log('hi') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |

Call Stack에 더 이상 실행할 것이 없다는 것은 js도 실행할 것이 없다는 뜻 입니다.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   |              | | timeout, 1000 |
    console.log('hi') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |

그리고 나서야, Web APIs가 가동하게 되고, 1000ms가 끝나게 되면 이벤트 루프에 코드를 추가하여 JS에 알립니다. 

이미 실행 중인 코드를 방해할 수 있기 때문에 Call Stack에 직접 푸시하지 않으며 이상한 상황에 빠지게 됩니다.

참고로 이벤트 루프는 큐로써 가장 먼저 push된 항목이 가장 먼저 Pop 되어 사라지게 됩니다. (선입선출)

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function        <---function     | |               |
    console.log('hi') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function          |              | |               |
    console.log('hi') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
> hi

마지막으로 이 함수에는 실행할 다른 명령이 없으므로 호출 스택에서 제거됩니다.

그렇게 우리의 프로그램은 실행이 종료되게 됩니다.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   |              | |               |
    console.log('hi') |                   |              | |               |
  }, 1000)            |                   |              | |               |
                      |                   |              | |               |
> hi

 

 

관련글 더보기

댓글 영역