[Study] JavaScript 동작 원리

productuidev·2022년 1월 15일
1

FE Study

목록 보기
1/67
post-thumbnail

자바스크립트

진짜 쉽게 알아보는 자바스크립트 동작 원리

싱글 스레드 = 자바스크립트는 머리가 하나!

이 의미는 쉽게 풀어쓰면 한 번에 한 가지 일 밖에 처리할 수 없다는 것을 의미합니다. (어려운 용어로는 Call stack이 하나라고 표현 합니다)
현실 세계에 비유해서 이야기 하면 멀티 태스킹 안되는 글쓴이와 같습니다.

즉, 현재 실행하고 있는 함수가 있는 경우에 다른 일을 할 수가 없고, 다른 일들이 블락되게 됩니다. 이렇게 되면 브라우저에서 오래 걸리는 작업이 실행될 경우, 웹 페이지의 UI는 멈춰버리고, 사용자는 어떠한 행동도 할 수 없게 됩니다.

자바스크립트는 머리가 하나이기 때문에, 1분 동안 아무것도 안하고 기다리면 브라우저는 아무런 행동(화면 스크롤, 버튼 클릭…)도 하지 못하게 됩니다. 그렇다면 어떻게 해야할까요? 이 때 사용하는 것이 바로 비동기 콜백입니다.

Memory Heap

데이터를 임시 저장하는 곳으로, 함수나 변수, 함수를 실행할 때 사용하는 값들을 저장합니다.

호출 스택 (Call Stack)

코드가 실행되면 코드의 내부의 실행 순서를 기록해 놓고, 하나씩 순차적으로 진행할 수 있도록 도와주는 곳입니다.

자바스크립트는 기본적으로 작업을 싱글스레드로 처리한다. 즉, 단 하나의 호출 스택을 사용한다. 호출 스택에 쌓인 함수나 코드를 위에서부터 아래로 차례차례 실행한다. 그리고 하나의 작업이 끝나면 pop하고 바로 아래의 함수나 코드를 실행한다. 작업을 차례대로 실행하므로 하나의 작업이 끝날 때까지 또 다른 작업을 실행하지 않는다.
→ 즉 실제로 코드를 실행했을 때, 다음에 실행되어야 할 코드를 순서대로 기록을 하며, 순차적으로 코드를 실행 할 수 있게 도와 줍니다

다음과 같은 코드를 살펴보자.

function first(){
    second();
    console.log("첫번째");
}
function second(){
    third();
    console.log("두번째");
}
function third(){
    console.log("세번째");
}
first();

먼저 first 함수가 호출되고, 그안의 second 함수가 호출되고,
마지막에 third 함수가 호출된다.

노랑색으로 칠해진 코드가 어떠한 이유로 시간이 오래걸리게 된다면 당연히 다음에 호출되어야 코드는 실행에 시간이 걸릴 수 밖에 없습니다. 이와 같은 상황이 서두에서 이야기했던 특정 코드가 오래 걸려 다른 코드를 실행하지 못하게 되어 block이 된 상태입니다.
마냥 오래 걸리는 일을 지켜볼 순 없으니, 어떻게 해야 할까요? 이 때 효과적으로 event를 관리하기 위해 필요한 것이 바로 web API와 Callback Queue, event loop입니다.

자바스크립트 런타임

자바스크립트 엔진 밖에서도 자바스크립트에 관여하는 요소들이 있다. Wep API, Task Queue, Event Loop등이다. 런타임은 특정 언어로 만든 프로그램들을 실행할 수 있는 환경이다. Node.js나 크롬등의 브라우저들은 자바스크립트가 구동되는 환경이기 때문에, 이를 자바스크립트 런타임이라고 한다.

Web API

Web API는 브라우저에서 제공되는 API이다. 자바스크립트 엔진에서 정의되지 않았던 setTimeout이나 HTTP 요청(ajax) 메소드, DOM 이벤트 등의 메소드를 지원한다.

