JavaScript가 어떻게 탄생하게 되었는지, 어떠한 성장을 이루고, 현재 얼마만큼 많이 사용되는지는 이번 챕터에서 기록하지 않을 것이다. 오히려 JavaScript의 특징에 중점을 두어 기록하려고 한다.
JavaScript의 핵심개념으로는 여러가지 키워드들(객체,함수,프로토타입,실행 컨텍스트, 클로저 등)이 있지만, 추후에 기록할 부분에 포함되는 키워드들은 제외하고, 더 큰 틀에서 JavaScript의 특징으로 몇가지 키워드를 정리해 보려고 한다.
1. 객체 지향 인터프리터 언어(스크립트 언어)
2. Non-blocking (비동기식 처리)
3. Single-thread(싱글스레드), Event-driven 방식
객체 지향 인터프리터 언어
JavaScript는 일급 함수를 사용하는 가벼운 객체 지향 인터프리터 언어이며 웹페이지의 스크립트 언어로 잘 알려져 있지만, 브라우저가 아닌 환경에서도 많이 사용된다.
고급언어로 작성된 프로그램을 입력자료로 하여 컴퓨터가 알아들을 수 있는 언어(기계어)로 번역하는 대표적인 번역기 중에는 컴파일러와 인터프리터가 있는데, JavaScript는 인터프리터를 기반으로 번역된다.
* 물론 현재 가장 많이 사용되고 있는 JavaScript엔진 V8에서는 저스트인타임 컴파일러를 사용한다.
컴파일러는 고급 프로그램이 언어를 기계어로 번역하는 프로그램으로 어휘분석 단계, 구문분석 단계, 의미분석 단계, 중간 코등 생성 단계, 코드최적화 단계, 목적 코드 생성 단계, 총 6단계로 구분되어 번역된다.
반면 인터프리터는 컴파일러와 같은 단계를 거치지 않고, 원시 프로그램을 그때그때 처리하므로 인터프리터의 처리과정은 컴파일러와는 다르다.
컴파일러와 인터프리터의 차이점은 번역 처리과정, 특징, 언어로 나눌 수 있는데, 다음과 같다.
또한 컴파일시 자료형, this, 변수, scope등이 정해지는 Java와는 다르게 JavaScript는 문장단위로 번역한 후 실행되는 언어이기 때문에 실행 중에 자료형, this, 변수, scope등이 정해지고 변경된다.
이것을 동적 바인딩이라고 한다.
인터프리터 또는 JavaScript코드를 실행하는 프로그램(번역기)은 JavaScript 엔진이라고도 불리운다.
JavaScript엔진은 표준적인 인터프리터로 구현될 수도 있고 또는 JavaScript코드를 byte코드로 컴파일하는 저스트인타임(Just-in-time)컴파일로도 구현할 수 있다.
다양한 종류의 JavaScript엔진이 구현되어 있지만, JavaScript엔진마다 지원하는 범위는 차이가 있지만, 현재 여러 엔진 중에서 가장 많이 사용되는 엔진은 V8이다.
V8은 구글에서 개발하였고, 현재 크롬에서 사용 중에 있다. 또한 node js의 runtime(런타임)으로도 사용된다는 점이 다른 JavaScript엔진과의 큰 차이점이라고 할 수 있다.
V8은 웹 브라우저 내부에서 JavaScript 수행 속도의 개선을 위해 인터프리터를 사용하는 대신 저스트인타임(just-in-time) 컴파일러를 구현함으로써 코드를 더 효율적인 머신 코드로 번역한다.
V8에 관련된 자세한 내용은 추후 기록할 예정이다.
Non-blocking (비동기식 처리)
* https://poiemaweb.com/js-async 동기식 처리 모델 vs 비동기식 처리모델 내용을 발췌하여 기록한 내용입니다.
동기식 처리모델은 직렬적으로 태스크(task)를 수행한다. 즉, 태스크는 순차적으로 실행되며 어떤 작업이 수행 중이면 다음 작업은 대기하게 된다.
일상생활에서도 동기식과 비동기식을 자주 접할 수 있다.
(a) 동기식 처리모델을 비유하고 있는 사진을 보면 한 사람이 주문을 할때 나머지 사람들은 주문이 끝날때까지 대기를 하여야 한다.
(b) 비동기식 처리모델을 비유하고 있는 사진을 보면 직렬처리가 아닌 병렬로 처리되기 때문에, 한사람이 주문을 할 때, 다른 사람도 다른 일을 할 수가 있다.
만약 서버에서 데이터를 요청하고 데이터가 응답될 때까지 이후 태스크들은 blocking 되는 모델을 동기식 처리모델이라고 하며, 태스크가 종료되지 않은 상태라 하더라도 대기하지 않고 다음 태스크를 실행할 수 있는 모델을 비동기식 처리 모델이라고 한다.
JavaScript의 대부분의 DOM Event 핸들러와 Timer 함수(setTimeout, SetInterval), Ajax 요청은 비동기식 처리모델로 동작한다.
동기식 처리모델 코드 vs 비동기식 처리모델 코드
아래 사진은 동기식 처리모델과 비동기식 처리 모델을 비교한 코드이다.
동기식 처리 모델 코드의 순서는 다음과 같다.
1. func1()이 호출되면, 'func1'이 출력되고, func2()를 호출한다.
2. fun2 함수 내부에서 'func2'가 출력되고, func3()을 호출한다.
3. 'func3'을 출력한다.
'func1' -> 'func2' -> 'fun3'
위에 순서처럼 동기식 처리 모델은 순차적으로 실행되는 것을 볼 수 있다.
이에 반해 비동기식 처리 모델은 순차적으로 실행되지 않는다. 아래 코드를 보자
비동기식 처리 모델 코드의 순서는 다음과 같다.
1. func1()이 호출되면, 'func1'이 출력되고, func2()를 호출한다.
2. fun2 함수 내부에서 setTimeout 함수만 출력되고, setTimeout 내부에 있는 function()함수는 실행되지 않고, func3()을 호출한다.
3. func3함수 내부에서 'func3'을 출력 한후 setTimeout 내부에 있는 function()함수에 'fun2'가 출력된다.
'func1' -> 'func3' -> 'fun2'
setTimeout 메소드는 비동기 함수이기 때문에 위와 같은 순서로 출력된다.
이와 같은 실행과정을 알기 위해서는 JavaScript의 3번째 특징 Single-thread(싱글스레드), Event-driven 방식을 공부할 필요가 있다.
Single-thread(싱글스레드), Event-driven 방식
* https://poiemaweb.com/js-event 이벤트 내용을 발췌하여 기록한 내용입니다.
javaScript는 싱글스레드, 이벤트 드리븐(event-driven) 방식으로 동작한다. 싱글 스레드는 스레드가 하나뿐이라는 의미이며, 곧 하나의 작업(Task)만을 처리할 수 있다는 것을 의미한다.
하지만 실제로 웹 어플리케이션에서는 많은 작업(Task)이 동시에 처리되는 것처럼 느껴지는데, 이벤트 루프(Event Loop)가 작동되는 이벤트 드리븐 방식(event-driven)이 그 이유이다.
먼저 이벤트 드리븐 방식(event-driven)을 알아보기 전 싱글스레드가 무엇인지 멀티스레드와 비교를 통해 알아보도록 하자.
싱글스레드: 하나의 직렬로 처리되는 스레드 방식, 즉 하나의 요청이 있으면 하나를 처리할때까지 다음요청은 대기 상태, 하나씩만 실행할 수 있다. 직렬방식이기 때문에 하나의 요청은 빠르게 처리됨
멀티스레드: 여러개의 스레드를 병렬로 처리하여 번갈아 가며 처리하는 방식, 번갈아 가며 처리하기 때문에 동시에 작업이 처리되는 것처럼 느낄 수 있음. 병렬방식이기 때문에 다수의 요청을 동시에 처리할 수 있음
하나의 요청이 끝날때까지 다음 요청이 대기해야 하는 싱글스레드 방식은 하나의 요청은 빠르게 처리할 수 있지만, 다수의 요청이 들어왔을 시 다음 요청은 대기해야하는 단점이 존재한다. 이런 단점을 해결하고자 JavaScript는 이벤트 드리븐(Event-Driven)방식을 적용하였다.
다음은 브라우저의 환경을 그림으로 표현한 것이다.
구글의 V8을 비롯한 대부분의 JavaScript 엔진은 크게 2개의 영역으로 나뉜다.
Call Stack(호출 스택): 작업이 요청되면 요청된 작업은 순차적으로 Call Stack에 쌓이게 되고 순차적으로 실행된다. JavaScript는 단 하나의 Call Stack을 사용하기 때문에 해당 task가 종료되기 전까지 다른 어떤 task도 수행 될수 없다.
Heap: 동적으로 생성된 객체 인스턴스가 할당되는 영역이다.
앞에서 언급한 동시성을 지원하기 위해 필요한 비동기 요청(Event 핸들러, Timer 함수, Ajax 요청)처리는 JavaScript 환경(브라우저 or Node js)이 담당한다.
Event Queue(Task Queue): Event 핸들러, Timer 함수의 콜백 함수가 보관되는 영역으로 이벤트 루프(Event Loop)에 의해 특정시점(Call Stack이 비어졌을 때)에 순차적으로 Call Stack으로 이동되어 실행된다.
Event Loop(이벤트 루프): Call Stack 내에서 현재 실행중인 task가 있는지 그리고 Event Queue에 task가 있는지 반복하여 확인하다. 만약 Call Stack이 비어있다면 Event Queue 내의 task를 Call Stack으로 이동하고 실행한다.
그렇다면 앞에서 언급했던 비동기식 처리모델 코드를 이벤트 드리븐(event-driven)방식을 적용하여 다시 한번 해석해보자.
1. func1()이 호출되면, 'func1'이 출력되고, func2()를 호출한다.
2. fun2 함수 내부에서 setTimeout 함수만 출력되고, setTimeout 내부에 있는 function()함수는 실행되지 않고, func3()을 호출한다.
3. func3함수 내부에서 'func3'을 출력 한후 setTimeout 내부에 있는 function()함수에 'fun2'가 출력된다.
'func1' -> 'func3' -> 'fun2'
다음 자료는 JavaScript 특징(2)이다.
< 참고자료 >
[도서] You don't know JS - 카일심슨 지음-
https://developer.mozilla.org/ko/docs/Learn/JavaScript/First_steps/What_is_JavaScript
<ES5 series> chapter 1, JavaScript특징(1) end
'Language & Framework & Library > JavaScript' 카테고리의 다른 글
ES5 .Object(1) - Object(객체) (2) | 2019.11.22 |
---|---|
ES5 .Object(0) - Intro (0) | 2019.11.22 |
ES5 .Intro(4) - URI vs URL (0) | 2019.11.22 |
ES5 .Intro(3) - Browser 동작 원리 (0) | 2019.11.22 |
JavaScript - 기록을 시작하기전에 (0) | 2019.11.22 |