[꼼꼼한 개발자] 꼼코더

[JS] - 자바스크립트 동작 원리 간단하고 쉽게 이해하기(콜스택, 이벤트 루프, 태스크 큐) 본문

간단하고 쉽게/JavaScript

[JS] - 자바스크립트 동작 원리 간단하고 쉽게 이해하기(콜스택, 이벤트 루프, 태스크 큐)

꼼코더 2025. 9. 11. 20:43
반응형

🧹 간단 정리

  • JS 엔진: 자바스크립트 코드를 읽고, 해석하고, 실행하는 프로그램
  • 싱글 스레드 : 한 번에 하나의 작업만 처리 가능한 개념.
  • Web API : 한 번에 여러 작업이 가능한 비동기 처리 도우미. (브라우저/Node가 제공)
    • 예) setTimeout, fetch, DOM 이벤트, Node의 fs, http 등
  • 콜스택(Call Stack) : 함수 실행이 쌓이는 스택. 실행 끝나면 빠져나옴.
  • 태스크 큐(Task Queue) : 완료된 Web API(비동기)의 속에 있는 콜백 함수가 대기하는 공간
  • 이벤트 루프(Event Loop) : (콜스택 비어있음 확인 후) 태스크 큐에서 대기하던 콜백을 꺼내 콜스택으로 전달

 

위 내용만 봐서는 모르는 게 당연합니다!

아래에 정말 정말 재밌고 쉽게 설명했으니 꼭! 확인해 보세요!

 

(개념 훝고 ->  맨 마지막 영상을 보시면 완벽하게 이해할 수 있어요!)

 

 

 

 


☕️ 자바스크립트는 싱글 스레드 - 우리 카페는 바리스타가 1명

브라우저 속에 있는 자바스크립트 엔진은 자바스크립트 코드를 읽고 실행합니다.

다만 싱글 스레드이기에 동시에 실행하지 못하고 한 줄씩 차례대로 처리해야 하죠.

 

그래서 자바스크립트는 싱글 스레드 언어라고 불립니다.

 

자바스크립트 싱글 스레드를 카페의 한 명 바리스타에 비유한 그림
싱글 스레드 이미지 비유설명

 

👨🏻‍🍳 비유설명

마치 카페바리스타(JS 엔진)가 한 명뿐인 상황과 같습니다.

  • 손님이 커피 주문하면 → 바리스타는 바로 커피를 만듭니다.
  • 동시에 다른 손님들이 주문하여도 동시에 만들 수 없이 한 사람씩 차례대로 처리해야 하죠.
  • 👉 결국, 중요한 건 바리스타(JS 엔진)가 주문(함수)을 차례대로 실행하는 것입니다.
    • (??? : 내가 아는 바리스타는 동시에 음료 여러개 만들던데요?!!?)

 


📚 콜스택(Call Stack) - 할 일 모음 집

콜스택(Call Stack)실행할 함수가 담겨있는 공간입니다.

먼저 들어온 요소가 나중에 나가는 ‘스택 구조(LIFO)’이죠.

  • 함수 실행 → 스택에 push, 실행 종료 → 스택에서 pop

콜스택을 바리스타의 할 일 메모지로 설명한 그림
콜 스택 이미지 비유설명

 

👨🏻‍🍳 비유설명

스택은 바리스타(JS 엔진)할 일 메모지 모음집 같은 존재입니다.

  • “아메리카노 만들기” 메모 붙임(push)끝나면 메모 떼기(pop)
  • “라떼 만들기” 메모 붙임(push) → 끝나면 메모 떼기(pop)
  • 👉 결국, 콜스택(할 일 모음집)바리스타(JS 엔진)어떤 일들을 처리 중인지 확인하는 공간입니다.

 


🧑🏻‍🍳 비동기 처리(Web API) - 만능 알바생 등장이요

이전에 JS 엔진(바리스타)은 싱글 스레드이기에 한 번에 하나의 일만 처리할 수 있다고 했는데..

만약 콜스택(할 일 메모지)에 ‘긴 작업(예: 오래 걸리는 계산)’이 쌓여 있다면?

👉 그동안 버튼 클릭 이벤트나 모달 열기 같은 동작이 전혀 처리되지 않습니다.

즉, 화면이 “먹통”처럼 멈춘 것처럼 보이게 되죠.

 

🔹 비유하자면, 바리스타가 너무 바빠서 손님 주문이 줄줄이 쌓여 있는 상황과 같습니다.

 

이럴 때 필요한 게 비동기 처리!

비동기 처리를 활용하면 동시에 여러 일을 효율적으로 처리할 수 있습니다.

  • 비동기 처리 종류 : Web API(setTimeout, fetch), DB 요청, 파일 읽기 등..
  • (다만, 이 글에서는 Web API를 이용한 비동기 처리만 다루겠습니다!)

 

