javaScript는 어떻게 동작하는거야?

형동킴·2022년 3월 28일
36
post-thumbnail

자바스크립트를 공부하면서 어느 순간 자바스크립트 코드의 동작에 대해 의문이 생겼습니다. 다른 프로그래밍 언어들처럼 자바스크립트도 기본적으로는 위에서부터 순차적으로 코드를 실행하는 것 같기는 한데, 마냥 그런 것 같지 않았기 때문입니다. 그래서 자바스크립트가 어떻게 동작하는지 공부하게 되었고, 공부한 내용을 정리해 보았습니다.

A,B,C,? or A,C,B?

console.log('A');

setTimeout(
  ()=>{
    console.log('B');
  },1000);

console.log('C')

이 자바스크립트 코드는 어떤 결과를 출력할까요? 아마 코딩을 한번쯤 해본 사람이라면 A,B,C로 결과를 예상할 것입니다. 프로그래밍 언어는 기본적으로 코드를 위에서 아래로 하나씩 실행하기 때문입니다. 하지만, 실제 결과는 A,C,B로 출력됩니다. 자바스크립트는 어떻게 동작하기에 A,C,B같은 결과가 출력되는 걸까요? 한번 자바스크립트의 동작 원리에 대해 알아보겠습니다.

자바스크립트는 싱글 스레드 언어이다

자바스크립트는 싱글 스레드(single thread)방식으로 동작하는 언어입니다. 구체적으로 말하자면, 자바스크립트 코드를 실행시켜주는 자바스크립트 엔진이 싱글 스레드 방식으로 동작하는 것입니다.

  • 스레드는 쉽게 말하자면, 일꾼입니다.

  • 싱글 스레드는 일꾼이 한명이라는 뜻이고, 따라서 한 번에 한 가지 작업만 할 수 있다는 것을 의미합니다.

  • 여기서 작업은 함수를 실행하는 것을 의미합니다. 즉, 자바스크립트는 한 번에 하나의 함수만을 실행할 수 있다는 뜻입니다.

자바스크립트 엔진

자바스크립트 엔진은 자바스크립트 코드를 해석하고 실행하는 프로그램 또는 인터프리터입니다.

  • 자바스크립트 엔진은 모든 브라우저에 내장되어 있습니다.

  • 대표적인 자바스크립트 엔진으로는 chrome과 node.js에서 사용하는 Google V8이 있습니다.

  • 자바스크립트 엔진은 하나의 call stack과 memory heap으로 구성되어 있습니다.

Memory Heap

변수와 객체의 메모리 할당을 담당하는 곳을 말합니다.
(변수,객체 등이 저장되는 곳)

Call Stack

stack 방식으로 호출된 함수를 호출 순서에 따라 쌓고 실행 완료된 함수를 제거하는 작업이 이루어지는 곳입니다.
(정확히 말하면 실행 컨택스트가 스택으로 쌓입니다)

*스택(stack): 한 쪽 끝에서만 자료를 넣고 뺄 수 있는 LIFO(Last In First Out) 형식의 자료 구조

자바스크립트 엔진을 통해 알아본 자바스크립트 동작 방식

  • 자바스크립트 엔진은 call stack을 하나만 가지고 있습니다. 함수(코드)를 처리하는 일꾼이 한 명, 즉 싱글 스레드입니다.

  • 또, call stack에서 stack 방식으로 함수를 처리하기 때문에, 자바스크립트는 위에서 아래로 코드를 실행시키고 현재 실행중인 함수가 (완료되지 않으면 stack에 남아 있기 때문에) 작업을 마쳐야 다음 함수를 실행할 수 있습니다.

자바스크립트는 순차적으로 함수를 실행하고 한 번에 하나의 함수만 실행할 수 있습니다. 그리고 현재 작업이 완료되기 전까지 다른 함수를 실행할 수 없습니다.


자바스크립트 엔진을 통해 자바스크립트의 기본적인 동작 원리에 대해 알아봤습니다. 하지만, 이것만으로는 앞의 setTimeout예시의 결과가 A,C,B로 나오는 이유를 여전히 설명할 수 없습니다. 위 방식대로라면 A,B,C로 출력되야하기 때문입니다.

그러면 어째서 A,B,C가 아니고 A,C,B일까요? 그 이유는 바로 setTimeout이 비동기로 처리되기 때문입니다.

  • 비동기(Asynchronous: 동시에 발생하지 않는)
    : 현재 요청에 대한 응답과 다음 작업의 요청이 동시에 발생하지 않아도 되는 작업 방식을 뜻합니다. 현재 작업의 완료여부와 상관없이 다음 작업을 수행할 수 있습니다. 예를 들어, 세탁기를 돌리는 동안 설거지를 하는 것을 비동기라고 할 수 있습니다. 세탁기가 완료되어야 설거지를 할 수 있는 것이 아니기 때문입니다.
    (동기는 이와 반대로, 요청과 응답이 동시에 발생해야 하는 작업 방식입니다. 즉, 한 작업이 끝나야 다음 작업을 할 수 있습니다.)

비동기란 실행중인 함수의 완료 여부와 상관없이 바로 다음 함수를 실행하는 방식입니다. 즉, setTimeout 코드의 결과가 A,C,B로 출력되는 것은 setTimeout이 지정된 시간을 기다리는 동시에 C를 출력하기 때문입니다.

자바스크립트는 싱글 스레드라며..?

자바스크립트는 싱글 스레드입니다. 따라서 자바스크립트 자체만으로(자바스크립트 엔진)는 비동기 방식으로 함수를 처리할 수 없습니다. 그럼 어떻게 비동기 작업이 가능한걸까요?

