모던 자바스크립트 Deep dive 책 리뷰
요약
자바스크립트는 표준화를 거친 웹 어플리케이션 프로그래밍 언어이다.
자바스크립트는 1995년 웹페이지의 보조적인 기능을 수행하기 위한 경량 프로그래밍 언어로 도입되었다.
하지만, 여러 브라우저에서 각 자사의 브라우저 점유율을 높이기 위한 브라우저 전쟁이 일어나며, 자사 브라우저에 호환하는 자바스크립트의 파생 버전을 도입한다.
이로 인해, 브라우저에 따라 웹페이지가 정상적으로 동작하지 않는 크로스 브라우징 이슈가 발생하게 된다.
이에 자바스크립트의 파편화를 방지하고 모든 브라우저에서 정상적으로 동작하는 표준화된 자바스크립트의 필요성이 대두된다.
그렇게, 비영리 표준화 기구인 EMCA 인터내셔널에서 자바스크립트는 ECMAScript로 명명한다.
ECMAScript(ES)의 주요 버전별 특징은 다음과 같다.
ES6 버전에서 여러 주요 기능들이 도입되었기 때문에, 주로 해당 버전을 기준으로 많이 삼는다.
초창기 자바스크립트는 웹페이지의 보조적인 기능을 수행하기 위해 한정적인 용도로 사용되었다.
이 당시, 웹페이지는 HTML코드를 서버로부터 전송받아 웹페이지 전체를 렌더링하는 방식으로 동작했다.
따라서 화면이 전환되면 서버로부터 새로운 HTML을 전송받아 변경할 필요가 없는 부분까지 웹페이지 전체를 처음부터 다시 렌더링 했다.
하지만, 1999년 자바스크립트를 이용해 서버와 브라우저가 비동기 방식으로 데이터를 통신하는기능인 Ajax가 XMLHttpRequest라는 이름으로 등장했다.
이를 통해 서버로부터 필요한 데이터만 전송받아 변경해야 하는 부분만 한정적으로 렌더링하는 방식이 가능해졌다.
2005년, 구글이 발표한 구글 맵스는 웹 어플리케이션 프로그래밍 언어로서 자바스크립트의 가능성을 확인하게 되고, 이러한 웹 어플리케이션을 구축하려는 시도가 늘면서 더욱 빠르게 동작하는 자바스크립트 엔진의 필요성이 대두된다.
2008년, 구글에서 V8 자바스크립트 엔진을 발표하여 자바스크립트는 데스크톱 어플리케이션과 유사한 UX를 제공하는 웹 어플리케이션 언어로 정착하게 되었다.
모던 웹 어플리케이션은 웹 페이지와는 달리 개발 규모와 복잡도가 상승하게 되었고, 이에 따라 많은 패턴과 라이브러리가 출현했다.
CBD(Component Based Development)방법론을 기반으로 하는 SPA(Single Page Application)가 되중화 되면서 React, Vue, Angular 등 다양한 프레임워크/라이브러리가 존재한다.
자바스크립트는 객체 기반, 프로토타입 기반 언어이며, 자바스크립트를 구성하는 모든 것이 객체다.
객체는 프로퍼티와 메서드로 구성된 집합체이며, 변경 가능한 값(mutable)이다.
const counter = {
num: 0, // 프로퍼티
incresase: function() { // 메서드
this.num++;
}
}
counter.num = 1; // 프로퍼티 값 갱신
counter.name = 'counter1'; // 프로퍼티 동적 생성
자바스크립트 객체는 크게 3개의 객체로 분류할 수 있다.
Obejct
, String
, Boolean
, Math
, Promise
등DOM
, fetch
, Web Storage
등객체지향 프로그래밍은 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임이다.
상속(inheritance)은 객체지향 프로그래밍의 핵심으로, 자바스크립트는 프로토타입을 기반으로 상속을 구현한다.
function Person(name) {
this.name = name;
}
// 프로토타입 메서드
Persion.prototype.sayHello = function() {
console.log(`Hi! My name is ${this.name}`);
}
const me = new Person('Lee');
// hasOwnProperty는 Object.prototype의 메서드임
console.log(me.hasOwnProperty('name')); // true
위의 코드의 상속 관계는 다음과 같다. me
> Person.prototype
> Object.prototype
자바스크립트는 객체의 프로퍼티에 접근하려고 프로토타입의 체인으로 프로퍼티를 순차적으로 검색한다.
자바스크립트는 렉시컬 스코프를 가지며, 렉시컬 환경과 실행 컨텍스트를 통해 소스코드를 처리한다.
스코프(scope)는 유효범위이며, 유효범위는 다음과 같이 결정된다.
이렇게, 함수가 정의될 때 상위 스코프가 정적으로 정의되는 것을 정적 스코프 또는 렉시컬 스코프라고 한다.
렉시컬(Lexical)은 '사전'을 뜻하는 'Lexicon'에서 파생되었다.
우리가 사전에 정의된 단어의 의미에 따라 문장의 의미를 파악하는 것 처럼, 함수의 상위 스코프는 함수 정의가 실행될 때 정적으로 결정된다.
예제에서 함수 bar가 정의될 때의 상위 스코프는 전역 스코프이므로, 실행 결과는 다음과 같다.
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
스코프(유효범위)에 따라, 코드가 실행되기 위해서는 코드의 문맥을 알아야 한다.
즉, 코드가 어디서 실행되며, 주변에 어떤 코드가 있는지를 알아야 하며 이를 렉시컬 환경이라고 한다.
렉시컬 환경은 이론상의 객체로, 두가지로 구성한다.
예제 코드를 도식화 해보면 렉시컬 환경은 다음과 같다.
소스코드(실행 가능한 코드)는 실행 컨텍스트를 생성한다.
실행 컨텍스트는 소스코드를 실행하는데 필요한 환경을 제공하고, 코드의 실행을 관리한다.
자바스크립트 엔진은 다음의 과정으로 소스코드를 실행 컨텍스트를 통해 처리한다
예제 코드의 실행 컨텍스트의 처리를 도식화 해보면 다음과 같다.
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
자바스크립트의 모든 함수는 자신의 상위 스코프를 기억한다.
하지만, 모든 함수를 클로저라고 하지 않고 외부 함수보다 중첩 함수의 생명주기가 긴 함수를 클로저라고 한다.
const x = 1;
function outer() {
const x = 10;
const inner = function() { console.log(x) };
return inner;
}
const innerFunc = outer();
innerFunc();
위의 예시에서 outer
함수는 호출 후 실행 컨텍스트에서 제거되지만, outer
함수의 렉시컬 환경까지 소멸하는 것은 아니다.
이러한 클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다
// case 1: 전역 변수
let num = 0;
const increaseGlobal = function () {
return ++num;
}
console.log(increaseGlobal); // 1
console.log(increaseGlobal); // 2
console.log(increaseGlobal); // 3
// case 2: 지역 변수
const increaseLocal = function () {
let num = 0;
return ++num;
}
console.log(increaseLocal); // 1
console.log(increaseLocal); // 1
console.log(increaseLocal); // 1
// case 3: 클로저
const increaseCloser = (function() {
let num = 0;
return function (){
return ++num;
};
}());
console.log(increaseCloser); // 1
console.log(increaseCloser); // 2
console.log(increaseCloser); // 3
자바스크립트 엔진은 싱글 쓰레드로 동작하지만, 브라우저는 멀티 쓰레드로 동작한다.
렌더링은 HTML, CSS, Javascript로 작성된 문서를 파싱하여 브라우저에 시각적으로 표현하는 것이다.
1 브라우저는 서버에 HTML 문서를 요청한다.
2 브라우저의 렌더링 엔진은 응답받은 HTML 문서를 순차적으로 파싱하여 브라우저가 이해할 수 있는 자료구조인 DOM을 생성한다
3 브라우저의 렌더링 엔진은 응답받은 HTML 문서를 순차적으로 파싱하다가 CSS를 로드하는 link를 만나면 DOM 생성을 일시 중단하고, CSS 파일을 파싱하여 CSSOM을 생성한다.
4 DOM과 CSSOM이 결합하여 렌더 트리를 생성한다.
5 완성된 렌더 트리는 각 HTML 요소의 레이아웃을 계산하는 데 사용되며, 브라우저 화면에 픽셀을 렌더링하는 페인팅 처리에 입력된다.
이러한 렌더링 과정은 다음의 경우에 반복적으로 실행된다.
이와 같은 과정은 리렌더링이라고 하며, 성능에 영향을 주므로 필요한 때에만 실행되도록 해야한다.
자바스크립트 엔진은 실행컨텍스트라는 하나의 스택으로 한 가지의 태스크만 실행할 수 있는 싱글 스레드 방식으로 동작한다.
이러한, 싱글 쓰레드 방식은 한 번에 한 가지 태스크만 동기적으로 실행할 수 있으므로, 시간이 오래 걸리는 태스크가 있는 경우 이후 태스크가 실행되지 않는 블로킹이 발생한다.
하지만, 브라우저는 비동기적으로 태스크를 실행하여 여러 요청을 동시에 실행하는 동시성을 보장한다.
자바스크립트는 표준 스펙(ECMAScript)이 존재하며 버전이 다양하고 지속적으로 업그레이드 되기 때문에, 브라우저에서 최신 버전의 자바스크립트를 작동시키기 위한 개발 환경을 구축하는 것이 필요하다.
개발 환경 구축에는 트렌스파일링과 모듈 번들링, 폴리필이 존재한다.