이벤트 루프를 한 문장으로 설명하면 다음과 같다.
"The event loop job is to look at the stack and look at the task queue. If the stack is empty, it takes the first thing on the queue and pushed it on to the stack."
What the heck is the event loop anyway? - JSConf YouTube
Call stack과 Task Queue를 주시하고 Call stack이 비어있다면 Task Queue에 있던 작업을 Call stack으로 보내주는 역할을 한다.
자바스크킵트 엔진은 Memory Heap과 Call Stack으로 이루어져 있다. 가장 유명한 예시는 Google의 V8 엔진이 있다.
자바스크립트는 Call stack을 하나만 가지고 있으므로 한 번에 하나의 함수만 처리할 수 있다.
function a() {
b();
console.log("a");
}
function b() {
console.log("b");
}
a();
이러한 코드가 Call stack에서 어떻게 실행되는지 그림으로 보자.
이처럼 자바스크립트 엔진에서는 하나의 작업만 실행 할 수 있지만 자바스크립트는 자바스크립트 엔진으로만 돌아가는 것이 아니다.
Web APIs는 자바스크립트 엔진에 존재하지 않고 브라우저에서 제공하는 API로, DOM, AJAX, Timeout등이 있다.
자바스크립트에서 setTimeout
과 같은 함수를 실행하면, 자바스크립트 엔진은 Web API에 setTimeout
을 요청하고 동시에 setTimeout
에 넣어준 Callback 까지 전달한다. Call stack 에서는 Web API 요청 이후 setTimeout
작업이 완료되어 제거된다.
Web API는 방금 받은 setTimeout
을 완료하고, 동시에 전달받은 Callback 을 Task Queue라는 곳에 넘겨준다.
TaskQueue는 Callback Queue라고도 한다. 큐 형태로 Web API에서 넘겨받은 Callback 함수를 저장한다. 이 Callback 함수들은 자바스크립트 엔진의 Call stack의 모든 작업이 완료되면 순서대로 추가된다. 이 때 Call stack에 남은 작업이 있는지와 Task Queue에 Task가 존재하는지를 판단하고 Task를 Call stack으로 보내주는 역할을 하는 것이 Event Loop의 역할이다.
다음 코드가 어떻게 동작하는지 애니메이션으로 보자.
setTimeout(function() {
console.log("Hello World");
}, 5000);
호이스팅이란 모든 선언(var, let, const, function, function*, class)문에 해당 스코프의 최상단으로 옮겨진 것처럼 동작하는 특성을 말한다.
먼저 변수가 어떤 생성 과정을 거치는지 보자.
undefined
로 초기화 된다.undefined
로 초기화된 변수에 실제 값을 할당한다.var 키워드로 변수를 생성하면 선언 단계와 초기화 단계가 동시에 이루어진다.
// 스코프의 선두에서 선언 단계와 초기화 단계가 실행된다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 있다.
console.log(foo); // undefined
var foo;
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
스코프에 변수를 동록하고(선언 단계) 메모리에 변수를 위한 공간을 확보한 후, undefined
로 초기화 한다(초기화 단계). 따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않는다. 이후 변수 할당문에 도달하면 그 때 값이 할당된다.
let 키워드로 변수를 생성하면 선언과 초기화가 분리되어 진행된다.
// 스코프의 선두에서 선언 단계가 실행된다.
console.log(foo); // ReferenceError: foo is not defined
let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
스코프에 변수를 등록하지만(선언 단계) 초기화 단계는 변수 선언문에 도달했을 때 이루어진다. 초기화 이전에 변수에 접근하려고 하면 ReferenceError
가 발생한다. 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문이다. 따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다. 이 구간을 일시적 사각지대(Temporal Dead Zone; TDZ) 라고 부른다.
const 키워드는 반드시 선언과 동시에 할당이 이루어져야 한다. 때문에 let
과 마찬가지로 TDZ
의 제한을 받는다.
let
, const
에서는 호이스팅이 일어나지 않는다는 생각을 할 수도 있지만 사실은 호이스팅은 일어나지만 TDZ
의 영향 때문에 호이스팅이 일어나지 않는 것처럼 보이는 것이다.
함수 선언식으로 선언한 함수는 호이스팅이 되며 함수 자체가 호이스팅 된다.
sum(10, 20); // this funcion has been hoisted
function sum(a, b) {
return a + b;
}
위 코드는 아래와 같이 동작한다.
function sum(a, b) {
return a + b;
}
sum(10, 20);
함수 표현식으로 선언한 함수는 변수 호이스팅이 발생하게 된다.
sum(10, 20);
let sum = function (a, b) {
return a + b;
};
// Uncaught ReferenceError: sum is not defined
실제로는 이렇게 동작한다.
// 스코프의 선두에서 선언 단계 실행
sum(10, 20); // 초기화 전에 접근해서 에러 발생
sum = function (a, b) { // 초기화 단계와 할당 단계 실행
return a + b;
};
// Uncaught ReferenceError: sum is not defined
let
변수에 대해서 호이스팅을 하는데 let
은 TDZ
의 영향을 받기 때문에 호이스팅은 수행하지만 초기화 되기전에 참조를 해서 ReferenceError
가 출력되는 것이다.
그럼 var 키워드 일때는 어떻게 될까
sum(10, 20);
var sum = function (a, b) {
return a + b;
};
// Uncaught TypeError: sum is not a function
실제로는 이렇게 동작한다.
var sum;
sum(10, 20);
sum = function (a, b) {
return a + b;
};
// Uncaught TypeError: sum is not a function
var sum;
을 선언하고 초기화도 함께 되었지만 sum(10, 20);
을 실행할 때에는 변수이기 때문에 sum is not a function
오류가 발생한다.
실행 컨텍스트(Execution context, EC)는 실행 가능한 코드가 실행되기 위해 필요한 환경을 말한다.
자바스크립트가 가지고 있는 세 가지 실행 컨텍스트가 있다.
1. Global execution context(GEC) - 기본적으로 자바스크립트 코드가 시작할 때는 기본적으로 가지고 시작하는 실행 컨텍스트이다. 이 GEC는 무조건 하나만 생성된다.(자바스크립트는 싱글 스레드 언어이기 때문에)
2. Functional execution context(FEC) - 함수가 실행될 때 마다 정의되는 컨텍스트이다. GEC는 단 한 번만 정의하는 것과 달리, FEC는 매 실행시마다 정의되어 함수 실행이 종료되면 Call Stack에서 제거된다.
3. Eval context: eval 함수로 실행한 코드의 컨텍스트이다.
ECS는 만들어진 컨텍스트들을 스택 형태로 저장하는 저장소이다. 전역 실행 컨텍스트는 기본적으로 ECS에 있으며 스택의 맨 아래에 존재한다. 자바스크립트 엔진이 함수 호출을 찾으면 해당 함수에 대한 컨텍스트를 ECS에 push한다. 그 다음 자바스크립트 엔진은 ECS 스택의 맨 위에 컨텍스트 함수(FEC)를 실행하고 함수의 모든 코드가 실행되면 해당 함수를 pop 하고 그 아래에 있는 함수에 대해서 동일하게 진행한다.
아래 코드를 보면서 동작 과정을 이해해보자.
var a = 10;
function functionA() {
console.log("Start function A");
function functionB(){
console.log("In function B");
}
functionB();
}
functionA();
console.log("GlobalContext");
functionA
를 호출하면functionA
에 대한 실행 컨텍스트를 스택에 push하고 functionA
를 실행한다.functionA
컨텍스트 안의 functionB
를 호출하면 functionB
에 대한 실행 컨텍스트를 스택에 push한다. functionB
의 모든 코드가 실행되고 나면 자바스크립트 엔진은 스택에서 functionB
실행 컨텍스트를 pop한다.functionA
의 남아 있는 코드들을 실행하고 모두 실행하면 자바스크립트 엔진은 스택에서 functionA
실행 컨텍스트를 pop한다.더욱 자세한 설명과 실행 컨텍스트의 생성 과정을 알고 싶다면
교차 출처 리소스 공유(Cross-origin resource sharing, CORS)는 추가적인 HTTP헤더를 사용해서 애플리케이션이 다른 origin의 리소스에 접근할 수 있도록 하는 메커니즘을 말한다.
브라우저에서 기본적으로 API요청을 할 때에는 브라우저의 현재 주소와 API 주소의 도메인이 일치해야만 데이터를 접근할 수 있게 되어있다. 만약 다른 도메인에서 API를 요청해서 사용할 수 있게 해주려면 CORS요청을 가능하게 서버에서 특정 헤더인 Access-Control-Allow-Origin과 함께 응답하면 된다.
app.get('/', (req,res) => {
res.header("Access-Control-Allow-Origin", "*"); // 모든 도메인
...
}
또는
app.get('/', (req,res) => {
res.header("Access-Control-Allow-Origin", "허용하고자 하는 도메인");
...
}
위와 같이 서버 쪽에 코드를 작성해주면 된다.
REST 란 REpresentational State Transfer 의 약자로 웹에 존재하는 모든 자원(이미지, 동영상, DB 자원)에 고유한 URI를 부여해 활용하는 것으로, 자원을 정의하고 자원에 대한 주소를 지정하는 방법론을 의미한다고 한다.
더 구체적으로는 HTTP URI을 통해 자원을 명시하고, HTTP Method(POST, GET, DELETE, PUT)를 통해 해당 자원에 대한 CRUD(CREATE, READ, UPDATE, DELETE) 오퍼레이션을 적용하는 것이라고 한다.
기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그래도 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일이다.
자원, 행위, 표현으로 구성되어 있다.
REST API는 REST 기반으로 서비스 API를 구현한 것이다.
REST API의 특징으로는
RESTful은 일반적으로 REST라는 아키텍처를 구현하는 웹 서비스를 나타내기 위해 사용되는 용어이다. REST API를 제공하는 웹 서비스를 RESTful 하다고 할 수 있다.