바로 자바스크립트 엔진을 구동하는 환경, 즉 자바스크립트의 런타임 환경인 **브라우저에서 이러한 비동기 작업을 가능하게 해줍니다.

런타임 환경에서 자바스크립트의 비동기 동작

자바스크립트의 런타임 환경은 다음 그림처럼, 자바스크립트를 해석해주고 실행시켜주는 자바스크립트 엔진뿐만 아니라 비동기 작업을 지원해주는 Web API, callback Queue(Task Queue), Event Loop로 구성되어 있습니다.

  • Web APIs
    Web api는 비동기 작업을 지원하기 위해 브라우저에서 제공하는 api입니다. 앞에서 봤던 setTimeout은 사실 자바스크립트 자체 함수가 아니라 브라우저에서 제공하는 web api입니다. 따라서, setTimeout은 call 엔진에서 처리하는 것이 아니라, (API이기 때문에) 다른 일꾼(쓰레드)을 호출하여 setTimeout의 작업을 위임하는 것입니다.

    • setTimeout뿐만 아니라 DOM Events (addEventListener), Ajax(xmlHttpRequest)함수 등도 브라우저가 제공하는 Web API입니다.

    • 작업을 마치고 호출된 Callback함수는 callback queue로 넘겨지게 됩니다.

  • Callback Queue(Task Queue)
    web api에 의해 수행된 작업이 완료된 후 호출된 콜백함수들이 대기하는 곳입니다. 태스크 큐는 FIFO(First In First Out) 방식을 따릅니다. 즉 가장 먼저 추가된 콜백이 가장 먼저 삭제됩니다.

  • Event Loop
    이벤트 루프는 현재 실행중인 태스크가 없는지 주기적으로 확인하며(call stack이 비어있는지 확인), 실행중인 태스크가 없을 시(call stack이 비었을 시) 태스크 큐에 담겨있는 콜백 함수들을 순서대로 콜스택에 넘겨주는 역할을 합니다.

이제 위의 방식으로 setTimeout 코드의 결과가 왜 A,C,B로 출력되는지 설명해보겠습니다.

console.log('A');

setTimeout(
  ()=>{
    console.log('B');
  },1000);

console.log('C')
  1. console.log('A')가 호출되고 call stack에 올라갑니다.

  2. A를 출력하고 console.log('A')는 stack에서 제거됩니다.

  3. setTimeout이 호출되어 call stack에 올라가면 브라우저에서 타이머를 실행시키고 카운트 다운을 시작합니다. 이 후 호출을 완료한 setTimeout는 call stack에서 제거됩니다.

  4. console.log('C')가 call stack에 올라가고 C를 출력한 후 call stack에서 제거됩니다.

  5. WEB API에서 카운트 작업이 완료되면 호출된 callback을 task queue로 넘깁니다.

  6. 이 때 event loop가 call stack이 비어있는지 확인하고, 비어있으면 task queue에 있는 callback을 call stack에 올립니다.

  7. console.log('B')가 call stack에 올라가고 B를 출력하고 call stack에서 제거됩니다.

setTimeout의 시간이 0초더라도 위의 동작 과정을 거치게 되어 A,C,B로 출력됩니다.

자바스크립트 동작을 직접 확인해볼 수 있는 영상

영상에 나오는 사이트 주소

정리

자바스크립트는 싱글 스레드입니다. 그렇기 때문에 한 번에 하나의 작업만 처리할 수 있습니다. 만약 오랜 시간을 대기하는 함수를 만나면 거기서 자바스크립트의 동작이 멈춰버릴 것입니다. 더군다나 이런 함수들이 많다면 브라우저는 자주 멈추고 느려지게 될 것입니다. 따라서 브라우저에서는 이러한 문제를 해결하고자 비동기 작업을 지원합니다.

브라우저에서 비동기 작업을 지원하지만, 결국 자바스크립트 코드를 실행하는 것은 call stack입니다. call stack이 바쁘면 브라우저는 느려지기 마련입니다. 따라서 call stack을 항상 염두하면서 코드를 작성해야 합니다.

글을 마치며..

자바스크립트 동작에 대해 정리를 해보았습니다. 단순한 호기심에 시작한 공부였는데 생각보다 많이 어렵고 모르는 것들이 계속 꼬리를 물고 늘어져 정리하는데 많은 시간이 들었습니다. 덕분에 그동안 제대로 모르고 있던 callback, 비동기, 쓰레드같은 것들을 조금은 알게된 것 같습니다. 아무래도 자바스크립트나 브라우저에 대해 잘 모르는 채로 핵심적인 부분을 파고 들어서 그런지 아직도 어설프게 아는 것 같습니다. 나중에 좀 더 공부를 하고 다시 내용을 정리해봐야할 것 같습니다.

profile
결과보다 성장을!

4개의 댓글

comment-user-thumbnail
2022년 4월 5일

좋은글 감사합니다

1개의 답글
comment-user-thumbnail
2022년 4월 7일

좋은 글 감사합니다.

자바스크립트는 싱글 스레드(single thread)방식으로 동작하는 언어입니다. 구체적으로 말하자면, 자바스크립트 코드를 실행시켜주는 자바스크립트 엔진이 싱글 스레드 방식으로 동작하는 것입니다.

이게 무슨 뜻인지 알 수 있을까요? 언어 자체는 threading에 대한 spec이 없는 것으로 알고 있어서요.

예를 들어, 일부(아마 대부분) 브라우저는 js코드를 single threaded로 처리하지만, 만약 제가 새로운 브라우저를 만들고 js를 multi threaded처리하게 구현했다면, single thread방식으로 동작하는게 아닙니다.

1개의 답글