블럭과 논블럭, 동기와 비동기를 비슷한 의미 같지만 다른...참 헷갈리는 개념이다. 자바스크립트 엔진이 동작하는 원리를 이해하기 위해서는 반드시 이 개념들을 이해할 필요가 있다.
두가지 개념은 작업에 상태, 프로그램 실행 흐름을 제어하는 방식을 설명하는 것에서 동일하지만, 작업을 바라보는 관점이 다르기다. 지금부터 각각 어떤 개념인지, 자바스크립트에는 어떤 방식이 사용되고 있는지 알아보자.
먼저, 들어가기 앞서 알아야 하는 개념이 있다.
제어권 : "작업을 관리하는 권한"
프로그램 실행 중 시스템 자원, 데이터 처리 등의 작업을 관리하는 권한을 제어권이라고 한다.
제어권이 없으면 프로그램은 처리해야할 작업들을 처리하지 못하게 된다.
현재 작업의 흐름을 계속 실행 하는가에 대한 여부
다시 말해, 작업을 실행할 제어권
을 가지고 있느냐! 없느냐를 이야기하는 것이라고 할 수 있다.
프로그램을 실행하여 작성한 코드를 처리한다고 할 때, 이때 여러 작업들을 처리하게 될 것이다. 이때 작업을 처리하는 과정 가운데 Block/non-block의 개념을 통해 작업을 "멈추느냐, 계속 이어가느냐"를 판단하게 되는 것이다.
"이때 작업을 처리하는 흐름의 줄기를 Thread(쓰레드)라고 한다."
자바스크립트는 엔진은싱글 쓰레드
로 동작하기 때문에, 작업을 처리하는 친구는 1명뿐이다.
쉬운 이해를 위해 예시를 들어보자.
- 프로그램 ➡️ 카페
- 쓰레드를 처리하는 친구 ➡️ 바리스타
- 쓰레드에서 처리하는 작업들 ➡️ 주문
"작업이 순차적으로(동시에) 진행되고 있는가?에 대한 여부"
동기 비동기의 개념은 아주 간단하다. 여러 작업이 있는데 순서대로 진행되는가 안되고 있는가를 나눈 개념이다.
그런데...
"자바스크립트(JS)는 싱글스레드"이면 당연히 순차적으로,(동기적)으로 동작하는 거 아닌가??
맞다. 자바스트립트(JS)는 싱글스레드이기 때문에 동기적으로 동작한다.
하지만, 자바스크립트(JS)는 비동기적으로 동작한다. 동기와 비동기에 대해 자세히 살펴보고, 자바스크립트는 왜 비동기적으로 동작하는지, 마지막에 설명하도록 하겠다.
"현재 작업의 응답(결과)과 다음 작업의 요청(호출)이 동시에 일어나는 것"
"갑이 을의 작업이 끝났는지 계속확인함
"
대부분의 글에서 동기
란, "동시에 발생하는 것" 이라고 설명한다. 여기서 동시에 일어난다는 것은 우리가 알아볼 자바스크립트 엔진에서는 "일련의 작업"을 의미한다. 즉, 일련의 작업들이 동시에 일어난다는 뜻이다.
자바스크립트에서 일하는 친구(바리스타)는 JS엔진
과 브라우저
2명이 있다.
(이때 JS엔진
이 갑, 브라우저
가 을인 관계...)
이들이 일하는 방법은 이렇다.
JS엔진
는 메인으로 일을한다. 이때 JS엔진
은 들어온 많은 주문 중 몇가지를 브라우저
에게 만들라고 명령한다.
이때 JS엔진
은 브라우저
에게 시킨일이 언제 끝날지 계속 궁금해한다.
브라우저
이라는 친구가 하고 있는 작업이 끝나는과 "동시에"JS엔진
이 자신의 다음 작업을 시작하는 것을 "동기적"이라고 말하는 것이다.
그러니까 동기적이라는 것은 JS엔진
이 브라우저
에게 시킨 일이 끝날때까지 집착하는 것이라고 생각하면 된다.
JS 엔진
: "너 작업 끝났어?"
브라우저 : "아니요..."
(몇초 뒤)
JS 엔진
: "너 작업 끝났어?"
브라우저 : "아니요..."
(몇초 뒤)
JS 엔진
: "너 작업 끝났어?"
브라우저 : "아니요..."
" 작업이 순차적으로 진행되지 않고, 작업을 병렬적으로 "
"동기"적인 것을 추구하는 갑은 을에게 관심이 너무 많다. 계속 작업이 언제 끝나는지 확인한다...
다행히도 "비동기"적인 것을 추구하는 갑은 을을 정말 많이 믿어준다.
그래서 작업을 던져주고, 아무런 관심을 가지지 않는다..!!!
브라우저
에게 일 하라고 명령하고, 잘하고 있는지, 언제 끝나는지 상관없이JS엔진
은 알아서 자신의 다른 일을 이어나가는 것을 "비동기적"이라고 말한다!!
한번 여러가지 경우의 수를 가지고 비교해보자.
"갑
이 을
에게 일하라고 명령을 내린 상태라고 가정을 해보자!!!"
"
갑
은을
이 일을 다 마칠 때까지, 아무것도 못하고 기다린다. 언제 끝나는지 계속 확인 한다."
아래의 그림과 같이 작업을 던져주면, 제어권
을 넘겨주고, 일하지 못하고 기다린다.
어플리케이션은 제어권
이 없기 때문에, 일하지 못하고, 커널이 끝나기만을 기다린다.
(예시)로는 사용자의 입력이 있다. 입력 값이 들어오기 전까지는 대기 상태에 있다가 값이 입력되면 다음 동작으로 넘어간다.
"
갑
은을
이 일을 다 마칠 때까지, 아무것도 못해고 있는데, 언제 끝날지 관심도 없다."
요 경우는 정말 드문 경우이다.
제어권
이 없어서 일을 못하는데, 상대의 결과도 관심이 없어 그냥 기다리는 상태이다.
자주 구현되는 경우는 아니나 하나의 예시로는 비동기적으로 다중 입력을 받아 처리하는 경우가 있다.
"
갑
은 자기의 일을 열심히 한다. 그런데을
이 언제 끝날지 관심이 많아 계~~~속 언제끝나는지 확인한다."
작업은 넘기지만, 제어권
은 여전히 자신에게 있어서, 일을 계속 처리해 나간다. 그런데, 계속 넘긴 작업의 결과가 나왔는지, 계속 확인한다.
예시로는 "로딩 진행률을 나타내는 경우"를 생각해볼 수 있다.
파일 업로드를 한 후 그 진행률이 몇 퍼센트인지 사용자에게 보여준다고 할 때, 중간중간 작업의 완료 여부를 확인하여 진행률을 사용자에게 출력하는 작업을 실행하는 경우이다.
"
갑
은 자기의 일을 열심히 한다.을
언제 끝날지 관심도 없다."
일을 맡기고 제어권
이 여전히 자신에게 있어서 일을 열심히 한다. 이때, 맡긴 작업의 결과에 관심이 없어서 그냥 계속 자기 일에 집중할 수 있다.
맡긴 일에 대해서는 알아서 결과를 알려 주겠지~~~하고 바쁘게 자기 할일을 하는 셈이다.
.
.
.
여기서 갑
이 내 상사라면... 가장 좋은 상사는 과연 누구일까...
맞다. 내가 생각해도 4️⃣번 성향의 갑
이 제일 좋을 것 같다ㅎㅎ
그래서(?) 자바스크립트가 4️⃣번(Non-Block + 비동기)의 방식으로 동작한다.
JS가 비동기적으로 동작할 수 있는 이유는,
JS는 런타임 환경(브라우저, node.js 등)에서 동작하기 때문이다.
런타임 환경인 브라우저와 node.js에서는 자바스크립트 엔진이 처리하는데 오래걸리는 작업들(네트워킹, 이벤트, 파일 등)을 대신해서 처리해주는 역할을 해주기 때문에 JS는 비동기적
으로 동작할 수 있다고 하는 것이다.
JS에서 오래 걸리는 작업들을 혼자 다 처리했다면, 아주 느려터진 언어가 되어 아마 웹 사이트를 이용하는 사람들은 속터졌을 것이다...
위 그림은 자바스크립트가 Web 브라우저에서 동작하는 것을 나타낸 그림이다.
자바스크립트는 싱글스레드로 하나의callstack
과 하나의 Task Queue
를 가지고 있고, 다음과 같은 구조로 동작한다.
개발자가 작성한 코드를 실행하면, 전역 함수부터 차례대로 함수가 호출될 것이다.(이때 실행 컨텍스트가 호출되는 순서대로 쌓이는 것이다.) 처리할 작업을 쌓아두는 공간을 Call stack
코드에서 오래 걸리는 작업(파일 입출력, 네트워크 작업, 이벤트 등)을 수행할 수 있도록 미리 브라우저에서 제공하는 API를 호출하게 되면, 브라우저가 인식하고, 이것을 대신 수행한다. 이때 자바스크립트는 브라우저에게 일을 넘기고 다음 작업을 실행하게 된다.
이때, 개발자는 브라우저에게 일을 넘길때
"(오래걸리는 작업)이 끝나면 [이 (작업)함수를 넘겨줘]" 라고 코드를 작성한다
이때 함께 넘겨주는 함수를 다시 돌려 받는다고 하여 콜백(callback)함수
라고 한다!
작업이 끝난 후 콜백(callback)함수
는 Task Queue
라는 공간에 저장이 된다.
Event Loop
라는 친구가 중간에서 "call Stack"을 지켜보다가 텅 비게 되면, 그때, Task Queue
에 보관 되어 있던 콜백(callback)함수
들을 "call Stack"에 이동하고, 나머지 작업을 처리하게 된다.
참고