자바스크립트가 많이 쓰임에 따라 다양한 곳에서 쓰여지고 있다. 

(프런트엔드, 백엔드, 하이브리드 애플리케이션, 임베디드 장치 등등 다양하게 서비스된다.)

 

그렇다면 이렇게 다양하게 쓰이는 자바스크립트가 기본적으로 어떻게 작동되는지에 대해 살펴보고자 한다. 

자바스크립트의 여러 엔진 중 하나인 자바스크립트 V8엔진을 통해 알아보도록 하자.

 

* 이 부분은 어려운 부분이기에 간략하게 읽고 넘어가도 좋다.

너무 어려운 부분을 깊게 파고 들면 자칫 흥미를 잃을 수도 있으므로 아 이러한 구도로 진행되는 군! 정도면 충분하다고 생각한다. 


자바스크립트 V8 엔진이란?

V8엔진 로고

V8 자바스크립트 엔진은 구글에서 C++로 작성된 자바스크립트 엔진이다. 구글 크롬(Google Chrome)이 아니더라도 따로 실행이 가능하다. 대표적으로 Node.js가 V8로 빌드되었다. 이러한 V8엔진은 기본적으로 싱글 스레드 (Single-thread)와 콜백 큐(Callback-Queue)가 있다. 

 

크롬은 기본적으로 자바스크립트 엔진 V8을 탑재하고 있다. 

자바스크립트 엔진은 구글의 V8엔진 외에도 다른 여러 엔진이 존재한다. 하지만 구글은 더 빠르고 효율적으로 구동하는 엔진을 가지고 싶어했다. 그래서 자바스크립트 코드를 인터프리터를 사용하는 대신 특정 컴파일러를 활용해서 별다른 변형 없이 기계 코드로 변환하는 방식으로 채택하였다. Rhino(Moziila)나 SpiderMonkey 엔진 처럼 JIT(Just in Time) 컴파일러를 구현해서 다이렉트로 기계코드로 변환시키는 것이다. [ V8엔진은 bytecode나 intermediate code를 만들지 않는다. ]

* 사실 자사에 맞는 조금 더 효율적인 자바스크립트 엔진을 갖기 위해서 V8 엔진을 만들었다고 생각한다.

 

  • 싱글 스레드(Single-thread)?

스레드란 기본적으로 하나의 프로그램에서 프로세스가 실행되는 흐름의 단위를 이야기한다.

그렇다면 프로세스란 무엇인가? 

 

 

프로세스는 우리가 작업관리자에서도 볼 수 있다. 프로그램은 실행되기를 기다리는 특정한 코드(명령어들)과 데이터들의 묶음이라 볼 수 있다. 이러한 프로그램을 실행하게 되면 코드와 데이터들의 묶음이 메모리에 적재(Load)되면 프로세스가 된다.

프로세스는 일반적으로 실행 중인 프로그램이라 생각할 수 있으며, 실행 중인 무언가라 생각할 수 있다.

 

반대로 하나의 프로세스는 여러개의 스레드로 구성이 가능하다.

쓰레드는 결국 프로세스에서 하나의 흐름을 이야기한다고 보면 된다. 싱글 스레드는 결국 프로그램에서 하나의 흐름을 갖는다는 이야기이다. (프로세스 내에서 할당 받은 실행의 한 단위라고 생각하면 된다.)

* 스레드는 프로세스 안에서 메모리 공간을 공유하지만, 프로세스는 프로세스 별로 각각의 메모리 공간을 갖는다.

 

싱글 스레드는 하나의 콜 스택(Call Stack)과 하나의 힙(Heap)을 가진다. 하나의 콜 스택이 있으면 한 번에 하나의 동작만 실행할 수 있다. 하지만 단순히 자바스크립트가 한 번에 하나의 동작만을 실행하고 있다고 생각되진 않는다.

단적인 예로 방 청소를 할 때를 생각해보면 된다. 청소기를 돌리고 있는 상황에서는 청소기 밖에 못 돌리는 것이다. 다른 어떤 일도 처리할 수 없다. 이를 홈페이지에서 적용해보면 된다. 특정 화면이 변하고 있는 상황에서는 아무것도 클릭하지도 조작할 수도 없다. 로딩이 끝날 때까지 기다려야 하는 것이다. 하지만 우리는 클릭도 가능하고, 마우스 스크롤을 통해 홈페이지를 살펴볼 수도 있다. 

즉 자바스크립트를 비동기적으로 코드를 실행시킬 수 있게 해주는 것이 Web API, Task Queue, Event Loop가 된다.

 

자바스크립트 자체는 한번에 하나의 코드만을 실행시킬 수 있지만, 여러 외부 요소가 Web API, Task Queue, Event Loop가 되는 것이다. 

 

 

기본적인 도식도를 생각해보면 이러한 형태로 이루어져 있다. 

