반응형

자바스크립트는 싱글 스레드(Single thread) 언어라고 알려져 있다. 엄밀히 말하자면 자바스크립트의 메인 스레드가 싱글 스레드인 것이다. 하지만 크롬과 같은 웹 브라우저가 자바스크립트를 실행하는 원리를 살펴본다면 단순히 싱글 스레드 언어라고 말하기 어렵다. 이를 이해하기 위해 이벤트 루프(Event loop)와 모던 브라우저 구조를 한 번 살펴보자.

시작하기 전에


본 포스팅은 코드스피츠(CodeSpitz) 강의 내용을 정리한 글입니다.

스스로의 이해를 위해 여러 자료를 참고하여 정리하였으며, 출처는 맨 하단에서 확인할 수 있습니다.

아래 유튜브 링크를 클릭하면 녹화된 강의를 볼 수 있습니다.

 

키워드


  • 스레드(thread) : 프로세스 내 실행 단위. 하나일 경우 싱글 스레드(Single thread), N개일 경우 멀티 스레드(Multi thread)라고 함
  • 동시성(Concurrency) : 여러 작업이 마치 동시에 일어나는 것처럼 보이는 것
  • 병행성(Parallelism) : 여러 작업이 동시에 일어나는 것
  • 브라우저 엔진 : 크롬과 같은 웹 브라우저 엔진으로, 다양한 작업을 수행
  • 호출 스택(Call Stack) : 자바스크립트 에서는 수행해야 할 함수를 순차적으로 호출 스택에 담아 처리함
  • 콜백 큐(Callback Queue) : 자바스크립트 런타임 환경에서 처리해야 하는 명령어를 임시로 저장하는 대기 큐임. Task Queue, Event Queue라고도 함
  • Web API : 웹 브라우저에서 제공하는 API로, Browser API라고도 칭한다. 노드js의 경우 C++ API가 존재한다.

 

자바스크립트가 싱글 스레드라면...

console.log("1");
setTimeout(console.log, 5000, "2"); // 5초 후, console.log 함수 실행
console.log("3");

// [출력]
// 1
// 3
// 2

왜 출력값이 1, 2, 3이 아닐까?

setTimeout(...)는 5초 후에 콘솔창에 '2'를 출력할 것을 요구하고 있다.

싱글 스레드라면 setTimeout(...) 함수가 끝난 후 다음 코드를 실행해야 할 것 같다.

하지만 '3'이 먼저 출력되었다. (0ms로 해도 결과는 동일함. setTimeout에 지연이 존재)

이런 현상을 이해하기 위해서는 이벤트 루프에 대해 알 필요가 있다.

 

이벤트 루프(Event Loop)와 동시성(Concurrency)

방금 전의 코드가 어떻게 처리되는 지 천천히 살펴보자.

 

line 1 : console.log("1") 코드 수행

1) Call Stack에 console.log("1") 추가

2) console.log("1") 실행, 콘솔창에 '1'이 출력

3) 실행 완료 후 Call Stack에서 console.log("1") 제거

 

line 2 : setTimeout(console.log, 5000, "2") 코드 수행

1) Call Stack에 setTimeout(...) 추가

2) setTimeout(...) 실행, Web API의 timer 스레드에 작업을 넘김

즉, 5초 간 기다리는 작업을 timer 스레드라는 서브 스레드에서 수행하는 것이다. 이 덕분에 메인 스레드에서는 5초 간의 블로킹이 일어나지 않고 다음 코드를 수행할 수 있다. 자바스크립트가 호출 스택이 하나인 싱글 스레드임에도 여러 가지 일을 동시에 처리하는 것처럼 동작할 수 있는 이유이다. 이를 동시성(Concurrency)라고 표현한다.

3) 실행 완료된 setTimeout(...)는 Call Stack에서 사라진다.

 

line 3 : console.log("3") 코드 수행

1) Call Stack에 console.log("3") 적재

2) console.log("3") 실행, 콘솔창에 '3' 출력

3) 실행이 완료된 console.log("3") 는 Call Stack에서 제거

1) 5초 뒤, Web API의 timer 스레드는 console.log("2")를 Callback Queue로 옮김

(엄밀히 말하면 5초의 대기를 보장하는 것이지, 정확히 5초 뒤에 실행되는 것은 아님)

 

1) 이벤트 루프가 Callback Queue에 있던 console.log("2")를 Call Stack에 추가

이벤트 루프(Event Loop)는 항상 루프를 돌면서 Call Stack과 CallBack Queue를 확인한다. 그러다 Call Stack이 빈 상태가 되면, Callback Queue에 있는 함수를 하나씩 Call Stack으로 올려주는 것이다.

2) Call Stack에 올라간 함수가 실행되어, 콘솔창에 '2' 출력

3) 실행이 완료된 console.log("2") 는 Call Stack에서 제거

⇒ 자바스크립트는 싱글 스레드이지만 이벤트 루프, Web APIs(노드의 경우 C++ APIs), Callback Queue가 존재하기 때문에 비동기 콜백 작업이 가능하다.

 

모던 브라우저와 이벤트 루프

이제 크롬과 같은 웹 브라우저에서는 자바스크립트를 어떻게 실행시키는지 알아보자.

아래 이미지는 모던 브라우저 구조를 간단하게 나타낸 것이다.

각 항목을 상세히 살펴보자.

  • Engine work (브라우저 엔진의 작업)

먼저, 브라우저 엔진은 렌더링, 통신 준비, 메모리 초기화, 프로세스 정리 등과 같은 수많은 작업을 백그라운드에서 멀티 스레드(Multi thread)로 처리한다.

싱글 스레드(Single thread)로 처리될 때에는, 내가 작성한 js코드나 메모리에 영향을 끼치는 작업을 수행할 때이다. 이러한 작업은 엔진이 하는 일 중에서 극히 일부분이지만, 나의 코드에 따른 엔진의 일을 표시하기 위해 싱글 스레드, 동시성 안에 표시해두었다.

  • Check Queue

실행해야할 Callback queue가 존재하는지 검사한다.

큐가 존재한다면 명령어를 꺼내 ④ Run, 존재하지 않다면 다시 ① Engin work 단계로 돌아간다.

  • Callback Queue

Web API 스레드는 각자 해야할 일을 수행 후, Callback Queue에 콜백 함수를 적재시킨다.

이때 Web API는 멀티 스레드로, 각 작업을 병행적으로 처리한다.

때문에 Callback Queue를 기준으로 싱글 스레드와 멀티 스레드 작업으로 나눌 수 있다.

즉, Callback Queue 상단은 싱글 스레드로 작업을 Concurrent하게 처리하고, 하단은 멀티 스레드로 작업을 병행적으로 처리한다.

  • Run js

명령어 수행

 

마무리 (+내용 추가)

자바스크립트의 엔진은 싱글 스레드가 맞다.

하지만 현실에서 자바스크립트 엔진(싱글스레드)이 독립적으로 실행되지 않고, 웹 브라우저나 노드js와 같은 멀티스레드 환경에 임베디드되어 실행된다. 때문에 자바스크립트와 Web API, 이벤트루프 등을 분리하여 말하기 어렵다.

때문에 자바스크립트에서는 멀티스레드로 작업이 가능하지만, 싱글 스레드와 멀티 스레드의 타이밍이 존재한다는 것을 명심할 필요가 있다.

포스팅 내용에 잘못된 부분이 있다면 댓글 부탁드립니다.

 

참고자료


반응형