반응형

자료 출처 :
- https://medium.com/@gaurav.pandvia/understanding-javascript-function-executions-tasks-event-loop-call-stack-more-part-1-5683dea1f5ec
- https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf
- https://new93helloworld.tistory.com/358
- https://velog.io/@jakeseo_me/2019-03-15-2303-%EC%9E%91%EC%84%B1%EB%90%A8-rmjta5a3xh
- https://meetup.toast.com/posts/89

웹 개발자로 년차가 쌓이고 있는 현 시점에서 자바스크립트의 내부 동작을 이해할 필요가 있었다.
자바스크립트의 내부에서 어떤식으로 돌아가는지에 대한 내용들을 파악해보고 참고한 내용들을 참고하여 작성하는 글이다.


🪄자바스크립트의 변화

과거 웹 페이지의 동작만을 위해 동작하던 자바스크립트는 현대에 와서 많은 변화가 있었는데, 더 이상 웹 페이지만을 위해 존재하지 않는다.
2008년 구글에서 V8엔진을 사용하여 크롬을 출시한다. 이를 시작으로 2009년 노드가 탄생하였고, 서버와 클라이언트, 어플리케이션 개발 등 다양한 분야에서 사용되게 된다.

🔥자바스크립트 엔진

V8은 자바스크립트의 엔진이다. 크롬 브라우저의 엔진이면서, Node.js의 기반이다.
싱글스레드로 동작하면서 메모리힙콜스택으로 구성되어 있다.

메모리 힙(Memory Heap): 원시타입(Primitive type), 객체(Array, Object, Function 등)타입이 선언되면 메모리힙에 할당된다. 사용이 끝나면 자동으로 해제(Garbage collection)된다.
콜스택(Call Stack): 실행해야 할 코드가 쌓인다.

🧙Call Stack

실행할 코드가 쌓인다고 가볍게 설명했는데, 더 자세히 알아보자.
일단 여기서 이름 자체에 Stack이라는 키워드로 어느정도 동작을 유추할 수 있다.
후입선출를 가진 스택은 마지막에 쌓인 데이터가 먼저 출력되는 방식이다.
요청이 들어올 때마다 이 콜스택이라는곳에 쌓아두는데, 아래와 같은 소스가 있다면 어떻게 쌓이고 동작할까?

function tenSum(x){
    return x + 10;
}

function printSum(x){
    const sum = tenSum(x);
    console.log(sum);
}
printSum(5);

동작 처리 설명

  • Stack1: printSum(5)가 push된다.
  • Stack2: printSum(5) 내부에 있는 tenSum(x)가 push된다.
  • Stack3: tenSum(x)가 연산되면서 15를 출력하고 sum이라는 변수에 담아주고 pop이 된다. console.log(sum)이 push된다.
  • Stack4: console.log(sum)이 출력되면서 pop된다.
  • Stack5: printSum(5)가 출력되면서 pop된다.

🔁Event Loop & Task Queue

아래와 같은 코드를 만나면 어떻게 될까?

console.log("start", new Date())
setTimeout(()=>{console.log("setTimeout", new Date())}, 0);
const wakeUpTime = Date.now() + 1000;
while (Date.now() < wakeUpTime) {}
console.log("test1");
console.log("test2");
console.log("test3");
console.log("end", new Date())

위 소스에 대해 설명하자면 아래와 같다

  • 단순 console.log를 실행한다.
  • setTimeout이 동작한다. 동작시간은 0초로 설정하였다.
  • 이후 현재시간의 1초뒤의 시간을 wakeUpTime에 작성하여 while문에서 시간으로 체크한다.
  • 1초동안 강제로 동기화를 시켜서 페이지가 살짝 멈추게 된다.
  • 이후 단순한 console.log들을 출력한다.

예상한 결과가 나오길 빌면서 아래 출력 결과를 공유한다.

setTimeout메소드가 2번째에 있고 동작시간을 0으로 처리했음에도 콜백안에 있는 console.log는 가장 나중에 출력되고 있는걸 볼 수 있다.
심지어 4번째 while문에서 강제로 블로킹 처리를 해서 페이지가 멈추는 현상까지 발생는데 어떻게 이런 결과가 나왔을까?
이런 현상에 대해서는 이벤트 루프, 테스트 큐에 대해 알아야한다.

먼저 테스크 큐콜백 함수들이 대기하는 큐 형태이다.
Queue는 선입선출 구조로 먼저 들어온 것들이 먼저 나간다.
위 예제의 실제 동작을 설명하자면 아래와 같다.

  • 콜스택에 console,log("start")가 push되고 출력되면서 pop된다.
  • setTimeout이 콜스택에 push되고 콜백 내용이 테스크 큐에 등록되면서 pop된다.
  • 0ms가 지나고 이벤트 큐는 콜 스택이 비워질때까지 기다리면서 콜백을 실행시킬 준비를 한다.
  • wakeUpTime의 변수가 메모리 힙에 올라가고 이후부턴 콜스택들이 동작된다...

위와 같은 비동기 처리나 I/O의 작업이 자바스크립트에서 발생하면 콜백함수가 콜스택에서 처리되는게 아니라 별도의 테스크 큐라는 곳에 쌓였다가 이벤트 루프에 의해 실행되기 때문에 뜻밖의 결과를 얻을 수 있다.

⭐브라우저의 내부 환경

자바스크립트의 엔진을 구동하는 환경의 브라우저의 내부 모습은 아래와 같다.

그림에서도 볼 수 있듯이 개발하면서 자주 사용했던 Ajax, setTimeout과 같은 비동기 처리들은 자바스크립트가 해주는게 아니라 Web API영역에 따로 정의되어 있다. Node.JS에서는 동시성을 위해 libuv라이브러리를 도입했는데, 이 libuv라이브러리가 이벤트 루프를 제공한다.

정리하자면 비동기 작업을 하게되면 Node.js의 API가 호출되고, 그러면서 작성된 콜백 함수들은 이벤트 루프에 의해 관리가 되고 실행이 된다.

반응형