Task Queue

이벤트 발생 후 호출되어야 할 콜백 함수들이 기다리는 공간. 이벤트 루프가 정한 순서대로 줄을 서 있으므로 콜백 큐(Callback Queue) 라고도 한다.
자바스크립트에서 비동기로 호출되는 함수들은 호출 스택(Call Stack)에 쌓이지 않고 태스크 큐(Task Queue)로 보내진다.

console.log("A");
setTimeout(function() {
  console.log("B");
}, 0);
console.log("C");

위 코드는 “A”, “B”, “C” 순으로 콘솔에 표시될 것 같지만 그렇지 않다. 실행 결과는 “A”, “C”, “B” 순으로 콘솔에 찍힌다. setTimeout 함수로는 인수로 받은 콜백 함수를 일정 시간이 지난 후 실행하도록 예약하는 처리만 하고, 그다음 바로 다음 코드가 실행되기 때문이다.

Event Loop

이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정한다.

이 3가지를 왜 쓰느냐??
능률적으로 일하기 위해서 (web API, Callback Queue, event loop)

라면을 끓일 때를 생각해보겠습니다.

라면의 조리 과정은 물을 올리고, 라면을 넣고, 파나 계란을 넣는 순서로 진행됩니다. 이 때, 우리가 물을 끓이면 끓을 때까지 물 앞에서 지켜보고 있다가 라면을 조리하는 분은 거의 없을 것입니다. 왜냐하면 물을 끓이면서 다른 일을 할 수 있는데 기다리는 것은 능률적이지 않기 때문이죠.
그래서 물을 올려놓은 다음 라면도 꺼내고, 파도 썰고, 계란도 꺼내어 조리를 합니다.

Javascript의 세계도 마찬가지로 하나하나씩 순차적으로 진행하는 것이 아니라 효과적으로 시간을 분배하면서 이벤트를 관리합니다. 그럼 어떻게 진행되는지 하나하나씩 알아보도록 하겠습니다.

물이 끓고 나면 해야 하는 일 (Callback Function)

라면에 물이 다 끓으면 어떻게 하나요? 자연스럽게 우리는 팔팔 끓는 물에 라면을 넣거나, 스프를 넣습니다. 스프 먼저, 면 먼저처럼 기호가 다르듯, 하나의 일이 끝나면 사람마다 다음 해야 할 일이 다를 수 있습니다.
자바스크립트에서는 함수가 실행이 끝나면, 다음에 실행할 일을 정할 수 있는데 이것을 Callback이라고 부릅니다. 아래의 코드를 예로 들어 보겠습니다.

setTimeout(function(){
	console.log('1초가 지나갔다')
}, 1000)

setTimeout 함수는 첫번째 인자로 callback function을 받고,
두번째 인자로 기다릴 시간을 받는 함수입니다.

위 코드를 실행해보면 1초 후에 function(){ console.log('1초가 지나갔다')} 가 실행 됨을 알 수 있습니다. 즉, callback Function은 함수가 정해놓은 일이 끝난 뒤,후속으로 하는 일을 알려주는 함수입니다.

setTimeout 이외에도 Callback 함수는 AJAX, Dom을 관리하는 event 등에 사용되며, javascript를 효과적으로 사용하기 위해선 반드시 알아야 하는 개념입니다.

오래 걸리는 일 따로 처리하기 (browser web APIs)

토이가 아침 식사를 준비한다고 생각해보겠습니다.

토스트기 없이 프라이팬에 빵을 구우면, 빵을 굽는 동안은 식탁을 셋팅 한다거나, 음료를 준비하는 등 다른 일을 할 수가 없습니다. 반면에 토스트기가 있다면 토스트기에 빵 굽는 일은 시키고, 저는 다른 일을 할 수 있습니다.

