예전 웹 브라우저 환경은 정적인 웹사이트만 존재했고,
이에 불편함을 느낀 NetScape의 Brendan Eich는
웹 사이트에서 동작했으면 하는 사소한 기능들의 작동을 위해
생성된 아주 가벼운 프로그래밍 언어가 JavaScript다.
JavaScript의 탄생으로 인해 웹 사이트는 더욱 발전할 수 있었고,
이를 유심히 본 마이크로소프트는 자사 브라우저의 사용성을 늘리기 위해
자사 브라우저에만 존재하는 추가적인 기능들을 선보였다.
이로 인해 브라우저마다 어느 브라우저에서는 작동이 되고
어느 브라우저에서는 작동하지 않는 크로스 브라우징 이슈가 발생했다.
그리고, JavaScript는 10일만에 만들어진 언어이기 때문에
시대가 지나면 지날수록 점점 많은 오류를 찾아볼 수가 있었다.
해당 문제를 해결하기 위해 자바스크립트의 표준 문법과 사용가능한 기능을 나타내는 ECMAScript가 탄생했다.
우리가 유심히 살펴봐야할 ECMAScript의 버전은 2015년에 공개된 ECMAScript 2015, 즉 ES6다.
ES6의 도입으로 인해
프로그래밍 언어로서 필수적으로 있어야할
기능들이 어느정도 도입된 가장 큰 변화다.
매년 기능들을 추가하여 ECMAScript를 발표했지만,
많은 기업들은 최소 ES6이상의 JavaScript의 문법을 요구하기 때문에,
최소 ES6까지의 문법과 기능들은 모두 사용할 수 있어야 한다고 생각한다.
DOM트리와 CSSOM트리가 만들어지면, 두 트리를 합치는 과정이다.
두 트리에서 페이지를 렌더링하는데 필요한 노드만 포함하여 Render Tree를 생성한다.
레이아웃 단계에서는 각 요소의 정확한 위치와 크기를 계산한다.
모든 측정 값들은 화면에서 절대적인 픽셀로 전환된다.
페인팅 단계에서는 필요한 화면의 요소들을 실제로 그리는 과정이다.
레이아웃 단계에서 계산된 모든 위치, 크기등을 실제 픽셀로 변환하여 화면에 제작한다.
브라우저의 상황에 따라 특정 요소들을 변경하지 않고 위치만 변경하는 과정을 의미한다.
하지만 해당 요소만 움직이지 않고 주변 요소들의 변화가 함께 일어나야 하는 경우에는 페인트를 다시하는 repaint 또는 레이아웃을 다시하는 reflow가 발생하기도 한다.
브라우저는 다음과 같은 과정으로 렌더링을 수행한다.
DOM -> CSSOM -> Render Tree -> Layout -> Paint -> Composition
자바스크립트는 웹 브라우저에서 유일하게 돌아가는 프로그래밍 언어이며,
한번에 한가지 작업을 하는 싱글 쓰레드 기반의 언어다.
웹에서 유일하게 돌아가는 언어이기 때문에 웹 프론트엔드 개발자와 자바스크립트는 뗄레야 뗄 수 없는 사이다.
그러므로, 자바스크립트에 익숙해 지기 위해서는 자바스크립트가 어떤 방식으로 작동이 되고,
어떤 내부 구조를 가지고 있는지를 정확하게 파악하는 것이 중요하다고 생각한다.
사실 JavaScript에 대해 깊이 알지 못한다면 싱글 스레드 기반의 언어라고 상상하지 못할것이라고 생각한다.
웹 브라우저에서 돌아가는 사이트들을 보면
특정 화면을 보여주고 데이터를 받아오는 작업은 또 따로하기 때문에
멀티 스레드 언어라고 착각하는 사람들이 존재할 것이라고 생각한다.
해당 상황은 맞지만 JavaScript는 분명한 싱글 스레드 기반의 언어다.
어떻게 싱글 스레드 기반의 언어가 멀티 스레드 언어처럼 작동하는지
하나씩 그 작동 내부를 알아보자.
자바스크립트를 대표하는 엔진은 Google의 V8엔진이다.
해당 엔진은 사용자들이 가장 많이 사용하는 브라우저인 Chrome과
웹 백엔드를 구축할 수 있는 Node.js가 사용하는 엔진이기도 하다.
해당 엔진은 메모리 할당이 일어나는 Memory Heap과,
코드에서 실행한 특정 호출 스택이 쌓여있는 Call Stack으로 나뉘어진다.
아까 언급했듯이, 자바스크립트는 한번에 한 작업만 처리하는 싱글 쓰레드 기반 언어다.
그렇기 때문에 작업을 처리하는 Call Stack이 하나라는 것을 의미한다.
Call Stack은 우리가 현재 프로그램의 어떤 단계를 실행하고 있는지를 알려주는 스택이다.
말 그대로 자료구조의 스택과 같은 의미이기 때문에,
LIFO의 방식으로 작동되며,
나중에 추가된 작업을 먼저 처리한 후,
작업이 완료되면 Call Stack에서 제거하는 방식으로 작동된다.
Call Stack 역시 수용할 수 있는 작업의 한계치가 있기 때문에
자리가 꽉찼는데도 작업을 추가하는 경우에는
더 이상 작업을 처리해줄 수 없어 작업이 넘쳐버리는 상황.
사실 웹 브라우저는 자바스크립트 엔진 요소인 Memory Heap과 Call Stack으로만 작동하지 않는다.
브라우저에서 제공하는 API를 의미하는 Web API와 함께 작동을 하며
자바스크립트 엔진에서 정의되지 않은
setTimeout, HTTP 메서드, DOM 이벤트등을 추가로 처리해준다.
쉽게 말해, JavaScript 엔진은 위에 언급한 자신이 모르는 기능은
Web API에게 넘겨 해당 작업 처리를 요청하게 된다.
해당 이벤트를 처리한 Web API는 해당 작업 후
실행해야 하는 콜백 함수들을 저장하고 있는 Task Queue에
해당 콜백함수를 전달하게 된다.
즉, 비동기적으로 실행해야 하는 작업들을 대부분 수행하게 된다.
Task Queue는 비동기적인 작업 실행 후 처리되어야 할
콜백 함수들을 저장하고 있는 큐다.
해당 공간 역시 자료구조의 큐와 같은 의미이며,
FIFO의 방식으로 작동되며,
먼저 추가된 작업을 먼저 처리한 후
해당 콜백 함수를 Event Loop의 도움을 받아 Call Stack으로 전달해준다.
Microtask Queue는 데이터를 받아오는 비동기 작업을 하는 Promise의 작업이 끝난 후
실행되는 콜백 함수를 저장하고 있는 공간이다.
Animation frames의 경우는 requestAnimationFrame API 작업이 끝난 후
실행되어야 하는 콜백함수를 저장하고 있는 공간이다.
Event Loop는 Call Stack과 Task Queue, Microtask Queue, Animation frames를
항상 주시하고 있는 감시자라고 생각하면 쉽게 이해될 것 같다.
Event Loop은 Call Stack이 비어있는 것을 확인한 순간,
Microtask Queue, Animation frames, Task Queue의 순으로
콜백함수를 Call Stack으로 이동시켜 작업을 처리한다.
다음과 같은 작업을 틱(tick)이라고 부르고,
해당 틱은 모든 콜백함수가 모두 처리될때까지 반복하여 실행된다.
JavaScript 엔진은 싱글 스레드가 확실하다.
하지만 JavaScript의 작동 환경은 웹 브라우저를 통해 Web API 작업을 처리하고,
Event Loop의 도움으로 인해 각각 작업 후,
실행되어야 할 콜백 함수가 저장되어 있는
Task Queue, Microtask Queue, Animation frames에 저장되어 있는 콜백 함수들을
- Microtask Queue
- Animation frames
- Task Queue
순서대로 우선순위를 정해 Call Stack으로 해당 작업을 이동시켜 처리해준다.
이와 같은 과정으로 인해 동작하는 과정이 멀티 스레드처럼 보여지게 된다.
사실 해당 내용을 알지 못해도 JavaScript로 어느정도 코드를 짤 수 있다는 것은 부정할 수 없다.
해당 내용을 알아야 하나라는 의문을 가지는 사람들에게 적절한 비유를 들어보겠다.
예를 들어, 새로운 전자제품을 샀다고 가정을 해보자.
보통 새로운 전자제품을 사면,
해당 제품의 작동 원리와 효율적으로 사용하는 방법을 적어놓은 사용 설명서도 함께 포함되어 있다.
하지만 사용설명서를 읽기 귀찮다는 이유로 사용 설명서를 읽지 않고 해당 제품을 이용한다면,
나도 모르는 사이에 잘못된 방법으로 제품을 사용하게 되고,
제품이 고장이 나도 어느 부분 때문에 고장이 났는지 파악하는 것은 불가능하다.
특정 기술을 배우는 것도 새로운 가전제품을 구매하는 것과 똑같다.
그러므로, JavaScript을 다양한 기능을 다루기 전에
JavaScript의 렌더링 과정과 작동원리를 확실하게 파악하는 것이
JavaScript를 능숙하게 다루기 위한 첫번째 단계라고 생각한다.
정리 너무 잘해주셨어요:)