처음부터 다시보는 JS

예빈·2023년 7월 31일
0

ETC

목록 보기
15/15
post-thumbnail

JavaScript Module

CommonJS

자바스크립트의 모듈 시스템을 구현하는 방법 중에 하나로, 서버 사이드 자바스크립트(Node.js) 환경에서 주로 사용되었다. ECMAScript 5(ES5) 이전의 자바스크립트에서 모듈화를 지원하기 위해서 개발되었다.

동기적으로 로드되며, 모듈이 로드되기 전까지 다음 코드의 실행이 차단된다. 서버 환경에서는 초기 로딩 속도 이외에도 서버 코드의 단순함과 가독성이 중요하기 때문에, 직관적이고 단순한 방식으로 처리하는 것이 선호된다. 그래서 Node.js와 같은 서버 환경에서 모듈을 동기적으로 로드하는 데 적합하게 사용되었다.

// app.js
const myModule = require('./myModule');
myModule.myFunction();

// myModule.js
function myFunction() {
}

module.exports = myFunction;

ECMAScript Modules

ECMAScript는 JavaScript의 표준화된 버전을 말하며, ESM은 ECMAScript 6 (ES6)에서 도입된 모듈 기능이다.

비동기적으로 모듈을 로딩해서, 웹 페이지나 애플리케이션이 초기에 모든 스크립트를 미리 로드하지 않아도 된다. 필요한 모듈을 필요한 시점에 동적으로 로드하여 초기 로딩 속도를 개선하는데 도움을 준다.

또한, 정적으로 모듈을 분석해서 사용하지 않는 코드를 제거하는 Tree Shaking을 지원한다. 이는 애플리케이션의 용량을 최적화하는 데 도움을 준다.

🌲🌳 Tree Shaking
정적 분석으로 코드를 실행하지 않고도 코드의 구조와 의존성을 분석한다. 또한, 애플리케이션 실행 시 실제로 사용되지 않는 함수, 클래스, 변수 등을 찾아 제거해서 번들의 크기를 줄여준다.

// app.js
import { myFunction } from './myModule.js';
myFunction();

// myModule.js
export function myFunction() {
}

JavaScript 동작 원리

Single Thread

자바스크립트는 단일 스레드로 동시에 하나의 작업만 처리할 수 있다.
그러면 어떻게 여러 작업을 처리하고 있을까?

자바스크립트 엔진 V8의 구조

  • Memory Heap : 메모리 할당이 일어나는 곳
  • Call stack : 코드 실행에 따라 스택 프레임이 쌓이는 곳 (LIFO 후입 선출)

호출 스택이 하나이므로 자바스크립트가 단일 스레드인 것은 맞다 !

Runtime

그러나 자바스크립트 엔진을 '구동하는 환경', 브라우저나 Node.js에서는 여러 개의 스레드가 사용된다.

  • Web APIs : DOM, AJAX, Timer 등 브라우저에서 제공하는 api
  • Callback Queue : 콜백 함수들이 대기하는 곳 (FIFO 선입선출)
  • Event Loop : Call Stack이 비워질 때마다 Callback Queue에 대기 중인 콜백함수가 있다면, Callback 함수를 Call Stack에 보내준다.

자바스크립트 엔진과 상호 연동하기 위해 사용하는 장치가 Event Loop이다.

Call Stack

현재 어떤 함수가 동작하고 있는지, 그 함수 내에서 어떤 함수가 동작하는지, 다음에 어떤 함수가 호출되어야 하는지를 제어한다.

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);

Event Loop

싱글 스레드의 한계를 보완하기 위해서 비동기 콜백(asynchronous callback)을 이용한다.
비동기 콜백에는 Web APIs, ES6의 Promise가 있다.

자바스크립트 코드를 실행 중에 이벤트를 만나면 이벤트가 콜백 큐에 차례대로 쌓인다.
콜백 큐는 FIFO 선입선출을 따른다.

이벤트 루프는 콜 스택이 비었는지 확인하고, 비었으면 콜백 큐에 있는 이벤트를 가져다 콜 스택에 밀어넣는다. 이 한 번의 작업을 틱(tick) 이라고 한다. 이벤트 루프는 이 작업은 반복(loop)한다.


Animation

VSync

