본문 바로가기
Develop/백엔드 & 서버

Javascript는 비동기를 어떻게 처리해 - 동작 원리

by 구운밤이다 2021. 1. 19.
728x90
반응형

시리즈 목차

자바스크립트는 비동기 작업을 할 수 있다! 정도로 보통 알고 있죠. 이러면 안됩니다. 저 또한 이 과정을 모른 채 그냥 코딩만 해왔었는데 이젠 하나하나 공부해보려 합니다. 프로미스가 뭔지, async await가 뭔지 궁금해하고, 위의 내부과정을 알고싶어해야 했죠. 이를 알고 싶다면 먼저 자바스크립트에 대해 알아야 합니다. 이번 글에서는 자바스크립트가 비동기를 어떻게 처리하는지 그 원리에 대해 알아보고, 이후 글에서 프로미스, async await, fetch에 대해 비교하고 글을 수정해볼게요.

Javascript 동작

Javascript는 싱글스레드로 동작하는 언어입니다. 즉 메인스레드 하나로 구성되어 있는데, 이는 한번에 하나의 작업만을 수행할 수 있음을 의미합니다. 다른 작업이 끼어들거나 먼저 수행할 수 없으므로 비동기나 동시성은 본래 javascript의 성질이 아닌게죠. 즉 자바스크립트는 런타임에서 자체적으로 비동기 API를 지원하지 않습니다! 그렇다면 어떻게 동시성을 가진 비동기 함수들을 사용할 수 있었는가에 대해 답을 해봅시다.

자바스크립트 런타임에서는 비동기를 지원하지 않지만, 동시성을 보장하는 비동기, 논블로킹 작업은 javascript 엔진을 구동하는 런타임 환경(브라우저, Nodejs)에서 지원합니다. 자바스크립트는 코드를 그대로 실행하기만 할뿐입니다. 이를 실행하는 환경(런타임 환경)에서 이벤트를 스케줄해주고, 비동기 작업을 처리해주게 되죠.

자바스크립트는 런타임에서 메모리힙과 콜스택으로 구성되어 있습니다. 메모리 힙은 메모리를 할당을 담당하는 곳이고, 콜스택은 코드를 호출하며 스택으로 쌓는 곳입니다. 함수를 실행하면 call stack에 쌓이며 LIFO(last in first out) 스택 방식으로 나중에 들어온 함수부터 처리되죠. 마지막 함수를 실행 후 제거하고, 그 다음 함수를 처리 후 제거, 이런 방식으로 코드를 실행해 나갑니다.

위 이미지는 자바스크립트엔진이 외부의 런타임 환경과 있을 때의 모습입니다. 위에서 얘기했듯, 자바스크립트엔진 내부에는 콜스택과 메모리힙이 있고 그 외부에 브라우저일때 사용하는 webapi와 (노드등 다른 런타임환경에서는 그에 맞는 라이브러리와 api 사용) 이벤트 루프, 콜백 큐가 추가되어 있죠. 이벤트 루프는 이벤트 발생 시 호출되는 콜백 함수들을 관리합니다. 이 콜백 함수를 콜백 큐에 전달하고, 콜스택에 쌓인 함수가 없다면 콜백 큐에 담긴 콜백 함수들을 콜스택에 넘겨줍니다.콜백 큐는 web api 에서 비동기 작업들이 실행된 후 호출되는 콜백함수들이 기다리는 공간입니다. 이벤트 루프가 정해준 순서대로 기다리며 큐처럼 행동(FIFO)합니다. Web api는 위에서 설명했든 브라우저에서 지원하는 api입니다. Dom 이벤트, ajax (XmlHttpRequest), setTimeout등의 비동기 작업들을 수행할 수 있게 해줍니다.

비동기 동작 원리

이 비동기 작업을 실행하는 과정에서 논블로킹 I/O 개념이 적용되는데, 만약 http 요청을 동기로 수행했다거나 잘못 설계해 무한루프를 돌게되고 콜스택에 함수가 쌓인 채로 머물며 작업이 끝날때까지 기다리게 하겠죠.(블로킹) 자바스크립트는 싱글 스레드로 동작하므로 1개의 call stack에서 함수를 처리하기 때문입니다. 즉 함수를 호출하면 해당 함수가 리턴될 때까지 로딩상태에 빠지게 되고 심하면 무한로딩을 겪을수도 있습니다.. 하지만 이 오래걸리는 http 요청 작업을 webapi(브라우저)에게 넘겨줌으로써, 해당 작업은 브라우저에게 맡기고 다른 코드들을 실행할 수 있습니다. 이것이 논블로킹. 자세한 실행과정은 다음과 같습니다.

  1. 코드가 호출 스택에 쌓이고 실행되면, 자바스크립트의 엔진은 비동기 작업을 webapi에게 위임.
  2. webapi는 해당 작업을 수행하고 콜백함수를 이벤트 루프를 통해 콜백 큐에게 넘겨줍니다.
  3. 이벤트 루프는 콜스택에 쌓인 함수가 없을 때, 콜백 큐에 대기하던 콜백함수를 콜스택으로 넘겨줍니다.
  4. 콜스택에 쌓인 콜백함수를 실행하고 콜스택에서 제거합니다.

비동기 작업 예시

단골 예시를 통해 설명해보겠습니다.

console.log('1번 콘솔로그 함수 실행.');
setTimeout(() => console.log('비동기함수의 콜백함수'), 1000);
console.log('논블로킹');

이렇게 실행하면 결과가 어떻게 나올까요?

1번  콘솔로그  함수  실행.
논블로킹
비동기함수의  콜백함수

이렇게 나오겠죠? 위 실행과정을 통해 설명해 봅시다. 먼저 맨 첫 줄의 함수 console.log(‘1번 콘솔로그 함수 실행.’); 가 콜스택에 쌓이고 실행되어 제거됩니다. 이후 setTimeout함수가 콜스택에 들어가죠. 이 함수를 실행하면 webapi에 타이머가 생깁니다. 이후 논블로킹으로 3번째 줄에 console.log(‘논블로킹'); 이 바로 콜스택에 쌓이고 마찬가지로 바로 실행되고 제거됩니다.

그렇다면 webapi에서 생성된 timer는 어떻게 동작하고 있을까요? 타이머는 생성된 시점 이후로 1000ms를 기다리고 콜백큐로 콜백함수 () => console.log(‘비동기함수의 콜백함수’) 를 전달합니다. 콜백 큐에서는 콜스택에 스택이 비어있다면 함수를 전달한다고 했죠! 비어있음을 확인하고 콜백큐에 저 함수를 다시 전달해 실행하고 제거합니다.

이를 다시 보면 setTimeout 함수는 두 번째 인자로 전달 받은 시간 + 콜스택이 빌때까지 기다리는 시간 을 기다리는 것을 확인해볼 수 있죠! 콜스택에 많이 함수들이 쌓이지 않게 실행이 너무 오래 걸리는 함수는 작업을 세분화하여 비동기 호출을 할 필요가 있겠죠.

이렇게 자바스크립트가 비동기 작업을 어떻게 처리하는 지 알아보았습니다. 다음으로 자바스크립트의 비동기 함수들에 대해 알아보겠습니다.


참고 - Javascript 동작원리

728x90
반응형

댓글