더보기

😎 (번외) Web API 목록 - 만능 알바생의 능력치

setTimeout, DOM 이벤트, fetch… 등

  • setTimeout → “3초 뒤에 실행해 줘”라고 알바생(Web API)에게 맡김
  • fetch → 서버에 요청 보내고, 응답 오면 알려줌
  • addEventListener → 클릭이 발생할 때까지 계속 기다려줌

이렇게 하면 ‘메인 스레드(JS 엔진)’는 다른 일을 처리하면서도, 나중에 일이 끝났을 때 다시 실행할 수 있습니다.

 

Web API를 주방 알바생으로 비유해 비동기 처리를 설명한 그림
Web API 이미지 비유설명

 

👨🏻‍🍳 비유설명

바리스타는 한번에 하나의 일만 처리가 가능하기에 주방 알바생(Web API)에게 도움을 요청합니다.

  • “2분 뒤에 샌드위치 만들어줘(setTimeout)“라고 부탁 후 바리스타는 다른 주문을 처리할 수 있어요.
  • 주방 알바생이 2분 후 시간이 되면 “다 됐습니다!” 하고 픽업대에 올려놓습니다.
    • 🧑🏻‍🍳 알바생 : “다 만들었고 픽업대(task Queue)에 올려놓을테니 나머지 작업(콜백함수)은 대신해주세요!"

 

📨 태스크 큐(Task Queue) 

1. Web API가 작업을 마무리 후

2. 그 속에 있는 콜백 함수(남은 작업)를 태스크 큐에 올려 차례대로 올려놓습니다.

3. 콜백 함수들은 올려진 순서대로 실행될 차례를 기다리며 대기합니다.

  • 대기하는 이유? => 콜스택이 비워지기 전에는 실행이 불가하기 때문.
  • 큐는 먼저 들어온 요소가 먼저 나가는(FIFO) 구조입니다.

 

Task Queue를 픽업대로 비유해 콜백 함수가 대기하는 구조 설명
Task Queue 이미지 설명

 

👨🏻‍🍳 비유설명

  • 알바생(Web API)이 완료 후 남은 작업(콜백함수)들을 ‘픽업대(Task Queue)’에 올려놓았네요.
  • 남은 작업(콜백 함수)은 바로 바리스타에게 가지 않고 '픽업대(Task Queue)'에 줄 서서 대기합니다.
  • 왜냐고요? 아직도 바리스타(Call Stack)의 일이 끝나지 않았기 때문이죠!
    • (??? : 바리스타 아직까지도 아메리카노 만들고 있네)
    • (??? : 우리 팀 바리스타 뭐 함?)

 


 

👴🏽 이벤트 루프(Event Loop) - 악덕 매니저 등장 (바리스타 퇴사각)

이벤트 루프(Event Loop)는 2가지 역할을 가지고 있습니다.

  1. 콜스택이 비어 있는지 확인
  2. 비어 있으면 태스크 큐(Task Queue)에서 대기 중인 콜백 함수를 하나씩 콜스택(Call Stack)에 전달

중요한 점은 항시적으로 확인하는 것이 아닙니다!

  1. 처리해야 할 태스크가 있는 경우: 먼저 들어온 태스크부터 순차적으로 처리
  2. 처리해야 할 태스크가 없는 경우: 잠들어 있다가 새로운 태스크가 추가되면 다시 '1'로 돌아감

 

 

이벤트 루프를 카페 매니저로 표현해 태스크 큐와 콜스택을 연결하는 그림
Event Loop 이미지 비유설명

 

 

👨🏻‍🍳 비유설명

  • 매니저(Event Loop)는 평소에는 꾸벅꾸벅 졸고 있습니다.😪
  • 바리스타(Call Stack)가  모든 일을 마무리하고 손이 비면, 얼른 픽업대(Task Queue)에 있는 샌드위치(콜백 함수) 1개를 가져와 바리스타에게 전달합니다.
  • 바리스타는 남은 작업(콜백 함수)인 샌드위치를 데우며(함수 실행) 작업을 계속 진행합니다. 
    • (??? : 어제 그 바리스타분 왜 안 보이시지?)

 

(이제 얼추 개념은 이해했으니 예제 코드를 보면서 확인해 봅시다.)


🧑🏻‍💻 예제 코드 - 자바스크립트 동작 순서(JavaScript event loop video)

예제 코드 확인 후 영상 아래 설명을 참고해 주세요!!

설명 부분을 반복해서 보다 보면 완벽히 이해가 되실 겁니다!

// 함수 선언 영역
function First() { console.log('1') }
function Second() { console.log('2') }
function Third() { console.log('3') }