이를 브라우저에 대입 해보면, browser web API가 브라우저에서 토스트기와 같이 오래 걸리는 작업을 대신해 주는 역할을 합니다. browser web API 는 브라우저 안에 C++ 구현된 쓰레드로 주로 DOM event, AJAX request, setTimeout 등 비동기 이벤트를 처리합니다. javascript 싱글 쓰레드의 영향을 받지 않고, 독립적으로 이벤트를 처리할 수 있습니다.

머리 속에 다음에 할 일 생각해 놓기 (Callback Queue)

아까 아침 식사를 준비하던 때로 돌아 가보겠습니다.

저는 지금 토스트기에 식빵을 넣어둔 상황이고, 빵이 다 구워지면 빵에 잼을 바를 생각입니다.
이처럼 사람은 어떤 일이 끝나면 다음에 할 일을 머리 속에 저장해 놓지만, 브라우저의 경우 Callback Queue 에 저장 해놓고 사용합니다.

즉, Callback Queue 는 browser web API에 있는 event가 실행되고 나면 javascript에서 실행할 callback을 저장하고 있는 저장소입니다.

setTimeout(function(){
	console.log('1초가 지나갔다')
}, 1000)

예를 들어 위 코드를 실행하게 되면 1초가 지나면 Callback Queue에 function(){console.log(‘1초가 지나갔다’)} 함수가 담기게 됩니다.

할 일을 정돈하기 (event loop)

아침 식사로 다시 돌아가 보겠습니다.

토스트기에 빵을 넣고, 저는 냉장고에서 우유와 과일을 꺼내 셋팅을 하고 있습니다. 우유를 따르던 중에 토스트에서 빵이 다 구어졌습니다. 저는 빵이 다 구어지면 잼을 바를 생각이었지만, 우유를 따르고 있기 때문에 우유를 마저 따르고 빵을 꺼내 잼을 바를 것입니다. 이처럼 사람은 능률적으로 일하기 위해 우유를 마저 따르고, 빵을 굽는 행동을 효과적으로 판단하고 행동합니다.

Javascript 내에서도 효과적으로 일을 처리하기 위한 비슷한 매커니즘이 있는데 이를 event loop라고 부릅니다.

console.log('시작!');
setTimeout(function timeout(){
	console.log('1초가 지났습니다');
});
console.log('끝!');

위 코드 실행했을 때, event loop 매커니즘이 어떻게 처리하는 알아보도록 하겠습니다.

1) 처음 코드를 실행하면 console.log(‘시작’)코드가 call stack에 들어가게 되고 실행이 됩니다.

2) Call Stack에 setTimeout 함수가 들어갑니다.

3) setTimeout 함수가 비동기 함수이기 때문에 Call Stack 에서 바로 실행되지 않고, web API로 콜백 함수 timeout()이 들어 갑니다.

4) 다음 코드인 console.log('끝!');을 실행합니다. 아직 1초가 지나지 않아 timeout() 함수는 여전히 web API에 있습니다.

5) 1초가 지나 callback Queue에 timeout()함수가 들어 옵니다.

6) event loop가 Call Stack이 비어있는지 확인하고, timeout() 함수를 call Stack으로 보냅니다.

7) timeout 함수 안에 있던 console.log(‘1초가 지났습니다’) 코드가 호출됩니다.

8) 모든 함수가 호출되고 Call Stack이 비워집니다.

결과적으로 event loop는 Call Stack 비어있는지를 주기적으로 확인하여 Callback Queue에서 Callback function을 가져와 Call Stack에서 Javascript 코드가 실행될 수 있도록 돕는 역할을 합니다. event loop가 반복적으로 Call Stack이 비어있는지 확인 하는 것을 tick이라고 합니다.

console.log('script start'); 

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

출처
https://blog.toycrane.xyz/%EC%A7%84%EC%A7%9C-%EC%89%BD%EA%B2%8C-%EC%95%8C%EC%95%84%EB%B3%B4%EB%8A%94-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-c7fbdc44cc97

profile
필요한 내용을 공부하고 저장합니다.

0개의 댓글