( Stack은 컴퓨터에서 사용되는 자료구조의 한 종류로써 블럭을 쌓는 구조라 생각하면 된다. 먼저 넣은 블럭은 차곡차곡 쌓여서 다시 꺼낼때에는 제일 나중에 빼야하는 구조이다. - 자료구조에서 더 자세히 볼 수 있다. )

( Heap은 컴퓨터에서 사용되는 자료구조의 한 종류로써 매점에서 줄을 서는 구조와 비슷하다. 먼저 서서 기다리면 가장 먼저 처리되는 구조이다. - 자료구조에서 더 자세히 보도록 하자.)

 

  • Event Loop : Call Stack이 비어잇는지 아닌지 체크를 하고 비어있다면 callback을 Call Stack으로 옮긴다. (그림 상에서 Stack이라 불리는 부분이 Call Stack이다.)
  • Task Queue : 실행해야할 여러 일을 임시로 대기시키는 큐이다. 일종의 대기열이라고 생각하면 된다. 
  • Web API : Web API는 브라우저 자체에서 지원하는 여러 API들을 의미합니다. 여기서 Web API는 setTimeout, DOM이벤트, Ajax (XMLHttpRequest) 등의 비동기 작업을 수행하도록 API로 지원한다.

EventLoop 참조 : developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop

* API ( Application Programming Interface) : (응용 프로그램 프로그래밍 인터페이스) 이하 API는 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만드는 인터페이스를 의미한다. 

 

* 인터페이스(Interface) : 영어 자체를 들여다보면 Inter(사이에, 중간에, 도중에 = between) + face(어떤 것을 바라보다) 란 느낌으로 둘이 서로를 바라보는 것을 의미한다. 그렇다면 interface는 서로가 바라보고 있는 연결면, 접점 등을 의미한다. 하드웨어 인터페이스는 컴퓨터에 있는 랜선과 랜선 연결 커넥터 같은 것들도 하드웨어 인터페이스라 볼 수 있다. 대표적인 인터페이스는 User Interface인 UI가 있으며 이는 컴퓨터와 사람 간의 상호작용에 대한 접점으로 볼 수 있다.

 

이러한 기본 구조를 통해 자바스크립트가 어떻게 구동되는지 간단하게 옅볼 수 있다.

 


코드 상에서는 어떻게 작동되는가?

자바스크립트가 비동기적으로 작동하고 있다는 사실은 아래의 코드를 실행해봄으로써 바로 알 수 있다.

function first_call_function() {
	setTimeout(() => {
    	console.log("first_call")
        }, 1000);
    };
    
    
 function second_call_function() {
 	setTimeout( () => {
    	console.log("second_call")
        }, 500);
    };
    
    first_call_function();
    second_call_function();

원래대로라면 first_call_function()이 먼저 call하고, second_call_function()이 그 다음으로 call 되엇으므로 first_call이 먼저 나오고 second_call이 메시지로 나와야 한다. 하지만 결과는 다르다. 

 

결과는 second_call이 나오고 그 다음으로 first_call이 나온다. 이렇게 비동기적 작동이 가능한 이유는 위에 보았듯이 Event Loop와 Web API, Task Queue로 인해 가능해진 것이다.

 

비동기적 구성 자체를 처리하기 위해서 비동기적 실행이 필요한 것은 Web API를 호출하면서 따로 진행한다.

(이는 Node.js에서는 libuv를 통해 처리한다.)

 

이러한 콜 스택과 Web API 그리고 콜백 큐에 대해서 순서도를 눈으로 확인하려면 

latentflip.com/loupe/ 에서 간략하게 확인할 수 있다. 

 

더보기

1. 콜 스택(Call Stack)에서 first_call_function함수에서 setTimeout 함수가 호출되면 setTimeout은 WebAPI에 의해서 백그라운드에서 실행된다. 이후에 first_call_function함수의 setTimeout함수는 콜 스택(Call Stack)에서 사라진다.

2. first_call_function함수의 setTimeout 함수의 실행이 끝난 뒤에 바로 콜 스택(Call Stack)에서 second_call_function함수의 setTimeout함수가 호출된다. 1과 마찬가지로 setTimeout은 WebAPI에 의해서 백그라운드에서 실행되며, second_call_function함수의 setTimeout함수는 콜 스택(Call Stack)에서 사라진다.

 

3. WebAPI에서 의해 백그라운드 상에서 정해진 시간이 지난 뒤에 console.log("second_call")이 Task Queue로 옮겨진다. 그리고 그 다음 시간이 지난 후에 console.log("first_call")이 Task Queue로 옮겨진다. 

 

4. 이제 콜 스택(Call Stack)이 비워졌으므로 Task Queue에 있는 console.log("second_call")와 console.log("first_call")가 차례로 실행된다.     

이러한 구도로 움직인다고 생각하면 된다. 

조금 더 알고 싶다면, new93helloworld.tistory.com/361beomy.github.io/tech/javascript/javascript-runtime/

blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf 를 참조하도록 하자.

 

 

+ Recent posts