Language/Javascript (Typescript)

[Javascript] Tutorial(5): 싱글 스레드 기반 이벤트 루프와 비동기

ooeunz 2020. 3. 16. 21:29
반응형

이번 포스팅에서는 조금은 자바스크립트에서 벗어난 이야기를 해볼 예정입니다. 바로 이벤트 루프라는 녀석 때문입니다. 자바스크립트 튜토리얼이나 책에서 이벤트 루프에 관해서는 잘 언급하지 않습니다. 왜냐하면 정확히 이야기하자면 ECMAScrpt 스펙에는 이벤트 루프가 포함되어있지 않습니다. 이벤트 루프는 브라우저나 Node.js가 담당하는 부분이기 때문입니다. 하지만 이 튜토리얼에서 이벤트 루프를 언급하는 이유는 자바스크립트가 브라우저나 Node.js와의 연관성은 필연적이기 때문에 이 시점에서 비동기와 함께 짚고 넘어가는 것이 좋다고 생각했기 때문입니다.

 

이번 포스팅에서는 총체적인 접근이 조금 필요할 것 같습니다. 그래서 먼저 동기/비동기의 개념을 다루고, 자바스크립트에서 비동기가 적용되는 이벤트 루프에 대해 살펴보도록 하겠습니다. 그리고 마지막으로 callback 함수와 promise, async/await을 살펴보도록 하겠습니다.

 

 


5-1. 동기 / 비동기

흔히 자바스크립트는 가볍고 빠르다고 이야기 합니다. 이러한 성능은 자바스크립트에서 오는 것이 아니라 비동기I/O에서 옵니다. 그렇다면 비동기가 무엇인지 알아보도록 하겠습니다. 참고로 비동기는 자바스크립트에서만 활용되는 개념은 아닙니다. 다만 다른 언어에서는 동기적으로 동작하는 코드에 비동기 코드를 입히는 느낌이라면, 자바스크립트는 언어 자체가 비동기처리에 초점이 맞추져 있습니다. (제 개인적인 의견입니다.) 그럼 동기와 비동기의 개념부터 살펴보도록 하겠습니다.

 

동기(Synchronous)는 요청을 하는 시기와 응답을 받는 시기가 일치한다는 뜻입니다. 어떤 요청이 들어오면 응답이 완료 될 때까지 프로그램이 정지하고, 응답을 받게되면 그때 다시 진행을 하게 됩니다.

 

반대로 비동기(Asynchronous)의 경우엔 요청과응답 결과가 동시에 일어나지 않는다는 뜻입니다.

비동기로 수행할 경우, 요청에 대한 수행을 넘기고 응답을 기다리지 않고 바로 다음 작업을 수행하게 됩니다.

 