// 함수 실행 영역
First();
setTimeout(Second, 5000);
Third();

 

 

이해하기 쉽게 비유도 참고해 봅니다!

- 🏚️ 브라우저 = JS 엔진이 있는 곳 = 카페 =  바리스타의 소속

- 🤯 JS 엔진 = 바리스타 = Call Stack 속 작업(함수) 실행자

- 🙋🏻‍♂️ JS 함수 =  손님의 주문 =  First(),  Second(), Third(), console.log()

- 📚 Call Stack 할 일 모음 집  = 함수가 모여져 있는 곳 *원시값 변수도 저장되는 공간이지만 여기선 패스!

- 🧑🏻‍🍳 Web Apis = 알바생(도우미) = 비동기 작업 처리자

- 🗳️ Task Queue(CallBack Queue)  = 픽업대 = 비동기 함수 속 인자(콜백 함수)의 대기 공간 = Call Stack에 전달될 콜백함수

- 🔄 event Loop = 카페 매니저 = Task Queue에 콜백 함수를 Call Stack에 전달 = 바리스타 괴롭히는 사람

 

자바스크립트 이벤트 루프 동작 과정을 시각적으로 보여주는 애니메이션
자바스크립트 이벤트 루프 예시 비디오

1.  First() 실행

  1. First() 호출 → 콜스택에 First() push  콜스택에서 First() 실행
  2. console.log('1') 호출 → 콜스택에 console.log push console.log('1') 실행 → 콘솔에 1 출력
  3. console.log 실행 완료 → 콜스택에서 console.log pop
  4. First() 실행 완료 → 콜스택에서 First() pop

 

2.  setTimeout(Second, 5000) 실행

  1. setTimeout(Second, 5000) 호출 → 콜스택에 setTimeout push
  2. 콜스택에서 setTimeout 실행 → 브라우저(Web API)에 “5초 뒤에 Second 실행” 요청
  3. Web API는 타이머를 설정하고, setTimeout 실행 완료 → 콜스택에서 setTimeout pop
  4. Web API 영역에서 5초 카운트다운 시작

 

3.  Third() 실행

  1. Third() 호출 → 콜스택에 Third() push → 콜스택에서 Third() 실행
  2. console.log('3') 호출 → 콜스택에 console.log push   console.log('3') 실행 → 콘솔에 3 출력
  3. console.log 실행 완료 → 콜스택에서 console.log pop
  4. Third() 실행 완료 → 콜스택에서 Third() pop

 

4.  5초 후 (Web API → Task Queue 이동)

  1. Web API의 타이머 종료 → Second 함수(콜백)를 태스크 큐(Task Queue)에 넣음
  2. 콜스택이 비어 있을 때까지 대기

 

5.  이벤트 루프(Event Loop), Second() 실행

  1. 이벤트 루프 콜스택 비어 있음 확인
  2. 태스크 큐에서 Second 함수 꺼내 → 콜스택에 push  → 콜스택에서 Second() 실행
  3. 내부 console.log('2') 호출 → 콜스택에 console.log push  → console.log('2') 실행 → 콘솔에 2 출력
  4. console.log 실행 완료 → 콜스택에서 console.log pop
  5. Second() 실행 완료 → 콜스택에서 Second() pop

 


 

 

👁️ 개발 시 고려할 점 

🚫 콜스택(Call Stack)을 바쁘게 하지 말자 

  • 콜스택에서 오래 걸리는 작업(예: 무한 루프, 복잡한 계산, 대량의 DOM 조작 등)이 쌓이면 → 다른 이벤트가 처리되지 못합니다.

👉 긴 작업은 잘게 나누어 처리하거나(setTimeout, requestIdleCallback, Web Worker 사용), 메인 스레드를 막지 않도록 분리해 보세요!

 

🚫 태스크 큐(Task Queue)를 바쁘게 하지 말자 

  • 태스크 큐는 완료된 비동기 작업(콜백 함수)이 줄 서는 공간입니다.
  • 너무 많은 setTimeout, setInterval, 이벤트 핸들러가 동시에 쌓이면 큐가 과부하될 수 있습니다.
  • 그러면 실행이 지연되고, UI 반응이 늦어져서 사용자가 느끼기에 '버벅거린다'는 느낌을 줍니다.

👉 불필요한 이벤트 리스너 남발을 피하고, 이벤트 최적화(debounce, throttle) 기법을 활용해 보세요!

 


👋🏻 마무리 

잘 이해가 되셨나요..? 워낙 방대한 개념이라 Micro task Queue, Promise 등은 다루지 않았고

일부 개념들은 간단하게 설명하고 넘어간 부분들이 있기에 걱정은 되지만 이 글을 통해서 JS 동작 흐름을 잡는데 도움이 되었으면 좋겠네요!