그래픽 카드가 주사율과 동기화하도록 하는 기술이다. 그래픽 카드가 주사율과 일치하게 프레임을 생성해서 부드러운 화면을 제공한다. 모니터는 화면을 그리는 데 일정한 주기를 갖고 있으며, 이 주기를 주사율(Refresh Rate)이라고 한다. 주사율은 초당 화면이 몇 번 새로고침되는지를 나타내는 값으로 헤르츠(Hz)로 표시한다. (60Hz, 120Hz)

자바스크립트에서는 requestAnimationFrame을 사용하면 VSync에 맞춰서 애니메이션 UI를 구현할 수 있다.

하드웨어 가속

HTML 소스가 로딩되고 파싱되면 브라우저는 태그를 DOM 트리로 구성한다. DOM 트리는 화면에 표현되는 요소와 화면에 표현되지 않는 요소로 이루어져 있다. 예를 들어 <head><script> 등은 화면에 표현되지 않는 DOM 트리의 요소다.

브라우저는 화면에 표현되는 요소를 RenderObject 트리로 구성한다. RenderObject 트리의 노드는 z-index 속성이나 중첩 등을 처리하기 고안된 RenderLayer에 대응된다.

RenderLayer 가운데 실제 화면에 출력돼야 하는 노드는 다시 GraphicsLayer를 생성한다. 최상위(root) 노드나 <canvas>, <video> 등이 GraphicsLayer를 생성하는 RenderLayer다.

하드웨어 가속은 GraphicsLayer 단위로 렌더링된 이미지를 GPU를 이용해 한 장의 이미지로 합성(composition)해서 화면에 출력하는 기술이다.

RenderLayer에서 다음의 조건 중에 부합되면 GraphicsLayer로 설정된다.

  • <video> 또는 <canvas> 요소
  • CSS 3D Transform이나 perspective 속성이 적용된 경우
  • CSS3 애니메이션 함수나 CSS 필터 함수를 사용하는 경우

더 빠른 애니메이션

브라우저 렌더링의 composite 단계에서의 변화만으로 애니메이션을 구현한다면 더 빠르게 동작할 것이다.
composite단계의 변화를 줄 수 있는 CSS 속성은 아래와 같다.

  • transform
  • opacity
  • visibility
  • will-change

⚠️ 참고로, requestAnimationFrame은 리페인트 단계에서 실행된다.


Q&A

💭 fe에서 모듈방식으로 개발하는 이유는 무엇인가요?

모듈은 기능 단위로 개발되기 때문에, 재사용이 가능하고 코드의 구조가 명확해진다. 또한, 모듈이 독립적으로 동작해서 다른 모듈에 영향을 덜 줘서 유지보수에 도움이 된다. 마지막으로, 작은 단위로 분리되어 있으므로 테스트 코드를 짜기 편하다.


💭 이벤트 루프의 역할은 무엇인가요?

이벤트 루프는 비동기 작업을 관리하고 실행한다. 파일 읽기, 네트워크 요청, 타이머 설정 등과 같은 작업은 비동기적으로 실행된다. 이벤트 루프는 이러한 작업들을 콜백 큐에 넣고, 콜 스택의 작업이 비면, 콜 스택에 넣어서 실행되게 한다.


💭 setInterval 이 정기적으로 실행되지 못하게 되는 이유는 무엇인가요?

setInterval은 WebAPI로 비동기로 실행된다. 정해진 시간마다 콜백 큐에 들어가서 차례를 기다리는데, 만약 콜 스택의 작업이 늦어지면 계속 기다려야 한다. 그래서 setInterval과 setTimeout은 정기적으로 실행된다고 보장할 수 없다.


💭 rAF가 나온 이유는 어떤 문제를 해결하기 위함일까요?

위에서 설명한 setInterval과 setTimeout이 정기적으로 실행을 보장하지 못하기 때문에, 일관성 있는 애니메이션을 제공하기 위해서 나왔다. rAF는 VSync에 맞춰서 애니메이션을 구현할 수 있어서 애니메이션을 구현하기 위해서 권장되는 방식이다.


참고한 블로그 🧸💗

JS의 동작 원리
하드웨어 가속에 대한 이해

profile
작전명 청춘 💫

0개의 댓글