이미지 출처 (https://poiemaweb.com/js-async)

예를 들어 아래와 같은 블로킹 코드가 있다고 가정해보겠습니다. 아래의 코드를 실행할 경우

 

※ 사실 아래의 예제는 동기 / 비동기보다는 블로킹 / 논블로킹 예제에 어울리지만, 대부분의 독자가 동기 / 비동기라는 용어에 익숙하다고 생각하기 때문에 동기 == 블로킹, 비동기 == 논블로킹 이라고 가정하고 예제를 설명하겠습니다.

const longTime = () => {
  // 시간이 오래 걸리는 작업이 있다는 가정
  console.log('시간이 오래 걸리는 작업');
};

console.log('시작');

longTime();

console.log('종료');

 

1. 시작

2. 시간이 오래 걸리는 코드

3. 종료

 

의 순으로 코드의 결괏값이 출력될 것 입니다. 만약 자바스크립트 이외의 다른 프로그래밍 언어를 학습해보신 분이라면 위의 출력 순서를 어렵지 않게 예측할 수 있을 것입니다.

 

하지만 비동기적으로 실행되는 경우엔 결괏값이 조금 달라집니다. 아래는 비동기 코드입니다.

 

※ setTimeout은 대표적인 비동기 코드입니다. 아래의 코드에선 0초 후 longTime 함수를 실행하는 코드로 사용했습니다.

0초 후에 실행이기 때문에 

const longTime = () => {
  // 시간이 오래 걸리는 작업이 있다는 가정
  console.log('시간이 오래 걸리는 작업');
};

console.log('시작');

setTimeout(longTime, 0);

console.log('종료');

 

1. 시작

2. 종료

3. 시간이 오래 걸리는 코드

 

비동기 코드는 동기와 다르게 종료 코드가 먼저 출력된 다음 '시간이 오래 걸리는 작업'이 출력됩니다. 그 이유는 동기 코드 같은 경우엔 시간이 오래 걸리는 작업의 수행을 넘기면 결괏값이 리턴될 때까지 코드의 실행 흐름이 멈추게 됩니다. 그리고 수행을 넘긴 결괏값이 넘어 오면 그제서야 다음 코드를 실행하게 됩니다.

 

반면 비동기 코드의 경우엔 응답을 기다리지 않습니다. 따라서 시간이 오래 걸리는 작업은 수행을 넘기고, 결괏값을 기다리지 않고 바로 '종료'를 출력합니다. 그리고 시간이 오래 걸리는 작업이 완료될 경우 그제서야 '시간이 오래 걸리는 작업'을 출력하게 됩니다.

 

 setTimeout은 대표적인 비동기 코드입니다. 아래의 코드에선 0초 후 longTime 함수를 실행하는 코드로 사용했습니다.

0초 후에 실행이기 때문에 바로 출력이 될꺼라 생각했지만, 이러한 순서는 이벤트 루프가 동작하는 방식과 관련이 있습니다. 이와 관련해선 뒤의 이벤트 루프에서 살펴보도록 하겠습니다.


 

블로킹 / 논블로킹

동기와 비동기 이야기가 나오면 꼬리처럼 달라붙는 또 다른 키워드가 블로킹과 논블로킹입니다. 자바스크립트에서는 대부분의 경우 동기-블로킹, 비동기-논블로킹으로 짝지어지기 때문에 비동기 == 논블로킹이라고 생각하기 쉽지만 이 네가지 언어는 의미적인 차이가 있습니다.

 

  • 동기와 비동기 : 함수가 바로 return 되는지의 여부
  • 블로킹과 논블로킹 : 백그라운드 작업 완료 여부

위의 예시에서는 동기 코드는 시간이 오래걸리는 작업을 기다리기 위해 코드가 멈췄었습니다. : 블로킹

그리고 해당 함수의 리턴 값을 (리턴 값이 없었지만 있다고 가정하겠습니다.) 기다렸다가 다음 코드를 실행했습니다. : 동기

 

반대로 비동기 코드에서는

시간이 오래 걸리는 작업의 수행을 넘기고 멈추지 않고 바로 다음 코드를 실행했습니다. : 논블로킹

그리고 0초 후에 코드를 실행시키기 위해 콜백 함수로 longTime함수를 넣어주었습니다. : 비동기

 

 

비동기는 결코 쉬운 개념이 아닙니다. 위의 예제 외에도 다른 예제들을 찾아보며 확실히 이해하도록 노력하시는 것을 추천드립니다.

아래에 비동기 이해를 위한 심화 예제 링크를 걸어두겠습니다. 밑의 포스팅은 async/await 문법을 사용하기 때문에 이 포스팅을 끝까지 학습하고, 비동기에 대한 어느정도 이해가 생겼을 때 공부해보시는 것을 추천드립니다.

 

 

[Node.js] await vs return vs return await: 비동기 이해하기

비동기 함수를 작성할 때 await과 return을 하는 것 그리고 return await을 사용하는 것에는 실행에 차이가 있다. 이것을 올바르게 사용하지 않으면 우리는 예상치 못한 값을 반환받을 수 있다. 이 포스팅은 그러..

ooeunz.tistory.com

 


5-2. 이벤트 루프

자바스크립트는 싱글스레드이지만 비동기처리를 이용해 가볍고 효율적이라고 앞에서 언급했습니다. 여기서 싱글 스레드란 개발자가 컨트롤할 수 있는 스레드가 하나라는 의미로, 이 싱글스레드는 이벤트 루프가 싱글 스레드로 동작하기 때문에 그렇습니다.

 

아래 이미지는 setTimeout(run, 3); 코드가 실행되었을 경우의 이벤트루프 실행 순서를 나타낸 것입니다. 이미지에서 볼 수 있듯이 Node.js에는 call stack(호출스택), task queue, background가 존재합니다.

 

먼저 호출스택에 컨택스트가 쌓이게 되고 FILO(first in last out) 구조인 스택의 특성에 따라 가장 마지막에 들어온 setTimeout 함수가 실행되고 run과 함게 백그라운드에서 setTimeout 3초간 기다리게 됩니다. 그 후 3초가 지나면 run 함수가 task queue로 이동하게 됩니다. 만약 호출 스택이 비어있고 태스크큐에 작업이 남아 있다면 호출 스택은 run 함수를 호출 스택으로 가져가게됩니다.

 

이러한 순서에 따라 자바스크립트는 하나의 이벤트루프에 의존하여 비동기적으로 실행됩니다.

 

출처: https://www.zerocho.com/category/JavaScript/post/597f34bbb428530018e8e6e2
출처: https://www.zerocho.com/category/JavaScript/post/597f34bbb428530018e8e6e2

반응형